非现有组项目类别的零计数

时间:2012-02-26 02:16:28

标签: sql sql-server tsql group-by

我需要查找过去五天按日期分组的三种产品及其数量的清单。为此,我使用了下面的TSQL。

SELECT CONVERT(VARCHAR(10),WM_data,103) as Date, 
       Product = CASE 
                   WHEN WM_Product = 1 THEN 'Product A' 
                   WHEN WM_Product = 2 THEN 'Product B' 
                   ELSE 'Product C' 
                 END, 
       WM_qtd as Quantity 
  FROM Tb_InfoProduct 
GROUP BY CONVERT(VARCHAR(10),WM_data,103), WM_Product
  HAVING CONVERT(VARCHAR(10),WM_data,103) > convert(VARCHAR(10),dateadd(dd, -5, getdate()),103) 
ORDER BY CONVERT(VARCHAR(10),WM_data,103), WM_Product

因此我收到:

Date          Product     Quantity
-------------------------------------------------
16/02/2012    Product A    132
16/02/2012    Product C     10
17/02/2012    Product A    163
17/02/2012    Product B     61
17/02/2012    Product C     58
18/02/2012    Product A      7
18/02/2012    Product B      2

但我需要:

Date     Product     Quantity
-------------------------------------------------
16/02/2012   Product A   132
16/02/2012   Product B     0  -- This
16/02/2012   Product C    10
17/02/2012   Product A   163
17/02/2012   Product B    61
17/02/2012   Product C    58
18/02/2012   Product A     7
18/02/2012   Product B     2
18/02/2012   Product C     0   -- This

我怎样才能做到这一点?

2 个答案:

答案 0 :(得分:1)

首先,您的查询存在一些问题:

  1. 您的SELECT子句包含的列既不在GROUP BY也不在聚合 - WM_qtd。你的意思是SUM(WM_qtd),不是吗?

  2. 通常,使用CONVERT(103)格式化的日期无法进行有意义的比较或排序。我建议您使用不同的格式(如112)或使用不同的方法删除时间部分并稍后应用格式(仅在SELECT子句中,或者可能不是在SQL中)。

  3. 您正在使用HAVING子句过滤源数据集,这是低效的,因为所有数据都必须被提取,分组,然后才会被过滤。在这种特殊情况下,最好使用WHERE子句过滤数据。

  4. 现在关于你的问题......你所追求的结果集基本上是所有相关日期和所有相关产品的交叉产品(哎呀,对不起双关语)。它只包含一些附加信息,如数量。

    因此,您需要获取两个列表,日期和产品,交叉加入它们,然后您可以将结果集外部加入Tb_InfoProduct并按日期和产品分组以获取数字 - 与您相同已经在您的查询中执行,只有GROUP BYSELECT子句引用的日期和产品列必须是相应派生列表中的日期和产品列,而不是Tb_InfoProduct表中的列。

    出于我的回答的目的,我假设您请求的Tb_InfoProduct子集包含输出中必须存在的所有日期和所有产品,因此,为了获得这两个列表,我我只是从Tb_InfoProduct子集中选择不同的日期和不同的产品。这是一个完整的近似解决方案:

    SELECT
      Date = CONVERT(VARCHAR(10), d.Date, 103),
      Product = CASE p.WM_Product
        WHEN 1 THEN 'Product A'
        WHEN 2 THEN 'Product B'
        ELSE        'Product C'
      END,
      Quantity = SUM(WM_qtd)
    FROM (
      SELECT DISTINCT Date = DATEADD(DAY, DATEDIFF(DAY, 0, WM_data), 0)
      FROM Tb_InfoProduct
      WHERE WM_data >= DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()) - 4, 0)
    ) d
    CROSS JOIN (
      SELECT DISTINCT WM_Product
      FROM Tb_InfoProduct
      WHERE WM_data >= DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()) - 4, 0)
    ) p
    LEFT JOIN (
      SELECT
        Date = DATEADD(DAY, DATEDIFF(DAY, 0, WM_data), 0),
        WM_Product
      FROM Tb_InfoProduct
      WHERE WM_data >= DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()) - 4, 0)
    ) i ON d.Date = i.Date AND p.WM_Product = i.WM_Product
    GROUP BY
      d.Date,
      p.WM_Product
    ORDER BY
      d.Date,
      p.WM_Product
    

    如果请求的子集可能省略了您希望包含在输出中的某些日期和/或产品,则应以不同方式构建列表。例如,如果您有产品列表,则可以使用Products引用表作为产品列表。至于日期,您可能需要生成给定范围的日期列表,如@OMG Ponies所建议的那样(这个问题可能对您有帮助:Get a list of dates between two dates using a function)。< / p>

答案 1 :(得分:0)

如果使用sql 2008:

SELECT table1.date, table2.productid, isnull(table3.total,0) total FROM
--get the last 5 days
(
select distinct date from mytable1 where date > getdate() - 5
) table1
--get the 3 products
CROSS JOIN
(
select productid from product where productid in (1,2,3)
) table2
OUTER APPLY (SELECT SUM(1) Total from YourTable 
    where yourtable.date = table1.date 
    and yourtable.productid = table2.productid) Table3