I have a problem regarding joining tables with group_concat. Here are the details.
table_orders:
item_cd order_id descs quantity status seq_no
1 100 coca-cola 2 A 232
2 100 pizza 1 A 233
3 101 cheeseburger 5 A 234
4 102 pepsi 4 A 235
4
table_instructions:
item_cd instruction
3 more cheese
3 less vegetable
cancelled_item_table:
quantity seq_no
1 234
1 234
1 235
Now what I want to achieve is like this:
item_cd descs quantity instructions cancelled_item
1 coca-cola 2 - -
2 pizza 1 - -
3 cheeseburger 2 more cheese, less vegetable 1,1
4 pepsi 4 - 1
This is my current query:
SELECT
ord.item_cd,
ord.order_id,
ord.descs,
ord.quantity,
GROUP_CONCAT(x.quantity) as cancelled,
GROUP_CONCAT(i.instruction) as instruct
FROM table_orders ord
LEFT JOIN cancelled_item_table x ON ord.seq_no = x.seq_no
LEFT JOIN table_instructions i ON ord.item_cd = i.item_cd
WHERE ord.status = 'A'
GROUP BY ord.order_id
and here is the output:
item_cd descs quantity instructions cancelled_item
1 coca-cola 2 - 1
2 pizza 1 - 1
3 cheeseburger 2 more cheese, more cheese,
less vegetable, less vegetable 1,1,1,1
4 pepsi 4 - 1
If you notice, cheeseburger has 2 cancelled item and 2 instruction, but the output is 4, looks like it's multiplying.
Since the join with cancelled_item_table multiplies rows, you have to join to an already grouped subquery, like this:
SELECT
ord.item_cd,
ord.order_id,
ord.descs,
ord.quantity - coalesce(x.tot,0) as quantity,
GROUP_CONCAT(i.instruction) as instruct,
x.cancelled
FROM
table_orders ord LEFT JOIN table_instructions i
ON ord.item_cd = i.item_cd LEFT JOIN
(select seq_no, count(*) as tot, GROUP_CONCAT(quantity) as cancelled
from cancelled_item_table
group by seq_no) x ON ord.seq_no = x.seq_no
WHERE ord.status = 'A'
GROUP BY ord.item_cd, ord.order_id, ord.descs, quantity
Related
I have the following 3 tables and i would like to know the correct sql for the expected result as below.
my sql here is not working;
select h.pid,
h.name,
sum(r.amount1) as total1,
sum(r.amount2) as total2,
count(g.pid) as times,
sum(g.take) as totaltaken
from history h
left join rpt_revenue r on h.pid=r.pid
left join guest g on g.pid=r.pid
group by h.pid, h.name;
history
pid name
1 peter
2 may
rpt_revenue
id pid amount1 amount2
1 1 10.00 11.00
2 2 20.00 20.00
3 1 2.00 2.00
4 2 2.00 2.00
guest
gid pid id take
1 1 1 2
2 1 3 2
3 2 2 3
expected result
pid total1 total2 times totaltaken
1 12.00 13.00 2 4
2 22.00 22.00 1 3
So to be able to use aggregate function over join, you should first aggregate your data in a join subquery and then aggregate all of them at the top level
here some examples of aggregation
I have a purchase table like below
customerID orderID item category purDate sold price
1 001 book stationary 1/1/2018 1 5
2 002 ball toys 1/1/2018 2 2
3 003 shirt cloth 1/1/2018 1 10
1 004 pen stationary 1/1/2018 5 3
4 005 shirt cloth 1/1/2018 2 10
5 006 card stationary 1/2/2018 15 2
6 007 tshirt cloth 1/2/2018 3 7
2 008 book stationary 1/3/2018 6 5
3 009 car toys 1/3/2018 2 4
1 010 book stationary 1/3/2018 4 5
4 011 ball toys 1/4/2018 2 2
6 012 pen stationary 1/4/2018 2 3
7 013 notebk stationary 1/4/2018 2 3
and I would like to query the best selling item for each day and category using MySQL so the query result would look like
purDate category item total revenue
1/1/2018 stationary pen 5 15
1/1/2018 toys ball 2 4
1/1/2018 cloth shirt 3 30
1/2/2018 stationary card 15 30
1/2/2018 cloth tshirt 3 21
1/3/2018 stationary book 10 50
1/3/2018 toys car 2 8
1/4/2018 toys ball 2 4
1/4/2018 stationary pen 2 6
1/4/2018 stationary notebk 2 6
I've checked other related topics on stackoverflow but couldn't find what I'm looking for. I am trying to use window function with aggregate functions, but keep failing.
SELECT purDate, category, item, total, revenue
FROM (SELECT purDate, category, item, SUM(sold) AS total, sold * price AS revenue,
RANK() OVER(PARTITION BY purDate, category ORDER BY SUM(sold) DESC) AS rank
GROUP BY 1,2,3) q
WHERE q.rank = 1
Thank you for your help in advance.
You miss a FROM in your inner query. And you missed to also group by price and to build the revenue as sum(sold) * price. Other than that you were absolutely on the right path.
SELECT purdate,
category,
item,
total,
revenue
FROM (SELECT purdate,
category,
item,
sum(sold) total,
sum(sold) * price revenue,
rank() OVER (PARTITION BY purdate
ORDER BY sum(sold) DESC) r
FROM purchase
GROUP BY purdate,
category,
item,
price) x
WHERE r = 1
ORDER BY purdate;
db<>fiddle
Dont be afraid to break what you are trying to accomplish into multiple steps instead of one gigantic query.
Create table bleach select
pur_date, max(sold) as max_sold,
'' as total_rev
from your_table group by
pur_date;
Alter table bleach add
index(pur_date);
Alter table bleach add index
(max_sold);
Select a.* from your_table a,
bleach b where
a.pur_date=b.pur_date and
a.max_sold=b.max_sold;
Update bleach set
total_rev=sold*price
Drop table bleach;
I want for each unique pur_dates, how many items from the product_sold field was equal to items in productfield WHERE chk_date was within 31 days of the pur_date DIVIDE by total number of product_sold in that 31 days.
The 2 big conditions are that items in product_sold is equal to item in product and for the match to be valid, the date range from table2 must be within a month of table1.
The product field is unique while the product_sold field can have repeating products. The pur_date field only varies on year and month.
Table1
pur_date product
2015-07-01 shirt
2015-06-01 shoe
2015-04-01 purse
2015-04-01 bag
2014-05-01 key
2015-05-01 gloves
Table2
chk_date cost product_sold
2015-07-29 9 bag
2015-07-15 10 shoe
2015-06-30 8 shirt
2014-06-25 6 bag
2015-06-01 9 shirt
2015-05-28 8 shoe
2015-05-15 4 key
2015-04-28 5 shirt
2015-03-15 6 purse
2015-03-15 4 ring
2015-03-10 4 key
2015-03-01 2 bag
I have a non-working subquery to do something like this:
Select pur_date,
(Select SUM(CASE WHEN product_sold IN (select product from table1) THEN 1 ELSE 0 END)/COUNT(product_sold)
from table2
where (pur_date - chk_date) <=31) AS percent_sold
from (select distinct pur_date from table1) t;
Error i got was: Illegal expression in WHEN clause of CASE
Example:
output_table
pur_date num_product_match
2015-07-01 2/3 <--for the 3 product_sold in June,2 items(shirt) match product = shirt
2015-06-01 1/2 <-- for the 2 product_sold in May,1 item(shoe) match product = shoe
2015-05-01 1/1 <-- for th 1 product_sold in April, 1 item(shirt) match product = shirt
2015-04-01 2/4 <-- for the 4 product sold in March, 2 item(purse, bag) match product = purse and product = bag
SELECT pd.pur_date,
SUM(CASE WHEN t1.product IS NOT NULL THEN 1 ELSE 0 END) /
CAST(COUNT(t2.product_sold) as float) As num_product_match
FROM (SELECT DISTINCT pur_date FROM Table_1) pd
INNER JOIN Table_2 t2 ON t2.chk_date < pd.pur_date
AND t2.chk_date >= DATE_ADD(pd.pur_date, INTERVAL -1 MONTH)
LEFT JOIN Table_1 t1 ON t1.pur_date > t2.chk_date
AND t1.pur_date <= DATE_ADD(t2.chk_date, INTERVAL 1 MONTH)
AND t1.product = t2.product_sold
GROUP BY pd.pur_date
If I have a MySQL table looking something like this:
breeds
id name
-------------------------------
1 Labrador
2 Jack Russel Terrier
3 Shetland Sheepdog
And a MySQL table looking like this:
dogs
id owner breed sex
-----------------------------------
1 Sara 1 f
2 Kent 1 f
3 Billy 1 m
4 Joe 2 f
5 Billy 2 m
Is it possible to run a MySQL query to get output like this:
id name females males
------------------------------------------------
1 Labrador 2 1
2 Jack Russel Terrier 1 1
3 Shetland Sheepdog 0 0
I would like to have a JOIN or similar that count the number of females/males from the dogs table.
You can do this:
SELECT b.id,b.name,
IFNULL(SUM(CASE WHEN sex='f' THEN 1 ELSE 0 END),0) as females,
IFNULL(SUM(CASE WHEN sex='m' THEN 1 ELSE 0 END),0) as males
FROM breeds b LEFT JOIN
dogs d on b.id=d.breed
GROUP BY b.id,b.name
Explanation:
using LEFT JOIN will include the record eventhough there is male/female count. IFNULL will replace the null value with 0.
Result:
id name females males
-------------------------------------
1 Labrador 2 1
2 Jack Russel Terrier 1 1
3 Shetland Sheepdog 0 0
Sample result in SQL Fiddle.
Or alternatively:
SELECT id, name,
(SELECT COUNT(*) FROM dogs WHERE breed=b.id AND sex='f') females,
(SELECT COUNT(*) FROM dogs WHERE breed=b.id AND sex='m') males
FROM breeds b
see here: http://www.sqlfiddle.com/#!9/03da0/1
I have three tables:
Students
-------------------------------------------------------------
studentId first last gender weight
-------------------------------------------------------------
1 John Doe m 185
2 John Doe2 m 130
3 John Doe3 m 250
Lifts
-------------------
liftId name
-------------------
1 Bench Press
2 Power Clean
3 Parallel Squat
4 Deadlift
5 Shoulder Press
StudentLifts
------------------------------------------------
studentLiftId studentId liftId weight
------------------------------------------------
1 1 1 185
2 2 3 130
3 3 1 190
4 1 2 120
5 2 1 155
6 3 2 145
7 1 1 135
8 1 1 205
9 2 3 200
10 1 3 150
11 2 2 110
12 3 3 250
I would like to have four top lists:
Bench Press
Parallel Squat
Power Clean
Total of the above 3
I can successfully grab a top list for each specific lift using the following query:
SELECT s.studentId, s.first, s.last, s.gender, s.weight, l.name, sl.weight
FROM Students s
LEFT JOIN (
SELECT *
FROM StudentLifts
ORDER BY weight DESC
) sl ON sl.studentId = s.studentId
LEFT JOIN Lifts l ON l.liftId = sl.liftId
WHERE l.name = 'Bench Press'
AND s.gender = 'm'
AND s.weight > 170
GROUP BY s.studentId
ORDER BY sl.weight DESC
However, I am stuck on how to add the highest total of each lift for each student. How can I first find the highest total for each student in each lift, and then add them up to get a total of all three lifts?
Edit
The result set that I am looking for would be something like:
-------------------------------------------------
studentId first last weight
-------------------------------------------------
3 John Doe3 585
1 John Doe 475
2 John Doe2 465
I also forgot to mention that I would actually like two lists, one for students above 170 and one for students below 170.
SELECT -- join student a total weight to the student table
A.studentId,
A.first,
A.last,
C.totalWeight
FROM
Student A,
(
SELECT -- for each studet add the max weights
sum(B.maxWeight) as totalWeight,
B.studentID
FROM (
SELECT -- for each (student,lift) select the max weight
max(weight) as maxWeight,
studentId,
liftID
FROM
StudentLifts
GROUP BY
studentId,
liftID
) B
GROUP BY
studentId
) C
WHERE
A.studentID = C.studentId
-- AND A.weight >= 170
-- AND A.weight < 170
-- pick one here to generate on of the two lists.