Mam utrwaloną kolumnę obliczeniową na stole, która jest po prostu złożona z połączonych kolumn, np
CREATE TABLE dbo.T
(
ID INT IDENTITY(1, 1) NOT NULL CONSTRAINT PK_T_ID PRIMARY KEY,
A VARCHAR(20) NOT NULL,
B VARCHAR(20) NOT NULL,
C VARCHAR(20) NOT NULL,
D DATE NULL,
E VARCHAR(20) NULL,
Comp AS A + '-' + B + '-' + C PERSISTED NOT NULL
);
W tym Comp
nie jest unikalny, a D jest prawidłowy od daty każdej kombinacji A, B, C
, dlatego używam następującego zapytania, aby uzyskać datę końcową dla każdej A, B, C
(w zasadzie następnej daty początkowej dla tej samej wartości Comp):
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1
WHERE t1.D IS NOT NULL -- DON'T CARE ABOUT INACTIVE RECORDS
ORDER BY t1.Comp;
Następnie dodałem indeks do kolumny obliczanej, aby pomóc w tym zapytaniu (i innych):
CREATE NONCLUSTERED INDEX IX_T_Comp_D ON dbo.T (Comp, D) WHERE D IS NOT NULL;
Plan zapytania mnie jednak zaskoczył. Pomyślałbym, że skoro mam klauzulę where stwierdzającą to D IS NOT NULL
i sortuję według Comp
, i nie odnoszę się do żadnej kolumny poza indeksem, to że indeks w kolumnie obliczeniowej może być użyty do skanowania t1 i t2, ale widziałem indeks klastrowany skandować.
Zmusiłem więc użycie tego indeksu, aby sprawdzić, czy przyniósł lepszy plan:
SELECT t1.ID,
t1.Comp,
t1.D,
D2 = ( SELECT TOP 1 t2.D
FROM dbo.T t2
WHERE t2.Comp = t1.Comp
AND t2.D > t1.D
ORDER BY t2.D
)
FROM dbo.T t1 WITH (INDEX (IX_T_Comp_D))
WHERE t1.D IS NOT NULL
ORDER BY t1.Comp;
Co dało ten plan
To pokazuje, że używane jest wyszukiwanie klucza, którego szczegółami są:
Teraz, zgodnie z dokumentacją SQL-Server:
Można utworzyć indeks w kolumnie obliczanej, która jest zdefiniowana za pomocą deterministycznego, ale nieprecyzyjnego wyrażenia, jeśli kolumna jest oznaczona jako PERSISTED w instrukcji CREATE TABLE lub ALTER TABLE. Oznacza to, że Aparat baz danych przechowuje obliczone wartości w tabeli i aktualizuje je, gdy są aktualizowane inne kolumny, od których zależy obliczona kolumna. Aparat baz danych korzysta z tych utrwalonych wartości podczas tworzenia indeksu w kolumnie i do odwołania do indeksu w zapytaniu. Ta opcja umożliwia utworzenie indeksu w kolumnie obliczeniowej, gdy aparat bazy danych nie może dokładnie udowodnić, czy funkcja zwracająca wyrażenia w kolumnie obliczeniowej, w szczególności funkcja CLR utworzona w .NET Framework, jest zarówno deterministyczna, jak i precyzyjna.
Jeśli więc, jak mówią doktorzy: „ Aparat baz danych przechowuje obliczone wartości w tabeli” , a wartość jest również przechowywana w moim indeksie, dlaczego wymagane jest wyszukiwanie klucza, aby uzyskać A, B i C, gdy nie ma w nich odwołania zapytanie w ogóle? Zakładam, że są one używane do obliczania Comp, ale dlaczego? Ponadto, dlaczego zapytanie może korzystać z indeksu na t2
, ale nie na t1
?
Uwaga: otagowałem SQL Server 2008, ponieważ jest to wersja, na której jest mój główny problem, ale otrzymuję to samo zachowanie w 2012 roku.
FOJNtoLSJNandLASJN
), która powoduje, że rzeczy nie działają tak, jak byśmy tego spodziewali, i pozostawia śmieci (BaseRow / Checksums), które są przydatne w niektórych rodzajach planów (np. Kursory), ale nie są tutaj potrzebne.Chk
to suma kontrolna! Dzięki, nie byłem tego pewien. Początkowo myślałem, że może to mieć związek z ograniczeniami sprawdzania.Chociaż może to być trochę współwystępowanie ze względu na sztuczny charakter danych testowych, ponieważ, jak wspomniałeś SQL 2012, próbowałem przepisać:
Zaowocowało to dobrym, niedrogim planem z wykorzystaniem twojego indeksu i znacznie niższymi odczytami niż inne opcje (i takie same wyniki dla danych testowych).
Podejrzewam, że twoje prawdziwe dane są bardziej skomplikowane, więc mogą istnieć pewne scenariusze, w których to zapytanie zachowuje się semantycznie odmiennie od twojego, ale czasem pokazuje, że nowe funkcje mogą naprawdę zmienić.
Eksperymentowałem z kilkoma bardziej zróżnicowanymi danymi i znalazłem kilka scenariuszy do dopasowania, a niektóre nie:
źródło
comp
nie jest kolumną obliczeniową, nie widzisz sortowania.LEAD
funkcja działała dokładnie tak, jak chciałbym na mojej lokalnej instancji ekspresowej z 2012 roku. Niestety ta drobna niedogodność nie była dla mnie wystarczającym powodem do aktualizacji serwerów produkcyjnych ...Kiedy próbowałem wykonać te same czynności, otrzymałem inne wyniki. Po pierwsze, mój plan wykonania dla tabeli bez indeksów wygląda następująco:
Jak możemy zobaczyć z Clustered Index Scan (t2), predykat służy do określenia potrzebnych wierszy do zwrócenia (z powodu warunku):
Po dodaniu indeksu, bez względu na to, czy został zdefiniowany przez operatora WITH, czy nie, plan wykonania wyglądał następująco:
Jak widzimy, skanowanie indeksów klastrowych jest zastępowane przez skanowanie indeksów. Jak widzieliśmy powyżej, SQL Server używa kolumn źródłowych kolumny obliczanej, aby wykonać dopasowanie zagnieżdżonego zapytania. Podczas skanowania indeksu klastrowego wszystkie te wartości można uzyskać w tym samym czasie (nie są wymagane żadne dodatkowe operacje). Po dodaniu indeksu filtrowanie niezbędnych wierszy z tabeli (w głównym wyborze) przebiega zgodnie z indeksem, ale wartości kolumn źródłowych dla kolumny obliczanej
comp
nadal wymagają pobrania (ostatnia operacja Zagnieżdżona pętla) .Z tego powodu używana jest operacja Wyszukiwanie klucza - w celu uzyskania danych z kolumn źródłowych obliczonej.
PS Wygląda jak błąd w SQL Server.
źródło