Brzydki. Tak wyglądają nieposortowane dane. Ułatwiamy oczom dane, sortując je. I do tego służy SQL ORDER BY. Użyj co najmniej jednej kolumny lub wyrażenia jako podstawy do sortowania danych. Następnie dodaj ASC lub DESC, aby posortować rosnąco lub malejąco.
Składnia SQL ORDER BY:
ORDER BY <order_by_expression> [ASC | DESC]
Wyrażenie ORDER BY może być tak proste, jak lista kolumn lub wyrażeń. Może być również warunkowy przy użyciu bloku CASE WHEN.
Jest bardzo elastyczny.
Możesz także użyć stronicowania poprzez OFFSET i FETCH. Określ liczbę wierszy do pominięcia i wiersze do wyświetlenia.
Ale oto zła wiadomość.
Dodanie ORDER BY do zapytań może je spowolnić. A niektóre inne zastrzeżenia mogą sprawić, że ORDER BY „nie działa”. Nie możesz ich używać w dowolnym momencie, ponieważ mogą być kary. Więc co robimy?
W tym artykule przyjrzymy się nakazom i zakazom korzystania z ORDER BY. Każdy element rozwiąże problem, a rozwiązanie nastąpi.
Gotowy?
Co robić w SQL ORDER BY
1. Indeksuj kolumny SQL ORDER BY
Indeksy dotyczą szybkiego wyszukiwania. A umieszczenie go w kolumnach, których używasz w klauzuli ORDER BY, może przyspieszyć Twoje zapytanie.
Zacznijmy używać ORDER BY w kolumnie bez indeksu. Będziemy korzystać z AdventureWorks przykładowa baza danych. Przed wykonaniem poniższego zapytania wyłącz IX_SalesOrderDetail_ProductID indeks w Szczegółach Zamówień Sprzedaży stół. Następnie naciśnij Ctrl-M i uruchom go.
-- Get order details by product and sort them by ProductID
USE AdventureWorks
GO
SET STATISTICS IO ON
GO
SELECT
ProductID
,OrderQty
,UnitPrice
,LineTotal
FROM Sales.SalesOrderDetail
ORDER BY ProductID
SET STATISTICS IO OFF
GO
ANALIZA
Powyższy kod wyświetli statystyki we/wy na karcie Komunikaty programu SQL Server Management Studio. Zobaczysz plan wykonania w innej zakładce.
BEZ INDEKSU
Najpierw zdobądźmy logiczne odczyty z STATISTICS IO. Sprawdź rysunek 1.
Rysunek 1 . Odczyty logiczne przy użyciu ORDER BY nieindeksowanej kolumny. (Sformatowany przy użyciu statisticsparser.com )
Bez indeksu zapytanie użyło 1313 odczytów logicznych. I ten WorkTable ? Oznacza to, że SQL Server używał TempDB do przetworzenia sortowania.
Ale co wydarzyło się za kulisami? Przyjrzyjmy się planowi wykonania na rysunku 2.
Rysunek 2 . Plan wykonania zapytania przy użyciu ORDER BY niezindeksowanej kolumny.
Czy widziałeś ten operator równoległości (zbieranie strumieni)? Oznacza to, że SQL Server używał więcej niż 1 procesora do przetwarzania tego zapytania. Zapytanie było na tyle ciężkie, że wymagało większej liczby procesorów.
A co, jeśli SQL Server używał TempDB ? i więcej procesorów? To źle dla prostego zapytania.
Z INDEKSEM
Jak będzie wyglądać, jeśli indeks zostanie ponownie włączony? Dowiedzmy Się. Odbuduj indeks IX_SalesOrderDetail_ProductID . Następnie ponownie uruchom powyższe zapytanie.
Sprawdź nowe odczyty logiczne na rysunku 3.
Rysunek 3 . Nowe odczyty logiczne po przebudowie indeksu.
To jest dużo lepsze. Zmniejszyliśmy liczbę logicznych odczytów o prawie połowę. Oznacza to, że indeks sprawił, że zużywa mniej zasobów. Oraz stół roboczy ? Odeszło! Nie ma potrzeby używania TempDB .
A plan wykonania? Zobacz rysunek 4.
Rysunek 4 . Nowy plan wykonania jest prostszy, gdy indeks został odbudowany.
Widzieć? Plan jest prostszy. Nie ma potrzeby stosowania dodatkowych procesorów, aby posortować te same 121 317 rzędów.
Najważniejsze jest więc:Upewnij się, że kolumny, których używasz dla ORDER BY, są zindeksowane .
ALE CO, JEŚLI DODAWANIE INDEKSU WPŁYWA NA WYDAJNOŚĆ ZAPISU?
Dobre pytanie.
Jeśli to jest problem, możesz zrzucić część tabeli źródłowej do tabeli tymczasowej lub tabeli zoptymalizowanej pod kątem pamięci . Następnie zindeksuj tę tabelę. Użyj tego samego, jeśli zaangażowanych jest więcej tabel. Następnie oceń wydajność zapytania wybranej opcji. Szybsza opcja będzie zwycięzcą.
2. Ogranicz wyniki za pomocą opcji WHERE i OFFSET/FETCH
Użyjmy innego zapytania. Załóżmy, że musisz wyświetlić informacje o produkcie ze zdjęciami w aplikacji. Obrazy mogą sprawić, że zapytania będą jeszcze cięższe. Dlatego nie tylko sprawdzamy odczyty logiczne, ale także odczyty logiczne lobbowania.
Oto kod.
SET STATISTICS IO ON
GO
SELECT
a.ProductID
,a.Name AS ProductName
,a.ListPrice
,a.Color
,b.Name AS ProductSubcategory
,d.ThumbNailPhoto
,d.LargePhoto
FROM Production.Product a
INNER JOIN Production.ProductSubcategory b ON a.ProductSubcategoryID = b.ProductSubcategoryID
INNER JOIN Production.ProductProductPhoto c ON a.ProductID = c.ProductID
INNER JOIN Production.ProductPhoto d ON c.ProductPhotoID = d.ProductPhotoID
WHERE b.ProductCategoryID = 1 -- Bikes
ORDER BY ProductSubcategory, ProductName, a.Color
SET STATISTICS IO OFF
GO
Spowoduje to wyświetlenie 97 rowerów ze zdjęciami. Bardzo trudno je przeglądać na urządzeniu mobilnym.
ANALIZA
UŻYWANIE MINIMALNYCH WARUNKÓW GDZIE BEZ PRZESUNIĘCIA/POBIERANIA
Oto, ile logicznych odczytów potrzeba, aby pobrać 97 produktów ze zdjęciami. Zobacz rysunek 5.
Rysunek 5 . Odczyty logiczne i odczyty logiczne lob przy użyciu ORDER BY bez OFFSET/FETCH i przy minimalnym warunku WHERE . (Uwaga:Statisticsparser.com nie wyświetlało odczytów logicznych lob. Zrzut ekranu jest edytowany na podstawie wyników w SSMS)
Pojawiło się 667 logicznych odczytów lobu z powodu pobrania obrazów w 2 kolumnach. Tymczasem do reszty wykorzystano 590 logicznych odczytów.
Oto plan wykonania na rysunku 6, abyśmy mogli później porównać go z lepszym planem.
Rysunek 6 . Plan wykonania przy użyciu ORDER BY bez OFFSET/FETCH i z minimalnym warunkiem WHERE.
Nie ma wiele więcej do powiedzenia, dopóki nie zobaczymy drugiego planu wykonania.
UŻYWANIE DODATKOWYCH WARUNKÓW GDZIE I PRZESUNIĘCIE/POBIERANIE W ZAMÓWIENIU
Teraz dostosujmy zapytanie, aby zwracać minimalną ilość danych. Oto, co zamierzamy zrobić:
- Dodaj warunek do podkategorii produktu. W aplikacji wywołującej możemy sobie wyobrazić, że użytkownik może również wybrać podkategorię.
- Następnie usuń podkategorię produktu z listy SELECT kolumn i listy kolumn ORDER BY.
- Na koniec dodaj OFFSET/FETCH w kolejności ORDER BY. Tylko 10 produktów zostanie zwróconych i wyświetlonych w aplikacji do połączeń.
Oto edytowany kod.
DECLARE @pageNumber TINYINT = 1
DECLARE @noOfRows TINYINT = 10 -- each page will display 10 products at a time
SELECT
a.ProductID
,a.Name AS ProductName
,a.ListPrice
,a.Color
,d.ThumbNailPhoto
FROM Production.Product a
INNER JOIN Production.ProductSubcategory b ON a.ProductSubcategoryID = b.ProductSubcategoryID
INNER JOIN Production.ProductProductPhoto c ON a.ProductID = c.ProductID
INNER JOIN Production.ProductPhoto d ON c.ProductPhotoID = d.ProductPhotoID
WHERE b.ProductCategoryID = 1 -- Bikes
AND a.ProductSubcategoryID = 2 -- Road Bikes
ORDER BY ProductName, a.Color
OFFSET (@pageNumber-1)*@noOfRows ROWS FETCH NEXT @noOfRows ROWS ONLY
Ten kod ulegnie dalszemu ulepszeniu, jeśli przejdziesz do procedury składowanej. Będzie miał również parametry, takie jak numer strony i liczba wierszy. Numer strony wskazuje, którą stronę aktualnie przegląda użytkownik. Popraw to jeszcze bardziej, dostosowując liczbę wierszy w zależności od rozdzielczości ekranu. Ale to już inna historia.
Przyjrzyjmy się teraz logicznym odczytom na rysunku 7.
Rysunek 7 . Mniej odczytów logicznych po uproszczeniu zapytania. OFFSET/FETCH jest również używany w ORDER BY.
Następnie porównaj rysunek 7 z rysunkiem 5. Logiczne odczyty lobu zniknęły. Ponadto odczyty logiczne mają zauważalny spadek, ponieważ zestaw wyników również został zmniejszony z 97 do 10.
Ale co SQL Server zrobił za kulisami? Sprawdź plan wykonania na rysunku 8.
Rysunek 8 . Prostszy plan wykonania po uproszczeniu zapytania i dodaniu OFFSET/FETCH w ORDER BY.
Następnie porównaj rysunek 8 z rysunkiem 6. Bez badania każdego operatora widzimy, że ten nowy plan jest prostszy niż poprzedni.
Lekcja? Uprość zapytanie. Używaj OFFSET/FETCH, gdy tylko jest to możliwe.
Zakaz w SQL ORDER BY
Skończyliśmy z tym, co musimy zrobić, korzystając z ORDER BY. Tym razem skupmy się na tym, czego powinniśmy unikać.
3. Nie używaj ORDER BY podczas sortowania według klastrowego klucza indeksu
Ponieważ jest bezużyteczny.
Pokażmy to na przykładzie.
SET STATISTICS IO ON
GO
-- Using ORDER BY with BusinessEntityID - the primary key
SELECT TOP 100 * FROM Person.Person
ORDER BY BusinessEntityID;
-- Without using ORDER BY at all
SELECT TOP 100 * FROM Person.Person;
SET STATISTICS IO OFF
GO
Następnie sprawdźmy odczyty logiczne obu instrukcji SELECT na rysunku 9.
Rysunek 9 . 2 zapytania w tabeli Osoba pokazują te same odczyty logiczne. Jeden jest z ORDER BY, drugi bez.
Oba mają 17 logicznych odczytów. Jest to logiczne, ponieważ zwrócono te same 100 wierszy. Ale czy mają ten sam plan? Sprawdź rysunek 10.
Rysunek 10 . Ten sam plan, niezależnie od tego, czy ORDER BY jest używane, czy nie podczas sortowania według klastrowego klucza indeksu.
Obserwuj te same operatory i ten sam koszt zapytania.
Ale dlaczego? Podczas indeksowania jednej lub więcej kolumn do indeksu klastrowego, tabela będzie fizycznie posortowana przez klucz indeksu klastrowego. Tak więc, nawet jeśli nie posortujesz według tego klucza, wynik nadal będzie posortowany.
Konkluzja? Wybacz sobie, nie używając klastrowego klucza indeksu w podobnych przypadkach przy użyciu ORDER BY . Oszczędzaj energię za pomocą mniejszej liczby naciśnięć klawiszy.
4. Nie używaj ORDER BY Gdy kolumna ciągu zawiera liczby
Jeśli sortujesz według kolumny ciągów zawierającej liczby, nie oczekuj kolejności sortowania, jak typy liczb rzeczywistych. W przeciwnym razie czeka Cię wielka niespodzianka.
Oto przykład.
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY NationalIDNumber;
Sprawdź dane wyjściowe na rysunku 11.
Rysunek 11 . Porządek sortowania kolumny ciągów zawierającej liczby. Wartość liczbowa nie jest przestrzegana.
Na rysunku 11 przedstawiono leksykograficzny porządek sortowania. Aby to naprawić, użyj CAST na liczbę całkowitą.
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT)
Spójrz na Rysunek 12 dla stałego wyjścia.
Rysunek 12 . CAST to INT naprawiono sortowanie kolumny ciągów zawierającej liczby.
Tak więc, zamiast ORDER BY
5. Nie używaj SELECT INTO #TempTable z ORDER BY
Żądana kolejność sortowania nie będzie gwarantowana w docelowej tabeli tymczasowej. Zobacz oficjalną dokumentację .
Zmodyfikujmy kod z poprzedniego przykładu.
SELECT
NationalIDNumber
,JobTitle
,HireDate
INTO #temp
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT);
SELECT * FROM #temp;
Jedyną różnicą w stosunku do poprzedniego przykładu jest klauzula INTO. Wynik będzie taki sam, jak na rysunku 11. Wracamy do kwadratu 1, nawet jeśli przerzucimy kolumnę na INT.
Musisz utworzyć tymczasową tabelę za pomocą CREATE TABLE. Ale dołącz dodatkową kolumnę tożsamości i ustaw ją jako klucz podstawowy. Następnie INSERT do tabeli tymczasowej.
Oto naprawiony kod.
CREATE TABLE #temp2
(
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
NationalIDNumber NVARCHAR(15) NOT NULL,
JobTitle NVARCHAR(50) NOT NULL,
HireDate DATE NOT NULL
)
GO
INSERT INTO #temp2
(NationalIDNumber, JobTitle, HireDate)
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT);
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM #Temp2;
Wynik będzie taki sam, jak na rysunku 12. To działa!
Na wynos w użyciu SQL ORDER BY
Omówiliśmy typowe pułapki związane z używaniem SQL ORDER BY. Oto podsumowanie:
Zalecenia :
- Indeksuj kolumny ORDER BY,
- Ogranicz wyniki za pomocą opcji WHERE i OFFSET/FETCH,
Nie wolno :
- Nie używaj ORDER BY podczas sortowania według klastrowanego klucza indeksu,
- Nie używaj ORDER BY, gdy kolumna ciągu zawiera liczby. Zamiast tego prześlij najpierw kolumnę ciągu na INT.
- Nie używaj SELECT INTO #TempTable z ORDER BY. Zamiast tego utwórz najpierw tabelę tymczasową z dodatkową kolumną tożsamości.
Jakie masz wskazówki i triki dotyczące korzystania z ORDER BY? Daj nam znać w sekcji Komentarze poniżej. A jeśli podoba Ci się ten post, udostępnij go na swoich ulubionych platformach społecznościowych.