SQL count specific value over multiple columns and rows - mysql

I feel as if this should be quite easy, but can't seem to find a solution.
Suppose I have the following table:
|--------||---||---||---||---||---||---||---|
|Company ||q1 ||q2 ||q3 ||q4 ||q5 ||q6 ||q7 |
|--------||---||---||---||---||---||---||---|
|abc ||1 ||2 ||1 ||3 ||2 ||2 ||1 |
|abc ||2 ||2 ||1 ||2 ||3 ||1 ||1 |
|abc ||1 ||1 ||3 ||3 ||1 ||2 ||2 |
|abc ||1 ||2 ||1 ||3 ||0 ||1 ||3 |
I want to count the number of times '1' appears in the table, so the query should, in this case, result with 12. I tried 'hardcoding' it, like the following query. But that just results in the rows containing a 1, so in this case 4. How do I count the number of times '1' occurs, thus resulting in a count of 12?
SELECT COUNT(*)
FROM table
WHERE Company = 'abc'
AND (
q1 = '1'
OR q2 = '1'
OR q3 = '1'
OR q4 = '1'
OR q5 = '1'
OR q6 = '1'
OR q7 = '1'
)

SELECT SUM(
IF(q1 = 1, 1, 0) +
IF(q2 = 1, 1, 0) +
IF(q3 = 1, 1, 0) +
IF(q4 = 1, 1, 0) +
IF(q5 = 1, 1, 0) +
IF(q6 = 1, 1, 0) +
IF(q7 = 1, 1, 0)
)
FROM table
WHERE Company = 'abc'

This is very weird assignment but:
http://sqlfiddle.com/#!9/2e7aa/3
SELECT SUM((q1='1')+(q2='1')+(q3='1')+(q4='1')+(q5='1')+(q6='1')+(q7='1'))
FROM table
WHERE Company = 'abc'
AND '1' IN (q1,q2,q3,q4,q5,q6,q7)

Not so easy, each column needs to be hard-coded. I'd try something using a CASE or DECODE.
SELECT
SUM(
CASE WHEN q1 = 1 THEN 1 ELSE 0 END +
CASE WHEN q2 = 1 THEN 1 ELSE 0 END +
CASE WHEN q3 = 1 THEN 1 ELSE 0 END +
CASE WHEN q4 = 1 THEN 1 ELSE 0 END +
CASE WHEN q5 = 1 THEN 1 ELSE 0 END +
CASE WHEN q6 = 1 THEN 1 ELSE 0 END +
CASE WHEN q7 = 1 THEN 1 ELSE 0 END)
FROM table
WHERE Company = 'abc'
Using a SUM instead of a COUNT will allow the CASE statement to be SUMed.

Use conditional COUNT
SELECT COUNT(case when q1 = '1' then 1 end) +
COUNT(case when q2 = '1' then 1 end) +
COUNT(case when q3 = '1' then 1 end) +
COUNT(case when q4 = '1' then 1 end) +
COUNT(case when q5 = '1' then 1 end) +
COUNT(case when q6 = '1' then 1 end) +
COUNT(case when q7 = '1' then 1 end) as ones_total
FROM table
WHERE Company = 'abc'

For reasons of efficiency I don't recommend actually using this approach; but for learning purposes here's another way you could have broken down the problem along the lines of the way you were thinking about it.
select sum(c) as total_ones
from
(
select count(*) c from table where q1 = 1 union all
select count(*) from table where q2 = 1 union all
select count(*) from table where q3 = 1 union all
select count(*) from table where q4 = 1 union all
select count(*) from table where q5 = 1 union all
select count(*) from table where q6 = 1 union all
select count(*) from table where q7 = 1
) t

Related

How to group row values based on same column name

I have created the schema and query on SQL Fiddle:
SQL Fiddle(http://sqlfiddle.com/#!9/e43a13/1)
I am trying to get prices of work type according to the urgency and academic level, I am successful in getting that but I can't figure out how to format it in this way:
| Urgency | Academic Level Name 1 | Academic Level 2 | Academic Level 3 |
-------------------------------------------------------------------------------------------
| 1 hour |Price Of Academic Level 1|Price Of Academic Level 2 | Price Of Academic Level 3|
-------------------------------------------------------------------------------------------
I have as the output:
If you are trying to group by urgency, you may consider below query.
SELECT
utt.UrgencyTime as `Urgency`
, sum(case when al.AcademicID = 1 then wtd.Price else 0 end) as `Academic Level Name 1`
, sum(case when al.AcademicID = 2 then wtd.Price else 0 end) as `Academic Level Name 2`
, sum(case when al.AcademicID >= 3 then wtd.Price else 0 end) as `Academic Level Name 3`
FROM `WorkTypeData` wtd
JOIN UrgencyTimeTable utt ON utt.UrgencyID = wtd.UrgencyID
JOIN WorkType wt ON wt.WorkTypeID = wtd.WorkTypeID
JOIN AcademicLevel al ON al.AcademicID = wtd.AcademicLevelID
WHERE wt.WorkTypeID = '1'
GROUP BY utt.UrgencyTime
to get prices of work type according to the urgency and academic level, use the following query,
SELECT
utt.UrgencyTime as Urgency,
al.LevelName as "Academic Level Name",
wtd.Price
FROM `WorkTypeData` wtd
JOIN
UrgencyTimeTable utt
ON
utt.UrgencyID = wtd.UrgencyID
JOIN
WorkType wt
ON
wt.WorkTypeID = wtd.WorkTypeID
JOIN
AcademicLevel al
ON
al.AcademicID = wtd.AcademicLevelID
WHERE wt.WorkTypeID = '1'
group by utt.UrgencyTime, al.LevelName
I Think this might be helpful
SELECT
utt.UrgencyTime AS `Urgency` ,
CASE
WHEN al.AcademicID = 1 THEN wtd.Price
ELSE 0
END AS `Academic LEVEL Name 1` ,
CASE
WHEN al.AcademicID = 2 THEN wtd.Price
ELSE 0
END AS `Academic LEVEL Name 2` ,
CASE
WHEN al.AcademicID = 7 THEN wtd.Price
ELSE 0
END AS `Academic LEVEL Name 7` ,
CASE
WHEN al.AcademicID = 8 THEN wtd.Price
ELSE 0
END AS `Academic LEVEL Name 8` ,
CASE
WHEN al.AcademicID = 9 THEN wtd.Price
ELSE 0
END AS `Academic LEVEL Name 9`
FROM
`WorkTypeData` wtd
JOIN UrgencyTimeTable utt ON utt.UrgencyID = wtd.UrgencyID
JOIN WorkType wt ON wt.WorkTypeID = wtd.WorkTypeID
JOIN AcademicLevel al ON al.AcademicID = wtd.AcademicLevelID
group BY utt.UrgencyTime, al.AcademicID

How can I simplify this mysql query, based on 2 tables?

after 2 days of trial and error, I just completed this mysql query. It works fine, but it does look really hideous. Is it possible to simplify my query ? All help is of course greatly appreciated.
SELECT
p.*,
COUNT(oT.Ontmoeting_ID) + COUNT(oU.Ontmoeting_ID) AS 'played',
COUNT(case oT.Ontmoeting_Uitploeg_FF when 1 then 1 else case oT.Ontmoeting_Thuisploeg_Bonus when 3 then 1 else null end end)
+ COUNT(case oU.Ontmoeting_Thuisploeg_FF when 1 then 1 else case oU.Ontmoeting_Uitploeg_Bonus when 3 then 1 else null end end)
- COUNT(case oU.Ontmoeting_Thuisploeg_FF when 1 then case oU.Ontmoeting_Uitploeg_FF when 1 then 1 else null end end)
- COUNT(case oT.Ontmoeting_Uitploeg_FF when 1 then case oT.Ontmoeting_Thuisploeg_FF when 1 then 1 else null end end) AS 'won',
COUNT(case oT.Ontmoeting_Thuisploeg_Bonus when 1 then 1 else null end) +
COUNT(case oU.Ontmoeting_Uitploeg_Bonus when 1 then 1 else null end) AS 'draw',
COUNT(case oU.Ontmoeting_Uitploeg_FF when 1 then 1 else case oU.Ontmoeting_Uitploeg_Bonus when 0 then 1 else null end end)
+ COUNT(case oT.Ontmoeting_Thuisploeg_FF when 1 then 1 else case oT.Ontmoeting_Thuisploeg_Bonus when 0 then 1 else null end end) AS 'lost',
SUM(COALESCE(oT.Ontmoeting_Thuisploeg_Totaal, 0)) + SUM(COALESCE(oU.Ontmoeting_Uitploeg_Totaal, 0)) AS 'Totaal'
FROM ploegen AS p
LEFT OUTER JOIN ontmoetingen AS oT ON oT.Ontmoeting_Thuisploeg_ID = p.Ploeg_ID AND oT.Ontmoeting_Competitie_ID = 1 and oT.Ontmoeting_Speelweek <= 2 AND (oT.Ontmoeting_Thuisploeg_Totaal > 0 OR oT.Ontmoeting_Thuisploeg_FF = 1)
LEFT OUTER JOIN ontmoetingen AS oU ON oU.Ontmoeting_Uitploeg_ID = p.Ploeg_ID and oU.Ontmoeting_Competitie_ID = 1 AND oU.Ontmoeting_Speelweek <= 2 AND (oU.Ontmoeting_Uitploeg_Totaal > 0 OR oU.Ontmoeting_Uitploeg_FF = 1)WHERE p.Ploeg_ID IN (select Ontmoeting_Thuisploeg_ID from ontmoetingen WHERE Ontmoeting_Competitie_ID = 1)
GROUP BY p.Ploeg_ID
ORDER BY totaal DESC
The query is based on 2 tables, "ontmoetingen" and "ploegen" (in English: "encounters" and "teams"). It calculates the overall standings of the teams, adding all played, won and draw encounters. The main difficulty is that all teams play both home and away games and those have to be summarized ("Thuisploeg" = "Home Team", "Uitploeg" = "Away Team". Please let me know if more info on the tables is required to help me out. Thx !
Table: "Ontmoetingen":
Table: "ploegen":
Expected output:
All numbers in output are fictional, so not corresponding to table data. Hope this helps !

Complex querying on table with multiple userids

I have a table like this:
score
id week status
1 1 0
2 1 1
3 1 0
4 1 0
1 2 0
2 2 1
3 2 0
4 2 0
1 3 1
2 3 1
3 3 1
4 3 0
I want to get all the id's of people who have a status of zero for all weeks except for week 3. something like this:
Result:
result:
id w1.status w2.status w3.status
1 0 0 1
3 0 0 1
I have this query, but it is terribly inefficient on larger datasets.
SELECT w1.id, w1.status, w2.status, w3.status
FROM
(SELECT s.id, s.status
FROM score s
WHERE s.week = 1) w1
LEFT JOIN
(SELECT s.id, s.status
FROM score s
WHERE s.week = 2) w2 ON w1.id=w2.id
LEFT JOIN
(SELECT s.id, s.status
FROM score s
WHERE s.week = 3) w3 ON w1.id=w3.id
WHERE w1.status=0 AND w2.status=0 AND w3.status=1
I am looking for a more efficient way to calculate the above.
select id
from score
where week in (1, 2, 3)
group by id
having sum(
case
when week in (1, 2) and status = 0 then 1
when week = 3 and status = 1 then 1
else 0
end
) = 3
Or more generically...
select id
from score
group by id
having
sum(case when status = 0 then 1 else 0 end) = count(*) - 1
and min(case when status = 1 then week else null end) = max(week)
You can do using not exists as
select
t1.id,
'0' as `w1_status` ,
'0' as `w2_status`,
'1' as `w3_status`
from score t1
where
t1.week = 3
and t1.status = 1
and not exists(
select 1 from score t2
where t1.id = t2.id and t1.week <> t2.week and t2.status = 1
);
For better performance you can add index in the table as
alter table score add index week_status_idx (week,status);
In case of static number of weeks (1-3), group_concat may be used as a hack..
Concept:
SELECT
id,
group_concat(status) as totalStatus
/*(w1,w2=0,w3=1 always!)*/
FROM
tableName
WHERE
totalStatus = '(0,0,1)' /* w1=0,w2=1,w3=1 */
GROUP BY
id
ORDER BY
week ASC
(Written on the go. Not tested)
SELECT p1.id, p1.status, p2.status, p3.status
FROM score p1
JOIN score p2 ON p1.id = p2.id
JOIN score p3 ON p2.id = p3.id
WHERE p1.week = 1
AND p1.status = 0
AND p2.week = 2
AND p2.status = 0
AND p3.week = 3
AND p3.status = 1
Try this, should work

Count multiple columns

I have this table:
Teams:
ID | Size1 | Size2 | Size3 | Size4 | ... (other columns)..
----------------------------------------
1 | L | XL | L | M | ...
----------------------------------------
2 | S | L | S | XXL | ...
----------------------------------------
3 | M | XXL | L | M | ...
----------------------------------------
4 | L | XL | XS | XXL | ...
What is the most effective (and simplest) MySQL query to count all L in table?
I'd like to have only one field in result which is the count of all L in all columns.
EDIT:
Just to clarify little more, in my table there is 152 Ls in first column, 2 in second, 151 in third and 3 in fourth and I expect 308 as result
You can use SUM and CASE to do that:
SELECT
sum(case when Size1 = 'L' then 1 else 0 end) +
sum(case when Size2 = 'L' then 1 else 0 end) +
sum(case when Size3 = 'L' then 1 else 0 end) +
sum(case when Size4 = 'L' then 1 else 0 end)
FROM Teams;
Alternatively instead of CASE, you can use IF:
SELECT
sum(IF(Size1 = 'L',1, 0)) +
sum(IF(Size2 = 'L',1, 0)) +
sum(IF(Size3 = 'L',1, 0)) +
sum(IF(Size4 = 'L',1, 0))
FROM Teams;
This is actually the same.
Edit.
According to Andomar's comment there's even simpler solution:
SELECT
sum(Size1 = 'L') +
sum(Size2 = 'L') +
sum(Size3 = 'L') +
sum(Size4 = 'L')
FROM Teams;
This is correct since true is equal to 1 in MySQL. I've just verified this. ;-)
2nd Edit
Next step to simplify this - only one SUM usage:
SELECT sum(
(Size1 = 'L') +
(Size2 = 'L') +
(Size3 = 'L') +
(Size4 = 'L') )
FROM Teams;
Please try the following query:
SELECT sum(
IF(Size1='L', 1, 0) +
IF(Size2='L', 1, 0) +
IF(Size3='L', 1, 0) +
IF(Size4='L', 1, 0)
) as total FROM Teams
Assuming there can only be one 'L' per row:
select count(*)
from YourTable
where 'L' in (Size1, Size2, ..., SizeN)
Or a normalizing solution, which supports multiple 'L''s per row:
select count(*)
from (
select size1 as size
from YourTable
union all
select size2
from YourTable
union all
select size3
from YourTable
union all
...
)
where size = 'L'
Your WHERE condition is going to be quite large but you could use this:
SELECT count(Size1) + count(size2) AS total WHERE size1 = 'L' OR size2 = 'L'
This solution will count all columns and total based on individual column comparisons.

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