web-dev-qa-db-ja.com

主キーの自己結合

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)

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

これが fiddle で、テーブル作成と3つのクエリを使用します。inner join 's、left join' s、および混合です。実行計画もそこで見ることができます。

結果の実行計画ではleft joinsが削除されているようですが、inner joinsは削除されていません。ただし、なぜなのかはわかりません。

9
pkuderov

まず、(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 |高度な使用法については、以下の例を参照してください
 

enter image description here

--------------------------------------------------------------------------------
-- 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 |高度な使用法については、以下の例を参照してください
 

enter image description here

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 |高度な使用法については、以下の例を参照してください
 

enter image description here

18
ypercubeᵀᴹ

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

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

2
nvogel

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

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

1
indiri