【问题标题】:Returning all results in where in clause (Sql Server)在 where in 子句中返回所有结果(Sql Server)
【发布时间】:2014-03-22 04:09:47
【问题描述】:

我有一张如下表:

Book        BookSales    Date
BookA       $1           2013-03-04
BookB       $2           2013-03-04
BookA       $3           2013-03-05

... ...

我运行查询 -

select book,booksales,date 
from tblBook 
where date>='03/03/2013'  
and date<='03/05/2013' 
and book in ('BookA','BookB') 
order by date,book

现在它返回如下结果

BookA     $1            2013-03-04
BookB     $2            2013-03-04
BookA     $3            2013-03-05

但我想要的是返回书 b 的空结果($0),如果它在该日期不存在,如下所示)。此处,日期为 2013-03-05 的书 b 的记录不存在。

BookA     $1            2013-03-04
BookB     $2            2013-03-04
BookA     $3            2013-03-05
BookB     $0            2013-03-05  <-- I want to include this record

我怎样才能做到这一点?如果无法实现这种方式,我可以考虑其他什么方法?

【问题讨论】:

  • 你有主键吗?
  • 是的,有一个主键 'id(int)'。它是一个身份密钥。
  • 您的问题令人困惑。上下文中的 empty result ($0) 是什么?此外,预期结果集中包含的 $0 图书的日期确实具有 2013-03-05。
  • @Crono:$0 是图书销售额。原始表中不存在日期为 2013-03-05 的书 b 的记录。如果该日期不存在记录,则应包括该日期的图书销售额(0 美元)。
  • 啊,我想我现在明白了。您需要一个日期表才能加入。

标签: sql sql-server sql-server-2008 tsql


【解决方案1】:

必须有一种更简单的方法来做到这一点,但这就是我想出的。使用公用表表达式来获取不同的日期列表和不同的书籍列表,然后CROSS JOIN 它们以获取日期和书籍的完整矩阵。一旦你得到了完整的矩阵,你可以OUTER JOIN你的表给它,用 0 替换任何空值。

交叉连接很快就会变得混乱,但如果它只是日期列表和书籍列表,它应该可以正常工作。

;with dates AS
(
  select distinct date
  from tblBook
), books AS
(
  select distinct book
  from tblBook
)
select b.book, coalesce(tb.booksales, 0), d.[date]
from dates as d
cross join books as b
left outer join tblBook as tb
  on d.[date] = tb.[date]
    and b.book = tb.book
where d.[date] >='03/03/2013'  
and d.[date] <='03/05/2013' 
and b.book in ('BookA','BookB') 
order by d.[date], b.[book]

Working SQLFiddle here.

【讨论】:

    【解决方案2】:

    使用维度子查询和外部联接 SQL 解决缺少的聚合数据

    您可以通过使用生成您需要报告的所有不同值的子查询来解决您的问题。我假设输出应该提供一种方法来跟踪每天每本书的销售情况,即使没有在表中输入销售交易。这是一种可能的解决方案和输出:

    问题分析:

    通过构造一个代表所有可能值的sub query,无论它们是否已经存在于表中,都可以请求查看查询表中没有物理表示的数据值。

    在这种情况下,报告的两个维度是:“book”和“date”。对于图书销售表中记录的每一天的交易,每本书的总销售额必须有一个条目。

    假设:在示例中不清楚 tblBook 是否代表已经汇总的销售额,或者对于给定的“book”和“date”组合是否可以有多个记录...就像交易登记簿的风格一样,记录每笔交易的发生。解决方案查询汇总销售金额,以提供每本书每天的单一记录。

    注意:此示例在 MySQL RDBMS 上进行了测试。此示例中使用的 SQL 是 ANSI 标准,因此它也应该适用于 SQLServer 数据库。您可能需要找到与此解决方案中使用的 ifNULL() 函数等效的函数

    SQL 代码

     SELECT REPORTING_DIMENSIONS.book, 
        sum(ifNULL(sales_data.bookSales,0)) as totalSales,
        REPORTING_DIMENSIONS.date
    
     FROM 
       (SELECT book_list.book, date_list.date 
          FROM
            (SELECT distinct book
               FROM tblBook
               ) book_list
    
           CROSS JOIN
           (SELECT distinct date
              FROM tblBook
              ) date_list
       ) REPORTING_DIMENSIONS
    
       LEFT JOIN tblBook SALES_DATA
       ON REPORTING_DIMENSIONS.book = SALES_DATA.book
          AND REPORTING_DIMENSIONS.date = SALES_DATA.date
    
     GROUP BY REPORTING_DIMENSIONS.book, 
        REPORTING_DIMENSIONS.date
    

    输出:

     |  BOOK | TOTALSALES |                         DATE |
     |-------|------------|------------------------------|
     | BookA |          2 | March, 04 2013 00:00:00+0000 |
     | BookA |          6 | March, 05 2013 00:00:00+0000 |
     | BookB |          4 | March, 04 2013 00:00:00+0000 |
     | BookB |          0 | March, 05 2013 00:00:00+0000 |
    

    结果讨论

    BookB 在 2013 年 3 月 15 日的零值实际上是一个 NULL 值。当进行连接值不匹配的 OUTER(即 LEFT)连接时,将返回 null。

    唯一花哨的步骤是CROSS JOIN 名称。也称为CARTESIAN PRODUCT,它为现有表中记录的每个可能的“书籍”和“日期”组合生成一条记录。

    别名为REPORTING DIMENSIONS 的子查询是查询的固定、动态功能。它允许查询在将新书籍或日期添加到其范围时调整其输出。

    其他注意事项:

    您的报告查询范围内包含的一个、全部或多个日历日可能没有任何产品没有销售。这又会影响原始报表查询的输出,因为结果的输出会出现漏洞。

    这可以通过为日期值创建一个固定维度表来解决。在您的销售数据范围之前和之后的几年中,使用每个离散日历日的记录预先填充它。带有循环插入操作的简单 T/SQL 块可以轻松填充此表。一旦它被填充,它就不太可能需要被触摸。

    【讨论】:

      【解决方案3】:

      如果你有这个

      Book        BookSales    Date
      BookA       $1           2013-03-04
      BookB       $2           2013-03-04
      BookA       $3           2013-03-06
      

      想要

      BookA     $1            2013-03-04
      BookB     $2            2013-03-04
      BookA     $0            2013-03-05
      BookB     $0            2013-03-05
      BookA     $3            2013-03-06
      BookB     $0            2013-03-06
      

      盗自 Jeff Rosenberg 的回答和t-sql get all dates between 2 dates

      ;WITH dates AS
      (
        SELECT CAST('2013-03-04' AS DATETIME) AS [date]
        UNION ALL
        SELECT DATEADD(dd, 1, [date])
          FROM dates s
         WHERE DATEADD(dd, 1, [date]) <= CAST('2013-03-06' AS DATETIME)), books AS
      (
        select distinct book
        from tblBooks
      )
      select b.book, coalesce(tb.booksales, 0), d.[date]
      from dates as d
      cross join books as b
      left outer join tblBooks as tb
        on d.[date] = tb.[date]
          and b.book = tb.book
      

      如果你需要聚合

      ...
          SELECT b.Book, COALESCE(SUM(tb.BookSales), 0), d.[Date]
          FROM Dates d
              CROSS JOIN Books b
              LEFT JOIN tblBooks tb ON d.[Date] = tb.Date
                  AND b.Book = tb.Book
          GROUP BY b.Book, d.[Date]
      

      【讨论】:

        猜你喜欢
        • 2023-03-26
        • 2020-06-23
        • 2014-06-25
        • 2011-08-31
        • 1970-01-01
        • 2021-02-21
        • 2013-11-25
        • 2017-07-01
        • 1970-01-01
        相关资源
        最近更新 更多