web-dev-qa-db-ja.com

パフォーマンスの向上

少し遅いクエリがあります。

SELECT b.BreakdownClassificationId,
       k.IsinCode,
       k.SedolCode,
       ClassificationDate,
       NAME,
       InstrumentType,
       GeographicalLocation,
       CapSize,
       Currency,
       ExchangeName,
       HoldingDomicile,
       MaturityDate,
       Sector,
       MajorSector
FROM   #BreakdownSet b
       OUTER apply (SELECT TOP 1 IsinCode,
                                 SedolCode,
                                 ClassificationDate,
                                 NAME,
                                 InstrumentType,
                                 GeographicalLocation,
                                 CapSize CapSize,
                                 Currency,
                                 ExchangeName,
                                 HoldingDomicile,
                                 MaturityDate,
                                 Sector,
                                 MajorSector
                    FROM   dbfinex.dbo.PfPortfolioHoldingClassificationFtid x WITH (nolock)
                    WHERE  ( x.isincode > ''
                             AND x.isincode = b.breakdowncode )
                            OR ( x.sedolcode > ''
                                 AND x.sedolcode = b.breakdowncode )
                            OR ( x.sedolcode > ''
                                 AND x.sedolcode = b.sedolcode )
                            OR ( x.isincode > ''
                                 AND x.isincode = b.isincode )
                    ORDER  BY CASE
                                WHEN x.sedolcode = b.breakdowncode THEN 1
                                WHEN x.isincode = b.breakdowncode THEN 2
                                WHEN x.sedolcode = b.sedolcode THEN 3
                                WHEN x.isincode = b.isincode THEN 4
                                ELSE 5
                              END,
                              classificationdate DESC) k 

実行計画

enter image description here

Order By内部Cross Applyは非常にコストがかかります。このクエリを作成するより良い方法はありますか?

3

必要に応じて並べ替えを削除できますが、クエリのパフォーマンスを向上させる必要があるかどうかはわかりません。重要なのは、ORDER BY句と検索条件をどのように構築したかです。 x.sedolcode = b.breakdowncodeに一致する行がある場合は、その行を取得します。それ以外の場合は、次の条件に進みます。適切なインデックスを使用すると、APPLYを分割することでソートを回避できます。 SQL Serverクエリオプティマイザーは、実際にOR条件をUNIONsに変換するため、Niceヒントを提供します。

私が求めている一般的なクエリプランの形状を示すために、限られた例を作成します。また、PfPortfolioHoldingClassificationFtidテーブルのPK列に主キーとクラスタリングキーがあると仮定します。これが私のテストデータです:

CREATE TABLE #BreakdownSet (
    BreakdownClassificationId BIGINT NOT NULL,
    breakdowncode VARCHAR(10) NULL,
    sedolcode VARCHAR(10) NULL,
    isincode VARCHAR(10) NULL
);

INSERT INTO #BreakdownSet
SELECT 
  t.RN
, CASE WHEN RN % 10 = 1 THEN t.RN ELSE NULL END
, CASE WHEN RN % 10 = 4 THEN t.RN ELSE NULL END
, CASE WHEN RN % 10 = 7 THEN t.RN ELSE NULL END
FROM
(
    SELECT TOP (1500000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
    FROM master..spt_values t1
    CROSS JOIN master..spt_values t2
) t;

CREATE TABLE dbo.PfPortfolioHoldingClassificationFtid (
    PK BIGINT NOT NULL,
    isincode VARCHAR(10) NOT NULL,
    sedolcode VARCHAR(10) NOT NULL,
    ClassificationDate DATE NOT NULL,
    OTHER_COLUMN VARCHAR(200) NOT NULL,
    PRIMARY KEY (PK)
);

INSERT INTO dbo.PfPortfolioHoldingClassificationFtid WITH (TABLOCK)
SELECT 
  t.RN
, t.RN
, t.RN
, DATEADD(DAY, t.rn / 100, '20170101')
, REPLICATE('OTHER', 40)
FROM
(
    SELECT TOP (1500000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
    FROM master..spt_values t1
    CROSS JOIN master..spt_values t2
) t;

CREATE INDEX isin_date ON dbo.PfPortfolioHoldingClassificationFtid (isincode, ClassificationDate);

CREATE INDEX sedol_date ON dbo.PfPortfolioHoldingClassificationFtid (sedolcode, ClassificationDate);

これが私のテーブル定義を使用したクエリです。

SELECT b.breakdownclassificationid, 
       k.isincode, 
       k.sedolcode, 
       classificationdate, 
       other_column 
FROM   #breakdownset b 
       OUTER apply (SELECT TOP 1 isincode, 
                                 sedolcode, 
                                 classificationdate, 
                                 other_column 
                    FROM   dbo.pfportfolioholdingclassificationftid x WITH ( 
                           nolock) 
                    WHERE  ( x.isincode > '' 
                             AND x.isincode = b.breakdowncode ) 
                            OR ( x.sedolcode > '' 
                                 AND x.sedolcode = b.breakdowncode ) 
                            OR ( x.sedolcode > '' 
                                 AND x.sedolcode = b.sedolcode ) 
                            OR ( x.isincode > '' 
                                 AND x.isincode = b.isincode ) 
                    ORDER  BY CASE 
                                WHEN x.sedolcode = b.breakdowncode THEN 1 
                                WHEN x.isincode = b.breakdowncode THEN 2 
                                WHEN x.sedolcode = b.sedolcode THEN 3 
                                WHEN x.isincode = b.isincode THEN 4 
                                ELSE 5 
                              END, 
                              classificationdate DESC) k; 

当然のことながら、私はあなたとは別の計画を立てていますが、その種類にはまだ高い推定コストがあります。 APPLYを4つの部分に分割し、各APPLYがテーブルの主キーのみを返すようにするとどうなるでしょうか。各APPLYにカバーするインデックスがある場合、最大4つのインデックスシークで一致する行の主キーを見つけることができます。並べ替えは必要ありません。 APPLYsにフィルターを追加することで、不要なシークをスキップすることもできますが、これは保証された動作ではありません。これを記述する1つの方法を次に示します。

SELECT b.breakdownclassificationid, 
       k.isincode, 
       k.sedolcode, 
       classificationdate, 
       other_column 
FROM   #breakdownset b 
       OUTER apply (SELECT TOP 1 pk 
                    FROM   dbo.pfportfolioholdingclassificationftid x 
                    WHERE  x.sedolcode = b.breakdowncode 
                    ORDER  BY classificationdate DESC) a1 
       OUTER apply (SELECT TOP 1 pk 
                    FROM   dbo.pfportfolioholdingclassificationftid x 
                    WHERE  x.isincode = b.breakdowncode 
                           AND a1.pk IS NOT NULL 
                    ORDER  BY classificationdate DESC) a2 
       OUTER apply (SELECT TOP 1 pk 
                    FROM   dbo.pfportfolioholdingclassificationftid x 
                    WHERE  x.sedolcode = b.sedolcode 
                           AND a2.pk IS NOT NULL 
                    ORDER  BY classificationdate DESC) a3 
       OUTER apply (SELECT TOP 1 pk 
                    FROM   dbo.pfportfolioholdingclassificationftid x 
                    WHERE  x.isincode = b.isincode 
                           AND a3.pk IS NOT NULL 
                    ORDER  BY classificationdate DESC) a4 
       LEFT OUTER JOIN dbo.pfportfolioholdingclassificationftid k 
                    ON k.pk = COALESCE(a1.pk, a2.pk, a3.pk, a4.pk);

一時テーブルに150万行ある場合、最悪のケースは600万の非クラスター化インデックスシークと150万のクラスター化インデックスシークです。

クエリは私のマシンで2秒で実行されます。ナンセンスなデータがあるので、ランタイムは関係ありません。ただし、計画はありません。クエリプランを Paste The Plan にアップロードしました。将来の質問についても同様に検討する必要があります。これも実際の計画のスクリーンショットです。

actual plan hash

あなたが持っているクエリは、ネストされたループ結合を行い、並列処理を行いません。 LOOP JOINMAXDOP 1ヒントを使用すると、クエリは私のマシンで7秒で終了します。こちらが 計画 で、こちらがスクリーンショットです。

enter image description here

2
Joe Obbish