返回所有结果in子句(Sql Server)

时间:2014-03-21 18:07:29

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

我有一张如下表:

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

我怎样才能做到这一点?如果不可能实现这种方式,我还可以考虑采用其他方法吗?

3 个答案:

答案 0 :(得分:3)

必须有一种更简单的方法来做到这一点,但这就是我想出来的。使用公用表表达式获取不同的日期列表和不同的书籍列表,然后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

答案 1 :(得分:2)

使用维度子查询和外部联接SQL

解决缺少的聚合数据

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

问题分析:

通过构造代表表中所有可能值的sub query,可以查看查询表中没有物理表示的数据值的请求。

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

假设:示例中并不清楚tblBook是否代表已汇总的销售额,或者是否有针对给定“预订”和“日期”组合的多条记录。 ..就像交易记录的样式一样,记录每笔销售。解决方案查询汇总销售额,以便每天为每本书提供单个记录。

  

注意:此示例已在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-03-15的零值实际上是一个NULL值。当进行OUTER(即LEFT)连接时,连接值不匹配,返回null。

唯一的奇特步骤是CROSS JOIN指定。也称为CARTESIAN PRODUCT,这会为现有表格中记录的每个可能的“书籍”和“日期”组合生成记录。

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

其他注意事项:

  

在报告查询范围内包含的一个,所有或许多日历日内,可能没有任何产品销售。这将再次影响原始报表查询的输出,因为结果输出中将存在漏洞。

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

答案 2 :(得分:1)

如果你有这个

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

杰夫罗森伯格的答案被盗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]