getting distinct result with few more conditions in mysql [duplicate] - php

This question already has answers here:
How can I return pivot table output in MySQL?
(10 answers)
Closed 4 years ago.
Hai i have some sample data
bookId | bookPnr | bookDate | bookFullName | bookMobile | bookEmail | bookSource
9876543210 | BPT1100000000 | 2018-11-18 | User 1 | 9876543210 | test#gmail.com | Redbus
9876543211 | BPT1100000001 | 2017-11-18 | User 2 | 9876543211 | testOne#gmail.com | Redbus
9876543212 | BPT1100000002 | 2017-11-18 | User 3 | 9876543214 | testtwo#gmail.com | TicketGoose
I need a result like
Mobile | 2018 | 2017 | 2016 | Redbus | TicketGoose | total
9876543210 | 2 | 3 | 6 | 2 | 2 | 11
9876543211 | 1 | 1 | 1 | 2 | 1 | 3
So i need distinct mobile numbers based on year and source
I did query something like,
SELECT count(bookId), bookMobile, bookDate, bookSource FROM `booking_info`
GROUP by bookMobile, MONTH(bookDate), bookSource ORDER BY bookMobile DESC
Is it possible to do it with single query or we have to use PHP Any suggetions will be really appreciated.

You can use "conditional aggregates" to "pivot" your data. Basically this means placing a case expression inside an aggregation function. Here I have used COUNT():
SELECT
bookMobile
, count(case when year(bookDate) = 2016 then 1 end) as `2016`
, count(case when year(bookDate) = 2017 then 1 end) as `2017`
, count(case when year(bookDate) = 2018 then 1 end) as `2018`
, count(case when bookSource = 'Redbus' then 1 end) as Redbus
, count(case when bookSource = 'TicketGoose' then 1 end) as TicketGoose
FROM booking_info
GROUP BY
bookMobile
ORDER BY
bookMobile DESC

Related

Extracting best 8 done subjects from many done subjects using SQL and PHP

Am developing a school report card system .
The student can done 14 subjects but on grading, the system should pick on the best 8 done subject and SUM the points of the 8 subject . The system use this SUM to grade the student.
I tried to use ORDER BY point ASC and LIMIT 8 , this display the best 8 done subject but when it comes to SUM it SUM the points of all subjects done by the student.
What could be the problem?
SELECT stid, subid, total, point FROM oresults WHERE stid= '2' ORDER BY point ASC;
+-------+-------+-------+
| subid | total | point |
+-------+-------+-------+
| 3 | 78.5 | 1 |
| 10 | 71.5 | 2 |
| 12 | 65 | 4 |
| 9 | 61 | 4 |
| 4 | 62.5 | 4 |
| 5 | 56.5 | 5 |
| 8 | 55 | 6 |
| 14 | 52 | 6 |
| 6 | 50.5 | 6 |
| 1 | 45 | 8 |
| 11 | 31.5 | 9 |
| 13 | 39.5 | 9 |
| 7 | 37.5 | 9 |
| 2 | 28.5 | 9 |
+-------+-------+-------+
SELECT stid, subid, total, point FROM oresults WHERE stid= '2' ORDER BY point ASC LIMIT 8;
+-------+-------+-------+
| subid | total | point |
+-------+-------+-------+
| 3 | 78.5 | 1 |
| 10 | 71.5 | 2 |
| 12 | 65 | 4 |
| 9 | 61 | 4 |
| 4 | 62.5 | 4 |
| 5 | 56.5 | 5 |
| 8 | 55 | 6 |
| 14 | 52 | 6 |
+-------+-------+-------+
SELECT sum(point) FROM oresults WHERE stid= '2' ORDER BY point ASC LIMIT 8;
+------------+
| SUM(point) |
+------------+
| 82 |
+------------+
List item
You seem to misunderstand how 'LIMIT' works, 'LIMIT' is applied at the end, you are not limiting the number of results in the group, you are limiting the number of students to return. Using that you can build you query like this however:
SELECT SUM(point) FROM
(SELECT point FROM oresults WHERE stid= '2' ORDER BY point ASC LIMIT 8) ss
SQLFiddle
There are more elegant solutions using windowing functions that don't require you to only look at one student at a time. These may or may not work, depending on your MySQL version, they will work on newer versions of MySQL, but not older versions, which don't support ROW_NUMBER() or OVER().
Select stid, subid, total, point, SUM(point) OVER(partition by stid) as pointSum
FROM (SELECT *, ROW_NUMBER() OVER(PARTITION BY stid ORDER BY point DESC) rowNum FROM report_card) ss
WHERE rowNum<=8
This will give you the top 8 rows, with an extra column, pointSum which is the sum of the points in the top 8 rows by points, for each stid.
SQLFiddle
If you just one one row per stid, with just the ID and total you can do this instead
SELECT stid, SUM(point) as pointSum
FROM (SELECT *, ROW_NUMBER() OVER(PARTITION BY stid ORDER BY point DESC) rowNum FROM report_card) ss
WHERE rowNum<=8
GROUP BY stid
SQLFiddle

summing 2 columns data in single table with different condition

I have 2 tables jornal_main and journal_item , my journal_main table has got
id | po_no | period | post_date | vendor
1 | PO123 | 12 | 2018-02-12 | XYZ
2 | PO234 | 12 | 2018-02-13 | ABC
journal_item is
id | ref_id | type | sku | qty | desc
1 | 1 | poo | A123 | 12 | Order
2 | 1 | poo | B234 | 20 | Order
3 | 2 | por | A123 | 2 | Receive
4 | 2 | por | A123 | 3 | Receive
5 | 2 | por | B234 | 6 | Receive
Desired output is
po_no | date | vendor | item | ordered_qty | received_qty | balance
po123 | 2018-02-12 | XYZ | A123 | 12 | 5 | 7
po123 | 2018-02-12 | ABC | B234 | 20 | 6 | 14
i am not getting how to combine 2 queries in a single query. Here i have 2 queries which gives me ordered_qty and received_qty
for ordered_qty
select journal_main.id, journal_main.po_no, journal_main.post_date, journal_main.vendor, journal_item. sku, SUM(journal_item.qty) AS Oqty FROM journal_main INNER JOIN journal_item ON journal_main.id=journal_item.ref_id WHERE journal_item.type='poo' GROUP BY journal_item.sku, journal_main.id
for received_qty
select journal_main.id, journal_main.po_no, journal_main.post_date, journal_main.vendor, journal_item. sku, SUM(journal_item.qty) AS Rqty FROM journal_main INNER JOIN journal_item ON journal_main.id=journal_item.ref_id WHERE journal_item.type='por' GROUP BY journal_item.sku, journal_main.id
Just use conditional aggregation:
select ji.ref_id,
sum(case when ji.desc = 'Order' then qty else 0 end) as ordered,
sum(case when ji.desc = 'Receive' then qty else 0 end) as received,
sum(case when ji.desc = 'Order' then qty else - qty end) as total
from journal_item ji
where ji.desc in ('Order', 'Receive')
group by ji.ref_id;
This just provides the aggregation columns. You should be able to join in the rest of the columns that you want.

Use of MIN() to select just one record per code (MySQL) [duplicate]

This question already has answers here:
SQL Group By and min (MySQL)
(3 answers)
Closed 6 years ago.
[Foreword for the compulsives -1] I know this question has been answered, at least, a billion times, but the problem is that I can't model those answers to what I wanna obtain. I'm not an SQL expert, that's sure; I'm confident just with the classical commands like SELECT, UPDATE, DELETE, ecc. so I'm gonna thank anyone who will like to help me.
Said that, let's suppose I have a table like this one:
|----|--------|------------|----|----------|---------|---------|------|
| id | code | category | mq | weight | weave | price | show |
|----|--------|------------|----|----------|---------|---------|------|
| 1 | DT450R | carbon | 1 | 450 | plain | 90 | 1 |
| 2 | DT450R | carbon | 2 | 450 | plain | 40 | 1 |
| 3 | DT450R | carbon | 5 | 450 | plain | 75 | 1 |
| 4 | ZX300R | carbon | 1 | 300 | plain | 12 | 0 |
| 5 | ZX300R | carbon | 15 | 300 | plain | 128 | 1 |
| 6 | ZX300R | carbon | 30 | 300 | plain | 92 | 1 |
| 7 | PP120Q | carbon | 3 | 120 | twill | 28 | 1 |
| 8 | PP120Q | carbon | 7 | 120 | twill | 65 | 1 |
| 9 | PP120Q | carbon | 9 | 120 | twill | 49 | 1 |
What I would like my query to do is to select, for each code, just the row with the minimum price:
| 2 | DT450R | carbon | 2 | 450 | plain | 40 | 1 |
| 4 | ZX300R | carbon | 1 | 300 | plain | 12 | 0 |
| 7 | PP120Q | carbon | 3 | 120 | twill | 28 | 1 |
First attempt (based on the explanation of MIN() given in MySQL documentation or, at least, on what I understood of it):
$sql = 'SELECT code, weight, weave, MIN(price)
FROM products
WHERE category="carbon" AND show="1"
GROUP BY code
ORDER BY weight ASC';
Second attempt (based on this answer here on SO):
$sql = 'SELECT a.code, a.weight, a.price, a.weave
FROM products a
INNER JOIN
(
SELECT code, weight, MIN(price) AS minprice, weave
FROM products
GROUP BY code
)
b ON a.code = b.code AND a.weave = b.weave AND a.price = b.minprice AND AND a.weight = b.weight
WHERE category="carbon" AND show="1"
ORDER BY a.weight ASC';
Third attempt (based on this other answer here on SO):
$sql = 'SELECT code, weight, weave, price
FROM products
INNER JOIN
(
SELECT MIN(price) price, code, weight, weave
FROM products
GROUP BY code
)
AS MIN ON MIN.code = products.code AND MIN.weight = products.weight AND MIN.weave = products.weave
WHERE category="carbon" AND show="1"
ORDER BY a.weight ASC';
It's probably useless say that none of these attempts produced the expected result; just the third method outputs something while the others two return 0 matches. I understood that in the 2nd and 3rd methods I'm nesting a query into a query but I can't figure out why they don't work.
You're close with your second attempt. But the only columns you should be joining on are code and price. weight and weave then come from the row that's selected by this join condition.
SELECT a.code, a.weight, a.price, a.weave
FROM products a
INNER JOIN
(
SELECT code, MIN(price) AS minprice
FROM products
GROUP BY code
)
b ON a.code = b.code AND a.price = b.minprice
WHERE category="carbon" AND show="1"
ORDER BY a.weight ASC
This is the same as the answers in the questions you linked to. None of them suggest adding other columns to the ON clause, so I'm not sure where that came from.

How to implode the value of a record to multiple rows using MySQL?

I have a table like this:
// mytable
+----+--------+-------------------+
| id | word | numbers |
+----+--------+-------------------+
| 1 | hello | 1<br>2<br>3<br> |
| 2 | how | 12<br>15<br> |
| 3 | are | 453<br>1<br> |
| 4 | you | 3<br>33<br>453<br>|
+----+--------+-------------------+
And I want this output:
// mynewtable
+----+--------+---------+
| id | word | numbers |
+----+--------+---------+
| 1 | hello | 1 |
| 2 | hello | 2 |
| 3 | hello | 3 |
| 4 | how | 12 |
| 5 | how | 15 |
| 6 | are | 453 |
| 7 | are | 1 |
| 8 | you | 3 |
| 9 | you | 33 |
| 10 | you | 453 |
+----+--------+---------+
I can do that using PHP. First I have to fetch all rows and implode them by <br> then insert them again. But now I want to know, is there any better solution? (specially using pure MySQL)
Try this solution through query
SELECT #row := #row + 1 as row, t.word, t.number FROM (
SELECT
word,
SUBSTRING_INDEX(SUBSTRING_INDEX(numbers, '<br>', n.digit+1), '<br>', -1) number
FROM split_rec
INNER JOIN
(SELECT 0 digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) n
ON LENGTH(REPLACE(numbers, '<br>' , '')) <= LENGTH(numbers)-n.digit
ORDER BY id, n.digit
) AS t, (SELECT #row := 0) r
WHERE t.number <> ''
Output
row word number
1 hello 1
2 hello 2
3 hello 3
4 how 12
5 how 15
6 are 453
7 are 1
8 you 3
9 you 33
10 you 453
While I think that it might be possible using stored procedures, MySQL variables and a liberal use of LIKE "%...%"statements, it would be an order of magnitude more complex than a PHP solution.
Furthermore, I think it is not possible at all if you don't have access to stored procedures as you would need dynamically create new INSERT statements based on what you find in that last column.
TL;DR: No, make yourself happy and use PHP.

Query union, if search result is less than 10, fill with others from database order by date

I want to make a mysql union search. My purpose is: my total results back must be 10.
if search results are more than 10, the returned data come all from search result.
if search results are less than 10, first few returned data come from search result, and then fetch the remaining results from database order by date.
To make it clearer: if a client searches "today", my database only returns 7 results which contain "today", then add another 3 results from my database ORDER BY date. So that the total results are 10 items.
Another purpose: another 3 results are not repetitions from the 7 results which match the search. I think UNION or UNION DISTINCT can do that job, am I right?
So, how do I do a query like this?
PS: my code will fix the result order, but I need first select is always behind the second select
(SELECT * FROM table WHERE title like %$searchword% limit 0,10 ORDER BY date)
UNION
(SELECT * FROM table limit 0,10 ORDER BY date)
limit 0,10 ORDER BY date
If you always want 10 results:
SELECT
IF(m.id,1,0) AS has_match,
t.*
FROM
`table` t
LEFT JOIN `table` m ON m.id = t.id AND m.title LIKE '%$searchword%'
GROUP BY t.id
ORDER BY has_match DESC, date
LIMIT 10
Tested:
mysql> select * from `table`;
+----+------------------------+---------------------+
| id | title | date |
+----+------------------------+---------------------+
| 1 | test 1 | 2011-11-06 10:27:08 |
| 2 | test 2 match | 2011-11-06 10:27:14 |
| 3 | 3 match this too | 2011-11-06 10:27:23 |
| 4 | title does NOT | 2011-11-06 10:27:44 |
| 5 | Another matching title | 2011-11-06 10:27:55 |
| 6 | this does not either | 2011-11-06 10:29:22 |
| 7 | Do not put this first | 2011-11-06 10:29:37 |
| 8 | Is this number 8? | 2011-11-06 10:29:57 |
| 9 | The 9th is a match | 2011-11-06 10:30:07 |
| 10 | 10th does not | 2011-11-06 10:30:20 |
| 11 | 11th IS a match too! | 2011-11-06 10:30:37 |
| 12 | 12th gets ignored? | 2011-11-06 10:30:49 |
+----+------------------------+---------------------+
12 rows in set (0.00 sec)
mysql> SELECT IF(m.id,1,0) AS has_match, t.* FROM `table` t LEFT JOIN `table` m ON m.id = t.id AND m.title LIKE '%match%' GROUP BY t.id ORDER BY has_match DESC, date LIMIT 10;
+-----------+----+------------------------+---------------------+
| has_match | id | title | date |
+-----------+----+------------------------+---------------------+
| 1 | 2 | test 2 match | 2011-11-06 10:27:14 |
| 1 | 3 | 3 match this too | 2011-11-06 10:27:23 |
| 1 | 5 | Another matching title | 2011-11-06 10:27:55 |
| 1 | 9 | The 9th is a match | 2011-11-06 10:30:07 |
| 1 | 11 | 11th IS a match too! | 2011-11-06 10:30:37 |
| 0 | 1 | test 1 | 2011-11-06 10:27:08 |
| 0 | 4 | title does NOT | 2011-11-06 10:27:44 |
| 0 | 6 | this does not either | 2011-11-06 10:29:22 |
| 0 | 7 | Do not put this first | 2011-11-06 10:29:37 |
| 0 | 8 | Is this number 8? | 2011-11-06 10:29:57 |
+-----------+----+------------------------+---------------------+
10 rows in set (0.00 sec)

Categories