sql get all dates between two table dates - mysql

another SQL challenge!
I want to write a MySQL query that gets all days between two dates in one record.
opening_times
id | begin | end
1 | 10:00:00 | 17:00:00
2 | 10:00:00 | 18:00:00
3 | 10:00:00 | 19:00:00
opening_periods
id | opening_time_id | begin | end
1 | 3 | 2016-03-26 | 2016-03-28
2 | 2 | 2016-03-29 | 2016-04-01
3 | 1 | 2016-04-02 | 2016-04-03
I want to have this output:
date | begin | end
2016-03-26 | 10:00:00 | 19:00:00
2016-03-27 | 10:00:00 | 19:00:00
2016-03-28 | 10:00:00 | 19:00:00
2016-03-29 | 10:00:00 | 18:00:00
2016-03-30 | 10:00:00 | 18:00:00
2016-03-31 | 10:00:00 | 18:00:00
2016-04-01 | 10:00:00 | 18:00:00
2016-04-02 | 10:00:00 | 17:00:00
2016-04-03 | 10:00:00 | 17:00:00
Should I use a subquery for this?
Thx for pointing me in the right direction!

You can solve it in mysql with a complex query.
First you need to build a subquery that will generate an integer sequence, like in this answer:
SELECT #row := #row + 1 as rown FROM
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t1,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3,
(SELECT #row:=-1) x
This will be used to generate so many rows for each entry as many days you have in your dates interval.
SELECT DATEDIFF(`end`,`begin`) as number_of_days FROM `opening_periods`
And all put together will look like this:
SELECT DATE_ADD( o.`begin`, INTERVAL days day) as date_field, t.begin, t.end
FROM `opening_periods` o INNER JOIN (
SELECT id, rown as days
FROM `opening_periods`,
(SELECT #row := #row + 1 as rown FROM
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t1,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t2,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t3,
(SELECT #row:=-1) x) numbers_table
WHERE rown <= DATEDIFF(`end`,`begin`)) r
ON o.id = r.id
INNER JOIN `opening_times` t ON o.`opening_time_id` = t.id
ORDER BY o.id
Here is a fiddle: http://rextester.com/AKDRI84101

Related

MySQL: count unique days in period with overlaps and gaps (optimized)

How to calculate the number of unique days per month from a table with two date columns, in which the periods can have gaps and overlaps?
I rather not use a calendar table to get the unique days, because it generates a temporary table with thousands of records, and resources are limited.
Example table:
+---------+------------+------------+
| mygroup | alpha | omega |
+---------+------------+------------+
| 1 | 2017-02-04 | 2017-04-14 |
| 1 | 2017-03-25 | 2017-03-28 |
| 1 | 2017-01-23 | 2017-01-25 |
| 2 | 2017-02-05 | 2017-02-20 |
| 1 | 2017-04-28 | 2017-05-12 |
| etc. | etc. | etc. |
+---------+------------+------------+
Is it what you need?
select count(distinct selected_date),te.mygroup, MONTHNAME(selected_date)from
(select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) selected_date from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
cross join test te
where selected_date between te.alpha and te.omega
group by mygroup, MONTHNAME(selected_date)
Оutput for your example:
'17','1','April'
'25','1','February'
'3','1','January'
'31','1','March'
'12','1','May'
'16','2','February'
Count may be greater than number of the days in month because such overlap exists in few rows - it's not а mistake.
There is another way to do this roughly 10 times faster than a calendar table.
The biggest resource spoiler is the calendar table itself, it is used to filter unique days.
But instead of using a whole table record for that, it can be done using 31 bits in an UINT.
Recepe:
Create a calendar table with months only
Cut periods in months, and join them with the calendar table
Translate periods to bits of UINTs
OR the UINTs per month for uniqueness
Count their bits as unique days per month
Output:
+--------------+---------+---------+-------+
| Period | Group 1 | Group 2 | Total |
+--------------+---------+---------+-------+
| 2017 month 5 | 11 | 0 | 11 |
| 2017 month 4 | 15 | 0 | 15 |
| 2017 month 3 | 30 | 0 | 30 |
| 2017 month 2 | 24 | 15 | 39 |
| 2017 month 1 | 2 | 0 | 2 |
+--------------+---------+---------+-------+
MySQL query:
SELECT
`tabulate`.`period` AS `Period`,
SUM(IF(`tabulate`.`mygroup` = 1,
`tabulate`.`days`, 0)) AS `Group 1`,
SUM(IF(`tabulate`.`mygroup` = 2,
`tabulate`.`days`, 0)) AS `Group 2`,
SUM(`tabulate`.`days`) AS `Total`
FROM
( SELECT
`unique`.`period`,
BIT_COUNT(BIT_OR(CONV(CONCAT(
REPEAT("1", DAYOFMONTH(`unique`.`omega`) - DAYOFMONTH(`unique`.`alpha`)),
REPEAT("0", DAYOFMONTH(`unique`.`alpha`) - 1)
), 2, 10))) AS `days`,
`unique`.`mygroup`
FROM
( SELECT
DATE_FORMAT(`permonth`.`period_alpha`, "%Y month %c") AS `period`,
GREATEST(`permonth`.`period_alpha`, `permonth`.`example_alpha`) AS `alpha`,
LEAST(`permonth`.`period_omega`, `permonth`.`example_omega`) AS `omega`,
`permonth`.`mygroup`
FROM
( SELECT
`period`.`alpha` AS `period_alpha`,
DATE_SUB(`period`.`omega`, INTERVAL 1 DAY) AS `period_omega`,
`example`.`mygroup`,
IFNULL(`example`.`alpha`, `period`.`alpha`) AS `example_alpha`,
IFNULL(`example`.`omega`, CURDATE()) AS `example_omega`
FROM
( SELECT
DATE_ADD(
MAKEDATE(YEAR(CURDATE()), 1),
INTERVAL `season`.`n` + (`month`.`n` << 2) MONTH
) AS `alpha`,
DATE_ADD(
MAKEDATE(YEAR(CURDATE()), 1),
INTERVAL 1 + `season`.`n` + (`month`.`n` << 2) MONTH
) AS `omega`
FROM
( SELECT 0 AS `n`
UNION ALL SELECT 1
UNION ALL SELECT 2
) AS `month`
CROSS JOIN (SELECT 0 AS `n`
UNION ALL SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
) AS `season`
) AS `period`
INNER JOIN
( SELECT 1 AS `mygroup`, "2017-02-04" AS `alpha`, "2017-04-14" AS `omega`
UNION ALL SELECT 1, "2017-03-25", "2017-03-28"
UNION ALL SELECT 1, "2017-01-23", "2017-01-25"
UNION ALL SELECT 2, "2017-02-05", "2017-02-20"
UNION ALL SELECT 1, "2017-04-28", "2017-05-12"
) AS `example` ON (
(`example`.`alpha` < `period`.`omega` OR `example`.`alpha` IS NULL)
AND IFNULL(`example`.`omega`, CURDATE()) >= `period`.`alpha`
)
) AS `permonth`
) AS `unique`
GROUP BY
`unique`.`period`,
`unique`.`mygroup`
) AS `tabulate`
GROUP BY `tabulate`.`period`
ORDER BY `tabulate`.`period` DESC

SQL/Mysql query available dates and times in database

I need some help querying my calendar/dates table
Scenario: I have a "calendar" table with dates and times (see below), users will set their available dates, usually day by day with available time slots for each day. So my table looks like this:
+------+------------+---------------------+---------------------+
| ID | user_id | start_date | end_date |
+------+------------+---------------------+---------------------+
| 1 | 1 | 2016-09-01 08:00:00 | 2016-09-01 16:00:00 |
| 2 | 1 | 2016-09-03 00:00:00 | 2016-09-03 23:59:59 |
| 3 | 1 | 2016-09-04 00:00:00 | 2016-09-04 16:00:00 |
| 4 | 1 | 2016-09-05 08:00:00 | 2016-09-05 16:00:00 |
| 5 | 2 | 2016-09-05 08:00:00 | 2016-09-05 16:00:00 |
| 6 | 2 | 2016-09-07 08:00:00 | 2016-09-07 16:00:00 |
| 7 | 2 | 2016-09-08 08:00:00 | 2016-09-08 16:00:00 |
| 8 | 2 | 2016-09-08 18:00:00 | 2016-09-08 22:00:00 |
+------+------------+---------------------+---------------------+
We have 2 users here so I want the following:
If I search for start_date = 2016-09-05 08:00:00 and end_date = 2016-09-05 16:00:00 it should return user 1 and 2. Since both of them has an entry with these dates. Same goes as well if start_date = 2016-09-05 09:00:00 and end_date = 2016-09-05 15:00:00, this should as well return both users since the time im searching for is between the time slots as shown in the example.
Second scenario is a little bit more tricky, If user search for start_date = 2016-09-03 08:00:00 and end_date = 2016-09-04 16:00:00 i want the query to check the following:
see if the user is available each day at these times.
so in this case, is the user available on 2016-09-03 between 08:00:00 and 16:00:00 and as well on 2016-09-04 between 08:00:00 and 16:00:00.
In the example over this should return user 1.
Im open for suggestion on re-designed my schema if needed.
Hope some can help me with this.
DEMO include some aditional code comment, and show how the query evolve . Also I add another row for user_id = 2 to show how only match one of the two days in the range.
SELECT U.`user_id`
FROM (
select a.selectDate,
CONCAT(a.selectDate, ' ', time(#s_date)) as start_time,
CONCAT(a.selectDate, ' ', time(#s_date)) as end_time
from (
select '1900-01-01' + INTERVAL (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a) + (10000 * e.a)) DAY as selectDate
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
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 d
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 e
) a
CROSS JOIN (SELECT #s_date := '2016-09-03 08:00:00', #e_date := '2016-09-04 16:00:00') par
-- CROSS JOIN (SELECT #s_date := '2016-09-05 08:00:00', #e_date := '2016-09-05 16:00:00') par
-- CROSS JOIN (SELECT #s_date := '2016-09-05 09:00:00', #e_date := '2016-09-05 15:00:00') par
WHERE selectDate BETWEEN date(#s_date)
AND date(#e_date)
) D
CROSS JOIN (SELECT DISTINCT `user_id` FROM Table1) U
LEFT JOIN Table1 T
ON U.`user_id` = T.`user_id`
AND D.start_time <= T.`end_date`
AND D.end_time >= T.`start_date`
GROUP BY U.`user_id`
HAVING COUNT(U.`user_id`) = COUNT(T.`user_id`);
OUTPUT
Step 1: create a list of dates, in this case 273 years
Step 2: select all dates between the range define in the parameters, also include the time window to each date.
Step 3: join all together to see what dates have user in that time window
Step 4: select only user with a time window for all dates

Opening balance and closing balance for a non-working date

Need a help on how to get opening balances and closing balances of a non-working date in my transaction table in my database.
transaction table
+------------------+----------+-----------+
| id | trans_date | debit | credit |
+----+-------------+----------+-----------+
| 1 | 2016-05-09 | 200.00 | 0.00 |
| 2 | 2016-05-11 | 0.00 | 50.00 |
+---------------+-------------+-----------+
Want a result like below. You will realize there were no transaction on "2016-05-10" but the result shows both opening balance and closing balance. Thanks
+-------------+--------------+-----------+-----------+------------+
| trans_date | open_bal | debit | credit |closing_bal |
+-------------+--------------+-----------+-----------+------------+
| 2016-05-09 | 0.00 | 200.00 | 0.00 | 200.00 |
| 2016-05-10 | 200.00 | 0.00 | 0.00 | 200.00 |
| 2016-05-11 | 200.00 | 0.00 | 50.00 | 150.00 |
+-------------+-------------+--------------+------------+---------+
I tried this but "2016-05-10" does not show in the result.
SELECT trans_date,open_balance
FROM(SELECT s.gen_id, s.trans_id, s.trans_date,
s.narations, s.account_code,
s.op_balance as open_balance,
s.debit, s.credit, s.closing_balance
from ( select t.gen_id, t.trans_id,
t.narations, t.account_code,
t.trans_date, t.credit, t.debit,
#tot_debit := if(#prev_client = t.account_code, #tot_debit + t.debit,t.debit) as tot_cred,
#tot_credit := if(#prev_client = t.account_code,#tot_credit + t.credit,t.credit) as tot_deb,
#cur_bal := if(#prev_client = t.account_code, #tot_debit - #tot_credit,t.debit-t.credit) as closing_balance,
(#cur_bal + t.credit) - t.debit as op_balance, #prev_client := t.account_code
from (select * from journal WHERE account_code = 41003
GROUP BY trans_date order by trans_date,account_code,trans_id)t, (select #prev_client:=0,#cur_bal:=0,#tot_debit:=0,#tot_credit:= 0,#open_balance:=0)r )s) g where trans_date <= '2016-05-11'
You can use this to select list of dates in MySQL.
SELECT DATE_ADD(CURDATE() , INTERVAL -(a.n + 10 * b.n + 100 * c.n) DAY) AS `date`
FROM (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS a
CROSS JOIN (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS b
CROSS JOIN (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS c
If you want to include in result dates not presented at table transactions, it can be done with LEFT JOIN
SELECT * FROM
(SELECT DATE_ADD(CURDATE() , INTERVAL -(a.n + 10 * b.n + 100 * c.n) DAY) AS `date`
FROM (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS a
CROSS JOIN (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS b
CROSS JOIN (SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) AS c
) AS dates
LEFT JOIN transactions ON trans_date = dates.date
Note, that this will select dates from current for 999 past days. If you want more days ago, another CROSS JOIN can be added and 9 999 days will be returned.

Find dates between start date and end date in mysql

I got 2 columns, one is start date and the other is end date. I need to find every single day between the two dates.
Need it in another table with two coloums, one name pno that refers to the pno's id in the first table, and the other with the dates between start date and end date.
As an example this could be my input
pno startdate end date
p1 2012-12-03 2012-12-06
p2 2013-01-05 2013-01-08
p3 2013-01-15 2012-01-20
and this has to be my output.
pno dates
----------
p01 2012-12-03
p01 2012-12-04
p01 2012-12-05
p01 2012-12-06
p02 2013-01-05
p02 2013-01-06
p02 2013-01-07
p02 2013-01-08
p03 2013-01-15
...
You can do it like this
SELECT pno, startdate + INTERVAL q.n - 1 DAY dates
FROM table1 t CROSS JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N 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) a
,(SELECT 0 AS N 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) b
ORDER BY n
) q
WHERE q.n - 1 <= DATEDIFF(enddate, startdate)
ORDER BY pno, dates
The subquery generates a sequence of numbers from 1 to 100. You can adjust it for your needs (if date differences span more or less than 100 days) or completely substitute it with a persisted tally(numbers) table if you do a lot of such queries.
Output:
+------+------------+
| pno | dates |
+------+------------+
| p1 | 2012-12-03 |
| p1 | 2012-12-04 |
| p1 | 2012-12-05 |
| p1 | 2012-12-06 |
| p2 | 2013-01-05 |
| p2 | 2013-01-06 |
| p2 | 2013-01-07 |
| p2 | 2013-01-08 |
| p3 | 2013-01-15 |
| p3 | 2013-01-16 |
| p3 | 2013-01-17 |
| p3 | 2013-01-18 |
| p3 | 2013-01-19 |
| p3 | 2013-01-20 |
+------+------------+
Here is SQLFiddle demo
UPDATE: To create and populate persisted tally table use
CREATE TABLE tally (n INT NOT NULL PRIMARY KEY);
INSERT INTO tally (n)
SELECT a.n + b.n * 10 + c.n * 100 + d.n * 1000 + 1 n
FROM
(SELECT 0 AS n 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) a
,(SELECT 0 AS n 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) b
,(SELECT 0 AS n 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) c
,(SELECT 0 AS n 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) d
ORDER BY n;
You'll have in the tally table sequence of numbers from 1 to 10000. That will allow you to work with date ranges that span more than 27 years.
Now the query boils down to
SELECT pno, startdate + INTERVAL q.n - 1 DAY dates
FROM table1 t CROSS JOIN tally q
WHERE q.n - 1 <= DATEDIFF(enddate, startdate)
ORDER BY pno, dates
Here is SQLFiddle demo

Group and count by minute of day using MySQL

I want to get the count for each minute of the day from a large database of timestamps. I would like to be able build a graph from the data.
So there is 1440 minutes in a day, how would I get the count for each minute?
I can get the count for each minute within an hour,I'm stumped on how to expand to get the count for every minute in the day.
I've got this code that I'm using at the moment,
SELECT FROM_UNIXTIME(
CEILING(UNIX_TIMESTAMP(`timestamp`)/900)*900
) AS timeslice
, COUNT(*) AS mycount
FROM visitor
WHERE `timestamp` >= '20012-01-01'
AND `timestamp` < '20012-02-14'
GROUP BY timeslice
I hope this makes sense and thanks for any help!
Any questions please ask me
Dan
Depending on the number of records you could try to use the following query (check its execution time).
select d.everyminute,
(select count(*) from visitor
where `timestamp` between d.everyminute and
d.everyminute + interval 59 second) cnt
from
(
select #rownum:=#rownum+1,
date('2012-02-15') + interval (#rownum-1) minute everyminute from
(select 0 union all select 1 union all select 2) t,
(select 0 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) t1,
(select 0 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) t2,
(select 0 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) t3,
(select #rownum:=0) r where #rownum < 24*60
) d
The result is (I put some timestamps into the test DB)
2012-02-15 00:00:00 0
2012-02-15 00:01:00 0
2012-02-15 00:02:00 2
2012-02-15 00:03:00 1
2012-02-15 00:04:00 0
2012-02-15 00:05:00 1
2012-02-15 00:06:00 0
2012-02-15 00:07:00 0
2012-02-15 00:08:00 0
2012-02-15 00:09:00 0
2012-02-15 00:10:00 0
and so on until 2012-02-15 23:59:00
the existing records had timestamps 2012-02-15 00:02:00, 2012-02-15 00:02:00, 2012-02-15 00:03:00 and 2012-02-15 00:05:00
ps: to take into account summer/winter time jump you can rewrite where #rownum < 24*60 as having everyminute < (date('2012-02-15') + interval 1 day - interval 1 minute)
As you can see, it counts every minute even when there are no records in db within this minute.
It looks like you don't want it grouped by day/month/year, just minutes:
select minute, sum(if(adate is null, 0, 1)) DateAmount from (
select #num := #num + 1 as minute from
(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 union all
select 10 union all select 11 union all select 12) as t1,
(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 union all
select 10 union all select 11 union all select 12) as t2,
(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 union all
select 10) as t3,
(select #num := - 1) as t4
) as MinuteInDay
left join visitors v
on minute(v.aDate) = MinuteInDay.minute
group by minute
For this set of data:
+---------------------+
| aDate |
+---------------------+
| 2012-01-01 00:00:00 |
| 2012-01-01 00:01:00 |
| 2012-01-01 00:02:00 |
| 2012-01-01 00:02:00 |
| 2012-01-02 00:02:00 |
| 2012-01-03 00:03:00 |
+---------------------+
This will result in:
+--------+------------+
| MINUTE | DATEAMOUNT |
+--------+------------+
| 0 | 1 |
| 1 | 1 |
| 2 | 3 |
| 3 | 1 |
| 4 | 0 |
| 5 | 0 |
| ... | ... |
| 1439 | 0 |
+--------+------------+
Hope this helps.
PS: It would be much easier if you don't needed the minutes in 0 and just ommit them!
The easiest way to do this is create a utility table that already has minutes in it. If you're clever, you'd also put the hour and meridian.
You can find some more information by searching for datawarehouse time dimension.
I'd do something like this:
SELECT MOD(FLOOR(UNIX_TIMESTAMP(timestamp)/60), 1440) AS timeslice, COUNT(*) AS mycount
FROM visitor WHEREtimestamp>= '20012-01-01' ANDtimestamp< '20012-02-14' GROUP BY timeslice
It'll count batches of 1440 minutes since the unix epoch, ignoring daylight savings time, etc.