【问题标题】:How to add missing dates when calculating count on a table计算表格计数时如何添加缺失的日期
【发布时间】:2021-03-19 16:04:33
【问题描述】:

我有一个销售表,记录一件商品的销售情况。简化版如下

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

【问题讨论】:

  • 您想明确查询每个商品的每日销售额吗?首先,我建议完全避免使用ìd 列,因为技术键通常比结果中的信息更令人困惑。让item_id 成为您的第一列,并首先按该列对您的查询进行排序,然后按date。在没有出售某个商品的样品的日子里要格外小心,因为可能有 null 值,这可能会让您错过某些结果行!
  • 也许 left joincoalesce() 功能相结合可以为您完成一项简单的工作,但我目前正在寻找自己
  • 您希望每个项目都具有相同的范围吗?固定范围?仅基于每个项目的第一个和最后一个日期的范围?

标签: sql sql-server datetime count date-arithmetic


【解决方案1】:

正如其他答案所指出的,一种解决方案是递归 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 的答案是更好的答案。

【讨论】:

    【解决方案2】:
    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          |
    +----+--------+-------------------------+------------+
    

    【讨论】:

      【解决方案3】:

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

      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;
      

      【讨论】:

      • 不会在没有销售的日子里给null 值吗?
      • 不,不会。 COUNTNULL 产生的计数为零。
      • count(*) 确实出现了,但count(field_name) 也出现了?
      • 请在您使用的任何数据库中研究COUNT 函数的文档。
      • 嗯,看来你是对的。所以我在这里也学到了一些东西
      【解决方案4】:

      一个选项使用递归查询来生成日期。然后,您可以cross join 使用表中可用的不同项目列表,并使用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
      

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

      【讨论】:

        猜你喜欢
        • 2020-01-26
        • 2020-09-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多