Mysql
 sql >> Base de données >  >> RDS >> Mysql

Groupe MySQL par date et nombre, y compris les dates manquantes

Vous avez besoin d'un OUTER JOIN pour arriver à chaque jour entre un début et une fin car si vous utilisez un INNER JOIN cela limitera la sortie aux seules dates qui sont jointes (c'est-à-dire uniquement les dates dans le tableau du rapport).

De plus, lorsque vous utilisez un OUTER JOIN vous devez prendre soin que les conditions dans la where clause ne provoque pas de implicit inner join; par exemple AND domain_id =1 si l'utilisation dans la clause where supprimerait toute ligne pour laquelle cette condition n'était pas remplie, mais lorsqu'elle est utilisée comme condition de jointure, elle restreint uniquement les lignes de la table de rapport.

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;

J'ai également modifié la table dérivée all_dates, en utilisant DATE_ADD() pour repousser le point de départ dans le futur, et j'en ai réduit la taille. Ces deux éléments sont des options et peuvent être modifiés comme bon vous semble.

Démo chez SQLfiddle

pour arriver à un domain_id pour chaque ligne (comme indiqué dans votre question), vous devez utiliser quelque chose comme ce qui suit ; Notez que vous pouvez utiliser IFNULL() qui est spécifique à MySQL mais j'ai utilisé COALESCE() qui est SQL plus générique. Cependant, l'utilisation d'un @paramètre comme indiqué ici est de toute façon spécifique à MySQL.

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;

Voir ceci sur SQLfiddle