Bu iki işlevi göz önünde bulundurun:
ROW_NUMBER() OVER (PARTITION BY A,B ORDER BY C)
ROW_NUMBER() OVER (PARTITION BY B,A ORDER BY C)
Anladığım kadarıyla, tam olarak aynı sonucu veriyorlar. Başka bir deyişle, PARTITION BY
yan tümcedeki sütunları listelemenizin sırası önemli değildir.
Bir indeks varsa (A,B,C)
, optimizatörün bu indeksi her iki varyasyonda da kullanmasını bekledim.
Ancak, şaşırtıcı bir şekilde, optimize edici ikinci varyantta ekstra açık bir Sıralama yapmaya karar verdi.
SQL Server 2008 Standard ve SQL Server 2014 Express'te gördüm.
İşte onu yeniden oluşturmak için kullandığım tam bir komut dosyası.
Microsoft SQL Server 2014 üzerinde çalıştı - 12.0.2000.8 (X64) 20 Şubat 2014 20:04:26 Telif Hakkı (c) Windows NT 6.1 üzerinde Microsoft Corporation Express Edition (64 bit) (Derleme 7601: Service Pack 1)
ve Microsoft SQL Server 2014 (SP1-CU7) (KB3162659) - 12.0.4459.0 (X64) 27 Mayıs 2016 15:33:17 Telif Hakkı (c) Windows NT 6.1'de Microsoft Corporation Express Edition (64 bit) (Derleme 7601: Hizmet Paket 1)
ve kullanarak hem eski hem de yeni Kardinalite Tahmincisi OPTION (QUERYTRACEON 9481)
ile OPTION (QUERYTRACEON 2312)
.
Tablo, dizin, örnek veri ayarlama
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);
Sorguları
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);
Uygulama planları
A, B İLE BÖLME
B, A İLE BÖLME
Her ikisi de
Gördüğünüz gibi, ikinci planın fazladan bir Sıralaması var. B, A, C tarafından sipariş edilir. Optimize edici, görünüşe göre, bununla PARTITION BY B,A
aynı olduğunu PARTITION BY A,B
ve verileri yeniden sıraladığını fark edecek kadar akıllı değildir .
İlginçtir ki, üçüncü sorguda her iki varyantı da ROW_NUMBER
vardır ve fazladan Sıralama yoktur! Plan, ilk sorgudakiyle aynıdır. (Sekans Projesi, ekstra sütun için Çıktı Listesi'nde fazladan ifadeye sahiptir, ancak fazladan Sıralama içermez). Yani, bu daha karmaşık durumda, iyileştirici PARTITION BY B,A
, bunun aynı olduğunu anlayacak kadar akıllı görünüyordu PARTITION BY A,B
.
Birinci ve üçüncü sorgularda Dizin Tarama işleci, Sıralı: Doğru özelliğine sahiptir, ikinci sorguda Yanlış'tır.
Daha da ilginç, bu şekilde üçüncü sorguyu yeniden yazarsam (iki sütun takas):
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);
ekstra sıralama tekrar görünür!
Birisi biraz ışık tutabilir mi? Buradaki optimize edicide neler oluyor?