次の2つの機能を検討してください。
ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C)
ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C)
私の知る限り、まったく同じ結果が得られます。つまり、PARTITION BY
句内の列をリストする順序は重要ではありません。
インデックスがある場合(A,B,C)
、オプティマイザーが両方のバリアントでこのインデックスを使用することを期待しました。
しかし、驚くべきことに、オプティマイザーは2番目のバリアントで追加の明示的な並べ替えを行うことにしました。
SQL Server 2008 StandardおよびSQL Server 2014 Expressで見ました。
以下は、私がそれを再現するために使用した完全なスクリプトです。
Microsoft SQL Server 2014で試しました-12.0.2000.8(X64)2014年2月20日20:04:26 Copyright(c)Microsoft Corporation Express Edition(64-bit)on Windows NT 6.1(Build 7601:Service Pack 1)
およびMicrosoft SQL Server 2014(SP1-CU7)(KB3162659)-12.0.4459.0(X64)2016年5月27日15:33:17著作権(c)Windows NT 6.1(ビルド7601:サービス上のMicrosoft Corporation Express Edition(64ビット)パック1)
およびを使用して、新旧両方のカーディナリティ推定器を使用OPTION (QUERYTRACEON 9481)
しOPTION (QUERYTRACEON 2312)
ます。
テーブル、インデックス、サンプルデータを設定する
CREATE TABLE [dbo].[T](
[ID] [int] IDENTITY(1,1) NOT NULL,
[A] [int] NOT NULL,
[B] [int] NOT NULL,
[C] [int] NOT NULL,
CONSTRAINT [PK_T] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_ABC] ON [dbo].[T]
(
[A] ASC,
[B] ASC,
[C] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF,
DROP_EXISTING = OFF,
ONLINE = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON)
GO
INSERT INTO [dbo].[T] ([A],[B],[C]) VALUES
(10, 20, 30),
(10, 21, 31),
(10, 21, 32),
(10, 21, 33),
(11, 20, 34),
(11, 21, 35),
(11, 21, 36),
(12, 20, 37),
(12, 21, 38),
(13, 21, 39);
問い合わせ
SELECT -- AB
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
FROM T
ORDER BY C
OPTION(RECOMPILE);
SELECT -- BA
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
FROM T
ORDER BY C
OPTION(RECOMPILE);
SELECT -- both
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
FROM T
ORDER BY C
OPTION(RECOMPILE);
実行計画
A、Bによるパーティション
B、Aによる区分
どちらも
ご覧のとおり、2番目のプランには追加のソートがあります。B、A、Cで注文します。オプティマイザーは、明らかに、それPARTITION BY B,A
がPARTITION BY A,B
データと同じであり、データを再ソートすることに気付くほどスマートではありません。
興味深いことに、3番目のクエリには両方のバリエーションROW_NUMBER
があり、追加の並べ替えはありません!計画は最初のクエリと同じです。(シーケンスプロジェクトでは、出力リストに追加列の追加式がありますが、追加ソートはありません)。したがって、このより複雑なケースでは、オプティマイザーPARTITION BY B,A
は、それがと同じであることに気付くのに十分賢いように見えましたPARTITION BY A,B
。
最初のクエリと3番目のクエリでは、インデックススキャン演算子のプロパティはOrdered:Trueであり、2番目のクエリではFalseです。
さらに興味深いのは、次のように3番目のクエリを書き直した場合です(2列を入れ替えます)。
SELECT -- both
ID,A,B,C
,ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C) AS rnBA
,ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C) AS rnAB
FROM T
ORDER BY C
OPTION(RECOMPILE);
余分な並べ替えが再び表示されます!
誰かが光を当てることができますか?ここでオプティマイザーで何が起こっていますか?