优化T-sql查询

时间:2017-12-06 21:49:33

标签: sql sql-server tsql

我需要开发的查询是列出给定日期没有记录(零售交易)的网站。我已经能够为特定网站完成此任务,但我知道可以使用连接更有效地编写查询,但我对内部和外部的尝试并不是很有趣。

这是我到目前为止所拥有的:

DECLARE @StartDate DATE = '2017-11-01',
    @EndDate DATE = '2017-11-30';

SELECT tx.Txndate, count(*) as txCount
FROM [Report].[dbo].[FactTransactions] tx 
where tx.Site = 2
and tx.TxnDate between @StartDate and @EndDate
group by tx.TxnDate
union all
select db.daybookdate, 0 as txCount
from DimDaybook db
where db.daybookdate between @StartDate and @EndDate
and NOT EXISTS (SELECT 1 FROM [Report].[dbo].[FactTransactions] AS t WHERE t.TxnDate = db.daybookdate and t.txndate between @StartDate and @EndDate and t.site = 2) 
order by tx.Txndate

这将返回站点2的结果集

Txndate txCount
2017-11-01  1691
2017-11-02  1657
2017-11-03  1835
2017-11-04  1587
2017-11-05  1489
2017-11-06  1544
2017-11-07  1525
2017-11-08  1782
2017-11-09  1848
2017-11-10  1990
2017-11-11  0
2017-11-12  0

我真正想要的是一个看起来像这样的结果集并且运行得比我的黑客快得多(目前大约2分钟 - 对于上下文,事务表有83,486,412条记录)

Site    Date    Transactions
2   11/11/17    0
2   12/11/27    0
3   12/11/17    0
22  1/11/17     0

3 个答案:

答案 0 :(得分:1)

由于您已经拥有所有可用日期的列表(DimDaybook),您需要做的就是将所有日期的“左连接”计算到那些日期,然后将所有NULL替换为零。

请注意日期范围,使用“between”可能听起来像是一种很好的方法,但这不是最好的方法。而不是尝试指定11月的最后一天,而是指定12月的第一天,然后使用>=<的组合与您的日期参数,然后您的日期范围将适用于每个日期/时间数据类型(datetime2,datetime,smalldatime,date)

DECLARE @StartDate date = '2017-11-01'
      , @EndDate date = '2017-12-01'; -- this has changed!

SELECT
      db.daybookdate
    , COALESCE(txCount, 0)
FROM DimDaybook db
LEFT JOIN (
                  SELECT
                        tx.Txndate
                      , COUNT(*) AS txcount
                  FROM [Report].[dbo].[FactTransactions] tx
                  WHERE tx.Site = 2
                  AND tx.TxnDate >= @StartDate AND tx.TxnDate < @EndDate
                  GROUP BY
                        tx.TxnDate
      ) c ON db.daybookdate = c.Txndate
WHERE db.daybookdate >= @StartDate AND db.daybookdate < @EndDate
AND tx.Txndate IS NULL

当您建立'2017-11-30'之类的日期参考时,时间会自动设置为00:00:00+0000000。因此,对于您可能遇到的任何数据,其日期为'2017-11-30'但时间> 00:00:00,数据将被排除。简而言之:忽略当天的持续时间。只需将结束日期“向上”移动到第二天并使用少于该日期即可轻松克服这一问题,为此,您必须避免使用“之间”。

答案 1 :(得分:0)

使用cross join生成所有行。然后用数据过滤掉那些:

select s.site, db.daybookdate
from DimDaybook db cross join
     (select distinct site
      from [Report].[dbo].[FactTransactions] ft
     ) s
where db.daybookdate between @StartDate and @EndDate and
      not exists (select 1 
                  from [Report].[dbo].[FactTransactions] ft 
                  where ft.TxnDate = db.daybookdate and
                        ft.site = s.site
                 ) 
order by tx.Txndate

答案 2 :(得分:0)

一种方法可能如下:

DECLARE @StartDate DATE = '2017-11-01', @EndDate DATE = '2017-11-30'; 
With cte as (
 SELECT tx.Txndate, count(*) as txCount FROM 
[Report].[dbo].[FactTransactions] tx where tx.Site = 2 and tx.TxnDate between @StartDate and @EndDate group by tx.TxnDate)
Select db.daybookdate, isnull(txCount,0) from DimDaybook db left join cte on db.daybookdate = cte.Txndate

我错过了网站ID ,但我无法从您的示例代码中获取它。