需要 SQL Server 索引/性能帮助(索引扫描和排序需要 40 分钟)

7 sql-server-2008 sql-server sql-server-2008-r2

我终于找到了查询的逻辑,现在我需要加快速度......如果可能的话(它运行 40 分钟 +/-)。它位于两张桌子上,一张只有几百行 ( tblTradingDays),另一张有超过一百万行 ( tblDailySMA)。它返回 4800 万行。

我的基本逻辑是:我为交易品种、交易日期和周期值的每个组合返回一行,创建一个“慢v 快”周期对。所以在原始表中,有一个符号、一个交易日期、一个周期 (5,10,20,...) 和该组合的实际值。我想比较所有期间组合的值(期间 p1 = 期间 p2 除外)。我正在对要用于进一步处理的记录进行编号。希望这是有道理的。

我尝试了连接和谓词中涉及的字段的各种索引和组合。在估计的执行计划中,看起来排序是最昂贵的操作。tblDailySMA尽管我添加了其他索引,但该计划中出现的唯一索引是非聚集的、唯一的、 on:Symbol, TradeDate, Period和 includes Value。它被扫描。查询如下,希望有人可以帮助我。提前致谢..

SELECT 
   ROW_NUMBER() OVER 
   (
        ORDER BY t1.Symbol, t1.Period, t2.Period, t.TradingDate DESC
   ) RowNum,
   t1.Symbol, t.TradingDate, t1.Period, t2.Period, t1.Value FastValue, 
   t2.Value SlowValue, (t1.Value - t2.Value) SlowFastDiff,
   ChgSign = CASE WHEN t1.Value < t2.Value THEN 0 
                  WHEN t1.Value >= t2.Value THEN 1 
                  WHEN t1.Value IS NULL OR t2.Value IS NULL THEN NULL 
             END
FROM 
    tblTradingDays t 
RIGHT JOIN 
    tblDailySMA t1 ON t.TradingDate = t1.TradeDate
INNER JOIN 
    tblDailySMA t2 ON t1.Symbol = t2.Symbol AND t1.TradeDate  = t2.TradeDate
WHERE 
    t1.Period < t2.Period
Run Code Online (Sandbox Code Playgroud)

这是执行计划:

  |--Compute Scalar(DEFINE:([Expr1007]=[Market].[dbo].[tblDailySMA].[Value] as [t1].[Value]-[Market].[dbo].[tblDailySMA].[Value] as [t2].[Value], [Expr1008]=CASE WHEN [Market].[dbo].[tblDailySMA].[Value] as [t1].[Value]<[Market].[dbo].[tblDailySMA].[Value] as [t2].[Value] THEN (0) ELSE CASE WHEN [Market].[dbo].[tblDailySMA].[Value] as [t1].[Value]>=[Market].[dbo].[tblDailySMA].[Value] as [t2].[Value] THEN (1) ELSE NULL END END))
       |--Sequence Project(DEFINE:([Expr1006]=row_number))
            |--Segment
                 |--Parallelism(Gather Streams, ORDER BY:([t1].[Symbol] ASC, [t1].[Period] ASC, [t2].[Period] ASC, [t].[TradingDate] DESC))
                      |--Sort(ORDER BY:([t1].[Symbol] ASC, [t1].[Period] ASC, [t2].[Period] ASC, [t].[TradingDate] DESC))
                           |--Hash Match(Right Outer Join, HASH:([t].[TradingDate])=([t1].[TradeDate]), RESIDUAL:([Market].[dbo].[tblTradingDays].[TradingDate] as [t].[TradingDate]=[Market].[dbo].[tblDailySMA].[TradeDate] as [t1].[TradeDate]))
                                |--Parallelism(Distribute Streams, Hash Partitioning, PARTITION COLUMNS:([t].[TradingDate]))
                                |    |--Index Scan(OBJECT:([Market].[dbo].[tblTradingDays].[PK_tblTradingDays] AS [t]))
                                |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([t1].[TradeDate]))
                                     |--Merge Join(Inner Join, MANY-TO-MANY MERGE:([t1].[Symbol], [t1].[TradeDate])=([t2].[Symbol], [t2].[TradeDate]), RESIDUAL:([Market].[dbo].[tblDailySMA].[Symbol] as [t1].[Symbol]=[Market].[dbo].[tblDailySMA].[Symbol] as [t2].[Symbol] AND [Market].[dbo].[tblDailySMA].[TradeDate] as [t1].[TradeDate]=[Market].[dbo].[tblDailySMA].[TradeDate] as [t2].[TradeDate] AND [Market].[dbo].[tblDailySMA].[Period] as [t1].[Period]<[Market].[dbo].[tblDailySMA].[Period] as [t2].[Period]))
                                          |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([t1].[Symbol], [t1].[TradeDate]), ORDER BY:([t1].[Symbol] ASC, [t1].[TradeDate] ASC))
                                          |    |--Index Scan(OBJECT:([Market].[dbo].[tblDailySMA].[IX_tblDailySMA_Noncl_SymbTrDatePer] AS [t1]), ORDERED FORWARD)
                                          |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([t2].[Symbol], [t2].[TradeDate]), ORDER BY:([t2].[Symbol] ASC, [t2].[TradeDate] ASC))
                                               |--Index Scan(OBJECT:([Market].[dbo].[tblDailySMA].[IX_tblDailySMA_Noncl_SymbTrDatePer] AS [t2]), ORDERED FORWARD)
Run Code Online (Sandbox Code Playgroud)

小智 3

当我在大型查询中遇到此类性能问题时,我将其拆分为带有临时表的小型查询。对我来说这是一个解决方案,性能比可能是 10 比 1 或更高。

第一步:

with t1 as (
SELECT 
  t1.Symbol, 
  t1.Period, 
  t.TradingDate,
  t1.Value as FastValue
FROM      tblDailySMA t1 
LEFT JOIN tblTradingDays t 
        ON t.TradingDate = t1.TradeDate
) 
select * 
into #t1
from t1;

--I include period into idex to avoid table access on next query
create index t1_idx on ( Symbol, TradeDate, Period ) 
Run Code Online (Sandbox Code Playgroud)

第二步:

with t2 as (
SELECT
  t1.Symbol, 
  t1.Period as period_t1, 
  t.TradingDate,
  t1.Value as FastValue
  t2.Period as period_t2,
  t2.Value as SlowValue,
  t2.TradeDate
FROM       #t1 as t1
INNER JOIN tblDailySMA t2 
   ON t1.Symbol = t2.Symbol AND t1.TradeDate  = t2.TradeDate
WHERE t1.Period < t2.Period
)
select 
  *
into #t2
from t2;

--Here create indexes for t2
--Here next and final query 
Run Code Online (Sandbox Code Playgroud)

等等。该系统的好处之一是您可以逐步改进查询。