Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

MySQL agrupar por data e contagem, incluindo datas ausentes


Você precisa de um OUTER JOIN para chegar todos os dias entre um início e um fim, porque se você usar um INNER JOIN ele restringirá a saída apenas às datas que são unidas (ou seja, apenas as datas na tabela do relatório).

Além disso, quando você usa um OUTER JOIN você deve tomar cuidado para que as condições na where clause não cause uma implicit inner join; por exemplo AND domain_id =1 se o uso na cláusula where suprimiria qualquer linha que não tivesse essa condição atendida, mas quando usado como uma condição de junção, restringiria apenas as linhas da tabela do relatório.
SELECT
      COUNT(r.domain_id)
    , all_dates.Date AS the_date
    , domain_id
FROM (
        SELECT DATE_ADD(curdate(), INTERVAL 2 MONTH) - INTERVAL (a.a + (10 * b.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
      ) all_dates
      LEFT OUTER JOIN reports r
                  ON all_dates.Date = r.tracked_on
                        AND domain_id = 1
WHERE all_dates.Date BETWEEN '2014-09-01' AND '2014-09-30'
GROUP BY
      the_date
ORDER BY
      the_date ASC;

Também alterei a tabela derivada all_dates, usando DATE_ADD() para empurrar o ponto de partida para o futuro, e reduzi o seu tamanho. Ambas são opções e podem ser ajustadas como você achar melhor.

Demonstração no SQLfiddle

para chegar a um domain_id para cada linha (como mostrado em sua pergunta), você precisaria usar algo como o seguinte; Observe que você pode usar IFNULL() que é específico do MySQL, mas eu usei COALESCE() que é SQL mais genérico. No entanto, o uso de um @parameter como mostrado aqui é específico do MySQL de qualquer maneira.
SET @domain := 1;

SELECT
      COUNT(r.domain_id)
    , all_dates.Date AS the_date
    , coalesce(domain_id,@domain) AS domain_id
FROM (
        SELECT DATE_ADD(curdate(), INTERVAL 2 month) - INTERVAL (a.a + (10 * b.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
      ) all_dates
      LEFT JOIN reports r
                  ON all_dates.Date = r.tracked_on
                        AND domain_id = @domain
WHERE all_dates.Date BETWEEN '2014-09-01' AND '2014-09-30'
GROUP BY
      the_date
ORDER BY
      the_date ASC;

Veja isso em SQLfiddle