Getting Count based on values of multiple rows of the same column - mysql

I am relatively new to database.
Say that if I have table like below:
PHeader AHeader IHeader
p1 a1 0
p1 a1 2
p1 a1 3
p1 a2 0
p1 a3 0
p1 a4 0
p1 a4 2
p1 a4 3
p2 a5 0
The expected output is :
PHeader BCount TCount
p1 2 2
p2 0 1
BCount: is for the given PHeader and AHeader Value, if the IHeader values has all the values of (0,2 &3) then BCount value of PHeader is increased by 1 , Since for the p1 it has a1 & a4 with all the vaues of 0,2 & 3 as IHeader Values the BCount for p1 is given as 2
TCount: is for given given PHeader and AHeader value , if IHeader value has only 0 but not 2 or 3, then TCount for the given PHeader is increased by 1. Hence the TCount for p1
is given as 2.
Could you please give me tips in writing the query?

You can approach this using two aggregations. The inner one aggregates at the pheader, aheader level. This counts the values that you are interested in.
The outer one applies the logic that you've described to these summaries:
select pheader,
sum(ih_0 > 0 and ih_2 > 0 and ih_3 > 0) as bcount,
sum(ih_0 > 0 and (ih_2 = 0 or ih_3 = 0)) as tcount
from (select pheader, aheader,
sum(iheader = 0) as ih_0,
sum(iheader = 2) as ih_2,
sum(iheader = 3) as ih_3
from table t
group by pheader, aheader
) pa
group by pheader;
EDIT:
The question is tagged MySQL. The following will work in both databases:
select pheader,
sum(case when ih_0 > 0 and ih_2 > 0 and ih_3 > 0 then 1 else 0 end) as bcount,
sum(case when ih_0 > 0 and (ih_2 = 0 or ih_3 = 0) then 1 else 0 end) as tcount
from (select pheader, aheader,
sum(case when iheader = 0 then 1 else 0 end) as ih_0,
sum(case when iheader = 2 then 1 else 0 end) as ih_2,
sum(case when iheader = 3 then 1 else 0 end) as ih_3
from table t
group by pheader, aheader
) pa
group by pheader;

Please check beloq sql query
SELECT
PHeader ,
COUNT(*) as BCount , --total
SUM(CASE WHEN PHeader = 'p1' THEN 1 ELSE 0 END) as TCount
FROM
yourtablename
group by PHeader
Hope this will give you some idea.

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.

joining with a group by and pivot

I have two tables as below
tbl1
id qNum
1 1
2 2
3 3
tbl2
id qNum displayNum
1 1 3
2 2 1
3 2 2
4 2 4
Ideally I need a sql results to look like this
qNum display1 display2 display3 display4
1 0 0 1 0
2 1 1 0 1
3 0 0 0 0
I have tried the following sql but this was not correct
SELECT
tbl1.qNum,
CASE when tbl2.displayNum=1 then 1 else 0 end AS filter1,
CASE when tbl2.displayNum=2 then 1 else 0 end AS filter2,
CASE when tbl2.displayNum=3 then 1 else 0 end AS filter3,
CASE when tbl2.displayNum=4 then 1 else 0 end AS filter4,
CASE when tbl2.displayNum=5 then 1 else 0 end AS filter5
FROM
tbl1
Left Join tbl2 ON tbl1.qNum = tbl2.qNum
GROUP BY
tbl1.qNum
Could anyone help a little please!!
You have use MAX function to pivot the table
Try this:
SELECT tbl1.qNum,
MAX(CASE WHEN tbl2.displayNum=1 THEN 1 ELSE 0 END) AS filter1,
MAX(CASE WHEN tbl2.displayNum=2 THEN 1 ELSE 0 END) AS filter2,
MAX(CASE WHEN tbl2.displayNum=3 THEN 1 ELSE 0 END) AS filter3,
MAX(CASE WHEN tbl2.displayNum=4 THEN 1 ELSE 0 END) AS filter4,
MAX(CASE WHEN tbl2.displayNum=5 THEN 1 ELSE 0 END) AS filter5
FROM tbl1
LEFT JOIN tbl2 ON tbl1.qNum = tbl2.qNum
GROUP BY tbl1.qNum
Your query is almost correct, you're just missing an aggregate function:
SELECT
tbl1.qNum,
MAX(CASE when tbl2.displayNum=1 then 1 else 0 end) AS filter1,
MAX(CASE when tbl2.displayNum=2 then 1 else 0 end) AS filter2,
MAX(CASE when tbl2.displayNum=3 then 1 else 0 end) AS filter3,
MAX(CASE when tbl2.displayNum=4 then 1 else 0 end) AS filter4,
MAX(CASE when tbl2.displayNum=5 then 1 else 0 end) AS filter5
FROM
tbl1
Left Join tbl2 ON tbl1.qNum = tbl2.qNum
GROUP BY
tbl1.qNum
The columns you select should always be either in the group by clause or an aggregate function should be applied to them. A group by "collapses" a group of rows and if you don't have an aggregate function on a column (which is not in the group by) a random row of that group is displayed.
Here you can read about the different aggregate functions: GROUP BY (Aggregate) Functions
The MAX() function in our case here returns the greatest value (note: not the row with the greatest value. You can also have a query like this: select min(col), max(col) from whatever).
I just want to point out that in MySQL, you can simplify the expression. It doesn't need a case statement because booleans are treated as integers with values of 0 and 1:
SELECT tbl1.qNum,
MAX(tbl2.displayNum = 1) AS filter1,
MAX(tbl2.displayNum = 2) AS filter2,
MAX(tbl2.displayNum = 3) AS filter3,
MAX(tbl2.displayNum = 4) AS filter4,
MAX(tbl2.displayNum = 5) AS filter5
FROM tbl1 Left Join
tbl2
ON tbl1.qNum = tbl2.qNum
GROUP BY tbl1.qNum;
Normally, I prefer going with ANSI standard syntax. I do, however, find this easier to read than the case syntax.
Also, you may not need tbl1 for the query, if all values you are interested in are already in tbl2.

how to select and group mysql data based on the following table

how can I achieve the desired result in mysql if my table looks like this.
result|year
1 |2011
2 |2011
1 |2011
0 |2011
1 |2012
2 |2012
1 = Won, 2 = lost, 0 = draw
Every year can have multiple values like this. Not sure how I can get the desired result like below.
year won lost draw totalPlayed
2011 2 1 1 3
2012 1 1 0 2
I have tried the following query but does not get the desired result
select year,
league_types.league_name,
sum(if(result = 1,1,0)) as won,
sum(if(result = 0,1,0)) as draw,
sum(if(result = 4,1,0)) as noResult,
sum(if(result = 2,1,0)) as lost,
sum(if(result = 3,1,0)) as tied,
sum(if(result > 0 and result < 4,1,0)) as played
from match_score_card
inner join fixtures on match_score_card.match_id = fixtures.match_id
inner join league_types on fixtures.league_id = league_types.league_id
where
team_id = 1 group by year order by year desc
Here is the SQL Fiddle that demonstrates the following query:
SELECT m.year,
SUM(CASE WHEN m.result = 1 THEN 1 ELSE 0 END) AS 'Won',
SUM(CASE WHEN m.result = 2 THEN 1 ELSE 0 END) AS 'Lost',
SUM(CASE WHEN m.result = 0 THEN 1 ELSE 0 END) AS 'Draw',
COUNT(*) AS 'TotalPlayed'
FROM MyTable AS m
GROUP BY m.year
I'm not familiar with that IF function in mySQL, but this standard SQL should work:
select year
, league_types.league_name
, sum(CASE WHEN result = 1 THEN 1 ELSE 0 END) as won
, sum(CASE WHEN result = 2 THEN 1 ELSE 0 END) as lost
, sum(CASE WHEN result = 3 THEN 1 ELSE 0 END) as draw
, sum(CASE WHEN result = 4 THEN 1 ELSE 0 END) as noResult
, sum(CASE WHEN result = 1
or result = 2 THEN 1 ELSE 0 END) as played
from match_score_card
inner join fixtures
on match_score_card.match_id = fixtures.match_id
inner join league_types
on fixtures.league_id = league_types.league_id
where team_id = 1
group by year, league_types.league_name
order by year desc, league_types.league_name
I'm guessing that you only want to count wins and losses as "played".

MySQL request count from many tables

I have tables table_one, table_two, table_three, table_four all structure is about same.
it has columns id, name, status, user_id.
i.g. i have user John with user_id 345 in every table multiple times except table_four. and status for some entrys is 1 and other entry's is 0
Now i need to count with one query how many times status for this user was 1 in each of this tables.
So i do:
SELECT table_one.user_id,
SUM(CASE WHEN table_one.status = 1 THEN 1 ELSE 0 END) AS count_tblone
SUM(CASE WHEN table_two.status = 1 THEN 1 ELSE 0 END) AS count_tbltwo
SUM(CASE WHEN table_three.status = 1 THEN 1 ELSE 0 END) AS count_tblthree
SUM(CASE WHEN table_four.status = 1 THEN 1 ELSE 0 END) AS count_tblfour
FROM table_one
LEFT JOIN table_one ON table_one.user_id = table_one.user_id
LEFT JOIN table_two ON table_two.user_id = table_one.user_id
LEFT JOIN table_three ON table_three.user_id = table_one.user_id
LEFT JOIN table_four ON table_four.user_id = table_one.user_id
WHERE tbl_one.user_id = 345
Problem is that request outputs 4, 0, 0, 0 when it should be 2, 1, 1, 0
IF i just leave SUM(CASE WHEN table_one.status = 1 THEN 1 ELSE 0 END) AS count_tblone - count_tblone will equal 4 even if in table_one - status field equals 1 in only two records for user 345.
It's a lot more typing, but I'd recommend UNION, especially if user might not be in table_one.
SELECT
SUM(count_tblone) AS count_tblone,
SUM(count_tbltwo) AS count_tbltwo,
SUM(count_tblthree) AS count_tblthree,
SUM(count_tblfour) AS count_tblfour
FROM (
SELECT
SUM(CASE WHEN table_one.status = 1 THEN 1 ELSE 0 END) AS count_tblone,
0 AS count_tbltwo,
0 AS count_tblthree,
0 AS count_tblfour
FROM table_one
WHERE tbl_one.user_id = 345
UNION
SELECT
0 AS count_tblone,
SUM(CASE WHEN table_two.status = 1 THEN 1 ELSE 0 END) AS count_tbltwo,
0 AS count_tblthree,
0 AS count_tblfour
FROM table_two
WHERE tbl_one.user_id = 345
UNION
... tables 3 and 4 ...
) AS tblMyUnionedTables

Count data from multiple tables using SUM

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