主キーの自己結合


9

N自己結合で構成されるこのクエリについて考えてみます。

select
    t1.*
from [Table] as t1
join [Table] as t2 on
    t1.Id = t2.Id
-- ...
join [Table] as tN on
    t1.Id = tN.Id

N個のクラスター化インデックススキャンとN-1個のマージ結合を含む実行プランを作成します。

正直なところ、すべての結合を最適化せずに1つのクラスター化インデックススキャンだけを行う理由はありません。つまり、元のクエリを次のように最適化します。

select
    t1.*
from [Table] as t1

ご質問

  • 結合が最適化されないのはなぜですか?
  • すべての結合が結果セットを変更しないと言うのは数学的に正しくありませんか?

テスト済み:

  • ソースサーバーのバージョン:SQL Server 2014(12.0.4213)
  • ソースデータベースエンジンエディション:Microsoft SQL Server Standard Edition
  • ソースデータベースエンジンの種類:スタンドアロンSQLサーバー
  • 互換性レベル:SQL Server 2008(100)

クエリは意味がありません。それはちょうど私の頭に浮かんできました、そして私はそれについて今興味があります。

ここだフィドルテーブルの作成と3つのクエリとは:とinner joinの、とleft joinの混合しました。実行計画もそこで見ることができます。

left join結果実行プランでは、sは削除されてinner joinいないのに、sは削除されているようです。それでも理由はわかりません。

回答:


18

まず、それ(id)がテーブルの主キーであると仮定しましょう。この場合、はい、結合は(証明できる)冗長であり、削除することができます。

さて、それは単なる理論-または数学です。オプティマイザーが実際の削除を行うためには、理論をコードに変換し、オプティマイザーの最適化/書き換え/削除のスイートに追加する必要があります。それを実現するために、(DBMS)開発者は、効率に優れたメリットがあり、十分に一般的なケースであると考える必要があります。

個人的には、そのようには聞こえません(十分に一般的です)。クエリは-あなたが認めるように-かなりばかげて見えます、そしてそれが改善されてそして冗長な結合が削除されない限り、レビュアーはレビューを通過させるべきではありません。

とはいえ、消去が行われる同様のクエリがあります。Rob Farleyによる非常に優れた関連ブログの投稿:SQL ServerでのJOINの簡素化

私たちの場合、結合をLEFT結合に変更するために実行する必要があるすべてのこと。dbfiddle.ukを参照してください。この場合のオプティマイザは、結果を変更することなく、結合を安全に削除できることを認識しています。(単純化ロジックは非常に一般的であり、自己結合では特別なケースではありません。)

もちろん、元のクエリでは、INNER結合を削除しても結果が変わる可能性はありません。ただし、主キーで自己結合することはまったく一般的ではないため、オプティマイザはこのケースを実装していません。ただし、結合(または左結合)が一般的です。結合された列は、いずれかのテーブルの主キーです(多くの場合、外部キー制約があります)。これは、結合を排除する2番目のオプションにつながります:(自己参照!)外部キー制約を追加します。

ALTER TABLE "Table"
    ADD FOREIGN KEY (id) REFERENCES "Table" (id) ;

そして出来上がり、結合が削除されます!(同じフィドルでテスト済み):ここに

create table docs
(id int identity primary key,
 doc varchar(64)
) ;
GO
insert
into docs (doc)
values ('Enter one batch per field, don''t use ''GO''')
     , ('Fields grow as you type')
     , ('Use the [+] buttons to add more')
     , ('See examples below for advanced usage')
  ;
GO
影響を受ける4行
--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    join docs d2 on d2.id=d1.id
    join docs d3 on d3.id=d1.id
    join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | 文書                                      
-:| :----------------------------------------
 1 | フィールドごとに1つのバッチを入力し、「GO」は使用しないでください
 2 | 入力するとフィールドが大きくなります                  
 3 | [+]ボタンを使用してさらに追加します          
 4 | 高度な使用法については、以下の例を参照してください    

ここに画像の説明を入力してください

--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    left join docs d2 on d2.id=d1.id
    left join docs d3 on d3.id=d1.id
    left join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | 文書                                      
-:| :----------------------------------------
 1 | フィールドごとに1つのバッチを入力し、「GO」は使用しないでください
 2 | 入力するとフィールドが大きくなります                  
 3 | [+]ボタンを使用してさらに追加します          
 4 | 高度な使用法については、以下の例を参照してください    

ここに画像の説明を入力してください

alter table docs
  add foreign key (id) references docs (id) ;
GO
--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    join docs d2 on d2.id=d1.id
    join docs d3 on d3.id=d1.id
    join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | 文書                                      
-:| :----------------------------------------
 1 | フィールドごとに1つのバッチを入力し、「GO」は使用しないでください
 2 | 入力するとフィールドが大きくなります                  
 3 | [+]ボタンを使用してさらに追加します          
 4 | 高度な使用法については、以下の例を参照してください    

ここに画像の説明を入力してください


2

リレーショナル用語では、属性の名前を変更しない自己結合は何もしないので、実行計画から安全に削除できます。残念ながら、SQLはリレーショナルではなく、オプティマイザによって自己結合を排除できる状況は、少数のエッジケースに限定されます。

SQLのSELECT構文は、射影よりも結合論理の優先順位を与えます。SQLの列名に関するスコープルール、および列名の重複と名前のない列が許可されるという事実により、SQLクエリの最適化は、リレーショナル代数の最適化よりもはるかに困難になります。SQL DBMSベンダーはリソースが限られているため、サポートする最適化の種類を選択する必要があります。


1

主キーは常に一意であり、null値は許可されないため、(自己参照副キーを使用せず、whereステートメントを使用せずに)主キーでテーブルをそれ自体に結合すると、元のテーブルと同じ数の行が生成されます。

彼らがそれをすぐに最適化しない理由については、私は彼らが計画を立てていなかったか、人々がそうしないだろうと想定していたエッジケースであると言います。保証された一意の主キーでテーブルをそれ自体に結合することは目的を果たしません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.