MySQL Group By Query X or not X - mysql

This should be easy but I have not found the answer. I have a query that is running a count and I want to group this within the query into those where State = 1 and State <>1 and have the sum for each group.
SELECT count(`id_job`) as count, `state`
FROM job_table
GROUP BY `state`;

You can query a boolean expression and group by it too:
SELECT state = 1, COUNT(*)
FROM job_table
GROUP BY state = 1

SELECT count(id_job) as count,state FROM job_table
GROUP BY case when state = 1 then 1 else 0

In MySQL, you can easily put these into columns:
select sum( state = 1 ) as state_1,
sum( state <> 1 ) as state_2
from job_table;
If state can be NULL, you want to be a bit careful. The second condition is safer as:
select sum( state = 1 ) as state_1,
sum( not state <=> 1 ) as state_2
from job_table;

Try this
SELECT SUM(CASE WHEN State=1 THEN 1 END) as count1,
SUM(CASE WHEN State<>1 THEN 1 END) as count2,
State
FROM job_table
GROUP BY state

Related

MySQL - join/group concat returning too many items

I've got a simple query which brings up wins, draws and losses in a head-to-head matches table.
SELECT
SUM(CASE WHEN score_w > score_m THEN 1 ELSE 0 END) AS wins_w,
SUM(CASE WHEN score_m > score_w THEN 1 ELSE 0 END) AS wins_m,
SUM(CASE WHEN score_w = score_m THEN 1 ELSE 0 END) AS draws
FROM 6dos7me3xn8
All is well. I get a single row, with the data I need as columns.
Now I want to also pull out a group concat'd list of the most recent three match dates. I tried:
SELECT
SUM(CASE WHEN mn.score_w, 0) > mn.score_m THEN 1 ELSE 0 END) AS wins_w,
SUM(CASE WHEN mn.score_m > mn.score_w THEN 1 ELSE 0 END) AS wins_m,
SUM(CASE WHEN mn.score_w = mn.score_m THEN 1 ELSE 0 END) AS draws,
GROUP_CONCAT(jn.date) AS recent
FROM 6dos7me3xn8 mn
JOIN (SELECT date FROM 6dos7me3xn8 ORDER BY date DESC LIMIT 3) jn
...but the LIMIT is having no effect, seemingly - I get all of the dates group concat'd, not just 3.
I also tried removing the JOIN and replacing the GROUP_CONCAT with
GROUP_CONCAT((SELECT date FROM 6dos7me3xn8 ORDER BY date DESC LIMIT 3)) AS recent
...but that errors with 'Subquery returns more than 1 row.'
I'm sure it's something simple, but what am I doing wrong?
If you are running MySQL 8.0, you can do this with window functions:
select
sum(score_w > score_m) as wins_w,
sum(score_m > score_w) as wins_m,
sum(score_w = score_m) as draws,
group_concat(case when rn <= 3 then date end) as recent
from (
select t.*, row_number() over(order by date desc) rn
from `6dos7me3xn8` t
) t
The subquery ranks records by descending date; we can then use that information in the outer query. Note that you don't need the case expressions: MySQL evaluates true/false conditions as 1/0 in numeric contet.
In earlier versions, the simpler approach is probably a row-limiting subquery:
select
sum(score_w > score_m) as wins_w,
sum(score_m > score_w) as wins_m,
sum(score_w = score_m) as draws,
(select group_concat(date) from (select date from `6dos7me3xn8` order by date desc limit 3) t) as recent
from `6dos7me3xn8`
You are doing a cross join. TO get the most recent three dates, you can use:
FROM (SELECT mn.*, DENSE_RANK() OVER (ORDER BY date desc) as seqnum
FROM 6dos7me3xn8 mn
) mn
WHERE seqnum <= 3
You have no ON clause to specify the relationship between the subquery and the table you're joining it to. So you get a full cross product.
You also need ORDER BY to make it return the 3 most recent dates, not any 3 dates.
SELECT
SUM(CASE WHEN mn.score_w, 0) > mn.score_m THEN 1 ELSE 0 END) AS wins_w,
SUM(CASE WHEN mn.score_m > mn.score_w THEN 1 ELSE 0 END) AS wins_m,
SUM(CASE WHEN mn.score_w = mn.score_m THEN 1 ELSE 0 END) AS draws,
GROUP_CONCAT(jn.date) AS recent
FROM 6dos7me3xn8 mn
JOIN (
SELECT DISTINCT date
FROM 6dos7me3xn8
ORDER BY date DESC
LIMIT 3
) jn ON jn.date = mn.date

Count different values in a column, while doing total and group by different column values

We have a table with data from different nodes and one of the column will have status report as "compliant or non-compliant", sample data as below
I want to filter the table in such a way that if any of the checks on a node shows non compliant, it should be flagged as non-compliant and rest as compliant. Using below query i am able to do it
SELECT COUNT(*) AS total_nodes,
SUM(fully_compliant = 0) AS Non_compliant_nodes,
SUM(fully_compliant = 1) AS compliant_nodes
FROM (
SELECT Node, CASE WHEN SUM(Status = 'Compliant') = COUNT(*) THEN 1 ELSE 0 END AS fully_compliant
FROM your_table GROUP BY Node
)
Now, i want to group and split the result by dept as below, how can i achieve this
I think you're looking for this:
select dept,
count(*) as total_nodes,
sum(case when non_compliant_chk = 0 then 1 else 0 end) as compliant_nodes,
sum(case when non_compliant_chk > 0 then 1 else 0 end) as non_compliant_nodes
from (
select dept,
node,
sum(case when 'Non-Compliant' then 1 else 0 end) as non_compliant_chk
from your_table
group by dept,
node
) v
group by dept;
With few modifications to what Brian suggested, I am able to get the desired result
select dept,
count(*) as total_nodes,
sum(case when non_compliant_chk = 0 then 1 else 0 end) as compliant_nodes,
sum(case when non_compliant_chk > 0 then 1 else 0 end) as non_compliant_nodes
from (
select dept,
node,
COUNT(CASE WHEN Compliance-Status = 'Non-Compliant' THEN 1 END) 'non_compliant_chk'
from table WHERE DOR >= DATE(NOW()) - INTERVAL 7 DAY
group by Dept,
Node
) v
group by Dept;

How to solve error "Subquery returns more than 1 row" in mysql?

i made a query such as the following in codeigniter
$query = $this->db->query('SELECT tanggal,
( SELECT COUNT(*) from absensi_siswa where kehadiran="alfa" GROUP BY tanggal ) AS alfa,
( SELECT COUNT(*) from absensi_siswa where kehadiran="izin" GROUP BY tanggal ) AS izin,
( SELECT COUNT(*) from absensi_siswa where kehadiran="hadir" GROUP BY tanggal ) AS hadir,
( SELECT COUNT(*) from absensi_siswa where kehadiran="sakit" GROUP BY tanggal ) AS sakit
FROM absensi_siswa GROUP BY tanggal');
return $query->result();
Your each sub clause (select count(*) .. ) returns morethan 1 rows, From your query i assume you need individual counts based on different values of kehadiran, you simplify your query as
SELECT tanggal,
SUM(CASE WHEN kehadiran="alfa" THEN 1 ELSE 0 END) AS alfa,
SUM(CASE WHEN kehadiran="izin" THEN 1 ELSE 0 END) AS izin,
SUM(CASE WHEN kehadiran="hadir" THEN 1 ELSE 0 END) AS hadir,
SUM(CASE WHEN kehadiran="sakit" THEN 1 ELSE 0 END) AS sakit
FROM absensi_siswa
GROUP BY tanggal
Using mysql you could make sum clause even shorter like sum(kehadiran="alfa") as alfa, Putting expression in sum(a=a) will return as a boolean 0/1 or true/false and you will get a count based on your expression

SQL count when value = 1

I'm doing a select on two tables with this:
SELECT m.torneio, m.deck, m.top, m.lugar, sum( m.quantidade ) AS quantidade, m.formato AS formato, q.quantidade AS qtorneio, t.season AS season, sum( m.top ) AS totaltops, count( m.lugar = '1' ) AS venceu
FROM `metagame` AS m, quantidade AS q, torneios AS t
WHERE m.torneio = t.nome
AND m.torneio = q.nome
GROUP BY m.deck
My problem is that venceu is counting all instances instead of only the ones when lugar = 1. Why is that?
tried with sum() too with no good results too. How can i fix this?
I am surprised that count( m.lugar = '1' ) syntaxs but it does and returns the sames as count(*). You should probably change it to sum(case when lugar = 1 else 0 end) as venceu. You should also look closely at the group by to be sure it works as you expect (i suspect not).
count(x) does not accept an expression.
It's only counting how many times x is returned.
What you should do is check if m.lugar is 1 and yes add one to the counter else do nothing.
Inline checks can be done like so:
case when m.lugar = '1' then 1 else 0 end
Then add all the one you gets :
sum(case when m.lugar = '1' then 1 else 0 end)
Your final query should look like this:
SELECT
m.torneio,
m.deck,
m.top,
m.lugar,
sum( m.quantidade ) AS quantidade,
m.formato AS formato,
q.quantidade AS qtorneio,
t.season AS season,
sum( m.top ) AS totaltops,
sum(case when m.lugar = '1' then 1 else 0 end) AS venceu
FROM
`metagame` AS m,
quantidade AS q,
torneios AS t
WHERE
m.torneio = t.nome
AND m.torneio = q.nome
GROUP BY
m.deck
If I understand your question you can use this:
sum(case when m.lugar = '1' then 1 else 0 end)
or you can try having clause
SELECT column_name(s)
FROM table_name
WHERE condition
GROUP BY column_name(s)
HAVING condition
ORDER BY column_name(s);

get count of two table fields in one query

I am trying to get the count of females and males in the gender field of a table.
Is there a way to get the count of each in one query?
Something like:
select * from table count(where gender = 'm') as total_males, count(where gender = 'f') as total_females;
or will it require two queries?
select count(*) from table where gender = 'm';
select count(*) from table where gender = 'f';
This is basically a PIVOT. MySQL does not have a pivot so you can use an aggregate function with a CASE statement to perform this:
select
sum(case when gender = 'm' then 1 else 0 end) Total_Male,
sum(case when gender = 'f' then 1 else 0 end) Total_Female
from yourtable
See SQL Fiddle with Demo
Or using COUNT:
select
count(case when gender = 'm' then 1 else null end) Total_Male,
count(case when gender = 'f' then 1 else null end) Total_Female
from yourtable;
See SQL Fiddle with Demo
Something like this will work:
SELECT SUM(IF(t.gender='m',1,0)) AS total_males
, SUM(IF(t.gender='f',1,0)) AS total_females
FROM mytable t
The "trick" here is that we are using a conditional test to return either a 0 or a 1 for each row, and then adding up the 0's and 1's. To make this a little more clear, I am using the SUM aggregate function rather than COUNT, although COUNT could be used just as easily, though we'd need to return a NULL in place of the zero.
SELECT COUNT(IF(t.gender='m',1,NULL)) AS total_males
, COUNT(IF(t.gender='f',1,NULL)) AS total_females
FROM mytable t
Consider that the two expressions in the SELECT list of this query:
SELECT COUNT(1)
, SUM(1)
FROM mytable t
Will return the same value.
If you want to avoid the MySQL IF function, this can also be done using the ANSI SQL CASE expression:
SELECT SUM( CASE WHEN t.gender = 'm' THEN 1 ELSE 0 END )) AS total_males
, SUM( CASE WHEN t.gender = 'f' THEN 1 ELSE 0 END )) AS total_females
FROM mytable t
select sum(case when gender='m' then 1 else null end) as total_males, sum(case when gender='f' then 1 else null end) as total_females from ...
Should work just fine!
If your only issue is to avoid two queries, you can always write two queries as subselects of one query.
Select (select 1 from dual) as one, (select 2 from dual) as two from dual
This would work for your scenario, too.