15

SQL のパフォーマンスに問題があります。突然の理由で、次のクエリは非常に遅くなります。

特定のテーブルの ID を含む 2 つのリストがあります。Id が 2 番目のリストに既に存在する場合は、最初のリストからすべてのレコードを削除する必要があります。

DECLARE @IdList1 TABLE(Id INT)
DECLARE @IdList2 TABLE(Id INT)

-- Approach 1
DELETE list1
FROM @IdList1 list1
INNER JOIN @IdList2 list2 ON list1.Id = list2.Id

-- Approach 2
DELETE FROM @IdList1
WHERE Id IN (SELECT Id FROM @IdList2)

2 つのリストに 10.000 を超えるレコードが含まれている可能性があります。その場合、両方のクエリの実行にそれぞれ 20 秒以上かかります。

実行計画も、私が理解できないことを示していました。たぶん、それがとても遅い理由を説明しています: 両方のクエリのクエリプラン

両方のリストに 10.000 の連続した整数を入力したので、両方のリストに開始点として 1 ~ 10.000 の値が含まれていました。

@IdList2 の両方のクエリが表示されていることがわかるように、実際の行数は 50.005.000 です!!. @IdList1 は正しいです (実際の行数は 10.000 です)

これを解決する方法が他にもあることは知っています。最初のリストから削除するのではなく、3 番目のリストを埋めるようなものです。しかし、私の質問は次のとおりです。

これらの削除クエリが非常に遅いのはなぜですか? また、これらの奇妙なクエリ プランが表示されるのはなぜですか?

4

7 に答える 7

16

テーブル変数に主キーを追加して、それらが叫ぶのを見てください

DECLARE @IdList1 TABLE(Id INT primary Key not null)
DECLARE @IdList2 TABLE(Id INT primary Key not null)

これらのテーブル変数にはインデックスがないため、結合またはサブクエリは、10,000 かける 10,000 = 100,000,000 ペアの値のオーダーで調べる必要があります。

于 2013-05-23T13:02:46.593 に答える
12

SQL Server は、テーブル変数が空の場合にプランをコンパイルし、行が追加されてもプランを再コンパイルしません。試す

DELETE FROM @IdList1
WHERE Id IN (SELECT Id FROM @IdList2)
OPTION (RECOMPILE)

これにより、テーブル変数に含まれる実際の行数が考慮され、ネストされたループ計画が取り除かれます

もちろんId、制約を介してインデックスを作成することは、テーブル変数を使用する他のクエリにも役立つ可能性があります。

于 2013-05-23T13:02:54.963 に答える
2

テーブル変数のテーブルは主キーを持つことができるため、データがこれらIdの s の一意性をサポートしている場合は、

DECLARE @IdList1 TABLE(Id INT PRIMARY KEY)
DECLARE @IdList2 TABLE(Id INT PRIMARY KEY)
于 2013-05-23T13:03:23.607 に答える
2

可能な解決策:

1)このようにインデックスを作成してみてください

1.1) List{1|2}.Id 列に一意の値がある場合、次のような PK 制約を使用して一意のクラスター化インデックスを定義できます。

DECLARE @IdList1 TABLE(Id INT PRIMARY KEY);
DECLARE @IdList2 TABLE(Id INT PRIMARY KEY);

1.2) List{1|2}.Id 列の値が重複している可能性がある場合は、次のIDENTITYようなダミー列を使用して、PK 制約を使用して一意のクラスター化インデックスを定義できます。

DECLARE @IdList1 TABLE(Id INT, DummyID INT IDENTITY, PRIMARY KEY (ID, DummyID) );
DECLARE @IdList2 TABLE(Id INT, DummyID INT IDENTITY, PRIMARY KEY (ID, DummyID) );

HASH JOIN2)次のようなクエリ ヒントを追加してみてください。

DELETE list1
FROM @IdList1 list1
INNER JOIN @IdList2 list2 ON list1.Id = list2.Id
OPTION (HASH JOIN);
于 2013-05-23T13:10:11.380 に答える
1

を使用しているTable Variables場合は、主キーをテーブルに追加するか、それらを変更しTemporary TablesINDEX. これにより、パフォーマンスが大幅に向上します。経験則として、テーブルが小さい場合は を使用TABLE Variablesしますが、テーブルが拡大していて大量のデータが含まれている場合は、一時テーブルを使用します。

于 2013-05-23T13:03:57.723 に答える
0

私は試してみたくなります

DECLARE @IdList3 TABLE(Id INT);

INSERT @IdList3
SELECT Id FROM @IDList1 ORDER BY Id
EXCEPT
SELECT Id FROM @IDList2 ORDER BY Id

削除は必要ありません。

于 2013-05-23T13:11:53.797 に答える