インデックスは、個別のSELECTと比較してOR条件を使用するとはるかに遅くなります


8

これらの質問と与えられた回答に基づいて:

SQL 2008 Server-非常に大きなテーブルに接続されている可能性があるパフォーマンスの損失

履歴データを含む大きなテーブルは、SQL Server 2008 Stdを過剰に割り当てます。メモリ-他のデータベースのパフォーマンス低下

データベースSupervisionPに次のように定義されたテーブルがあります。

CREATE TABLE [dbo].[PenData](
    [IDUkazatel] [smallint] NOT NULL,
    [Cas] [datetime2](0) NOT NULL,
    [Hodnota] [real] NULL,
    [HodnotaMax] [real] NULL,
    [HodnotaMin] [real] NULL,
 CONSTRAINT [PK_Data] PRIMARY KEY CLUSTERED 
(
    [IDUkazatel] ASC,
    [Cas] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE [dbo].[PenData]  WITH NOCHECK ADD  CONSTRAINT [FK_Data_Ukazatel] FOREIGN KEY([IDUkazatel])
REFERENCES [dbo].[Ukazatel] ([IDUkazatel])

ALTER TABLE [dbo].[PenData] CHECK CONSTRAINT [FK_Data_Ukazatel]

これにはcca 211ミリオンの行が含まれます。

私は次のステートメントを実行します:

DECLARE @t1 DATETIME;
DECLARE @t2 DATETIME;

SET @t1 = GETDATE();
SELECT min(cas) from PenData p WHERE IDUkazatel=24
SELECT min(cas) from PenData p WHERE IDUkazatel=25
SET @t2 = GETDATE();
SELECT DATEDIFF(millisecond,@t1,@t2) AS elapsed_ms;


SET @t1 = GETDATE();
SELECT min(cas) from PenData p WHERE IDUkazatel=24 OR IDUkazatel=25 
SET @t2 = GETDATE();
SELECT DATEDIFF(millisecond,@t1,@t2) AS elapsed_ms;

結果は次のとおりです。

実行計画

3番目のSELECTは、SQL Serverメモリキャッシュにさらに多くのデータをロードします。

3番目のSELECTが最初の2つのSELECT(16ミリ秒)よりもはるかに遅い(8.5秒)のはなぜですか?ORを使用して3番目の選択のパフォーマンスを向上させるにはどうすればよいですか?次のSQLコマンドを実行したいのですが、この場合、カーソルを作成して個別のクエリを実行する方が、単一の選択よりもはるかに速いようです。

 SELECT MIN(cas) from PenData p WHERE IDUkazatel IN (SELECT IDUkazatel FROM  ...)

編集

デビッドが示唆したように、私は太い矢印の上にカーソルを合わせました:

FatArrow

回答:


11

最初の2つのクエリの場合、クラスタ化インデックスをスキャンして、その値の最初のエントリをスキャンするだけです。インデックスIDUkazatelの順序により、その行は、その値のcasの最小値になりますIDUkazatel

2番目のクエリでは、この最適化は価値がなく、おそらく最初の行を探してIDUkazatel=24、最後の行までインデックスをスキャンして、それらすべての行のIDUkazatel=25最小値を見つけcasます。

その太い矢印の上にカーソルを合わせると、多くの行(確かに24のすべての行、おそらく25の行もすべて)が読み取られていることがわかりますが、他の2つの計画の出力の細い矢印は、topそれが1つの行を検討します。

各クエリを実行してから、見つかった最小値の最小値を取得できます。

SELECT MIN(cas)
FROM   (
        SELECT cas=MIN(cas) FROM PenData p WHERE p.IDUkazatel = 24
        UNION ALL
        SELECT cas=MIN(cas) FROM PenData p WHERE p.IDUkazatel = 25
    ) AS minimums

とはIDUkazatelいえ、明示的なOR句ではなく値を持つテーブルがあるようです。以下のコードはその配置で動作し、テーブル名@TIDUkazatel値を含むテーブルの名前に置き換えるだけです。

SELECT 
    MinCas = MIN(CA.PartialMinimum)
FROM @T AS T
CROSS APPLY 
(
    SELECT 
        PartialMinimum = MIN(PD.Cas)
    FROM dbo.PenData AS PD
    WHERE 
        PD.IDUkazatel = T.IDUkazatel
) AS CA;

理想的な世界では、SQL Serverクエリオプティマイザーがこの書き換えを実行しますが、現在このオプションが常に考慮されているとは限りません。


派生テーブルなしで最後のものを書き換えることができますSELECT TOP (1) min_cas=MIN(CAS) ... ORDER BY min_cas;(ただし、プランはあなたのものと同じになると思います。)
ypercubeᵀᴹJul
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.