JOIN句でORを使用するよりも2つの結果セットのUNIONが優れている理由を示すために、サンプルクエリプランを作成しようとしています。私が書いたクエリプランには困惑しています。Users.Reputationの非クラスター化インデックスでStackOverflowデータベースを使用しています。
CREATE NONCLUSTERED INDEX IX_NC_REPUTATION ON dbo.USERS(Reputation)
SELECT DISTINCT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
OR Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5
クエリプランはhttps://www.brentozar.com/pastetheplan/?id=BkpZU1MZEにあります。クエリの所要時間は4:37分で、26612行が返されました。
このスタイルの定数スキャンが既存のテーブルから作成されるのを見たことがありません-常に一定のスキャンがユーザーによって入力された単一の行に対して使用される場合、すべての単一の行に対して一定のスキャンが実行される理由がわかりませんたとえば、SELECT GETDATE()。なぜここで使用されるのですか?このクエリプランを読む際に、いくつかのガイダンスをいただければ幸いです。
そのORをUNIONに分割すると、同じ26612行が返される12秒で実行される標準プランが作成されます。
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.OwnerUserId
WHERE Users.Reputation = 5
UNION
SELECT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts
ON Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5
この計画を次のように解釈します。
- 投稿からすべての41782500行を取得します(実際の行数は投稿のCIスキャンと一致します)
- 投稿の各41782500行について:
- スカラーを生成します。
- Expr1005:OwnerUserId
- Expr1006:OwnerUserId
- Expr1004:静的な値62
- Expr1008:LastEditorUserId
- Expr1009:LastEditorUserId
- Expr1007:静的な値62
- 連結では:
- Exp1010:Expr1005(OwnerUserId)がnullでない場合は、Expr1008(LastEditorUserID)を使用します。
- Expr1011:Expr1006(OwnerUserId)がnullでない場合はそれを使用し、そうでない場合はExpr1009(LastEditorUserId)を使用します
- Expr1012:Expr1004(62)がnullの場合はそれを使用し、そうでない場合はExpr1007(62)を使用します
- Computeスカラー:アンパサンドが何をするのかわかりません。
- Expr1013:4 [および?] 62(Expr1012)= 4およびOwnerUserId IS NULL(NULL = Expr1010)
- Expr1014:4 [および?] 62(Expr1012)
- Expr1015:16および62(Expr1012)
- 並べ替え順:
- Expr1013 Desc
- Expr1014 Asc
- Expr1010 Asc
- Expr1015 Desc
- マージ間隔で、Expr1013とExpr1015を削除しました(これらは入力ですが、出力ではありません)
- ネストされたループ結合の下のインデックスシークでは、Expr1010およびExpr1011をシーク述語として使用していますが、IX_NC_REPUTATIONからExpr1010およびExpr1011を含むサブツリーへのネストされたループ結合を実行していない場合、これらにアクセスする方法がわかりません。
- ネストされたループの結合は、以前のサブツリーで一致するUsers.IDのみを返します。述部プッシュダウンのため、IX_NC_REPUTATIONのインデックスシークから返されたすべての行が返されます。
- 最後のネストループ結合:各投稿レコードについて、以下のデータセットで一致が見つかったUsers.Idを出力します。
SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id IN (Posts.OwnerUserId, Posts.LastEditorUserId) ) ;
SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND ( EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.OwnerUserId) OR EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.LastEditorUserId) ) ;