Porównuję dwa zapytania w SQL Server 2012. Celem wyboru najlepszego zapytania jest wykorzystanie wszystkich istotnych informacji dostępnych w optymalizatorze zapytań. Oba zapytania dają takie same wyniki; maksymalne zamówienie dla wszystkich klientów.
Czyszczenie puli buforów zostało wykonane przed wykonaniem każdego zapytania za pomocą FREEPROCCACHE i DROPCLEANBUFFERS
Które zapytanie jest najlepszym wyborem na podstawie poniższych informacji?
-- Query 1 - return the maximum order id for a customer
SELECT orderid, custid
FROM Sales.Orders AS O1
WHERE orderid = (SELECT MAX(O2.orderid)
FROM Sales.Orders AS O2
WHERE O2.custid = O1.custid);
-- Query 2 - return the maximum order id for a customer
SELECT MAX(orderid), custid
FROM Sales.Orders AS O1
group by custid
order by custid
CZAS STATYSTYCZNY
Zapytanie 1 CZAS STATYSTYCZNY: Czas procesora = 0ms, czas, który upłynął = 24 ms
Zapytanie 2 CZAS STATYSTYCZNY: Czas procesora = 0 ms, czas, który upłynął = 23 ms
STATYSTYKA IO
Zapytanie 1 STATYSTYKI IO: Tabela „Zamówienia”. Liczba skanów 1, logiczne odczyty 5, fizyczne odczyty 2, wyprzedzające odczyty 0, logiczne odczyty 0, lob fizyczne odczyty 0, lob odczyty 0.
Zapytanie 2 STATYSTYKI IO: Tabela „Zamówienia”. Liczba skanów 1, logiczne odczyty 4, fizyczne odczyty 1, odczyt z wyprzedzeniem 8, logiczne odczyty 0, lob fizyczne odczyty 0, odczyt z wyprzedzeniem 0.
Plany wykonania
WYBIERZ właściwości Zapytanie 1
WYBIERZ właściwości Zapytanie 2
Wnioski:
Zapytanie 1
- Koszt partii 48%
- Odczyty logiczne 5
- Odczyty fizyczne 2
- Czytaj dalej Czyta: 0
- Czas procesora: 0ms
- Upływający czas 24ms
- Szacowany koszt poddrzewa: 0,0050276
- CompileCPU: 2
- CompileMemory: 384
- CompileTime: 2
Zapytanie 2
- Koszt partii 52%
- Odczyty logiczne 4
- Odczyty fizyczne 1
- Odczyt z wyprzedzeniem Odczyty: 8
- Czas procesora 0
- Upłynęło 23 ms
- Szacowany koszt poddrzewa: 0,0054782
- CompileCPU: 0
- CompileMemory: 192
- CompileTime: 0
Osobiście, mimo że Kwerenda 2 ma wyższy koszt partii zgodnie z planem graficznym, myślę, że jest bardziej skuteczna niż Kwerenda 1. To dlatego, że kwerenda 2 wymaga mniej logicznych odczytów, ma nieco krótszy czas, który upłynął, wartości compilecpu, kompilememory i kompilacji są niższy. odczyt z wyprzedzeniem wynosi 8 dla zapytania 2 i 0 dla zapytania 1.
Zaktualizuj 12:03
Definicja indeksu klastrowego
ALTER TABLE [Sales].[Orders] ADD CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED
(
[orderid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Indeks nieklastrowany idx_nc_custid
CREATE NONCLUSTERED INDEX [idx_nc_custid] ON [Sales].[Orders]
(
[custid] 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) ON [PRIMARY]
GO
źródło
Odpowiedzi:
Uwielbiam twoje podejście do starannego rozważania strojenia zapytań oraz przeglądu opcji i planów. Chciałbym, żeby więcej programistów to zrobiło. Jedna uwaga byłaby - zawsze testuj z dużą ilością wierszy, patrząc na logiczne odczyty, jest to niewielki stół. Spróbuj wygenerować przykładowe obciążenie i ponownie uruchom zapytanie. Jeden mały problem - w zapytaniu głównym nie pytasz o zamówienie, w zapytaniu dolnym jesteś. Powinieneś porównać je i porównać z kolejnością.
Właśnie szybko utworzyłem tabelę SalesOrders z 200 000 zamówień sprzedaży - wciąż nie jest to duże wyzwanie. I uruchomiłem zapytania z ORDER BY w każdym. Trochę też grałem z indeksami.
Bez indeksu klastrowego na OrderID, tylko nieklastrowy indeks na CustID Drugie zapytanie wypadło lepiej. Zwłaszcza z zamówieniem zawartym w każdym. W pierwszym zapytaniu było dwa razy więcej odczytów niż w drugim, a procent kosztów wynosił 67% / 33% między zapytaniami.
Z indeksem klastrowanym na OrderID i indeksem nieklastrowanym tylko na CustID Działały one z podobną prędkością i dokładnie taką samą liczbą odczytów.
Sugeruję więc zwiększenie liczby wierszy i przeprowadzenie dalszych testów. Ale moja ostateczna analiza twoich zapytań -
Możesz zauważyć, że zachowują się bardziej podobnie, niż zdajesz sobie sprawę, gdy zwiększasz rzędy, więc miej to na uwadze i przetestuj w ten sposób.
Jeśli wszystko, co kiedykolwiek chcesz zwrócić, to maksymalny identyfikator zamówienia dla każdego klienta, a chcesz ustalić, że dzięki identyfikatorowi zamówienia, który jest największym identyfikatorem zamówienia, drugie zapytanie z tych dwóch jest najlepszym sposobem na przejście z mojego sposobu myślenia - to trochę prostsze i choć nieco droższe w oparciu o koszty poddrzewa, jest szybsze i łatwiejsze do rozszyfrowania. Jeśli pewnego dnia zamierzasz dodać inne kolumny do zestawu wyników? Następnie pierwsze zapytanie pozwala to zrobić.
Zaktualizowano: Jednym z twoich komentarzy pod twoim pytaniem było:
Ale najlepsze na wynos do zrobienia tego - test z większą ilością danych - zawsze upewnia się, że masz dane spójne z produkcją i oczekiwaną przyszłą produkcją. Plany zapytań zaczynają szukać danych, gdy dodajesz więcej wierszy do tabel, i starasz się utrzymać dystrybucję, jakiej oczekujesz w produkcji. I zwracaj uwagę na takie rzeczy, jak włączanie lub wyłączanie Order By, tutaj nie sądzę, że robi to w końcu straszną różnicę, ale nadal warto się w to zagłębić.
Twoje podejście do porównywania tego poziomu szczegółowości i danych jest dobre. Koszty poddrzewa są w większości arbitralne i bez znaczenia, ale mimo to warto przynajmniej przyjrzeć się porównaniu między edycjami / zmianami, a nawet zapytaniami. Spojrzenie na statystyki czasu i IO są dość ważne, podobnie jak na planowanie wszystkiego, co wydaje się nie na miejscu ze względu na rozmiar danych, z którymi pracujesz i co próbujesz zrobić.
źródło