これは、テーブル内のデータ、インデックスなどに依存します。実行プランとio +時間の統計を比較できないとは言いがたいです。
私が期待する違いは、2つのテーブル間のJOINの前に行われる追加のフィルタリングです。私の例では、テーブルを再利用するために更新を選択に変更しました。
「最適化」による実行計画
実行計画
フィルター操作が発生していることがはっきりとわかります。私のテストデータでは、フィルターで除外されたレコードがないため、結果として改善が行われていません。
「最適化」なしの実行計画
実行計画
フィルターはなくなりました。つまり、不要なレコードをフィルターで除外するには、結合に依存する必要があります。
その他の理由
クエリを変更したことによる別の理由/結果は、クエリを変更したときに新しい実行プランが作成され、たまたま高速になったことです。この例は、エンジンが別の結合演算子を選択することですが、これは現時点で推測しているだけです。
編集:
2つのクエリプランを取得した後の説明:
クエリは、大きなテーブルから5億5,000万行を読み取り、それらを除外しています。
つまり、述語はシーク述語ではなく、ほとんどのフィルタリングを実行するものです。その結果、データが読み取られますが、返されるデータははるかに少なくなります。
SQLサーバーに別のインデックス(クエリプラン)を使用させる/インデックスを追加すると、これを解決できます。
では、なぜ最適化クエリにこれと同じ問題がないのですか?
別のクエリプランが使用されているため、シークではなくスキャンを使用します。
シークを行わずに、4M行のみを返します。
次の違い
更新の違いを無視して(最適化されたクエリでは何も更新されません)、ハッシュの一致が最適化されたクエリで使用されます。
非最適化でのネストされたループ結合の代わりに:
ネストされたループは、一方のテーブルが小さく、他方のテーブルが大きい場合に最適です。どちらも同じサイズに近いので、この場合はハッシュ一致の方が適していると主張します。
概観
最適化されたクエリ
最適化されたクエリのプランには並列処理があり、ハッシュ一致結合を使用し、残余IOフィルタリングを少なくする必要があります。また、ビットマップを使用して、結合行を生成できないキー値を排除します。(また、何も更新されていません)
非最適化クエリ
非最適化クエリのプランには並列処理がなく、ネストされたループ結合を使用し、550Mレコードで残余IOフィルタリングを実行する必要があります。(また、更新が行われています)
最適化されていないクエリを改善するために何ができますか?
しかし、関数の使用とこのテーブルが大きいため、これは最適なソリューションではない可能性があります。
- 統計を更新し、再コンパイルを使用してより良い計画を試行して取得します。
(HASH JOIN, MERGE JOIN)
クエリにOPTION を追加する
- ...
テストデータ+使用したクエリ
CREATE TABLE #smallTableOfPeople(importantValue int, birthDate datetime2, first_name varchar(50),last_name varchar(50));
CREATE TABLE #largeTableOfPeople(importantValue int, birth_date datetime2, first_name varchar(50),last_name varchar(50));
set nocount on;
DECLARE @i int = 1
WHILE @i <= 1000
BEGIN
insert into #smallTableOfPeople (importantValue,birthDate,first_name,last_name)
VALUES(NULL, dateadd(mi,@i,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @i += 1;
END
set nocount on;
DECLARE @j int = 1
WHILE @j <= 20000
BEGIN
insert into #largeTableOfPeople (importantValue,birth_Date,first_name,last_name)
VALUES(@j, dateadd(mi,@j,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @j += 1;
END
SET STATISTICS IO, TIME ON;
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å');
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
--AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å')
drop table #largeTableOfPeople;
drop table #smallTableOfPeople;
AND LEFT(TRIM(largeTbl.last_name), 1) BETWEEN 'a' AND 'z' COLLATE LATIN1_GENERAL_CI_AI
すべての文字をリストする必要がなく、コードを読みにくくすることなく、必要なことを実行できます