MySQL - 按月计数(包括丢失的记录)
MySQL - count by month (including missing records)
我有这个SELECT:
SELECT
DATE_FORMAT(`created`, '%Y-%m') as byMonth,
COUNT(*) AS Total
FROM
`qualitaet`
WHERE
`created` >= MAKEDATE(year(now()-interval 1 year),1) + interval 5 month
AND
`status`=1
GROUP BY
YEAR(`created`), MONTH(`created`)
ORDER BY
YEAR(`created`) ASC
得到这个结果:
| byMonth | Total |
| 2015-06 | 2 |
| 2015-09 | 12 |
| 2015-10 | 3 |
| 2015-12 | 8 |
| 2016-01 | 1 |
参见 SQL-Fiddle here
WHERE 子句很重要,因为我需要它作为从 6 月开始的当前财政年度,在我的示例中为 1。
如您所见,我没有 7 月、8 月和 11 月的记录。但是我需要总计为零的这些记录。
所以我的结果应该是这样的:
| byMonth | Total |
| 2015-06 | 2 |
| 2015-07 | 0 |
| 2015-08 | 0 |
| 2015-09 | 12 |
| 2015-10 | 3 |
| 2015-11 | 0 |
| 2015-12 | 8 |
| 2016-01 | 1 |
有没有办法得到这个结果?
您需要生成所有需要的日期,然后将您的数据左联接到这些日期。另请注意,将一些谓词放在左连接的 ON
子句中,并将其他谓词放在 WHERE
子句中很重要:
SELECT
CONCAT(y, '-', LPAD(m, 2, '0')) as byMonth,
COUNT(`created`) AS Total
FROM (
SELECT year(now()) AS y UNION ALL
SELECT year(now()) - 1 AS y
) `years`
CROSS JOIN (
SELECT 1 AS m UNION ALL
SELECT 2 AS m UNION ALL
SELECT 3 AS m UNION ALL
SELECT 4 AS m UNION ALL
SELECT 5 AS m UNION ALL
SELECT 6 AS m UNION ALL
SELECT 7 AS m UNION ALL
SELECT 8 AS m UNION ALL
SELECT 9 AS m UNION ALL
SELECT 10 AS m UNION ALL
SELECT 11 AS m UNION ALL
SELECT 12 AS m
) `months`
LEFT JOIN `qualitaet` q
ON YEAR(`created`) = y
AND MONTH(`created`) = m
AND `status` = 1
WHERE STR_TO_DATE(CONCAT(y, '-', m, '-01'), '%Y-%m-%d')
>= MAKEDATE(year(now()-interval 1 year),1) + interval 5 month
AND STR_TO_DATE(CONCAT(y, '-', m, '-01'), '%Y-%m-%d')
<= now()
GROUP BY y, m
ORDER BY y, m
以上是如何工作的?
CROSS JOIN
在所有可用年份和所有可用月份之间创建一个 cartesian product。这就是您想要的,您希望所有 year-month 组合都没有间隙。
LEFT JOIN
将所有 qualitaet
记录添加到结果(如果它们存在)并将它们连接到之前的 year-month 笛卡尔积。将 status = 1
谓词之类的谓词放在这里很重要。
COUNT(created)
仅计算 created
的 non-NULL 个值,即当 LEFT JOIN
不为任何给定的 year-month 生成任何行时,我们需要 0
结果,不是 1
,即我们不想计算 NULL
值。
性能说明
以上在 ON
和 WHERE
谓词中大量使用了字符串操作和日期时间算法。这不会对大量数据执行。在这种情况下,您应该更好地 pre-truncate 并在 qualitaet
table 中索引您的 year-month,并且仅对这些值进行操作。
我有这个SELECT:
SELECT
DATE_FORMAT(`created`, '%Y-%m') as byMonth,
COUNT(*) AS Total
FROM
`qualitaet`
WHERE
`created` >= MAKEDATE(year(now()-interval 1 year),1) + interval 5 month
AND
`status`=1
GROUP BY
YEAR(`created`), MONTH(`created`)
ORDER BY
YEAR(`created`) ASC
得到这个结果:
| byMonth | Total |
| 2015-06 | 2 |
| 2015-09 | 12 |
| 2015-10 | 3 |
| 2015-12 | 8 |
| 2016-01 | 1 |
参见 SQL-Fiddle here
WHERE 子句很重要,因为我需要它作为从 6 月开始的当前财政年度,在我的示例中为 1。
如您所见,我没有 7 月、8 月和 11 月的记录。但是我需要总计为零的这些记录。
所以我的结果应该是这样的:
| byMonth | Total |
| 2015-06 | 2 |
| 2015-07 | 0 |
| 2015-08 | 0 |
| 2015-09 | 12 |
| 2015-10 | 3 |
| 2015-11 | 0 |
| 2015-12 | 8 |
| 2016-01 | 1 |
有没有办法得到这个结果?
您需要生成所有需要的日期,然后将您的数据左联接到这些日期。另请注意,将一些谓词放在左连接的 ON
子句中,并将其他谓词放在 WHERE
子句中很重要:
SELECT
CONCAT(y, '-', LPAD(m, 2, '0')) as byMonth,
COUNT(`created`) AS Total
FROM (
SELECT year(now()) AS y UNION ALL
SELECT year(now()) - 1 AS y
) `years`
CROSS JOIN (
SELECT 1 AS m UNION ALL
SELECT 2 AS m UNION ALL
SELECT 3 AS m UNION ALL
SELECT 4 AS m UNION ALL
SELECT 5 AS m UNION ALL
SELECT 6 AS m UNION ALL
SELECT 7 AS m UNION ALL
SELECT 8 AS m UNION ALL
SELECT 9 AS m UNION ALL
SELECT 10 AS m UNION ALL
SELECT 11 AS m UNION ALL
SELECT 12 AS m
) `months`
LEFT JOIN `qualitaet` q
ON YEAR(`created`) = y
AND MONTH(`created`) = m
AND `status` = 1
WHERE STR_TO_DATE(CONCAT(y, '-', m, '-01'), '%Y-%m-%d')
>= MAKEDATE(year(now()-interval 1 year),1) + interval 5 month
AND STR_TO_DATE(CONCAT(y, '-', m, '-01'), '%Y-%m-%d')
<= now()
GROUP BY y, m
ORDER BY y, m
以上是如何工作的?
CROSS JOIN
在所有可用年份和所有可用月份之间创建一个 cartesian product。这就是您想要的,您希望所有 year-month 组合都没有间隙。LEFT JOIN
将所有qualitaet
记录添加到结果(如果它们存在)并将它们连接到之前的 year-month 笛卡尔积。将status = 1
谓词之类的谓词放在这里很重要。COUNT(created)
仅计算created
的 non-NULL 个值,即当LEFT JOIN
不为任何给定的 year-month 生成任何行时,我们需要0
结果,不是1
,即我们不想计算NULL
值。
性能说明
以上在 ON
和 WHERE
谓词中大量使用了字符串操作和日期时间算法。这不会对大量数据执行。在这种情况下,您应该更好地 pre-truncate 并在 qualitaet
table 中索引您的 year-month,并且仅对这些值进行操作。