Grim. Sådan ser usorterede data ud. Vi gør data let for øjnene ved at sortere dem. Og det er det, SQL ORDER BY er til for. Brug en eller flere kolonner eller udtryk som grundlag for at sortere data. Tilføj derefter ASC eller DESC for at sortere stigende eller faldende.
SQL ORDER BY-syntaksen:
ORDER BY <order_by_expression> [ASC | DESC]
ORDER BY-udtrykket kan være så simpelt som en liste over kolonner eller udtryk. Det kan også være betinget ved at bruge en CASE WHEN-blok.
Det er meget fleksibelt.
Du kan også bruge paging gennem OFFSET og FETCH. Angiv antallet af rækker, der skal springes over, og rækkerne, der skal vises.
Men her er de dårlige nyheder.
Tilføjelse af ORDER BY til dine forespørgsler kan bremse dem. Og nogle andre forbehold kan gøre BESTILLING VED "ikke at virke." Du kan ikke bare bruge dem når som helst du vil, da der kan være bøder. Så hvad gør vi?
I denne artikel vil vi undersøge do's og don'ts ved at bruge ORDER BY. Hvert element vil håndtere et problem, og en løsning vil følge.
Klar?
Gøremål i SQL ORDER BY
1. Indekser SQL ORDER BY kolonne(r)
Indeks handler om hurtige søgninger. Og at have en i de kolonner, du bruger i ORDER BY-klausulen, kan fremskynde din forespørgsel.
Lad os begynde at bruge ORDER BY i en kolonne uden et indeks. Vi vil bruge AdventureWorks prøvedatabase. Før du udfører forespørgslen nedenfor, skal du deaktivere IX_SalesOrderDetail_ProductID indeks i SalesOrderDetail bord. Tryk derefter på Ctrl-M og kør det.
-- 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
ANALYSE
Ovenstående kode vil udlæse I/O-statistikken på fanen Meddelelser i SQL Server Management Studio. Du vil se udførelsesplanen i en anden fane.
UDDEN ET INDEKS
Lad os først få de logiske læsninger fra STATISTICS IO. Se figur 1.
Figur 1 . Logiske læsninger ved hjælp af ORDER BY af en uindekseret kolonne. (Formateret med statisticsparser.com )
Uden indekset brugte forespørgslen 1.313 logiske læsninger. Og den WorkTable ? Det betyder, at SQL Server brugte TempDB at behandle sorteringen.
Men hvad skete der bag kulisserne? Lad os inspicere udførelsesplanen i figur 2.
Figur 2 . Udførelsesplan for en forespørgsel ved hjælp af ORDER BY af en uindekseret kolonne.
Så du operatøren Parallelism (Gather Streams)? Det betyder, at SQL Server brugte mere end 1 processor til at behandle denne forespørgsel. Forespørgslen var tung nok til at kræve flere CPU'er.
Så hvad nu hvis SQL Server brugte TempDB og flere processorer? Det er dårligt for en simpel forespørgsel.
Med ET INDEKS
Hvordan vil det klare sig, hvis indekset genaktiveres? Lad os finde ud af det. Genopbyg indekset IX_SalesOrderDetail_ProductID . Kør derefter forespørgslen ovenfor igen.
Tjek de nye logiske læsninger i figur 3.
Figur 3 . Nye logiske læsninger efter genopbygning af indekset.
Det her er meget bedre. Vi reducerede antallet af logiske læsninger med næsten det halve. Det betyder, at indekset fik det til at forbruge færre ressourcer. Og Arbejdstabellen ? Det er væk! Ingen grund til at bruge TempDB .
Og udførelsesplanen? Se figur 4.
Figur 4 . Den nye udførelsesplan er enklere, da indekset blev genopbygget.
Se? Planen er enklere. Intet behov for ekstra CPU'er for at sortere de samme 121.317 rækker.
Så den nederste linje er:Sørg for, at de kolonner, du bruger til ORDER BY, er indekseret .
MEN HVAD HVIS TILFØJELSE AF ET INDEKS PÅVIRKER SKRIVEYDELSEN?
Godt spørgsmål.
Hvis det er problemet, kan du dumpe en del af kildetabellen til en midlertidig tabel eller hukommelsesoptimeret tabel . Indekser derefter tabellen. Brug det samme, hvis flere tabeller er involveret. Vurder derefter forespørgselsydeevnen for den mulighed, du valgte. Den hurtigere mulighed vil være vinderen.
2. Begræns resultaterne med WHERE og OFFSET/FETCH
Lad os bruge en anden forespørgsel. Lad os sige, at du skal vise produktinformation med billeder i en app. Billeder kan gøre forespørgsler endnu tungere. Så vi vil ikke kun kontrollere logiske læsninger, men også lob logiske læsninger.
Her er 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
Dette vil udsende 97 cykler med billeder. De er meget svære at gennemse på en mobilenhed.
ANALYSE
BRUG AF MINIMAL WHERE TILSTAND UDEN OFFSET/FETCH
Her er, hvor mange logiske læsninger der skal til for at hente 97 produkter med billeder. Se figur 5.
Figur 5 . De logiske læsninger og lob logiske læsninger ved brug af ORDER BY uden OFFSET/FETCH og med minimal WHERE-tilstand . (Bemærk:statisticsparser.com viste ikke lob logiske læsninger. Skærmbilledet er redigeret baseret på resultatet i SSMS)
667 lob logiske læsninger dukkede op på grund af hentning af billeder i 2 kolonner. I mellemtiden blev 590 logiske læsninger brugt til resten.
Her er udførelsesplanen i figur 6, så vi kan sammenligne den senere med den bedre plan.
Figur 6 . Udførelsesplan med ORDER BY uden OFFSET/FETCH og med minimal WHERE-tilstand.
Der er ikke meget andet at sige, før vi ser den anden udførelsesplan.
BRUG AF YDERLIGERE WHERE CONDITION OG OFFSET/FETCH I REFERENCE EFTER
Lad os nu justere forespørgslen for at sikre, at minimale data returneres. Her er, hvad vi skal gøre:
- Tilføj en betingelse for produktunderkategorien. I opkaldsappen kan vi forestille os at lade brugeren også vælge underkategorien.
- Fjern derefter produktunderkategorien i SELECT-listen over kolonner og ORDER BY-listen over kolonner.
- Tilføj endelig OFFSET/FETCH i ORDER BY. Kun 10 produkter vil blive returneret og vist i opkaldsappen.
Her er den redigerede kode.
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
Denne kode vil forbedres yderligere, hvis du gør den til en lagret procedure. Det vil også have parametre som sidetal og antal rækker. Sidenummeret angiver, hvilken side brugeren ser i øjeblikket. Forbedre dette yderligere ved at gøre antallet af rækker fleksibelt afhængigt af skærmopløsningen. Men det er en anden historie.
Lad os nu se på de logiske læsninger i figur 7.
Figur 7 . Færre logiske læsninger efter forenkling af forespørgslen. OFFSET/FETCH bruges også i ORDER BY.
Sammenlign derefter figur 7 med figur 5. De logiske aflæsninger er væk. Derudover har de logiske aflæsninger et markant fald, da resultatsættet også blev reduceret fra 97 til 10.
Men hvad lavede SQL Server bag kulisserne? Tjek udførelsesplanen i figur 8.
Figur 8 . En enklere udførelsesplan efter forenkling af forespørgslen og tilføjelse af OFFSET/FETCH i ORDER BY.
Sammenlign derefter figur 8 med figur 6. Uden at undersøge hver enkelt operatør kan vi se, at denne nye plan er enklere end den forrige.
lektionen? Gør din forespørgsel enklere. Brug OFFSET/FETCH, når det er muligt.
Don'ts i SQL ORDER BY
Vi er færdige med, hvad vi skal gøre, når vi bruger ORDER BY. Lad os denne gang fokusere på, hvad vi bør undgå.
3. Brug ikke ORDER BY, når du sorterer efter Clustered Index Key
Fordi det er ubrugeligt.
Lad os vise det med et eksempel.
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
Lad os derefter tjekke de logiske læsninger af begge SELECT-sætninger i figur 9.
Figur 9 . 2 forespørgsler i tabellen Person viser de samme logiske læsninger. En er med BESTIL BY, en anden uden.
Begge har 17 logiske læsninger. Dette er logisk på grund af de samme 100 returnerede rækker. Men har de samme plan? Se figur 10.
Figur 10 . Den samme plan, uanset om ORDER BY bruges eller ej, når der sorteres efter den klyngede indeksnøgle.
Overhold de samme operatører og samme forespørgselsomkostninger.
Men hvorfor? Når du indekserer en eller flere kolonner til et klynget indeks, vil tabellen være fysisk sorteret ved hjælp af den klyngede indeksnøgle. Så selvom du ikke sorterer efter den nøgle, vil resultatet stadig blive sorteret.
Bundlinie? Tilgiv dig selv ved ikke at bruge den klyngede indeksnøgle i lignende tilfælde ved at bruge ORDER BY . Spar din energi med færre tastetryk.
4. Brug ikke ORDER BY Når en strengkolonne indeholder tal
Hvis du sorterer efter en strengkolonne, der indeholder tal, skal du ikke forvente sorteringsrækkefølgen som reelle taltyper. Ellers får du en stor overraskelse.
Her er et eksempel.
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY NationalIDNumber;
Kontroller outputtet i figur 11.
Figur 11 . Sorteringsrækkefølge for en strengkolonne, der indeholder tal. Den numeriske værdi følges ikke.
I figur 11 følges den leksikografiske sorteringsrækkefølge. Så for at rette dette, brug en CAST til et heltal.
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT)
Se figur 12 for den faste udgang.
Figur 12 . CAST til INT fikserede sorteringen af en strengkolonne, der indeholder tal.
Så ii stedet for ORDER BY
5. Brug ikke SELECT INTO #TempTable med ORDER BY
Din ønskede sorteringsrækkefølge vil ikke blive garanteret i den midlertidige måltabel. Se den officielle dokumentation .
Lad os få en ændret kode fra det forrige eksempel.
SELECT
NationalIDNumber
,JobTitle
,HireDate
INTO #temp
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT);
SELECT * FROM #temp;
Den eneste forskel fra det foregående eksempel er INTO-sætningen. Outputtet vil være det samme som i figur 11. Vi er tilbage i felt 1, selvom vi CAST kolonnen til INT.
Du skal oprette en midlertidig tabel ved hjælp af CREATE TABLE. Men medtag en ekstra identitetskolonne og gør den til en primær nøgle. Indsæt derefter i den midlertidige tabel.
Her er den faste kode.
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;
Og outputtet vil være det samme som i figur 12. Det virker!
Takeaways ved brug af SQL ORDER BY
Vi har dækket de almindelige faldgruber ved at bruge SQL ORDER BY. Her er en oversigt:
Gøremål :
- Indeksér ORDER BY-kolonnerne,
- Begræns resultaterne med WHERE og OFFSET/FETCH,
Lad være med :
- Brug ikke ORDER BY, når du sorterer efter den klyngede indeksnøgle,
- Brug ikke ORDER BY, når en strengkolonne indeholder tal. CAST i stedet strengkolonnen til INT først.
- Brug ikke SELECT INTO #TempTable med BESTIL EFTER. I stedet skal du først oprette den midlertidige tabel med en ekstra identitetskolonne.
Hvad er dine tips og tricks til at bruge ORDER BY? Fortæl os det i kommentarfeltet nedenfor. Og hvis du kan lide dette opslag, så del det på dine foretrukne sociale medieplatforme.