How to group data in mysql for multiple datas? - mysql

I have a table
id | event_id | stat_count | month | year
1 | 1 | 12 | 01 | 2070
2 | 1 | 11 | 02 | 2070
3 | 2 | 14 | 01 | 2070
4 | 2 | 15 | 04 | 2070
and so on.
I need to fetch data by grouping by event_id. Month will be from 01 to 12 but not necessarily all months and stat_count might have been inserted in the table. The date provided is not in English date format.
I need to have result as follows
event data
Fevent 12,21,0,3,0,9,0,0,12,23,34,0
Fevent2 12,0,3,4,3,0,3,2,3,4,0,0
and so on.
Update:
Here is the query I have tried using group_concat too:
select
(select event_name from vital_events where id=VEC.vital_event_id) name,
group_concat(
CASE when VEC.month='01' then VEC.stat_count
when VEC.month='02' then VEC.stat_count
when VEC.month='03' then VEC.stat_count
when VEC.month='04' then VEC.stat_count
when VEC.month='05' then VEC.stat_count
when VEC.month='06' then VEC.stat_count
when VEC.month='07' then VEC.stat_count
when VEC.month='08' then VEC.stat_count
when VEC.month='09' then VEC.stat_count
when VEC.month='10' then VEC.stat_count
when VEC.month_nepali='11' then VEC.stat_count
when VEC.month='12' then VEC.stat_count
else 0 end order by VEC.month
)as data
from vital_event_counts as VEC
group by VEC.vital_event_id

Another version using a CROSS JOIN to find all months that should be included and then using a LEFT JOIN against the table with existing values;
SELECT x.event_id,
GROUP_CONCAT(COALESCE(stat_count,0) ORDER BY x.month) stat_count
FROM
(SELECT DISTINCT event_id, m.month FROM Table1 CROSS JOIN
(SELECT 1 month UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL
SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL
SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL
SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12) m
) x
LEFT JOIN Table1
ON table1.month = x.month AND table1.event_id = x.event_id
AND year = 2070
GROUP BY x.event_id
An SQLfiddle to test with.

Not pretty, but…
SELECT
event_id,
CONCAT(
(SELECT COALESCE(SUM(stat_count), 0) FROM tbl WHERE id = t.id AND month = 1), ',',
(SELECT COALESCE(SUM(stat_count), 0) FROM tbl WHERE id = t.id AND month = 2), ',',
(SELECT COALESCE(SUM(stat_count), 0) FROM tbl WHERE id = t.id AND month = 3), ',',
⋮
(SELECT COALESCE(SUM(stat_count), 0) FROM tbl WHERE id = t.id AND month = 12)
) AS data
FROM
tbl AS t

Related

MySQL - Count Rows between two values in a Column repeatedly

I have a table like so
id | status | data | date
----|---------|--------|-------
1 | START | a4c | Jan 1
2 | WORKING | 2w3 | Dec 29
3 | WORKING | 2d3 | Dec 29
4 | WORKING | 3ew | Dec 26
5 | WORKING | 5r5 | Dec 23
6 | START | 2q3 | Dec 22
7 | WORKING | 32w | Dec 20
8 | WORKING | 9k5 | Dec 10
and so on...
What I am trying to do, is to get the number of 'WORKING' rows between two 'START' i.e.
id | status | count | date
----|---------|--------|-------
1 | START | 4 | Jan 1
6 | START | 2 | Dec 22
and so on ...
I am using MySql 5.7.28.
Highly appreciate any help/suggestion!
date is unusable in the example, try using id as an ordering column instead
select id, status,
(select count(*)
from mytable t2
where t2.id > t.id and t2.status='WORKING'
and not exists (select 1
from mytable t3
where t3.id > t.id and t3.id < t2.id and status='START')
) count,
date
from mytable t
where status='START';
Fiddle
Assuming id is safe then you can do this by finding the next id for each block (and assigning some dummy values) then grouping by next id
drop table if exists t;
create table t
(id int,status varchar(20), data varchar(3),date varchar(10));
insert into t values
( 1 , 'START' , 'a4c' , 'Jan 1'),
( 2 , 'WORKING' , '2w3' , 'Dec 29'),
( 3 , 'WORKING' , '2d3' , 'Dec 29'),
( 4 , 'WORKING' , '3ew' , 'Dec 26'),
( 5 , 'WORKING' , '5r5' , 'Dec 23'),
( 6 , 'START' , '2q3' , 'Dec 22'),
( 7 , 'WORKING' , '32w' , 'Dec 20'),
( 8 , 'WORKING' , '9k5' , 'Dec 10');
SELECT MIN(ID) ID,
'START' STATUS,
SUM(CASE WHEN STATUS <> 'START' THEN 1 ELSE 0 END) AS OBS,
Max(DATE) DATE
FROM
(
select t.*,
CASE WHEN STATUS = 'START' THEN DATE ELSE '' END AS DT,
COALESCE(
(select t1.id from t t1 where t1.STATUS = 'START' and t1.id > t.id ORDER BY T1.ID limit 1)
,99999) NEXTID
from t
) S
GROUP BY NEXTID;
+------+--------+------+--------+
| ID | STATUS | OBS | DATE |
+------+--------+------+--------+
| 1 | START | 4 | Jan 1 |
| 6 | START | 2 | Dec 22 |
+------+--------+------+--------+
2 rows in set (0.00 sec)
This is a form of gaps-and-islands problem -- which is simpler in MySQL 8+ using window functions.
In older versions, probably the most efficient method is to accumulate a count of starts to define groupings for the rows. You can do this using variables and then aggregate:
select min(id) as id, 'START' as status, sum(status = 'WORKING') as num_working, max(date) as date
from (select t.*, (#s := #s + (t.status = 'START')) as grp
from (select t.* from t order by id asc) t cross join
(select #s := 0) params
) t
group by grp
order by min(id);
Here is a db<>fiddle.
SELECT id, status, `count`, `date`
FROM ( SELECT #count `count`,
id,
status,
`date`,
#count:=(#status=status)*#count+1,
#status:=status
FROM test,
( SELECT #count:=0, #status:='' ) init_vars
ORDER BY id DESC
) calculations
WHERE status='START'
ORDER BY id
> Since I am still in design/development I can move to MySQL 8 if that makes it easier for this logic? Any idea how this could be done with Windows functions? – N0000B
WITH cte AS ( SELECT id,
status,
`date`,
SUM(status='WORKING') OVER (ORDER BY id DESC) workings
FROM test
ORDER BY id )
SELECT id,
status,
workings - COALESCE(LEAD(workings) OVER (ORDER BY id), 0) `count`,
`date`
FROM cte
WHERE status='START'
ORDER BY id
fiddle

Get result for all dates within a given range including non-existent dates

(UPDATED)
I have two tables like below
Table subscribers:
id | service_id | subscribe_date
1 | 1 | 2016-08-10
2 | 2 | 2016-08-09
3 | 2 | 2016-08-05
4 | 1 | 2016-08-03
Table services:
id | service_name
1 | test1
2 | test2
3 | test2
subscribers.service_id has a foreign key on services.id
I want to get a data from this tables like below :
service_id | subscribe_date | count
1 | 2016-08-10 | 1
1 | 2016-08-09 | 0
2 | 2016-08-10 | 0
2 | 2016-08-09 | 1
3 | 2016-08-10 | 0
3 | 2016-08-09 | 0
I trying to get this data with below query:
SELECT COUNT(*), subscribe_date, service_id
FROM subscribers
INNER JOIN services ON subscribers.service_id = services.id
WHERE subscribe_date BETWEEN '2016-08-09' AND '2016-08-10'
GROUP BY service_id, subscribe_date;
But I am not successful. I get the below result:
1 | 2016-08-10 | 1
2 | 2016-08-09 | 1
Here you go:
Since you don't have any calendar table so that we need to created all the dates between your given date range (inclusive) through a query. But like I said you need to agree on the terms & conditions of this query before using it.
SELECT
dateWiseServices.id AS service_id,
dateWiseServices.`Day` AS subscribed_date,
COALESCE(yourQuery.total,0) AS cnt
FROM
(
SELECT
S.id,
dateTable.Day
FROM
(
SELECT ADDDATE('2016-08-09', INTERVAL #i:=#i+1 DAY) AS DAY
FROM (
SELECT a.a
FROM (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS a
CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS b
CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS c
) a
JOIN (SELECT #i := -1) r1
WHERE
#i < DATEDIFF('2016-08-10', '2016-08-09')
) AS dateTable
CROSS JOIN Services S
) AS dateWiseServices
LEFT JOIN
(
SELECT COUNT(*) AS total, subscribe_date, service_id
FROM subscribers
INNER JOIN services ON subscribers.service_id = services.id
WHERE subscribe_date BETWEEN '2016-08-09' AND '2016-08-10'
GROUP BY service_id, subscribe_date
) AS yourQuery
ON dateWiseServices.id = yourQuery.service_id AND dateWiseServices.`Day` = yourQuery.subscribe_date
ORDER BY dateWiseServices.id, dateWiseServices.`Day` DESC
Instead inner join use left join
SELECT COUNT(*), subscribe_date, service_id
FROM subscribers LEFT JOIN services ON subscribers.service_id = services.id
GROUP BY service_id,subscribe_date;
Please try this I hope this will help you..
SELECT service_id,subscribe_date,COUNT(service_id)
FROM subscribers WHERE subscribe_date BETWEEN '2016-08-09' AND '2016-08-10'
GROUP BY service_id, subscribe_date;
In case you trying to check foreign key also try below query.
SELECT subscribers.service_id,subscribers.subscribe_date,COUNT(subscribers.service_id)
FROM subscribers,services WHERE subscribers.service_id=services.id
AND subscribe_date BETWEEN '2016-08-09' AND '2016-08-10' GROUP BY service_id, subscribe_date;

MySQL query to return 0 if Count of month returns no results [duplicate]

I am formalating a query to give the number of reports submitted over the last year ordered by date.
I get the current year and month with php:
$year = date('Y') - 1;
$month = date('m');
and execute the following query:
SQL:
SELECT month(date_lm) AS `month` ,
count(*) AS `count`
FROM `reports`
WHERE (status = 'submitted')
AND (date_lm > 2012-08)
GROUP BY month(date_lm)
ORDER BY month(date_lm) ASC
And because there has only been 1 submitted in the last year it gives me only 1 result...
| month | count |
| 7 | 1 |
But I would like the result set to show:
| month | count |
| 9 | 0 |
| 10 | 0 |
| 11 | 0 |
| 12 | 0 |
| 1 | 0 |
| 2 | 0 |
| 3 | 0 |
| 4 | 0 |
| 5 | 0 |
| 6 | 0 |
| 7 | 1 |
| 8 | 0 |
Is that possible?
In order to do this, you could create a 'month' table and then use a left outer join between that table and the reports table.
I've never used mysql so apologies if the syntax is slightly off, but this would be the query:
SELECT months.monthNumber,
count(reports.id) AS `count`
FROM `months` left outer join `reports` on months.monthNumber = month(reports.date_lm)
WHERE (status = 'submitted')
AND (date_lm > 2012-08)
GROUP BY monthNumber
ORDER BY monthNumber ASC
Importantly, the count should be of a column in the reports table, not the months table, or else you would never get a zero.
count(col_name) AS count will give you count 0
For reference you can visit http://www.mysqlperformanceblog.com/2007/04/10/count-vs-countcol/
You should LEFT JOIN this table with 1..12 table.
Something like this:
SELECT Months.id AS `month` ,
COUNT(`reports`.date_lm) AS `count`
FROM
(
SELECT 1 as ID UNION SELECT 2 as ID UNION SELECT 3 as ID UNION SELECT 4 as ID
UNION
SELECT 5 as ID UNION SELECT 6 as ID UNION SELECT 7 as ID UNION SELECT 8 as ID
UNION
SELECT 9 as ID UNION SELECT 10 as ID UNION SELECT 11 as ID UNION SELECT 12 as ID
) as Months
LEFT JOIN `reports` on Months.id=month(`reports`.date_lm)
AND
(status = 'submitted')
AND (date_lm > 2012-08)
GROUP BY Months.id
ORDER BY Months.id ASC
SQL Fiddle demo
Hope this can help..
SELECT
t1.month_year AS month,
COALESCE(SUM(t1.total+t2.total), 0) AS count
FROM
(
SELECT
DATE_FORMAT(a.Date, "%Y-%m") AS md,
DATE_FORMAT(a.Date, "%b-%y") AS month_year,
'0' AS total
FROM (
SELECT curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY AS Date
FROM (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS a
CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS b
CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS c
) a
WHERE a.Date <= NOW() AND a.Date >= Date_add(Now(),INTERVAL - 11 MONTH)
GROUP BY md
)t1
LEFT JOIN
(
SELECT DATE_FORMAT(created_at, "%b") AS month, COUNT(*) AS total ,DATE_FORMAT(created_at, "%Y-%m") AS md
FROM reports
WHERE created_at <= NOW() AND created_at >= Date_add(Now(),INTERVAL - 11 MONTH) AND `status` = 1
GROUP BY md
)t2
ON t2.md = t1.md
LEFT JOIN
(
SELECT DATE_FORMAT(date_lm, "%b") AS month, COUNT(*) AS total ,DATE_FORMAT(date_lm, "%Y-%m") AS md
FROM tbl_users
WHERE date_lm <= NOW() AND date_lm >= Date_add(Now(),INTERVAL - 11 MONTH) AND status = 'submitted'
GROUP BY md
)t3
ON t3.md = t1.md
GROUP BY t1.md
ORDER BY t1.md
this will give data for last 12 months even if there is no data for it with a 0..

mysql select distinct but latest row

How can I select the latest 'score' from a table for each distinct 'site' that exists?
For example:
site | date | score
a | 20140101 | 10
a | 20140102 | 8
b | 20140103 | 11
b | 20140202 | 9
I'd like to return one result for a and b, but only their latest entry (by date).
a | 20140102 | 8
b | 20140202 | 9
I know how to group by site, or select distinct site, but not sure how to narrow it down to their latest dates.
edit: this should be dynamic for thousands of distinct sites.
You can do it with a union
(SELECT
site,date,score
FROM
`table`
WHERE
site = 'a'
ORDER BY date DESC
LIMIT 0,1)
UNION
(SELECT
site,date,score
FROM
`table`
WHERE
site = 'b'
ORDER BY date DESC
LIMIT 0,1)
A little more convoluted if you don't want to write a UNION
SELECT
t.site, t.date, t.score
FROM
`table` t
JOIN (
SELECT ti.site,MAX(ti.date) AS dt
FROM `table` ti
GROUP BY ti.site
) t2 ON t2.site = t.site and t2.dt = t.date

MySQL count rows within the same intervals to eachother

I have a table where one column is the date:
+----------+---------------------+
| id | date |
+----------+---------------------+
| 5 | 2012-12-10 10:12:37 |
+----------+---------------------+
| 4 | 2012-12-10 09:09:55 |
+----------+---------------------+
| 3 | 2012-12-09 21:12:35 |
+----------+---------------------+
| 2 | 2012-12-09 20:15:07 |
+----------+---------------------+
| 1 | 2012-12-09 20:01:42 |
+----------+---------------------+
What I need, is to count the rows which are for example whitin 3 hours to each other. In this example I want to join the upper row with the 2nd row, and the 3rd row with the 4th and 5th rows. So my output should be like this:
+----------+---------------------+---------+
| id | date | count |
+----------+---------------------+---------+
| 5 | 2012-12-10 10:12:37 | 2 |
+----------+---------------------+---------+
| 3 | 2012-12-09 21:12:35 | 3 |
+----------+---------------------+---------+
How could I do this?
I think you need a self-join for this:
select t.id, t.date, COUNT(t2.id)
from t left outer join
t t2
on t.date between t2.date - interval 3 hour and t2.date + interval 3 hour
group by t.id, t.date
(This is untested code so it might have a syntax error.)
If you are trying to divide everything into 3-hour intervals, you can do something like:
select max(t.date), t.id, count(*)
from (select t.*,
(date(date)*100 + floor(hour(date)/3)*3) as interval
from t
) t
group by interval
I am not sure how to do this with My SQL but i am able to build a set of queries in SQL Server 2005 which will provide the intended results. Here is the working sample, its very complex and may be overly complex but that's how i was able to get the desired result:
WITH BaseData AS
(
SELECT 5 AS ID, '2012-12-10 10:12:37' AS Date
UNION ALL
SELECT 4 AS ID, '2012-12-10 09:09:55' AS Date
UNION ALL
SELECT 3 AS ID, '2012-12-09 21:12:35' AS Date
UNION ALL
SELECT 2 AS ID, '2012-12-09 20:15:07' AS Date
UNION ALL
SELECT 1 AS ID, '2012-12-09 20:01:42' AS Date
),
BaseDataWithRowNum AS
(
SELECT ID,DATE, ROW_NUMBER() OVER (ORDER BY Date DESC) AS RowNum
FROM BaseData
),
InterRelatedDates AS
(
SELECT B1.RowNum AS RowNum1,B2.RowNum AS RowNum2
FROM BaseDataWithRowNum B1
INNER JOIN BaseDataWithRowNum B2
ON B1.Date BETWEEN B2.Date AND DATEADD(hh,3,B2.Date)
AND B1.RowNum < B2.RowNum
AND B1.ID != B2.ID
),
InterRelatedDatesWithinMultipleGroups AS
(
SELECT G1.RowNum1,G2.RowNum2
FROM InterRelatedDates G1
LEFT JOIN InterRelatedDates G2
ON G1.RowNum2 = G2.RowNum2
AND G1.RowNum1 != G2.RowNum1
)
SELECT BN.ID,
BN.Date,
CountExcludingOriginalGrouppingRecord +1 AS C
FROM
(
SELECT RowNum1 AS RowNum,COUNT(1) AS CountExcludingOriginalGrouppingRecord
FROM
(
-- If a row was used in only one group then it is ok. use as it is
SELECT D1.RowNum1
FROM InterRelatedDatesWithinMultipleGroups AS D1
WHERE D1.RowNum2 IS NULL
UNION ALL
-- In case a row was selected in two groups, choose the one with higher date
SELECT Min(D1.RowNum1)
FROM InterRelatedDatesWithinMultipleGroups AS D1
WHERE D1.RowNum2 IS NOT NULL
GROUP BY D1.RowNum2
) T
GROUP BY RowNum1
) T2
INNER JOIN BaseDataWithRowNum BN
ON BN.RowNum = T2.RowNum