MySQL query to show combinations of output - mysql

I have to write a mysql query to find total sales on particular days. I am able to find it, but unable to show combined results.
The table looks like:
Date, Order_id, Product_id, Quantity
'01-JUL-11',O1,P1,5
'01-JUL-11',O2,P2,2
'01-JUL-11',O3,P3,10
'01-JUL-11',O4,P1,10
'02-JUL-11',O5,P3,5
'02-JUL-11',O6,P4,6
'02-JUL-11',O7,P1,2
'02-JUL-11',O8,P5,1
'02-JUL-11',O9,P6,2
'02-JUL-11',O10,P2,4
Desired Output:
Product_id Total Sales on Day '02-JUL-11' Total Sales on Day '02-JUL-11'
P1 15 2
P2 2 4
P3 10 5
P4 0 6
P5 0 1
P6 0 2
What I have tried is:
Select distinct product_id
from orders;
Output:
P1
P2
P3
P4
P5
P6
Select product_id, sum(quantity) from orders
where order_day = '11-07-01'
group by product_id
OUTPUT:
P1 15
P2 2
P3 10
Select product_id, sum(quantity) from orders
where order_day = '11-07-02'
group by product_id
OUTPUT:
P1 2
P2 4
P3 5
P4 6
P5 1
P6 2
This gives me the desired result but I want to somehow combine the columns.
Query that worked for me: (However puts null for 0)
Select X.product_id, X.s, Y.t from
(SELECT A.product_id as product_id, B.s as s FROM
(Select distinct product_id
from orders) A
LEFT JOIN
(Select product_id, sum(quantity) as s from orders
where order_day = '11-07-01'
group by product_id) B
ON A.product_id = B.product_id) X
Left join
(Select product_id, sum(quantity) as t from orders
where order_day = '11-07-02'
group by product_id) Y
on X.product_id = Y.product_id;

If you need only two days summarized in your report, then a simple (non dynamic) pivot query should do the trick:
SELECT product_id,
SUM(CASE WHEN order_day = '11-07-01' THEN quantity ELSE 0 END) AS `Total Sales on Day '01-JUL-11'`,
SUM(CASE WHEN order_day = '11-07-02' THEN quantity ELSE 0 END) AS `Total Sales on Day '02-JUL-11'`
FROM orders
WHERE order_day = '11-07-01' OR order_day = '11-07-02'
GROUP BY product_id

Conditional aggregation is the way to go. But you should learn to use proper date formats. The ISO/ANSI standard is YYYY-MM-DD or YYYYMMDD:
Select product_id,
sum(case when order_day = '2011-07-01' then quantity else 0 end) as q_20110701,
sum(case when order_day = '2011-07-02' then quantity else 0 end) as q_20110702
from orders o
where order_day in ('2011-07-01', '2011-07-02')
group by product_id;
An alternative, if you can live with separate rows for each day, is:
Select product_id, order_day, sum(quantity)
from orders
where order_day in ('2011-07-01', '2011-07-02')
group by product_id, order_day,

Related

MySQL combine results of two unrelated tables

I'm trying to merge two query results in to one:
Query 1 and the reulsts:
SELECT MONTHNAME(tblFeesPaid.Pay_Date) AS 'Month',
SUM(tblFeesPaid.Fees_Paid) As 'Total Fees'
FROM tblFeesPaid
INNER JOIN tblFeesStructure ON tblFeesPaid.FID=tblFeesStructure.ID
WHERE Year(tblFeesPaid.Pay_Date)=2022
GROUP BY month(tblFeesPaid.Pay_Date);
Results
Month Total Fees
January 162000.00
February 69000.00
March 146926.00
Query 2 and results
SELECT MONTHNAME(tblTransFeesPaid.Pay_Date) AS 'Month',
SUM(tblTransFeesPaid.TransFee_Paid) As 'Transport Fees'
FROM tblTransFeesPaid
INNER JOIN tbltransfeesstructure ON tblTransFeesPaid.TransFID=tbltransfeesstructure.ID
WHERE Year(tblTransFeesPaid.Pay_Date)=2022
GROUP BY month(tblTransFeesPaid.Pay_Date);
Results
Month Transport Fees
March 7000.00
Could someone help me with the correct syntax that I'll achieve results as follows:
Expected results:
Month Total Fees Transport Fees
January 162000.00
February 69000.00
March 146926.00 7000.00
SELECT Z.Month, sum(Z.TotalFees) As 'Total Fees', sum(Z.TransportFees) As 'Transport Fees'
FROM
(
SELECT MONTHNAME(tblFeesPaid.Pay_Date) AS 'Month',
SUM(tblFeesPaid.Fees_Paid) As 'TotalFees',
0 As 'TransportFees'
FROM tblFeesPaid
INNER JOIN tblFeesStructure ON tblFeesPaid.FID=tblFeesStructure.ID
WHERE Year(tblFeesPaid.Pay_Date)=2022
GROUP BY month(tblFeesPaid.Pay_Date)
UNION
SELECT MONTHNAME(tblTransFeesPaid.Pay_Date) AS 'Month',
0 As 'TotalFees',
SUM(tblTransFeesPaid.TransFee_Paid) As 'TransportFees'
FROM tblTransFeesPaid
INNER JOIN tbltransfeesstructure ON tblTransFeesPaid.TransFID=tbltransfeesstructure.ID
WHERE Year(tblTransFeesPaid.Pay_Date)=2022
GROUP BY month(tblTransFeesPaid.Pay_Date)) Z
GROUP BY Z.Month;
First, you make sure both queries return same number of columns (add 0 as Transport Fees to first Query, and add 0 as Total Fees in second query);
Then, you UNION the queries, having one big resultset.
After that it's group by Month, and get MAX(Total Fees) and MAX(Transport Fees).
Would something like that work?
i.e.
SELECT Month, MAX(`Total Fees`), MAX(`Transport Fees`) FROM (
(
SELECT MONTHNAME(tblFeesPaid.Pay_Date) AS 'Month',
SUM(tblFeesPaid.Fees_Paid) As 'Total Fees',
0 AS 'Transport Fees'
FROM tblFeesPaid
INNER JOIN tblFeesStructure ON tblFeesPaid.FID=tblFeesStructure.ID
WHERE Year(tblFeesPaid.Pay_Date)=2022
GROUP BY month(tblFeesPaid.Pay_Date)
) t1
UNION
(
SELECT MONTHNAME(tblTransFeesPaid.Pay_Date) AS 'Month',
0 AS 'Total Fees',
SUM(tblTransFeesPaid.TransFee_Paid) As 'Transport Fees'
FROM tblTransFeesPaid
INNER JOIN tbltransfeesstructure ON tblTransFeesPaid.TransFID=tbltransfeesstructure.ID
WHERE Year(tblTransFeesPaid.Pay_Date)=2022
GROUP BY month(tblTransFeesPaid.Pay_Date)
) t2
) t3
GROUP BY Month;

Join 2 tables and display duplicated row value into new column

I've been spending 2 days working on this and cannot find anything help.
I have 2 tables.
1. players
id
name
1
AA
2
BB
3
CC
4
DD
2. matching
id
player_id
match_id
date
has_opponent
1
1
1
2021-06-23
0
2
2
1
2021-06-23
0
3
3
2
2021-06-21
0
4
4
3
2021-06-22
1
Expected Result
match_id
date
first_opponent_name
second_opponent_name
1
2021-06-23
AA
BB
2
2021-06-21
CC
3
2021-06-22
DD
DD
I want to join these 2 tables with new columns ( first_opponent_name & second_opponent_name )
if it meets condition as below:
If there is a duplicated match_id but it has different player_id, then display their names in 2 separated columns.
If there is only 1 match_id and it has 1 player_id and has_opponent column is 0, then this should display only first_opponent_name
If there is only 1 match_id and it has 1 player_id and has_opponent column is 1, then first_opponent_name and second_opponent _name should be displayed as the same value.
Not tested but I think this will work, basically it joins the table to itself on the different scenarios and then checks which scenario it's matched.
SELECT
m.match_id
,m.date
,p1.name AS first_opponent_name
,CASE WHEN p2.name IS NOT NULL THEN p2.name
WHEN nop2.match_id IS NOT NULL THEN ''
WHEN samep2.match_id IS NOT NULL THEN p1.name END AS second_opponent_name
FROM
matching AS m
INNER JOIN players AS p1
ON m.player_id = p1.player_id
LEFT JOIN matching AS hasp2
ON m.match_id = hasp2.match_id
AND m.player_id <> hasp2.player_id
LEFT JOIN players AS p2
ON hasp2.player_id = p2.player_id
LEFT JOIN (
SELECT
match_id
FROM
matching
WHERE
has_opponent = 0
GROUP BY
match_id
HAVING COUNT(*) = 1
) AS nop2
ON m.match_id = nop2.match_id
LEFT JOIN (
SELECT
match_id
FROM
matching
WHERE
has_opponent = 1
) AS samep2
ON m.match_id = samep2.match_id
You could arguably leave off either the hasp2 or samep2 join and just assume that if the other two scenarios are not met, depends on how well you trust the data to know if that's a viable option.
WITH cte AS ( SELECT DISTINCT
match_id,
`date`,
MIN(player_id) OVER (PARTITION BY match_id) p1,
MAX(player_id) OVER (PARTITION BY match_id) p2,
has_opponent
FROM matching )
SELECT match_id,
`date`,
p1.name first_opponent_name,
CASE WHEN has_opponent OR (p1 != p2) THEN p2.name END second_opponent_name
FROM cte
JOIN players p1 ON cte.p1 = p1.id
JOIN players p2 ON cte.p2 = p2.id
ORDER BY 1
If CTE and window functions are not supported then
SELECT match_id,
`date`,
p1.name first_opponent_name,
CASE WHEN has_opponent OR (p1 != p2) THEN p2.name END second_opponent_name
FROM ( SELECT match_id,
`date`,
MIN(player_id) p1,
MAX(player_id) p2,
has_opponent
FROM matching
GROUP BY match_id,
`date`,
has_opponent ) cte
JOIN players p1 ON cte.p1 = p1.id
JOIN players p2 ON cte.p2 = p2.id
ORDER BY 1
https://dbfiddle.uk/?rdbms=mysql_8.0&rdbms2=mysql_5.7&fiddle=4b947a2e7d31d50dfc1242b194b204fe
The query assumes that the same match_id strictly matches the same date and match_id.
The only tricky part is dealing with multiple players which is well-suited to using window functions and row_number(); Then it's just a case of aggregating these with a conditional case:
select match_Id, date,
Max(case when cnt=1
then name
else
case when rn=1 then name end
end) first_opponent_name,
Max(case when cnt=1 then
case when has_opponent=1 then name else '' end
else
case when rn=2 then name end
end) second_opponent_name
from (
select m.match_id, m.date, m.has_opponent, p.name,
Row_Number() over(partition by match_id order by player_id)rn,
Count(*) over (partition by match_id) cnt
from matching m
join players p on p.id=m.player_id
)x
group by match_id, date
See working example Fiddle

Multiple select with single Group By query

I have these 3 fields in the table.
trans_date | transaction_type | client_id
What I need is a count of entries by transaction_types for each date. For example,
Date : 07/07/2015 total count : 6 transaction_type 1 count : 3 ,
transaction_type 2 count : 1, transaction_type 3 count : 2 etc....
And I need this for all the dates grouped by each date.
Here's my current query,
SELECT count(id) as total_count,
(select count(id) where transaction_type=1) as type1_count,
(select count(id) where transaction_type=2) as type2_count,
(select count(id) where transaction_type=3) as type3_count
FROM tblTransactions
where client_id=1
GROUP BY date(trans_date/1000, 'unixepoch')
This returns weird numbers that doesn't match. What am I doing wrong?
The reason that you are getting weird values is that your sub queries are not filtered by date so you will get the total count for each transaction type. What you need is a correlated subquery that will get a paremeter from outer query:
SELECT count(id) as total_count,
(select count(id) where transaction_type=1 and trans_date=t.trans_date) as type1_count,
(select count(id) where transaction_type=2 and trans_date=t.trans_date) as type2_count,
(select count(id) where transaction_type=3 and trans_date=t.trans_date) as type3_count
FROM tblTransactions t
where client_id=1
GROUP BY date(trans_date/1000, 'unixepoch')
You can use sum function instead of subqueries
select date(trans_date/1000, 'unixepoch') d,
sum(case when transaction_type = 1 then 1 else 0 end) type1_count,
sum(case when transaction_type = 2 then 1 else 0 end) type2_count,
sum(case when transaction_type = 3 then 1 else 0 end) type3_count
from tblTransactions
where client_id=1
group by d

Join table with itself based on the condition t-sql

I'd appreciate if someone could help me with my query.
I have a table containing Currencies and Amounts for each currency.
My first query is:
select Currency, SUM(Amount) amnt from [MyTable]
where CODE in(410, 420)
group by Currency
So, I get:
Currency | amnt
USD | 15
I want to show all available currencies from my table and to join them with this result (amount for these currencies must be null)
Currency | amnt
USD | 15
EUR |
AED |
I get the list of all currencies by query:
select currency from [MyTable] group by currency
So, How can I join these two queries?
Thanks in advance!
Try this:
SELECT a.Currency, SUM(CASE WHEN a.CODE IN (410, 420) THEN a.Amount ELSE 0 END) amnt
FROM MyTable a
GROUP BY a.Currency
OR
SELECT A.Currency, SUM(B.Amount) amnt
FROM (SELECT DISTINCT Currency FROM MyTable) A
LEFT JOIN MyTable B ON A.Currency = B.Currency AND B.CODE IN (410, 420)
GROUP BY A.Currency
OR
SELECT A.Currency, SUM(B.Amount) amnt
FROM (SELECT Currency FROM MyTable GROUP BY Currency) A
LEFT JOIN MyTable B ON A.Currency = B.Currency AND B.CODE IN (410, 420)
GROUP BY A.Currency

SQL Subqueries Issues

My question is to retrieve a shop who sell each item at more than the average market price;
SELECT SHOP_NAME, Trade_Name
FROM SELL_ITEM f
WHERE PRICE >
(SELECT
AVG(PRICE)
FROM SELL_ITEM s
WHERE f.Trade_Name = s.Trade_Name
GROUP BY TRADE_NAME);
This query return me all the shop selling item > than average market price, but how can I filter out those not for each item?
Edited ... (missed a nuance the first time)
SELECT shop_name FROM
(
SELECT shop_name, MIN(case when f.price > a.avg_price then 1 else 0 end) AS is_always_higher_than_avg
FROM SELL_ITEM f
INNER JOIN
(
SELECT avg(price) AS avg_price, trade_name
FROM sell_item
GROUP BY trade_name
) a
ON f.trade_name = a.trade_name
WHERE f.price > a.avg_price
GROUP BY shop_name
) b
WHERE is_always_higher_than_avg = 1