add sum of all values mysql query - mysql

select(sum(
if (lower(st.c_gender) = 'male', 1, 0)) - ifnull(le.male, 0)) as male, (sum(
if (lower(st.c_gender) = 'female', 1, 0)) - ifnull(le.female, 0)) as female, FROM_UNIXTIME(st.c_admissionDate, '%Y') as year, le.male, le.female
from(SELECT s.*, zone.id as zoneid FROM groups s left join groups as zone ON s.parent_id = zone.id where s.id != s.parent_id) g, group_to_user u, student st
left join(SELECT ifnull(sum(
if (lower(l.c_gender) = 'male', 1, 0)), 0) as male, ifnull(sum(
if (lower(l.c_gender) = 'female', 1, 0)), 0) as female, FROM_UNIXTIME(l.c_leavingDate, '%Y') as year from student l
where l.c_leavingDate is not null group by FROM_UNIXTIME(l.c_leavingDate, '%Y')) as le on le.year = FROM_UNIXTIME(st.c_admissionDate, '%Y')
where g.id = u.groupid and st.c_userId = u.userId group by FROM_UNIXTIME(st.c_admissionDate, '%Y')
the above query returns the following output ,
male female year male female
1860 657 1970 NULL NULL
2 4 2012 NULL NULL
3 0 2013 NULL NULL
470 370 2014 0 1
0 365 2015 NULL NULL
0 367 2016 NULL NULL
1 260 2017 1 2
from the above output need to sum the before values like the following output
male female year male female
1860 657 1970 NULL NULL
1862 661 2012 NULL NULL
1865 661 2013 NULL NULL
2335 1031 2014 0 1
2335 1396 2015 NULL NULL
2335 1763 2016 NULL NULL
2336 2023 2017 1 2
anyway to make the output like the above help me

set #msum := 0;
set #fsum := 0;
select (#msum := #msum + T.male_sum) as male_sum, (#fsum := #fsum + T.female_sum) as female_sum, T.year, T.male, T.female
from (
select(sum(
if (lower(st.c_gender) = 'male', 1, 0)) - ifnull(le.male, 0)) as male_sum, (sum(
if (lower(st.c_gender) = 'female', 1, 0)) - ifnull(le.female, 0)) as female_sum, FROM_UNIXTIME(st.c_admissionDate, '%Y') as year, le.male, le.female
from(SELECT s.*, zone.id as zoneid FROM groups s left join groups as zone ON s.parent_id = zone.id where s.id != s.parent_id) g, group_to_user u, student st
left join(SELECT ifnull(sum(
if (lower(l.c_gender) = 'male', 1, 0)), 0) as male, ifnull(sum(
if (lower(l.c_gender) = 'female', 1, 0)), 0) as female, FROM_UNIXTIME(l.c_leavingDate, '%Y') as year from student l
where l.c_leavingDate is not null group by FROM_UNIXTIME(l.c_leavingDate, '%Y')) as le on le.year = FROM_UNIXTIME(st.c_admissionDate, '%Y')
where g.id = u.groupid and st.c_userId = u.userId group by FROM_UNIXTIME(st.c_admissionDate, '%Y')
)T;
Simplify this query but this will give you a general idea. Cannot be done in a non procedural query.

You really need a column for a reliable ordering.
But a method in MySQL is to use # variables like this.
SELECT
#male = #male + male as male
, #female = #female + female as female
, ordering_column
FROM (
<< your existing query here >>
)
CROSS JOIN ( SELECT #male := 0 m, #female := 0 f ) vars
ORDER BY
ordering_column

Related

Concat rows with the same user

I have the query:
SELECT e.id_taxe
, u.nm_user
, dt_taxe
, SUM(e.vl_taxe) as vl_taxe
FROM taxe as e
JOIN user as u
ON u.id_user = e.id_user
WHERE id_enterprise = 86
AND dt_taxe BETWEEN '2017-01-01' AND '2017-03-31'
AND lg_cancel = 0
GROUP
BY e.dt_taxe
, e.id_user
ORDER
BY e.id_user
, e.dt_taxe
As result, I have:
id_taxe nm_user dt_taxe vl_taxe
728 Maria 2017-01-01 17091.07048034668
727 Maria 2017-02-01 14091.07048034668
721 Maria 2017-03-01 1021.07048034668
731 Pedro 2017-01-01 16353.569854736328
732 Pedro 2017-02-01 6353.56231239
How I can concat the rows, to get the result:
id_taxe nm_user dt_taxe vl_taxe
728 Maria 2017-01-01 17091.07048034668 ,
2017-02-01 14091.07048034668,
2017-03-01 1021.07048034668
731 Pedro 2017-01-01 16353.569854736328,
2017-02-01 6353.56231239
With a database like PostgreSQL this could be done with window functions but MySQL has no support for window functions so you cannot do that
This type of operation is usually best done at the application layer. Your result set is not very SQLish, because ordering is very important.
However, you can do this with variables:
SELECT (CASE WHEN rn = 1 THEN id_taxe END) as id_taxe,
(CASE WHEN rn = 1 THEN nm_user END) as num_user,
dt_taxe, vl_taxe
FROM (SELECT eu.*,
(#rn := if(#u = e.id_user, #rn + 1,
if(#u := e.id_user, 1, 1)
)
) as rn
FROM (SELECT e.id_taxe, u.nm_user, e.dt_taxe, SUM(e.vl_taxe) as vl_taxe
FROM taxe e INNER JOIN
user u
ON u.id_user = e.id_user
WHERE id_enterprise = 86 AND
dt_taxe BETWEEN '2017-01-01' AND '2017-03-31' AND
lg_cancel = 0
GROUP BY e.dt_taxe, e.id_user
) eu CROSS JOIN
(SELECT #i := -1, #rn := 0)
ORDER BY id_user, dt_taxe
) eu;

Mysql Pivot table for attendance per weekdays of week

for a date interval, I want to display the attendance per weekdays of students records, recorded in three tables [Attendance, Students, Person].
The schema (relevant fields) of tables:
Attendance Table
---------------------
Attendance_Identifier
Student_Identifier
Classroom_Identifier
Attendance_Datetime
Attendance_Value
...
Student
------------------
Student_Identifier
Person_Identifier
Classroom_Identifier
...
Person
-----------------
Person_Identifier
Frist_Name
Last_Name
Gender
...
Expected Report Output:
Gender Student Mon Tue Wed Thu Fri WeeklyTotal
------------------------------------------------------------------------
Male Ab Stain 2/10 3/12 1/9 1/10 0/10 7/51
Male Pre Senter 10/10 12/12 9/9 9/10 10/10 50/51
...
Female Al Ways 10/10 12/12 9/9 10/10 10/10 51/51
Female Not Often 5/10 5/12 4/9 4/10 5/10 23/51
...
I also have a function to get the count of particular day of week from a date interval say for total Mondays in date interval #s, #e, just do: select get_weekday(#s,#e,0).
So my query for my stored procedure is :
set #s = '2016-01-01';
set #e = '2016-12-07';
SELECT
concat(p.Frist_Name, ' ',p.Last_Name) as Student, p.Gender
,GROUP_CONCAT(CASE WHEN DATE_FORMAT(Attendance_Datetime, '%a') = 'Mon'
THEN CONCAT(COUNT(Attendance_Value),'/',get_weekday(#s,#e,0)) ELSE NULL END) AS Mon
,GROUP_CONCAT(CASE WHEN DATE_FORMAT(Attendance_Datetime, '%a') = 'Tue'
THEN CONCAT(COUNT(Attendance_Value),'/',get_weekday(#s,#e,1)) ELSE NULL END) AS Tue
,GROUP_CONCAT(CASE WHEN DATE_FORMAT(Attendance_Datetime, '%a') = 'Wed'
THEN CONCAT(COUNT(Attendance_Value),'/',get_weekday(#s,#e,2)) ELSE NULL END) AS Wed
,GROUP_CONCAT(CASE WHEN DATE_FORMAT(Attendance_Datetime, '%a') = 'Thu'
THEN CONCAT(COUNT(Attendance_Value),'/',get_weekday(#s,#e,3)) ELSE NULL END) AS Thu
,GROUP_CONCAT(CASE WHEN DATE_FORMAT(Attendance_Datetime, '%a') = 'Fri'
THEN CONCAT(COUNT(Attendance_Value),'/',get_weekday(#s,#e,4)) ELSE NULL END) AS Fri
, SUM(COUNT(Attendance_Value)) as WeeklyTotal
FROM Attendance a JOIN Student s ON s.Student_Identifier=a.Student_Identifier
JOIN Person p ON p.Person_Identifier=s.Person_Identifier
WHERE date(Attendance_Datetime) BETWEEN #s AND #e AND a.Classroom_Identifier = '363'
AND (Attendance_Value = 'Present' OR Attendance_Value = 'Late') AND (p.Gender = 'Male' OR p.Gender = 'Female')
AND DATE_FORMAT(Attendance_Datetime, '%a') !='Sat' AND DATE_FORMAT(Attendance_Datetime, '%a')!='Sun'
GROUP BY p.Gender, Student, WeeklyTotal ORDER BY p.Gender, Student;
And I get the following group function error each time even with many tweaks to the code.
Error Code: 1111. Invalid use of group function
Edit
Based on #Solarflare 's first suggestion (I don't know how to try the second one), I got the query refined to:
SELECT
concat(p.Frist_Name, ' ',p.Last_Name) as Student, p.Gender
,CONCAT(CASE WHEN DATE_FORMAT(Attendance_Datetime, '%a') = 'Mon'
THEN COUNT(Attendance_Value) ELSE 0 END,'/',get_weekday(#s,#e,0) ) AS Mon
,CONCAT(CASE WHEN DATE_FORMAT(Attendance_Datetime, '%a') = 'Tue'
THEN COUNT(Attendance_Value) ELSE 0 END,'/',get_weekday(#s,#e,1) ) AS Tue
,CONCAT(CASE WHEN DATE_FORMAT(Attendance_Datetime, '%a') = 'Wed'
THEN COUNT(Attendance_Value) ELSE 0 END,'/',get_weekday(#s,#e,2) ) AS Wed
,CONCAT(CASE WHEN DATE_FORMAT(Attendance_Datetime, '%a') = 'Thu'
THEN COUNT(Attendance_Value) ELSE 0 END,'/',get_weekday(#s,#e,3) ) AS Thu
,CONCAT(CASE WHEN DATE_FORMAT(Attendance_Datetime, '%a') = 'Fri'
THEN COUNT(Attendance_Value) ELSE 0 END,'/',get_weekday(#s,#e,4) ) AS Fri
, COUNT(Attendance_Value) as WeeklyTotal
FROM Attendance a JOIN Student s ON s.Student_Identifier=a.Student_Identifier
JOIN Person p ON p.Person_Identifier=s.Person_Identifier
WHERE date(Attendance_Datetime) BETWEEN #s AND #e AND a.Classroom_Identifier = '363'
AND (Attendance_Value = 'Present' OR Attendance_Value = 'Late') AND (p.Gender = 'Male' OR p.Gender = 'Female')
AND DATE_FORMAT(Attendance_Datetime, '%a') !='Sat' AND DATE_FORMAT(Attendance_Datetime, '%a')!='Sun'
GROUP BY Student,Gender,DATE_FORMAT(Attendance_Datetime, '%a') ORDER BY p.Gender, Student,Mon DESC;
and the result is as below:
There were just some minor things left to make it work, try this:
SELECT
concat(p.Frist_Name, ' ',p.Last_Name) as Student, p.Gender
,concat(cast(COUNT(CASE WHEN weekday(Attendance_Datetime) = 0
then 1 END) as char), '/', get_weekday(#s,#e,0)) as Mon
,concat(cast(COUNT(CASE WHEN weekday(Attendance_Datetime) = 1
then 1 END) as char), '/', get_weekday(#s,#e,1)) as Tue
,concat(cast(COUNT(CASE WHEN weekday(Attendance_Datetime) = 2
then 1 END) as char), '/', get_weekday(#s,#e,2)) as Wed
,concat(cast(COUNT(CASE WHEN weekday(Attendance_Datetime) = 3
then 1 END) as char), '/', get_weekday(#s,#e,3)) as Thu
,concat(cast(COUNT(CASE WHEN weekday(Attendance_Datetime) = 4
then 1 END) as char), '/', get_weekday(#s,#e,4)) as Fri
,COUNT(Attendance_Value) as WeeklyTotal
FROM Attendance a
JOIN Student s ON s.Student_Identifier=a.Student_Identifier
JOIN Person p ON p.Person_Identifier=s.Person_Identifier
WHERE date(Attendance_Datetime) BETWEEN #s AND #e
AND a.Classroom_Identifier = '363'
AND (Attendance_Value = 'Present' OR Attendance_Value = 'Late')
AND (p.Gender = 'Male' OR p.Gender = 'Female')
AND weekday(Attendance_Datetime) not in (5,6)
GROUP BY Student, Gender ORDER BY p.Gender, Student, Mon DESC;
The pivot is done with the case, it counts when the date has the correct weekday. Otherwise the value is null (not 0), only then it doesn't count (else null has not to be written).

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

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

SQL Query to get the total sales per seller by month

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"?