排序运算符需要更多的查询费用

时间:2018-08-10 11:25:51

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

每天都有时间表表,许多客户都会在此表中插入时间表表, 表结构会像这样

Create table factSchedule (factID bigint identity (1,1) primary key, custId char(10), scheduleDate date, amount money, LoadedOn date)

说,

每个客户将有100多个时间表条目

每天将有10,000个客户插入相应的计划条目

此表将通过插入更新的计划条目进行每日更新,例如,对于第一个loadingOn日期,将有100条客户记录,而在第二loadedOn日期,将有100条记录,但金额不同,因此最终会有〜两天内为一位客户提供200条记录,因此您可以想象这张桌子有多大...

现在,我有一个SP,它将根据factSchedule表中给定输入日期的第一个scheduleDate返回一些计算出的存储桶字段。所以首先我需要一个cte表,该表仅包含给定LoadedOn字段的每个custId的第一个计划日期,作为输入参数,如下所示;

;with cteSchedule as
( SELECT result.*
     (select custId, scheduleDate, Amount,@inputDate as compareDate, row_number ()over ( partition by loadedon, custId order by loadedon, custId, scheduleDate)rownum From factSchedule Where LoadedOn = @inputDate and scheduleDate >= @ inputDate)
) as result
WHERE result.rownum = 1

现在我还有另一个表叫做Customertable,该表将为每个custId都有一个记录,因此该表中大约有10,000个客户。

所以我的SP在这里,

    ;with cteSchedule as
( SELECT result.*
     (select custId, scheduleDate, Amount,@inputDate as compareDate, row_number ()over ( partition by loadedon, custId order by loadedon, custId, scheduleDate)rownum From factSchedule Where LoadedOn = @inputDate and scheduleDate >= @ inputDate)
) as result
WHERE result.rownum = 1

Select Maintable.CustID,
(select case when scheduleDate < dateadd (day,7,compareday)
             then ‘Customertable.someAmount ’
       Else 0 end
  From  factSchedule and cteSchedule.custId = Maintable.custId) end as 
  Amt_0to7_days,

  (select case when ScheduleDate >= DATEADD (DAY,7,compareday) AND ScheduleDate <= DATEADD (MONTH,1,compareday)
             then ‘Customertable.someAmount’
       Else 0 end
  From  factSchedule and cteSchedule.custId = Maintable.custId) end as 
  Amt_7to30_days,

    (select case when ScheduleDate >= DATEADD (MONTH,1,compareday) AND ScheduleDate <= DATEADD (MONTH,3,compareday)
             then ‘Customertable.someAmount’
       Else 0 end
  From  factSchedule and cteSchedule.custId = Maintable.custId) end as 
  Amt_1to3_Months,

 -- "like wise it will have another five buckets."

 From Customertable 

以下是此SP的执行计划,它显示了我的第一个cteSchedule表中的Sort运算符为每个存储桶收取10%的查询成本,因此总共有8个存储桶,将有80%的查询成本,这导致了很多时间(超过1小时)。另外,排序运算符中的警告标志(操作员在执行门槛级别1时使用tempDb溢出数据)又意味着什么? enter image description here

是否还有其他方法可以减少这种排序运算符的查询成本,还是可以重写逻辑?无论如何,帮助将不胜感激。提前感谢

实际代码:

declare @RunDateLocal date = '20180709' 


;with cteSchedule AS
(SELECT  schedule.*
    FROM (select ContractIDKey,ScheduleDateKey, @RunDateLocal as compareDay,row_number ()over ( partition by loadedon,ContractIDKey order by loadedon,ContractIDKey,ScheduleDateKey)rownum
    from FactSchedules
    where  LoadedOn = DATEADD(D,1,@RunDateLocal) and ScheduleDateKey>= @RunDateLocal) as schedule
 WHERE schedule.rownum = 1
)

select FB.ContractIDKey,

(select case when ScheduleDateKey < DATEADD (DAY,7,compareday)
                            Then ISNULL(AIA.Closingbalance,0)
                        else 0 end
                        from cteschedule where rownum = 1 and cteSchedule.ContractIDKey = FB.ContractIDKey)  as Interest_0To7_Days,

(select case when ScheduleDateKey >= DATEADD (DAY,7,compareday) AND ScheduleDateKey <= DATEADD (MONTH,1,compareday) 
                            Then ISNULL(AIA.Closingbalance,0)
                        else 0 end
                        from cteschedule where rownum = 1 and cteSchedule.ContractIDKey = FB.ContractIDKey)  as Interest_7To30_Days,

(select case when ScheduleDateKey > DATEADD (MONTH,1,compareday) AND ScheduleDateKey <= DATEADD (MONTH,3,compareday) 
                            Then ISNULL(AIA.Closingbalance,0)
                        else 0 end
                        from cteschedule where rownum = 1 and cteSchedule.ContractIDKey = FB.ContractIDKey)  as Interest_1To3_Months,

(select case when ScheduleDateKey > DATEADD (MONTH,3,compareday) AND ScheduleDateKey <= DATEADD (MONTH,6,compareday)
                            Then ISNULL(AIA.Closingbalance,0)
                        else 0 end
                        from cteschedule where rownum = 1 and cteSchedule.ContractIDKey = FB.ContractIDKey)  as Interest_3To6_Months,

(select case when ScheduleDateKey > DATEADD (MONTH,6,compareday) AND ScheduleDateKey <= DATEADD (MONTH,12,compareday)
                            Then ISNULL(AIA.Closingbalance,0)
                        else 0 end
                        from cteschedule where rownum = 1 and cteSchedule.ContractIDKey = FB.ContractIDKey)  as Interest_6To12_Months,

(select case when ScheduleDateKey > DATEADD (YEAR,1,compareday) AND ScheduleDateKey <= DATEADD (YEAR,3,compareday)
                            Then ISNULL(AIA.Closingbalance,0)
                        else 0 end
                        from cteschedule where rownum = 1 and cteSchedule.ContractIDKey = FB.ContractIDKey)  as Interest_1To3_Years,

(select case when ScheduleDateKey > DATEADD (YEAR,3,compareday) AND ScheduleDateKey <= DATEADD (YEAR,5,compareday)
                            Then ISNULL(AIA.Closingbalance,0)
                        else 0 end
                        from cteschedule where rownum = 1 and cteSchedule.ContractIDKey = FB.ContractIDKey)  as Interest_3To5_Years,

(select case when ScheduleDateKey > DATEADD (YEAR,5,compareday) 
                            Then ISNULL(AIA.Closingbalance,0)
                        else 0 end
                        from cteschedule where rownum = 1 and cteSchedule.ContractIDKey = FB.ContractIDKey)  as Interest_5Over_years

from  FactBalances FB 
LEFT JOIN vAllDeposists_AIAAmount AIA ON AIA.ContractIDKey = FB.ContractIDKey

2 个答案:

答案 0 :(得分:1)

您是否尝试过在临时表中实现数据集而不是使用CTE?公用表表达式将在每次使用sql时运行sql,而临时表将仅运行一次并重用相同的结果集。这种方法应该减少排序的数量(减少到1)。

泄漏通常与不良数据有关。检查估计的执行计划与实际的执行计划,以确认这一点。放弃cte也可以提供帮助,因为可以重建统计信息。不用戳很难确定。

答案 1 :(得分:1)

对于cte中的查询,由于LoadedOn始终相同,因此无需按该列进行分区或排序。另外,当按ContractIDKey进行分区时,不需要按该列排序。

无需查询CTE或临时表8次。您用于原始cte的查询确实确实只返回ContractIDKey 1条记录,因此(外部)加入cte一次就足够了。我建议测试这样的查询(使用cte或临时表):

DECLARE @RunDateLocal date = '20180709';

WITH cteSchedule AS
(SELECT  ContractIDKey, ScheduleDateKey
    FROM (select ContractIDKey, ScheduleDateKey, row_number() over (partition by ContractIDKey ORDER BY ScheduleDateKey) rownum
    from FactSchedules
    where  LoadedOn = DATEADD(D,1,@RunDateLocal) and ScheduleDateKey>= @RunDateLocal) as schedule
 WHERE schedule.rownum = 1
)

SELECT 
  FB.ContractIDKey,

  case
    when cte.ScheduleDateKey < DATEADD(DAY,7,@RunDateLocal)
    then ISNULL(AIA.Closingbalance, 0)
    else 0
  end as Interest_0To7_Days,

  case
    when cte.ScheduleDateKey >= DATEADD(DAY,7,@RunDateLocal) AND ScheduleDateKey <= DATEADD(MONTH,1,@RunDateLocal) 
    then ISNULL(AIA.Closingbalance,0)
    else 0
  end as Interest_7To30_Days,

  case
    when ScheduleDateKey > DATEADD(MONTH,1,@RunDateLocal) AND ScheduleDateKey <= DATEADD(MONTH,3,@RunDateLocal) 
    then ISNULL(AIA.Closingbalance,0)
    else 0
  end as Interest_1To3_Months,

  case
    when ScheduleDateKey > DATEADD(MONTH,3,@RunDateLocal) AND ScheduleDateKey <= DATEADD(MONTH,6,@RunDateLocal)
    then ISNULL(AIA.Closingbalance,0)
    else 0 end Interest_3To6_Months,

  case
    when ScheduleDateKey > DATEADD(MONTH,6,@RunDateLocal) AND ScheduleDateKey <= DATEADD(MONTH,12,@RunDateLocal)
    then ISNULL(AIA.Closingbalance,0)
    else 0
  end as Interest_6To12_Months,

  case
    when ScheduleDateKey > DATEADD(YEAR,1,@RunDateLocal) AND ScheduleDateKey <= DATEADD(YEAR,3,@RunDateLocal)
    then ISNULL(AIA.Closingbalance,0)
    else 0
  end as Interest_1To3_Years,

  case
    when ScheduleDateKey > DATEADD(YEAR,3,@RunDateLocal) AND ScheduleDateKey <= DATEADD(YEAR,5,@RunDateLocal)
    then ISNULL(AIA.Closingbalance,0)
    else 0
  end as Interest_3To5_Years,

  case
    when ScheduleDateKey > DATEADD(YEAR,5,@RunDateLocal) 
    then ISNULL(AIA.Closingbalance,0)
    else 0
  end as Interest_5Over_years

FROM  FactBalances FB 
  LEFT JOIN cteSchedule cte ON cte.ContractIDKey = FB.ContractIDKey
  LEFT JOIN vAllDeposists_AIAAmount AIA ON AIA.ContractIDKey = FB.ContractIDKey