Você pode fazer isso usando group by com o nível desejado. Aqui está um exemplo usando os dados que você forneceu:
Primeiro o SQL para criar a tabela e preenchê-la. A coluna ID aqui não é "necessária", mas é recomendada se a tabela for grande ou tiver índices nela.
CREATE TABLE `test`.`events` (
`id` INT NOT NULL AUTO_INCREMENT,
`user` INT NULL,
`start` DATETIME NULL,
`end` DATETIME NULL,
`type` VARCHAR(45) NULL,
PRIMARY KEY (`id`));
INSERT INTO events (user, start, end, type) VALUES
(1, '2015-1-1 12:00:00', '2015-1-1 12:03:59', 'browsing'),
(2, '2015-1-1 12:03:00', '2015-1-1 12:06:00', 'browsing'),
(2, '2015-1-1 12:03:00', '2015-1-1 12:06:00', 'eating'),
(3, '2015-1-1 12:03:00', '2015-1-1 12:08:00', 'browsing');
Para obter uma lista de pares ordenados do número de minutos de duração para o número de eventos:
A consulta pode ser facilmente escrita usando a função timestampdiff, conforme mostrado abaixo:
SELECT
TIMESTAMPDIFF(MINUTE, start, end) as minutes,
COUNT(*) AS numEvents
FROM
test.events
GROUP BY TIMESTAMPDIFF(MINUTE, start, end)
A saída:
minutes numEvents
3 3
5 1
O primeiro parâmetro na seleção pode ser FRAC_SECOND, SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER ou YEAR.
Aqui estão mais alguns exemplos de consultas que você pode fazer:
Eventos por hora (função de piso é aplicada)
SELECT
TIMESTAMPDIFF(HOUR, start, end) as hours,
COUNT(*) AS numEvents
FROM
test.events
GROUP BY TIMESTAMPDIFF(HOUR, start, end)
**Eventos por hora com melhor formatação**
SELECT
CONCAT("<", TIMESTAMPDIFF(HOUR, start, end) + 1) as hours,
COUNT(*) AS numEvents
FROM
test.events
GROUP BY TIMESTAMPDIFF(HOUR, start, end)
Você pode agrupar por uma variedade de opções, mas isso definitivamente deve começar. A maioria dos pacotes de plotagem permitirá que você especifique coordenadas x y arbitrárias, então você não precisa se preocupar com os valores ausentes no eixo x.
Para obter uma lista de pares ordenados do número de eventos em um horário específico (para registro): Observe que isso é deixado para referência.
Agora para as consultas. Primeiro você precisa escolher qual item deseja usar para o agrupamento. Por exemplo, uma tarefa pode levar mais de um minuto, então o início e o fim seriam em minutos diferentes. Para todos esses exemplos, estou baseando-os na hora de início, pois foi quando o evento realmente ocorreu.
Para agrupar contagens de eventos por minuto, você pode usar uma consulta como esta:
SELECT
DATE_FORMAT(start, '%M %e, %Y %h:%i %p') as minute,
count(*) AS numEvents
FROM test.events
GROUP BY YEAR(start), MONTH(start), DAYOFMONTH(start), HOUR(start), MINUTE(start);
Observe como isso agrupa por todos os itens, começando com o ano, indo a cada minuto. Eu também tenho o minuto exibido como um rótulo. A saída resultante se parece com isso:
minute numEvents
January 1, 2015 12:00 PM 1
January 1, 2015 12:03 PM 3
Esses são os dados que você pode pegar usando php e prepará-los para exibição por uma das muitas bibliotecas gráficas existentes, plotando a coluna de minuto no eixo x e plotando os numEvents no eixo y.
Aqui estão mais alguns exemplos de consultas que você pode fazer:
Eventos por hora
SELECT
DATE_FORMAT(start, '%M %e, %Y %h %p') as hour,
count(*) AS numEvents
FROM test.events
GROUP BY YEAR(start), MONTH(start), DAYOFMONTH(start), HOUR(start);
Eventos por data
SELECT
DATE_FORMAT(start, '%M %e, %Y') as date,
count(*) AS numEvents
FROM test.events
GROUP BY YEAR(start), MONTH(start), DAYOFMONTH(start);
Eventos por mês
SELECT
DATE_FORMAT(start, '%M %Y') as date,
count(*) AS numEvents
FROM test.events
GROUP BY YEAR(start), MONTH(start);
Eventos por ano
SELECT
DATE_FORMAT(start, '%Y') as date,
count(*) AS numEvents
FROM test.events
GROUP BY YEAR(start);
Também devo salientar que, se você tiver um índice na coluna inicial dessa tabela, essas consultas serão concluídas rapidamente, mesmo com centenas de milhões de linhas.
Espero que isto ajude! Deixe-me saber se você tiver quaisquer outras perguntas sobre isso.