Ful. Det är så osorterad data ser ut. Vi gör data lätt för ögonen genom att sortera dem. Och det är vad SQL ORDER BY är till för. Använd en eller flera kolumner eller uttryck som grund för att sortera data. Lägg sedan till ASC eller DESC för att sortera stigande eller fallande.
SQL ORDER BY-syntaxen:
ORDER BY <order_by_expression> [ASC | DESC]
ORDER BY-uttrycket kan vara så enkelt som en lista med kolumner eller uttryck. Det kan också vara villkorat med ett CASE WHEN-block.
Det är väldigt flexibelt.
Du kan också använda sökning genom OFFSET och FETCH. Ange antalet rader som ska hoppa över och raderna som ska visas.
Men här är de dåliga nyheterna.
Att lägga till ORDER BY till dina frågor kan sakta ner dem. Och några andra varningar kan göra att BESTÄLLNING GENOM "inte fungerar." Du kan inte bara använda dem när du vill, eftersom det kan bli påföljder. Så, vad gör vi?
I den här artikeln kommer vi att undersöka vad som ska göras och inte göras när du använder ORDER BY. Varje objekt kommer att hantera ett problem, och en lösning kommer att följa.
Klar?
Göra i SQL ORDER BY
1. Indexera SQL ORDER BY kolumn(er)
Index handlar om snabba sökningar. Och att ha en i kolumnerna du använder i ORDER BY-satsen kan påskynda din fråga.
Låt oss börja använda ORDER BY i en kolumn utan index. Vi kommer att använda AdventureWorks exempeldatabas. Innan du utför frågan nedan, inaktivera IX_SalesOrderDetail_ProductID index i SalesOrderDetail tabell. Tryck sedan på Ctrl-M och kör den.
-- 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
ANALYS
Koden ovan kommer att mata ut I/O-statistiken på fliken Meddelanden i SQL Server Management Studio. Du kommer att se genomförandeplanen på en annan flik.
UTAN ETT INDEX
Låt oss först få de logiska läsningarna från STATISTICS IO. Kolla in figur 1.
Figur 1 . Logiska läsningar med ORDER BY av en oindexerad kolumn. (Formaterad med statisticsparser.com )
Utan index använde frågan 1 313 logiska läsningar. Och det där arbetsbordet ? Det betyder att SQL Server använde TempDB för att bearbeta sorteringen.
Men vad hände bakom kulisserna? Låt oss inspektera utförandeplanen i figur 2.
Figur 2 . Exekveringsplan för en fråga med ORDER BY för en oindexerad kolumn.
Såg du den där operatören Parallelism (Gather Streams)? Det betyder att SQL Server använde mer än en processor för att bearbeta denna fråga. Frågan var tillräckligt tung för att kräva fler processorer.
Så, tänk om SQL Server använde TempDB och fler processorer? Det är dåligt för en enkel fråga.
Med ETT INDEX
Hur kommer det att gå om indexet återaktiveras? Låt oss ta reda på. Bygg om indexet IX_SalesOrderDetail_ProductID . Kör sedan frågan ovan igen.
Kontrollera de nya logiska läsningarna i figur 3.
Figur 3 . Nya logiska läsningar efter ombyggnad av indexet.
Det här är mycket bättre. Vi minskade antalet logiska läsningar med nästan hälften. Det betyder att indexet fick det att förbruka färre resurser. Och Arbetstabellen ? Det är borta! Du behöver inte använda TempDB .
Och genomförandeplanen? Se figur 4.
Figur 4 . Den nya exekveringsplanen är enklare när indexet byggdes om.
Ser? Planen är enklare. Inget behov av extra processorer för att sortera samma 121 317 rader.
Så, slutsatsen är:Se till att kolumnerna du använder för ORDER BY är indexerade .
MEN VAD OM ATT LÄGA TILL ETT INDEX PÅVERKAR SKRIVPRESTANDA?
Bra fråga.
Om det är problemet kan du dumpa en del av källtabellen till en tillfällig tabell eller minnesoptimerad tabell . Indexera sedan tabellen. Använd samma sak om fler tabeller är inblandade. Bedöm sedan frågeprestanda för det alternativ du valde. Det snabbare alternativet vinner.
2. Begränsa resultaten med WHERE och OFFSET/FETCH
Låt oss använda en annan fråga. Låt oss säga att du måste visa produktinformation med bilder i en app. Bilder kan göra frågor ännu tyngre. Så vi kontrollerar inte bara logiska läsningar utan även logiska läsningar.
Här är koden.
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
Detta kommer att producera 97 cyklar med bilder. De är mycket svåra att bläddra på en mobil enhet.
ANALYS
ANVÄNDER MINIMAL WHERE CONDITION UTAN OFFSET/FETCH
Här är hur mycket logisk läsning som krävs för att hämta 97 produkter med bilder. Se figur 5.
Figur 5 . De logiska läsningarna och loblogiska läsningarna när du använder ORDER BY utan OFFSET/FETCH och med minimalt WHERE-villkor . (Obs:statisticsparser.com visade inte lobens logiska läsningar. Skärmdumpen redigeras baserat på resultatet i SSMS)
667 lob logiska läsningar dök upp på grund av hämtning av bilder i 2 kolumner. Under tiden användes 590 logiska läsningar för resten.
Här är utförandeplanen i figur 6 så att vi kan jämföra den senare med den bättre planen.
Figur 6 . Utförandeplan med ORDER BY utan OFFSET/FETCH och med minimalt WHERE-skick.
Det finns inte mycket mer att säga förrän vi ser den andra utförandeplanen.
ANVÄNDA YTTERLIGARE WHERE CONDITION OCH OFFSET/HEMTA I ORDNING EFTER
Låt oss nu justera frågan för att se till att minimal data returneras. Det här är vad vi ska göra:
- Lägg till ett villkor för produktunderkategorin. I samtalsappen kan vi tänka oss att låta användaren också välja underkategori.
- Ta sedan bort produktunderkategorin i SELECT-listan över kolumner och ORDER BY-listan med kolumner.
- Slutligen, lägg till OFFSET/FETCH i ORDER BY. Endast 10 produkter kommer att returneras och visas i uppringningsappen.
Här är den redigerade koden.
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
Denna kod kommer att förbättras ytterligare om du gör den till en lagrad procedur. Det kommer också att ha parametrar som sidnummer och antal rader. Sidnumret anger vilken sida användaren för närvarande tittar på. Förbättra detta ytterligare genom att göra antalet rader flexibelt beroende på skärmupplösning. Men det är en annan historia.
Låt oss nu titta på de logiska läsningarna i figur 7.
Figur 7 . Färre logiska läsningar efter förenkling av frågan. OFFSET/FETCH används också i ORDER BY.
Jämför sedan figur 7 med figur 5. Lobbens logiska avläsningar är borta. Dessutom har de logiska avläsningarna en märkbar minskning eftersom resultatuppsättningen också minskade från 97 till 10.
Men vad gjorde SQL Server bakom kulisserna? Kolla in utförandeplanen i figur 8.
Figur 8 . En enklare exekveringsplan efter att ha förenklat frågan och lagt till OFFSET/FETCH i ORDER BY.
Jämför sedan figur 8 med figur 6. Utan att undersöka varje operatör kan vi se att denna nya plan är enklare än den tidigare.
Lektionen? Förenkla din fråga. Använd OFFSET/FETCH när det är möjligt.
Don'ts i SQL ORDER BY
Vi är klara med vad vi behöver göra när vi använder ORDER BY. Den här gången ska vi fokusera på vad vi bör undvika.
3. Använd inte ORDER BY när du sorterar efter Clustered Index Key
För det är värdelöst.
Låt oss visa det med ett exempel.
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
Låt oss sedan kontrollera de logiska läsningarna av båda SELECT-satserna i figur 9.
Figur 9 . 2 frågor i tabellen Person visar samma logiska läsningar. En är med ORDER BY, en annan utan.
Båda har 17 logiska läsningar. Detta är logiskt på grund av samma 100 rader som returneras. Men har de samma plan? Kolla in figur 10.
Figur 10 . Samma plan oavsett om ORDER BY används eller inte vid sortering efter den klustrade indexnyckeln.
Observera samma operatörer och samma frågekostnad.
Men varför? När du indexerar en eller flera kolumner till ett klustrat index kommer tabellen att fysiskt sorterad med den klustrade indexnyckeln. Så även om du inte sorterar efter den nyckeln kommer resultatet fortfarande att sorteras.
Slutsats? Förlåt dig själv genom att inte använda den klustrade indexnyckeln i liknande fall med ORDER BY . Spara din energi med färre knapptryckningar.
4. Använd inte ORDER BY När en strängkolumn innehåller siffror
Om du sorterar efter en strängkolumn som innehåller siffror, förvänta dig inte sorteringsordningen som reella nummertyper. Annars får du en stor överraskning.
Här är ett exempel.
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY NationalIDNumber;
Kontrollera utgången i figur 11.
Figur 11 . Sorteringsordning för en strängkolumn som innehåller siffror. Det numeriska värdet följs inte.
I figur 11 följs den lexikografiska sorteringsordningen. Så för att fixa detta, använd en CAST till ett heltal.
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT)
Se figur 12 för den fasta utgången.
Figur 12 . CAST till INT fixade sorteringen av en strängkolumn som innehåller siffror.
Så iistället för ORDER BY
5. Använd inte SELECT INTO #TempTable med ORDER BY
Din önskade sorteringsordning kommer inte att garanteras i den tillfälliga måltabellen. Se den officiella dokumentationen .
Låt oss ha en modifierad kod från föregående exempel.
SELECT
NationalIDNumber
,JobTitle
,HireDate
INTO #temp
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT);
SELECT * FROM #temp;
Den enda skillnaden från föregående exempel är INTO-satsen. Resultatet blir detsamma som i figur 11. Vi är tillbaka i ruta 1 även om vi CASTAR kolumnen till INT.
Du måste skapa en tillfällig tabell med CREATE TABLE. Men inkludera en extra identitetskolumn och gör den till en primärnyckel. INFOGA sedan i den tillfälliga tabellen.
Här är den fasta koden.
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;
Och utsignalen blir densamma som i figur 12. Det fungerar!
Takeaways i att använda SQL ORDER BY
Vi har täckt de vanliga fallgroparna med att använda SQL ORDER BY. Här är en sammanfattning:
Göra :
- Indexera ORDER BY-kolumnerna,
- Begränsa resultaten med WHERE och OFFSET/FETCH,
Gör inte :
- Använd inte ORDER BY när du sorterar efter den klustrade indexnyckeln,
- Använd inte ORDER BY när en strängkolumn innehåller siffror. CAST istället strängkolumnen till INT först.
- Använd inte SELECT INTO #TempTable med ORDER BY. Skapa istället den tillfälliga tabellen först med en extra identitetskolumn.
Vilka är dina tips och tricks för att använda ORDER BY? Låt oss veta i kommentarsektionen nedan. Och om du gillar det här inlägget, vänligen dela det på dina favoritplattformar för sociala medier.