SQL Query to get the total sales per seller by month - mysql

I try this:
SELECT
profile.user_id,
profile.name,
total_month.total as month10
FROM profile
LEFT OUTER JOIN (SELECT
order.seller_id,
COUNT(*) AS total
FROM order
WHERE MONTH(order.data_hora) = 10
GROUP BY order.seller_id) AS total_month
ON total_month.seller_id= profile.user_id;
The result was this:
-------------------------
|user_id| name |month10|
-------------------------
| 5 |user1 | 73 |
| 1 |user2 | 1 |
-------------------------
But I need more months like this:
-------------------------------------------------
| user_id | name | month10 | month11 | month12 |
-------------------------------------------------
| 5 | user1 | 73 | 52 | 65 |
| 1 | user2 | 67 | 56 | 78 |
-------------------------------------------------
How could I do this without creating a function?

You can extend your query to do what you want. Just be more flexible in the subquery:
SELECT p.user_id, p.name,
tm.month10, tm.month11, tm.month12
FROM profile p LEFT OUTER JOIN
(SELECT o.seller_id,
sum(o.data_hora = 10) as month10,
sum(o.data_hora = 11) as month11,
sum(o.data_hora = 12) as month12
FROM order o
WHERE MONTH(order.data_hora) in (10, 11, 12)
GROUP BY order.seller_id
) tm
ON tm.seller_id = p.user_id;

Well as I said in the comments supporting the #Schalk comment, in order to get this working as you want you will need a function to get a DYNAMIC PIVOT TABLE or TRANSPOSE ROWS TO COLUMNS google it if you prefer.
For your solution I've created a query that gives you all months/values like this:
user_id name jan feb mar apr may jun jul aug sep oct nov dec
1 a 0 0 1 10 0 0 1 2 7 2 3 0
2 b 1 0 0 0 2 0 0 0 3 1 1 0
If it fits to your problem this is your query:
select
profile.user_id,
profile.name,
sum(if( MONTH(orderr.data_hora) = 1, 1, 0 )) as Jan,
sum(if( MONTH(orderr.data_hora) = 2, 1, 0 )) as Feb,
sum(if( MONTH(orderr.data_hora) = 3, 1, 0 )) as Mar,
sum(if( MONTH(orderr.data_hora) = 4, 1, 0 )) as Apr,
sum(if( MONTH(orderr.data_hora) = 5, 1, 0 )) as May,
sum(if( MONTH(orderr.data_hora) = 6, 1, 0 )) as Jun,
sum(if( MONTH(orderr.data_hora) = 7, 1, 0 )) as Jul,
sum(if( MONTH(orderr.data_hora) = 8, 1, 0 )) as Aug,
sum(if( MONTH(orderr.data_hora) = 9, 1, 0 )) as Sep,
sum(if( MONTH(orderr.data_hora) = 10, 1, 0 )) as Oct,
sum(if( MONTH(orderr.data_hora) = 11, 1, 0 )) as Nov,
sum(if( MONTH(orderr.data_hora) = 12, 1, 0 )) as Dece
from
profile left join orderr
on profile.user_id = orderr.seller_id
group by profile.user_id,
profile.name
I've created a fiddle to it (but the data_hora column I created as integer to make it quickly to do it, it is for understanding).
http://sqlfiddle.com/#!2/4a1a2e/5

It was perfect!
The field type 'data_hora' is datetime, so I made a small change.
SELECT p.user_id,
p.name,
tm.month10,
tm.month11,
tm.month12,
(tm.month10+tm.month11+tm.month12) AS final_total
FROM profile p
LEFT OUTER JOIN
(SELECT o.seller_id,
sum(month(o.data_hora) = 10) AS month10,
sum(month(o.data_hora) = 11) AS month11,
sum(month(o.data_hora) = 12) AS month12
FROM order o
WHERE MONTH(ORDER.data_hora) IN (10, 11, 12)
GROUP BY ORDER.seller_id ) tm
ON tm.seller_id = p.user_id
ORDER BY final_total DESC;
How could I optimize the field "final_total"?

Related

CASE WHEN IN ({set of numbers})

I need a following case statement in MySQL.
When column value is (1, 2, 5, 7, 14, 17) - return 0, otherwise return 1 and I need to use it in order by clause.
My first impression was to make query like this:
SELECT ... ORDER BY (CASE column WHEN IN (1, 2, 5, 7, 14, 17) THEN 0 ELSE 1 END) DESC
but this obviously fails.
I can write it like this:
SELECT ... ORDER BY (CASE column WHEN 1 THEN WHEN 2 THEN 0 WHEN 5 THEN ... 0 ELSE 1 END) DESC
But I am looking for a more elegant way. Is there any other elegant syntax?
This needs to work
SELECT ...
ORDER BY
CASE WHEN (column IN (1, 2, 5, 7, 14, 17) THEN 0 ELSE 1 END) DESC
Demonstration:
with cte0 as
(
select 120 x from dual union
select 1 from dual union
select 22 from dual union
select 7 from dual
)
select * from cte0
order by (case when x in (22) then 0 else 1 end) desc;
| X |
| --: |
| 1 |
| 120 |
| 7 |
| 22 |
db<>fiddle here
You can use nested query, e.g.:
SELECT B.*
FROM (
SELECT A, CASE WHEN B IN (1, 2, 5, 7, 14, 17) THEN 1 ELSE 0 END AS ORDERING
FROM TABLE
) B
ORDER BY B.ORDERING DESC;

Sum in group by with join Mysql, multiple group by

I want to find the sum by months. The result returned 0 rows of records.
Table 1: aidat
1 Messi
2 Ronaldo
3 Neymar
Table 2: aidat_hareket
12 1(messi) 200 12-12-2018
13 3 150 12-11-2018
14 2 25 10-10-2018
15 1 150 10-12-2018
16 3 125 08-05-2018
I want to result this:
id name jan feb march april may ... oct nov dec
1 messi 0 0 0 0 0 0 0 350
2 ronaldo 0 0 0 0 0 25 0 0
3 neymar 0 0 0 0 125 0 150 0
it returned 0 rows in mysql. I tried group by month(ah.datee) and a.student_id; , month(ah.datee), a.student_id; and a.student_id; in inner join. But result is the same.
SELECT o.id as o_id, o.ad, s.ad as s_ad, s.plaka, v.id as veli_id,v.ad as
veli_ad, v.telefon,s.demo,
if(month(ah.datee)=1 , ah.collection,0) as jan,
if(month(ah.datee)=2 , ah.collection,0) as feb,
if(month(ah.datee)=3 , ah.collection,0) as march,
if(month(ah.datee)=4 , ah.collection,0) as april,
if(month(ah.datee)=5 , ah.collection,0) as may,
if(month(ah.datee)=6 , ah.collection,0) as june,
if(month(ah.datee)=7 , ah.collection,0) as july,
if(month(ah.datee)=8 , ah.collection,0) as august,
if(month(ah.datee)=9 , ah.collection,0) as sep,
if(month(ah.datee)=10, ah.collection,0) as oct,
if(month(ah.datee)=11, ah.collection,0) as nov,
if(month(ah.datee)=12, ah.collection,0) as december
from aidat a
inner join (select student_id, sum(collection_tutari) as collection, collection_datee as datee from aidat_hareket )
ah on ah.student_id = a.student_id
inner join student o on o.id = a.student_id
inner join service s on s.id = o.service_id
inner join service_firma sf on sf.id = s.firma_id
left join aile al on al.student_id = a.student_id
left join veli v on v.id = al.veli_id
inner join aylar ay on ay.id = month(ah.datee)
where a.aylik_odeme_tutari != 0 and sf.id = 23
group by month(ah.datee), a.student_id;
How can I fixed it?

Why MySQL full outer join returns nulls?

Why MySQL full outer join returns nulls?
Hi
I have the following data:
s_id,date,p_id,amount_sold
1, '2015-10-01', 1, 10
2, '2015-10-01', 2, 12
7, '2015-10-01', 1, 11
3, '2015-10-02', 1, 11
4, '2015-10-02', 2, 10
5, '2015-10-15', 1, 22
6, '2015-10-16', 2, 20
8, '2015-10-22', 3, 444
and i want my query to output something like this: (A = sum of amount_sold for p_id=1 for that date,B = sum of amount_sold for p_id=2 for that date)
date,A,B,Difference
'2015-10-01',21,12,9
'2015-10-02',11,10,1
'2015-10-15',22,0,22
'2015-10-01',0,20,-20
I tried with this query, but the order its returning is having NULLS and the output is wrong:
SELECT A.p_id,A.date,sum(A.amount_sold) A,B.Bs, (sum(A.amount_sold) - B.Bs) as difference FROM sales as A
LEFT JOIN (
SELECT SUM( amount_sold ) Bs,p_id,s_id, DATE
FROM sales
WHERE p_id =2
group by date
) as B ON A.s_id = B.s_id
where A.p_id=1 or B.p_id=2
group by A.date, A.p_id
UNION
SELECT A.p_id,A.date,sum(A.amount_sold) A,B.Bs, (sum(A.amount_sold) - B.Bs) as difference FROM sales as A
RIGHT JOIN (
SELECT SUM( amount_sold ) Bs,p_id,s_id, DATE
FROM sales
WHERE p_id =2
group by date
) as B ON A.s_id = B.s_id
where B.p_id=2
group by A.date, A.p_id
It returned:
p_id date A Bs difference
1 2015-10-01 21 NULL NULL
2 2015-10-01 12 12 0
1 2015-10-02 11 NULL NULL
2 2015-10-02 10 10 0
1 2015-10-15 22 NULL NULL
2 2015-10-16 20 20 0
What am i doing wrong here? and what is the correct way of doing it? any help would be appreciated.
A full join isn't needed. You can use conditional aggregation instead:
select
date,
sum(case when p_id = 1 then amount_sold else 0 end) a,
sum(case when p_id = 2 then amount_sold else 0 end) b,
sum(case when p_id = 1 then amount_sold else 0 end)
- sum(case when p_id = 2 then amount_sold else 0 end) difference
from sales
where p_id in (1,2)
group by date

Distinct on row level

It seemed so easy.
I am getting following table by using COALESCE. I need to perform distinct on row level.
1 1 5 5 5 (null)
2 2 2 2 25 25
3 7 35 35 35 35
That's what I am looking for.
1 5 null
2 25
3 7 35
Here's a Demo on http://sqlfiddle.com/#!3/e945b/5/0
This is the only way I can think of doing it.
Do not currently have enough time to explain its operation, so please post questions in comments;
WITH DataCTE (RowID, a, b, c, d, e, f) AS
(
SELECT 1, 1, 1, 5, 5, 5, NULL UNION ALL
SELECT 2, 2, 2, 2, 2, 25, 25 UNION ALL
SELECT 3, 3, 7, 35, 35, 35, 35
)
,UnPivotted AS
(
SELECT DC.RowID
,CA.Distinctcol
,OrdinalCol = ROW_NUMBER() OVER (PARTITION BY DC.RowID ORDER BY CA.Distinctcol)
FROM DataCTE DC
CROSS
APPLY (
SELECT Distinctcol
FROM
(
SELECT Distinctcol = a UNION
SELECT b UNION
SELECT c UNION
SELECT d UNION
SELECT e UNION
SELECT f
)DT
WHERE Distinctcol IS NOT NULL
) CA(Distinctcol)
)
SELECT RowID
,Col1 = MAX(CASE WHEN OrdinalCol = 1 THEN Distinctcol ELSE NULL END)
,Col2 = MAX(CASE WHEN OrdinalCol = 2 THEN Distinctcol ELSE NULL END)
,Col3 = MAX(CASE WHEN OrdinalCol = 3 THEN Distinctcol ELSE NULL END)
,Col4 = MAX(CASE WHEN OrdinalCol = 4 THEN Distinctcol ELSE NULL END)
,Col5 = MAX(CASE WHEN OrdinalCol = 5 THEN Distinctcol ELSE NULL END)
FROM UnPivotted
GROUP BY RowID

How can I sum columns using conditions?

I'm trying to do the sum when:
date_ini >= initial_date AND final_date <= date_expired
And if it is not in the range will show the last net_insurance from insurances
show last_insurance when is not in range
Here my tables:
POLICIES
ID POLICY_NUM DATE_INI DATE_EXPIRED TYPE_MONEY
1, 1234, "2013-01-01", "2014-01-01" , 1
2, 5678, "2013-02-01", "2014-02-01" , 1
3, 9123, "2013-03-01", "2014-03-01" , 1
4, 4567, "2013-04-01", "2014-04-01" , 1
5, 8912, "2013-05-01", "2014-05-01" , 2
6, 3456, "2013-06-01", "2014-06-01" , 2
7, 7891, "2013-07-01", "2014-07-01" , 2
8, 2345, "2013-08-01", "2014-08-01" , 2
INSURANCES
ID POLICY_ID INITIAL_DATE FINAL_DATE NET_INSURANCE
1, 1, "2013-01-01", "2014-01-01", 100
2, 1, "2013-01-01", "2014-01-01", 200
3, 1, "2013-01-01", "2014-01-01", 400
4, 2, "2011-01-01", "2012-01-01", 500
5, 2, "2013-01-01", "2014-01-01", 600
6, 3, "2013-01-01", "2014-01-01", 100
7, 4, "2013-01-01", "2014-01-01", 200
I should have
POLICY_NUM NET
1234 700
5678 600
9123 100
4567 200
Here is what i tried
SELECT p.policy_num AS policy_num,
CASE WHEN p.date_ini >= i.initial_date
THEN SUM(i.net_insurance)
ELSE (SELECT max(id) FROM insurances GROUP BY policy_id) END as net
FROM policies p
INNER JOIN insurances i ON p.id = i.policy_id AND p.date_ini >= i.initial_date
GROUP BY p.id
Here is my query http://sqlfiddle.com/#!2/f6077b/16
Somebody can help me with this please?
Is not working when is not in the range it would show my last_insurance instead of sum
Try it this way
SELECT p.policy_num, SUM(i.net_insurance) net_insurance
FROM policies p JOIN insurances i
ON p.id = i.policy_id
AND p.date_ini >= i.initial_date
AND p.date_expired <= i.final_date
GROUP BY i.policy_id, p.policy_num
UNION ALL
SELECT p.policy_num, i.net_insurance
FROM
(
SELECT MAX(i.id) id
FROM policies p JOIN insurances i
ON p.id = i.policy_id
AND (p.date_ini < i.initial_date
OR p.date_expired > i.final_date)
GROUP BY i.policy_id
) q JOIN insurances i
ON q.id = i.id JOIN policies p
ON i.policy_id = p.id
Output:
| POLICY_NUM | NET_INSURANCE |
|------------|---------------|
| 1234 | 700 |
| 5678 | 600 |
| 9123 | 100 |
| 4567 | 200 |
Here is SQLFiddle demo
This will ensure that you only get the last insurance if the policy has no insurances within the policy period.
SELECT policy_num, SUM(IF(p.cnt > 0, p.net_insurance, i.net_insurance)) AS net_insurance
FROM
(
SELECT
p.id,
p.policy_num,
SUM(IF(p.date_ini >= i.initial_date AND p.date_expired <= i.final_date,
i.net_insurance, 0)) AS net_insurance,
SUM(IF(p.date_ini >= i.initial_date AND p.date_expired <= i.final_date,
1, 0)) AS cnt,
MAX(IF(p.date_ini >= i.initial_date AND p.date_expired <= i.final_date,
0, i.id)) AS max_i_id
FROM policies p
INNER JOIN insurances i ON p.id = i.policy_id
GROUP BY p.id
) as p
LEFT JOIN insurances i ON i.id = p.max_i_id
GROUP BY p.id
Here is my SQLFiddle
Try splitting grouping from conditioning :)
SELECT
policy_num
, CASE
WHEN TRUE
AND p.date_ini <= src.initial_date
AND p.date_expired >= src.final_date
THEN src.sum_insurances
ELSE i.net_insurance
END AS something
FROM (
SELECT
max(id) AS latest_id
, sum(net_insurance) AS sum_insurances
, initial_date
, final_date
, policy_id
FROM insurances
GROUP BY policy_id
) src
JOIN policies p ON p.id = src.policy_id
JOIN insurances i ON i.id = src.latest_id
ORDER BY p.id