grouping resultset - mysql - mysql

I have the following sql which returns the total number of books grouped by status
select COUNT(BOOK_ID) AS book_num, BOOK_STATUS_FK from BOOKS group by BOOK_STATUS_FK;
+---------+------------------+
| book_num | BOOK_STATUS_FK |
+---------+------------------+
| 57 | 2 |
| 162 | 3 |
| 9736 | 4 |
| 104 | 5 |
| 29 | 22 |
| 1 | 23 |
| 5 | 25 |
| 14 | 54 |
+---------+------------------+
I would like to group the resultset into 2 rows only where one row represents the number of books with BOOK_STATUS_FK > 4 and the 2nd to represent the number of books with BOOK_STATUS_FK <= 4
Is there a way of doing that in sql?
Thanks for your suggestions.

The 2 row solution Gordon Linoff suggests wont produce 2 rows when one of the counts is 0.
The following will give both counts in a single row:
select ifnull( sum( if( book_status_fk > 4, 1, 0 ) ), 0), ifnull( sum( if( book_status_fk <= 4, 1, 0 ) ), 0 )
from books
Edit: added ifnull's

This is an aggregation with a case statement:
select (case when book_tatus_fk > 4 then '>4' else '<=4' end) as grp, count(*)
from books
group by (case when book_tatus_fk > 4 then '>4' else '<=4' end)

If you always need two rows, even if count of a group is 0, you can use palindrom's solution or you can use this slightly modified version of Gordon Linoff's query:
select grp.g, count(BOOK_STATUS_FK)
from
(select '<=4' g union all select '>4') grp left join books
on grp.g = case when book_status_fk > 4 then '>4' else '<=4' end
group by grp.g

Related

MySQL - count by month (including missing records)

I have this SELECT:
SELECT
DATE_FORMAT(`created`, '%Y-%m') as byMonth,
COUNT(*) AS Total
FROM
`qualitaet`
WHERE
`created` >= MAKEDATE(year(now()-interval 1 year),1) + interval 5 month
AND
`status`=1
GROUP BY
YEAR(`created`), MONTH(`created`)
ORDER BY
YEAR(`created`) ASC
and get this result:
| byMonth | Total |
| 2015-06 | 2 |
| 2015-09 | 12 |
| 2015-10 | 3 |
| 2015-12 | 8 |
| 2016-01 | 1 |
see SQL-Fiddle here
The WHERE clause is important because i need it as current fiscal year starting on June, 1 in my example.
As you can see, i have no records for Jul, Aug and Nov. But i need this records with zero in Total.
So my result should look like this:
| byMonth | Total |
| 2015-06 | 2 |
| 2015-07 | 0 |
| 2015-08 | 0 |
| 2015-09 | 12 |
| 2015-10 | 3 |
| 2015-11 | 0 |
| 2015-12 | 8 |
| 2016-01 | 1 |
is there a way to get this result?
You need to generate all the wanted dates, and then left join your data to the dates. Note also that it is important to put some predicates in the left join's ON clause, and others in the WHERE clause:
SELECT
CONCAT(y, '-', LPAD(m, 2, '0')) as byMonth,
COUNT(`created`) AS Total
FROM (
SELECT year(now()) AS y UNION ALL
SELECT year(now()) - 1 AS y
) `years`
CROSS JOIN (
SELECT 1 AS m UNION ALL
SELECT 2 AS m UNION ALL
SELECT 3 AS m UNION ALL
SELECT 4 AS m UNION ALL
SELECT 5 AS m UNION ALL
SELECT 6 AS m UNION ALL
SELECT 7 AS m UNION ALL
SELECT 8 AS m UNION ALL
SELECT 9 AS m UNION ALL
SELECT 10 AS m UNION ALL
SELECT 11 AS m UNION ALL
SELECT 12 AS m
) `months`
LEFT JOIN `qualitaet` q
ON YEAR(`created`) = y
AND MONTH(`created`) = m
AND `status` = 1
WHERE STR_TO_DATE(CONCAT(y, '-', m, '-01'), '%Y-%m-%d')
>= MAKEDATE(year(now()-interval 1 year),1) + interval 5 month
AND STR_TO_DATE(CONCAT(y, '-', m, '-01'), '%Y-%m-%d')
<= now()
GROUP BY y, m
ORDER BY y, m
How does the above work?
CROSS JOIN creates a cartesian product between all available years and all available months. This is what you want, you want all year-month combinations with no gaps.
LEFT JOIN adds all the qualitaet records to the result (if they exist) and joins them to the year-month cartesian product from before. It is important to put prediactes like the status = 1 predicate here.
COUNT(created) counts only non-NULL values of created, i.e. when the LEFT JOIN produces no rows for any given year-month, we want 0 as a result, not 1, i.e. we don't want to count the NULL value.
A note on performance
The above makes heavy use of string operations and date time arithmetic in your ON and WHERE predicates. This isn't going to perform for lots of data. In that case, you should better pre-truncate and index your year-months in the qualitaet table, and operate only on those values.

merge sum of counts from id in mysql

I not mysql expert and I need help to make count query, I need merge sum of count from id 5 and 11 to one count number = 286 and give platform name as GCS in this case.
SELECT DISTINCT (p.id) AS id, (p.name) AS platform,
IFNULL(count(e.id), 0) AS count
FROM event e, lu_platform p
WHERE e.platform_id = p.id
AND p.id NOT IN ( 10, 15, 17, 18 )
AND e.sourcetype_id = 1
AND e.event_datetime BETWEEN '2013-11-4'
AND '2013-11-10' AND e.sender_id NOT IN ( 759, 73 )
GROUP BY p.id ORDER BY id;
+----+---------------------------+-------+
| id | platform | count |
+----+---------------------------+-------+
| 3 | GGG | 414 |
| 4 | KIKI | 156 |
| 5 | KJC | 284 |
| 6 | LOLO | 4 |
| 7 | MOD | 1147 |
| 8 | MARKT | 1049 |
| 11 | GCS | 2 |
| 12 | POLAR | 30 |
| 14 | GUAE | 145 |
+----+---------------------------+-------+
One possible way to do it - use a subquery, a sum, and IF function, or a case expression CASE WHEN THEN:
SELECT sum( case when id in ( 5, 11 ) then count else 0 end ) as count_5_11,
sum( if( id in ( 3, 4 ), count, 0 ) ) As count_3_4
FROM (
-- your query goes here
SELECT DISTINCT (p.id) AS id, (p.name) AS platform,
IFNULL(count(e.id), 0) AS count
....
....
....
....
....
) AS some_alias
Try to use sub-query
SELECT SUM(count) as count, `GCG` as platform
FROM
(
SELECT DISTINCT (p.id) AS id, (p.name) AS platform,
IFNULL(count(e.id), 0) AS count
FROM event e, lu_platform p
WHERE e.platform_id = p.id
AND p.id NOT IN ( 10, 15, 17, 18 )
AND e.sourcetype_id = 1
AND e.event_datetime BETWEEN '2013-11-4'
AND '2013-11-10' AND e.sender_id NOT IN ( 759, 73 )
GROUP BY p.id ORDER BY id;
) T
WHERE id IN (5,11)
Thank you for reply.
In the both above answers I get only one results instead of list. I think the best way is do this in application as was mentioned by sarwar026.
I noticed also that ISNULL(count(e.id), 0) is not working as I expected, platforms without records not returning 0, they are skipped.

MySQL count per item

I'm currently trying to make a mysql query that will count the number of zeros and ones per item, in the following way:
Table:
ID | PollID | Value
------------------------------------
1 | 1 | 1
2 | 1 | 1
3 | 2 | 0
4 | 2 | 1
5 | 1 | 0
And the result I want is:
Poll | one | zero
----------------------------------
1 | 2 | 1
2 | 1 | 1
Thanks for the help!
This is the shortest possible answer in MySQL because it supports boolean arithmetic.
SELECT PollID,
SUM(value = 1) AS `One`,
SUM(value = 0) AS `Zero`
FROM tableName
GROUP BY PollID
SQLFiddle Demo
select z.pollid,z.ones,s.zeros
from (select a.pollid,count(a.value) as ones from test a
where a.value=1
group by a.pollid) z
left join
(select b.pollid,count(b.value) as zeros from test b
where b.value=0 group by b.pollid) s
on z.pollid=s.pollid;
try this
select table.pollid,
Switch(table.value Like 1, 1)AS one,
Switch(table.value Like 0, 1)AS zero
from table
group by pollid

MySQL : How to convert multiple row to single row? in mysql

I want to convert multiple rows to a single row, based on week. It should look like the following. Can any one help me?
id | Weight | Created |
1 | 120 | 02-04-2012 |
2 | 110 | 09-04-2012 |
1 | 100 | 16-04-2012 |
1 | 130 | 23-04-2012 |
2 | 140 | 30-04-2012 |
3 | 150 | 07-05-2012 |
Result should look like this:
id | Weight_week1 | Weight_week2 | weight_week3 | weight_week4 |
1 | 120 | 100 | 130 | |
2 | 110 | 140 | | |
3 | 150 | | | |
Thanks in advance.
if this a single table then
SELECT GROUP_CONCAT(weight) as Weight,
WEEK(Created) as Week
Group by Week(Created)
This will give you a row each having week id and comma seperated whights
You could do it like this:
SELECT
t.id,
SUM(CASE WHEN WeekNbr=1 THEN Table1.Weight ELSE 0 END) AS Weight_week1,
SUM(CASE WHEN WeekNbr=2 THEN Table1.Weight ELSE 0 END) AS Weight_week2,
SUM(CASE WHEN WeekNbr=3 THEN Table1.Weight ELSE 0 END) AS Weight_week3,
SUM(CASE WHEN WeekNbr=4 THEN Table1.Weight ELSE 0 END) AS Weight_week4
FROM
(
SELECT
(
WEEK(Created, 5) -
WEEK(DATE_SUB(Created, INTERVAL DAYOFMONTH(Created) - 1 DAY), 5) + 1
)as WeekNbr,
Table1.id,
Table1.Weight,
Table1.Created
FROM
Table1
) AS t
GROUP BY
t.id
I don't know if you want a AVG,SUM,MAX or MIN but you can change the aggregate to what you want.
Useful references:
Function for week of the month in mysql
you cannot create fields on the fly like that but you can group them.
use GROUP_CONCAT to deliver results with a delimiter that you can separate on later.
You could also do this:
SELECT id, created, weight, (
SELECT MIN( created ) FROM weights WHERE w.id = weights.id
) AS `min` , round( DATEDIFF( created, (
SELECT MIN( created )
FROM weights
WHERE w.id = weights.id ) ) /7) AS diff
FROM weights AS w
ORDER BY id, diff
This code does not do pivot table. You should add some additional code to convert the data to your needs. You may run into trouble if you use WEEK() because of the years.

MySQL: How to return only one row based on criteria within a resultset

I have the following table:
id | group | value
1 | 1 | 10
2 | 1 | 20
3 | 1 | 30
4 | 0 | 20
5 | 0 | 20
6 | 0 | 10
I want to return the highest value where the group is 1 (=30) and all of the values where the group is 0, into one resultset.
I have to do this in one statement, and I guess I should use an IF statement within a SELECT statement, but I can't work out how. Can anyone help to point me in the right direction?
(select max(value) from the_table where group = 1)
union
(select value from the_table where group = 0)
If (group +value) is unique, you can also do it without union (as proposed by Ray Toal)
SELECT a.value
FROM table1 a
WHERE a.`group`=0 or (a.`group`=1 AND a.value =
(SELECT MAX(value) FROM table1 b WHERE b.`group`=1))