如何在 table 上计算计数时添加缺失的日期

How to add missing dates when calculating count on a table

我有一个销售额 table 记录了一件商品的销售额。简化版如下

ID ItemID SaleTime
1 1234 2020-12-01 12:44:22
2 1234 2020-12-01 17:12:22
3 1234 2020-12-02 12:44:22
4 1234 2020-12-04 17:12:22

我正在编写一个查询来计算每天售出的商品,它工作正常并给出以下结果。

ID ItemID Date Sale count
1 1234 2020-12-01 2
2 1234 2020-12-02 1
3 1234 2020-12-04 1

我如何包括没有销售的天数,如下所示。

ID ItemID Date Sale count
1 1234 2020-12-01 2
2 1234 2020-12-02 1
3 1234 2020-12-03 0
4 1234 2020-12-04 1

一个选项使用递归查询来生成日期。然后,您可以 cross join 使用 table 中可用的不同项目列表,并将 table 与 left join 一起使用。最后一步是聚合:

with cte as (
    select min(convert(date, saletime)) as dt, max(convert(date, saletime)) as max_dt from mytable
    union all
    select dateadd(day, 1, dt), max_dt from cte where dt < max_dt
)
select c.dt, i.itemid, count(t.id) as sale_count
from cte c
cross join (select distinct itemid from mytable) i
left join mytable t 
    on  t.itemid = i.itemid
    and t.date >= c.dt
    and t.date <  dateadd(day, 1, c.dt)
group by c.dt, i.itemid

在现实生活中,您可能有一个单独的引用 table 来存储项目,您将使用它来代替 select distinct 子查询。

一种方法是使用日历 table 来跟踪您希望在报告中显示的所有日期:

WITH dates AS (
    SELECT CAST('20201201' AS date) AS dt
    UNION ALL
    SELECT DATEADD(dd, 1, dt)
    FROM dates
    WHERE DATEADD(dd, 1, dt) <= '20201231'
)

SELECT
    t.ITEMID,
    d.dt,
    COUNT(t.ID) AS [SALE COUNT]
FROM dates d
LEFT JOIN yourTable t
    ON CAST(t.SALETIME AS date) = d.dt
GROUP BY
    t.ITEMID,
    d.dt
ORDER BY
    d.dt;
DECLARE @start_date DATETIME = '2020-11-28 00:00:00.000';
DECLARE @end_date DATETIME = '2020-12-13 00:00:00.000';

;WITH AllDays AS (SELECT @start_date AS [DATE]
                   UNION ALL
                   SELECT DATEADD(DAY, 1, [Date])
                   FROM   AllDays
                   WHERE  [Date] < @end_date),
     Items AS (SELECT distinct itemid from Sales)
     SELECT ROW_NUMBER() OVER (ORDER BY i.itemid, a.[DATE]) AS [ID], i.itemid as [ITEMID], a.[DATE],  count(s.itemid) AS [SALE COUNT]
     FROM Items i
     CROSS JOIN AllDays a
     LEFT JOIN Sales s ON a.[DATE] = convert(date, s.salestime) and i.itemid = s.itemid
     GROUP BY i.itemid, a.[DATE]
     ORDER BY i.itemid, a.[DATE]
     OPTION (MAXRECURSION 0)

结果(两个项目 ID 和 16 天):

+----+--------+-------------------------+------------+
| ID | ITEMID | DATE                    | SALE COUNT |
+----+--------+-------------------------+------------+
| 1  | 1234   | 2020-11-28 00:00:00.000 | 0          |
| 2  | 1234   | 2020-11-29 00:00:00.000 | 0          |
| 3  | 1234   | 2020-11-30 00:00:00.000 | 0          |
| 4  | 1234   | 2020-12-01 00:00:00.000 | 2          |
| 5  | 1234   | 2020-12-02 00:00:00.000 | 1          |
| 6  | 1234   | 2020-12-03 00:00:00.000 | 0          |
| 7  | 1234   | 2020-12-04 00:00:00.000 | 1          |
| 8  | 1234   | 2020-12-05 00:00:00.000 | 0          |
| 9  | 1234   | 2020-12-06 00:00:00.000 | 0          |
| 10 | 1234   | 2020-12-07 00:00:00.000 | 0          |
| 11 | 1234   | 2020-12-08 00:00:00.000 | 0          |
| 12 | 1234   | 2020-12-09 00:00:00.000 | 0          |
| 13 | 1234   | 2020-12-10 00:00:00.000 | 0          |
| 14 | 1234   | 2020-12-11 00:00:00.000 | 0          |
| 15 | 1234   | 2020-12-12 00:00:00.000 | 0          |
| 16 | 1234   | 2020-12-13 00:00:00.000 | 0          |
| 17 | 1235   | 2020-11-28 00:00:00.000 | 0          |
| 18 | 1235   | 2020-11-29 00:00:00.000 | 0          |
| 19 | 1235   | 2020-11-30 00:00:00.000 | 0          |
| 20 | 1235   | 2020-12-01 00:00:00.000 | 0          |
| 21 | 1235   | 2020-12-02 00:00:00.000 | 0          |
| 22 | 1235   | 2020-12-03 00:00:00.000 | 0          |
| 23 | 1235   | 2020-12-04 00:00:00.000 | 1          |
| 24 | 1235   | 2020-12-05 00:00:00.000 | 0          |
| 25 | 1235   | 2020-12-06 00:00:00.000 | 0          |
| 26 | 1235   | 2020-12-07 00:00:00.000 | 0          |
| 27 | 1235   | 2020-12-08 00:00:00.000 | 0          |
| 28 | 1235   | 2020-12-09 00:00:00.000 | 0          |
| 29 | 1235   | 2020-12-10 00:00:00.000 | 0          |
| 30 | 1235   | 2020-12-11 00:00:00.000 | 0          |
| 31 | 1235   | 2020-12-12 00:00:00.000 | 0          |
| 32 | 1235   | 2020-12-13 00:00:00.000 | 0          |
+----+--------+-------------------------+------------+

正如其他答案所指出的,一种解决方案是递归 CTE。您特别想为一个 itemid 执行此操作,因此我建议:

with dates as (
      select min(convert(date, saletime)) as dte, max(convert(date, saletime)) as max_dte
      from mytable
      union all
      select dateadd(day, 1, dt), max_dt
      from dates
      where dte < max_dte
    )
select c.dt, v.itemid, count(t.id) as sale_count
from dates d cross join
     (values (1234)) v(itemid) left join
     mytable t
     on t.itemid = v.itemid and
        t.date >= d.dte
        t.date <  dateadd(day, 1, c.dt)
group by d.dte, v.itemid;

请注意,如果您有超过 100 天,那么您还需要添加 OPTION (MAXRECURSION 0) 以避免日期生成错误。

如果您想要 所有 项目 ID 的解决方案,那么 GMB 的答案是更好的答案。