【sqlserver】一个CTE递归调用案例优化

-- 一个CTE递归调用案例优化
/*** 相关数据已脱敏 ***/
--set statistics io on
--set statistics time on 

--待优化高耗时SQL
with cte as
 (select t.sid,
         t.end_date,
         t.nav,
         t.d_value,
         t.s_value,
         t.factor,
         t.rn,
         cast(1 AS decimal(18, 8)) as pre_factor
    from stg.etl_dg t
   where t.rn = 1
  union all
  select t1.sid,
         t1.end_date,
         t1.nav,
         t1.d_value,
         t1.s_value,
         t1.factor,
         t1.rn,
         cast(t1.factor * t2.pre_factor AS decimal(18, 8)) as pre_factor
    from stg.etl_dg t1
    join cte t2
      on t1.sid = t2.sid
     and t1.RN = t2.RN + 1)

select t3.sid,
       t3.end_date,
       t3.nav,
       t3.d_value,
       t3.s_value,
       t3.factor,
       t3.pre_factor
  from cte t3 OPTION(MAXRECURSION 0)

 /*
 耗时>1h
*/   

--表信息
select count(1) from stg.etl_dg --3124331
sp_helpindex 'stg.etl_dg' --NO INDEX


--减少测试数据量,方便查看优化效果
with cte as
 (select t.sid,
         t.end_date,
         t.nav,
         t.d_value,
         t.s_value,
         t.factor,
         t.rn,
         cast(1 AS decimal(18, 8)) as pre_factor
    from stg.etl_dg t
   where t.rn = 1
     and t.rn <= 10 ---###
  union all
  select t1.sid,
         t1.end_date,
         t1.nav,
         t1.d_value,
         t1.s_value,
         t1.factor,
         t1.rn,
         cast(t1.factor * t2.pre_factor AS decimal(18, 8)) as pre_factor
    from stg.etl_dg t1
    join cte t2
      on t1.sid = t2.sid
     and t1.RN = t2.RN + 1
     and t1.rn <= 10 ---###
  )

select t3.sid,
       t3.end_date,
       t3.nav,
       t3.d_value,
       t3.s_value,
       t3.factor,
       t3.pre_factor
  from cte t3
/*
SQL Server parse and compile time: 
   CPU time = 4 ms, elapsed time = 4 ms.

(45278 行受影响)
Table 'Worktable'. Scan count 45280, logical reads 9863362, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'etl_dg'. Scan count 2, logical reads 56232, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 13344 ms,  elapsed time = 13686 ms.
*/




--建立索引测试
CREATE NONCLUSTERED INDEX [idx_etl_dg_inc]
ON [stg].[etl_dg] ([RN])
INCLUDE ([sid],[end_date],[nav],[d_value],[s_value],[factor])

/*
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 4 ms.

(45278 行受影响)
Table 'Worktable'. Scan count 45280, logical reads 9926793, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'etl_dg'. Scan count 2, logical reads 26845, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 14891 ms,  elapsed time = 15729 ms.
*/



--尝试改写SQL,测试优化效果不大
with cte as(
  select t.sid,t.factor,t.rn,cast(1 AS decimal(18,8)) as pre_factor
  from stg.etl_dg t
  where t.rn = 1
  and t.rn<=10 ---###
  union all
  select t1.sid,t1.factor,t1.rn,cast(t1.factor*t2.pre_factor AS decimal(18,8)) as pre_factor
  from stg.etl_dg t1 join cte t2
  on t1.sid =  t2.sid
  and t1.RN = t2.RN + 1
  and t1.rn<=10 ---###
  )

select t3.sid,t.end_date,t.nav,t.d_value,t.s_value,t3.factor,t3.pre_factor
from cte t3,stg.etl_dg t
where t3.RN=t.RN and t3.sid=t.sid

/*
--增加唯一约束
ALTER TABLE stg.etl_dg ADD CONSTRAINT etl_dg$BPK_AK_Key UNIQUE NONCLUSTERED(RN,sid) WITH(ONLINE=ON,FillFactor=90)
--增加字段返还索引
CREATE NONCLUSTERED INDEX idx_etl_dg_inc ON stg.etl_dg (RN) INCLUDE (sid,factor) WITH(ONLINE=ON,FillFactor=90)
*/

/*
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 8 ms.

(45278 行受影响)
Table 'Worktable'. Scan count 45280, logical reads 9831102, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'etl_dg'. Scan count 3, logical reads 27236, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 14453 ms,  elapsed time = 15041 ms.
*/

--建立索引测试2
create clustered index etl_dg_asc on stg.etl_dg(sid, RN) WITH(ONLINE=ON,FillFactor=90)
--drop index etl_dg_asc on stg.etl_dg
/*
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

(45278 行受影响)
Table 'Worktable'. Scan count 2, logical reads 262902, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'etl_dg'. Scan count 45279, logical reads 136567, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 922 ms,  elapsed time = 1518 ms.
*/



--最终优化效果
数据库变更:
create clustered index etl_dg_asc on stg.etl_dg(sid, RN) WITH(ONLINE=ON,FillFactor=90)
CREATE NONCLUSTERED INDEX idx_etl_dg_inc ON stg.etl_dg (RN) INCLUDE (sid,end_date,nav,d_value,s_value,factor) WITH(ONLINE=ON,FillFactor=90)
/*
SQL Server parse and compile time: 
   CPU time = 3 ms, elapsed time = 3 ms.

(3124331 行受影响)
Table 'Worktable'. Scan count 2, logical reads 18737220, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'etl_dg'. Scan count 3124332, logical reads 9421054, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 68734 ms,  elapsed time = 91236 ms.
*/

--运行时长控制在一分半钟左右,逻辑读减少85%,时间减少95%+
--整个存储过程运行时间由45min+减少到3min以内

你可能感兴趣的:(SqlServer,Performance,Tuning)