Count data from multiple tables using SUM - mysql

I have table with positions
tbl_positions
id position
1 Driver
2 Lobby
3 Support
4 Constructor
and in other table i have users
EDIT:
tbl_workers
id name position position2 status
1 John 2 3 4
2 Mike 3 2 2
3 Kate 2 0 3
4 Andy 1 0 0
i do request of positions
SELECT p.id,
p.position,
SUM(CASE w.Status WHEN 2 THEN 1 ELSE 0 END) AS booked,
SUM(CASE w.Status WHEN 3 THEN 1 ELSE 0 END) AS placed
FROM tbl_positions AS p LEFT JOIN tbl_workers AS w
ON w.position=p.id
GROUP BY p.id, p.position
I need output like this in single query.
Position booked placed
Driver 0 0
Lobby 1 2
Support 0 2
Constructor 0 0
I need to evalate both field positon1 and position2 instead of one. I think its easy to modify it but i cannot find the right solution please help.
EDIT : Added status 4

Instead of joining to tbl_workers you could join to its unpivoted variation where position and position2 would be in the same column but in different rows.
Here's how the unpivoting might look like:
SELECT
w.id,
w.name,
CASE x.pos WHEN 1 THEN w.position ELSE w.position2 END AS position,
w.status
FROM tbl_workers AS w
CROSS JOIN (SELECT 1 AS pos UNION ALL SELECT 2) AS x
Here's the entire query, which is basically your original query with the above query substituting for the tbl_workers table:
SELECT p.id,
p.position,
SUM(CASE w.Status WHEN 2 THEN 1 ELSE 0 END) AS booked,
SUM(CASE w.Status WHEN 3 THEN 1 ELSE 0 END) AS placed
FROM tbl_positions AS p
LEFT JOIN (
SELECT
w.id,
w.name,
CASE x.pos WHEN 1 THEN w.position ELSE w.position2 END AS position,
w.status
FROM tbl_workers AS w
CROSS JOIN (SELECT 1 AS pos UNION ALL SELECT 2) AS x
) AS w
ON w.position=p.id
GROUP BY p.id, p.position
UPDATE
This is a modified script according to additional request in comments:
SELECT p.id,
p.position,
SUM(CASE w.Status WHEN 2 THEN 1 ELSE 0 END) AS booked,
SUM(CASE w.Status WHEN 3 THEN 1 ELSE 0 END) AS placed
FROM tbl_positions AS p
LEFT JOIN (
SELECT
w.id,
w.name,
CASE x.pos WHEN 1 THEN w.position ELSE w.position2 END AS position,
CASE w.status
WHEN 4 THEN CASE x.pos WHEN 1 THEN 3 ELSE 2 END
ELSE w.status
END AS status
FROM tbl_workers AS w
CROSS JOIN (SELECT 1 AS pos UNION ALL SELECT 2) AS x
) AS w
ON w.position=p.id
GROUP BY p.id, p.position
The idea is to substitute the 4 status in the subselect with 3 or 2 depending on whether we are currently to pull position or position2 as the unified position. The outer select keeps using the same logic as before.

not really sure what you mean by "evaluate both fields" - but here is an example on how to sum up both:
SELECT
P.ID,
P.POSITION,
((SELECT SUM (CASE W.STATUS WHEN 2 THEN 1 ELSE 0 END) FROM TBL_WORKERS W WHERE W.POSITION = P.ID) + (SELECT SUM (CASE W.STATUS WHEN 2 THEN 1 ELSE 0 END) FROM TBL_WORKERS W WHERE W.POSITION2 = P.ID)) AS BOOKED,
((SELECT SUM (CASE W.STATUS WHEN 3 THEN 1 ELSE 0 END) FROM TBL_WORKERS W WHERE W.POSITION = P.ID) + (SELECT SUM (CASE W.STATUS WHEN 3 THEN 1 ELSE 0 END) FROM TBL_WORKERS W WHERE W.POSITION2 = P.ID)) AS PLACED
FROM TBL_POSITIONS P

since your data is not normalized (ie: two columns both representing a position in a single row), I would do a UNION of the workers first, then apply your outer count/group by... something like... Each part of the inner query is pre-aggregating and only counting for the records qualifying the status 2 or 3 and have a position. So, if you have a large table, it would be more noticeable for performance. Then, that result gets summed at the outer level for all possible positions using COALESCE() to prevent NULL values in your output result.
SELECT
p.id,
p.position,
coalesce( SUM(w.Booked), 0 ) AS booked,
coalesce( SUM(w.Placed), 0 ) AS placed
FROM
tbl_positions AS p
LEFT JOIN
( select w1.Position as PositionID,
sum( if( w1.status = 2, 1, 0 )) as Booked,
sum( if( w1.status = 3, 1, 0 )) as Placed
from
tbl_workers w1
where
w1.status in ( 2, 3 )
and w1.Position > 0
group by
PositionID
union all
select w2.Position2 as PositionID,
sum( if( w2.status = 2, 1, 0 )) as Booked,
sum( if( w2.status = 3, 1, 0 )) as Placed
from
tbl_workers w2
where
w2.status in ( 2, 3 )
and w2.Position2 > 0
group by
PositionID ) as w
on p.id = w.positionID
group by
p.id,
p.position

Related

make 2-dimension coordinate using mysql select results

I have question about mysql queries.
I have a table which have data below.
From To Weight
--------------
A B 1
A C 3
B C 2
D E 4
And I want to get sql result like below..
(?) A B C D E
----------------------
A 0 1 3 0 0
B 0 0 2 0 0
C 0 0 0 0 0
D 0 0 0 0 4
E 0 0 0 0 0
And what data is in original table is not determined.
How can I acheive this?
If you know the original columns, you can do:
select c.col1,
sum(case when to = 'A' then weight else 0 end) as a,
sum(case when to = 'B' then weight else 0 end) as b,
sum(case when to = 'C' then weight else 0 end) as c,
sum(case when to = 'D' then weight else 0 end) as d,
sum(case when to = 'E' then weight else 0 end) as d
from (select 'A' as col1 union all select 'B' union all select 'C' union all select 'D' union all select 'E'
) c left join
t
on t.from = c.col1
group by c.col1;
If you don't know the original columns, you could combine the values into a single string:
select col1.col,
group_concat(col2.col, ':', t.weight order by col2.col)
from ((select `from` as col from t
) union -- on purpose to remove duplicates
(select `to` from t
)
) col1 cross join
((select `from` as col from t
) union -- on purpose to remove duplicates
(select `to` from t
)
) col2 left join
t
on col1.col = t.`from` and col2.col = t.`from`
group by col1.col;
If you actually want separate columns and don't know the values, then you would need dynamic SQL.

how to get column count

i have a query like below
select project_task_id,
status_id,
sum(case when StatusID=1 then 1 else 0 end) as task_id=1,
sum(case whenStatusID=2 then 1 else 0 end) as task_id=2,
sum(case when StatusID=3 then 1 else 0 end) as task_id=3,
sum(case when StatusID=4 then 1 else 0 end) as task_id=4,
sum(case when StatusID=5 then 1 else 0 end) as task_id=5,
sum(case when StatusID=6 then 1 else 0 end) as task_id=6,
sum(case when StatusID=7 then 1 else 0 end) as task_id=7,
from"Projects".work_unit_status
group by project_task_id,status_id;
I'm getting the below attached output:
https://i.stack.imgur.com/1wfD1.png
and I want to get the below expected output:
https://i.stack.imgur.com/Zql9z.png
include zero's if the status_id is blank
please any one help on this
Try this: use the addition of all sum column
select project_task_id,status_id,
isnull(sum(case when StatusID=1 then 1 else 0 end),0)+
isnull(sum(case whenStatusID=2 then 1 else 0 end),0) +
isnullsum(case when StatusID=3 then 1 else 0 end),0) +
isnullsum(case when StatusID=4 then 1 else 0 end),0)+
isnullsum(case when StatusID=5 then 1 else 0 end),0) +
isnullsum(case when StatusID=6 then 1 else 0 end),0) +
isnullsum(case when StatusID=7 then 1 else 0 end),0) as count_status
from"Projects".work_unit_status group by project_task_id,status_id
use in with your case
with t1 as (
select project_task_id,
status_id,
sum(case when StatusID in (1,2,3,4,5,6,7) then 1 else 0)
as sum_s
from "Projects".work_unit_status
group by project_task_id,status_id
) ,
t2 as
(
select * from (
select 1 as statusid
union
select 2
union
select 3
union
select 4
union
select 5
union
select 6
union
select 7 ) t
) select t1.project_task_id,
t2.statusid,
case when t1.sum_s>0 or not null
then sum_s else 0 end as total
t2 full join t1 on t2.statusid=t1.status_id
Without knowing the exact table structure I assumed that status_id and statusId refer to the same column. (If they are different columns, we need to use StatusId in the COUNT.)
Based on the expected output, you want to count the status_id and group by project_task_id. To make sure that every status is represented for every task, first, we need to create a subquery of all possible project_task_id/status_id combinations. Then we use that with the aggregate values of the original table.
select
ps.project_task_id,
ps.status_id,
count(w.status_id) as total
from (
select distinct
project_task_id,
s.status_id
from work_unit_status
cross join (select distinct status_id from work_unit_status) s
) ps
left join work_unit_status w
on ps.project_task_id = w.project_task_id and ps.status_id = w.status_id
group by
ps.project_task_id,
ps.status_id
If you really need to hardcode the statuses from 1 to 7, use the query below.
select
ps.project_task_id,
ps.status_id,
count(w.status_id) as total
from (
select distinct
project_task_id,
s.status_id
from work_unit_status
cross join (
select 1 as status_id
union select 2
union select 3
union select 4
union select 5
union select 6
union select 7
) s
) ps
left join work_unit_status w
on ps.project_task_id = w.project_task_id and ps.status_id = w.status_id
group by
ps.project_task_id,
ps.status_id
order by
ps.project_task_id,
ps.status_id

SUM CASE adding from joined table instead main table

I have a query that retrieves from a customer:
sum of time expent on his tickets (every ticket can have 1 or more interventions)
average resolution time
category of each ticket
The query:
SELECT SUM(f.duree) as total_time, SUM(timestampdiff(second, t.datec, t.date_close))/COUNT(distinct t.rowid) as average_resolution, COUNT(distinct t.rowid) as tickets,
SUM( CASE t.category_code WHEN 'WITHOUT_MAINTENANCE' THEN 1 ELSE 0 END) as sin_mantenimiento,
SUM( CASE t.category_code WHEN 'WITH_MAINTENANCE' THEN 1 ELSE 0 END) as con_mantenimiento,
SUM( CASE t.category_code WHEN 'WITH_BILLABLE_MAINTENANCE' THEN 1 ELSE 0 END) as con_mantenimiento_facturable,
SUM( CASE t.category_code WHEN 'NO_BILLABLE_COASER_WARRANTY' THEN 1 ELSE 0 END) as no_facturable_garantia_coaser,
SUM( CASE t.category_code WHEN 'OFERTADO' THEN 1 ELSE 0 END) as ofertado
FROM llx_ticketsup as t
JOIN llx_element_element as ee on ee.fk_source = t.rowid
JOIN llx_fichinter as f on f.rowid = ee.fk_target
WHERE t.fk_soc = 47 AND t.fk_statut = 8
where
llx_ticketsup is main tickets table
llx_fichinter is table where interventions from tickets are stored
llx_element_element is table between tickets and interventions
My problem is, if one ticket has more than 1 intervention, on sum of category types to know how many are from this category and how many from the other, it sums interventions instead tickets. For example, if query returns 2 tickets and they have a total of 6 interventions, it returns 6 tickets of category WITHOUT_MAINTENANCE, instead 2 tickets of category WITHOUT_MAINTENANCE.
Whats wrong? Thanks.
Nothings 'wrong' that's expected behaviour try using a sub query to get the duration. Something like
SELECT (select sum(f.duree) from llx_fichinter as f on f.rowid = ee.fk_target) as total_time, SUM(timestampdiff(second, t.datec, t.date_close))/COUNT(distinct t.rowid) as average_resolution, COUNT(distinct t.rowid) as tickets,
SUM( CASE t.category_code WHEN 'WITHOUT_MAINTENANCE' THEN 1 ELSE 0 END) as sin_mantenimiento,
SUM( CASE t.category_code WHEN 'WITH_MAINTENANCE' THEN 1 ELSE 0 END) as con_mantenimiento,
SUM( CASE t.category_code WHEN 'WITH_BILLABLE_MAINTENANCE' THEN 1 ELSE 0 END) as con_mantenimiento_facturable,
SUM( CASE t.category_code WHEN 'NO_BILLABLE_COASER_WARRANTY' THEN 1 ELSE 0 END) as no_facturable_garantia_coaser,
SUM( CASE t.category_code WHEN 'OFERTADO' THEN 1 ELSE 0 END) as ofertado
FROM llx_ticketsup as t
JOIN llx_element_element as ee on ee.fk_source = t.rowid
WHERE t.fk_soc = 47 AND t.fk_statut = 8
If that doesn't work add sample data and expected result to the question as text so we can play with it.

How to use user variable as counter with inner join queries that contains GROUP BY statement?

I have 2 tables odds and matches :
matches : has match_id and match_date
odds : has id, timestamp, result, odd_value, user_id, match_id
I had a query that get the following information from those tables for each user:
winnings : the winning bets for each user. (when odds.result = 1)
loses : the lost bets for each user.(when odds.result != 1)
points : the points of each user.(the sum of the odds.odd_value) for each user.
bonus : for each continuous 5 winnings i want to add extra bonus to this variable. (for each user)
How to calculate bonus?
I tried to use this query and I faced a problem : (you can check it here SQL Fiddle)
the calculated bonus are not right for all the users :
first user:(winnings:13, bonus=2).
second user:(winnings:8, bonus=2)bonus here should be 1.
third user:(winnings:14, bonus=3)bonus here should be 2.
why does the query not calculate the bonus correctly?
select d.user_id,
sum(case when d.result = 1 then 1 else 0 end) as winnings,
sum(case when d.result = 2 then 1 else 0 end) as loses,
sum(case when d.result = 1 then d.odd_value else 0 end) as points,
f.bonus
FROM odds d
INNER JOIN
(
SELECT
user_id,SUM(CASE WHEN F1=5 THEN 1 ELSE 0 END) AS bonus
FROM
(
SELECT
user_id,
CASE WHEN result=1 and #counter<5 THEN #counter:=#counter+1 WHEN result=1 and #counter=5 THEN #counter:=1 ELSE #counter:=0 END AS F1
FROM odds o
cross join (SELECT #counter:=0) AS t
INNER JOIN matches mc on mc.match_id = o.match_id
WHERE MONTH(STR_TO_DATE(mc.match_date, '%Y-%m-%d')) = 2 AND
YEAR(STR_TO_DATE(mc.match_date, '%Y-%m-%d')) = 2015 AND
(YEAR(o.timestamp)=2015 AND MONTH(o.timestamp) = 02)
) Temp
group by user_id
)as f on f.user_id = d.user_id
group by d.user_id
I am not sure how your result related to matches table,
you can add back WHERE / INNER JOIN clause if you need.
Here is link to fiddle
and the last iteration according to your comments:
And here is a query:
SET #user:=0;
select d.user_id,
sum(case when d.result = 1 then 1 else 0 end) as winnings,
sum(case when d.result = 2 then 1 else 0 end) as loses,
sum(case when d.result = 1 then d.odd_value else 0 end) as points,
f.bonus
FROM odds d
INNER JOIN
(
SELECT
user_id,SUM(bonus) AS bonus
FROM
(
SELECT
user_id,
CASE WHEN result=1 and #counter<5 AND #user=user_id THEN #counter:=#counter+1
WHEN result=1 and #counter=5 AND #user=user_id THEN #counter:=1
WHEN result=1 and #user<>user_id THEN #counter:=1
ELSE
#counter:=0
END AS F1,
#user:=user_id,
CASE WHEN #counter=5 THEN 1 ELSE 0 END AS bonus
FROM odds o
ORDER BY user_id , match_id
) Temp
group by user_id
)as f on f.user_id = d.user_id
group by d.user_id

GROUP BY on table returning incorrect counts in MySQL with LEFT JOIN

I am trying to return multiple counts and averages from multiple tables sorting by gender and am getting incorrect data. I understand that the following is incorrect, but I am unsure of how to fix it. (Edit: Problem with group by gender. See below.)
Here is the query:
SELECT c.gender AS 'Gender',
COUNT(DISTINCT mr.mailing_recipient_id) AS 'Mailing Recipients',
(SELECT COUNT(DISTINCT CASE WHEN mrc.mailing_recipient_click_type_id = 2 THEN 1 ELSE 0 END) ) AS 'Open Total',
AVG(CASE WHEN mrc.mailing_recipient_click_type_id = 2 THEN 1 ELSE 0 END) AS 'Avg Open',
(SELECT COUNT(DISTINCT CASE WHEN mrc.mailing_recipient_click_type_id = 1 THEN 1 ELSE 0 END) ) AS 'Click Total',
AVG(CASE WHEN mrc.mailing_recipient_click_type_id = 1 THEN 1 ELSE 0 END) AS 'Avg Click',
COUNT(DISTINCT ca.cons_action_contribution_id) AS Donations,
AVG(ca.transaction_amt) AS 'Avg Donation Amt'
FROM ((mailing m
LEFT JOIN mailing_recipient mr ON m.mailing_id = mr.mailing_id)
LEFT JOIN mailing_recipient_click mrc ON mr.mailing_recipient_id = mrc.mailing_recipient_id
LEFT JOIN cons_action_contribution ca ON mr.cons_id = ca.cons_id
LEFT JOIN cons c ON c.cons_id = ca.cons_id)
WHERE m.mailing_id = 1
AND gender IS NOT NULL
GROUP BY c.gender;
Here is the table which would be correct if the totals in the fields were correct:
GENDER Mailing Recipient Open Total Avg Open Click Total Avg Click Donations Avg Amt
F 105 2 0.5000 2 0.5000 105 22.5000
M 98 2 0.5000 2 0.5000 98 18.8780
EDIT: Here is an example of what I am hoping to achieve. I am certain that the above values are being repeated. The below values are just examples of what I am expecting:
GENDER Mailing Recipient Open Total Avg Open Click Total Avg Click Donations Avg Amt
F 105 8 0.0761 4 0.0380 2 22.5000
M 98 2 0.0204 1 0.0102 1 18.8000
Edit:
After playing around a bit, I thought that I had discovered that the joining the cons table was what is giving me problematic returns, but the problem is with GROUP BY when using gender. To illustrate, this query (which is grouped by mailing name instead of gender) works beautifully.
select m.mailing_name AS 'mailing',
COUNT(DISTINCT mr.mailing_recipient_id) AS 'Mailing Recipients',
SUM(CASE
when mrc.mailing_recipient_click_type_id = 2 THEN 1
END)
AS 'Open Total',
AVG(CASE
WHEN mrc.mailing_recipient_click_type_id = 2 THEN 1
ELSE 0
END) AS 'Avg Open',
SUM(CASE
WHEN mrc.mailing_recipient_click_type_id = 1 THEN 1
END)
AS 'Click Total',
AVG(CASE
WHEN mrc.mailing_recipient_click_type_id = 1 THEN 1
ELSE 0
END) AS 'Avg Click',
COUNT(ca.cons_action_contribution_id) AS Donations,
AVG(ca.transaction_amt) AS 'Avg Donation Amt'
FROM
mailing m
LEFT JOIN mailing_recipient mr ON m.mailing_id = mr.mailing_id
LEFT JOIN mailing_recipient_click mrc
ON mr.mailing_recipient_id = mrc.mailing_recipient_id
LEFT JOIN cons_action_contribution ca ON mr.cons_id = ca.cons_id
LEFT JOIN cons c ON mr.cons_id = c.cons_id
WHERE m.mailing_id = 1
GROUP BY m.mailing_name;
The statement is identical with the exception of the first and last lines.
Try this:
I'm not sure what you mean by Avg Open and Avg Click.
SELECT c.gender AS 'Gender',
COUNT(DISTINCT mr.mailing_recipient_id) AS 'Mailing Recipients',
SUM(CASE WHEN mrc.mailing_recipient_click_type_id = 2 THEN 1 ELSE 0 END) AS 'Open Total',
AVG(CASE WHEN mrc.mailing_recipient_click_type_id = 2 THEN 1 ELSE 0 END) AS 'Avg Open',
SUM(CASE WHEN mrc.mailing_recipient_click_type_id = 1 THEN 1 ELSE 0 END) AS 'Click Total',
AVG(CASE WHEN mrc.mailing_recipient_click_type_id = 1 THEN 1 ELSE 0 END) AS 'Avg Click',
COUNT(DISTINCT ca.cons_action_contribution_id) AS Donations,
AVG(ca.transaction_amt) AS 'Avg Donation Amt'
FROM mailing m
LEFT JOIN mailing_recipient mr ON m.mailing_id = mr.mailing_id
LEFT JOIN mailing_recipient_click mrc ON mr.mailing_recipient_id = mrc.mailing_recipient_id
LEFT JOIN cons_action_contribution ca ON mr.cons_id = ca.cons_id
LEFT JOIN cons c ON c.cons_id = ca.cons_id
WHERE m.mailing_id = 1
AND gender IS NOT NULL
GROUP BY c.gender;
I also think that mrc.mailing_recipient_click_type_id = 2 means open and mrc.mailing_recipient_click_type_id = 1 mean click seems strange to me. I would expect this data to be exclusive and stored in two different fields.