Database
 sql >> Base de données >  >> RDS >> Database

SQL ORDER BY :les 5 choses à faire et à ne pas faire pour trier les données comme un pro

Laid. Voilà à quoi ressemblent les données non triées. Nous facilitons les données pour les yeux en les triant. Et c'est à cela que sert SQL ORDER BY. Utilisez une ou plusieurs colonnes ou expressions comme base pour trier les données. Ensuite, ajoutez ASC ou DESC pour trier par ordre croissant ou décroissant.

La syntaxe SQL ORDER BY :

ORDER BY <order_by_expression> [ASC | DESC]


L'expression ORDER BY peut être aussi simple qu'une liste de colonnes ou d'expressions. Il peut également être conditionnel à l'aide d'un bloc CASE WHEN.

C'est très flexible.

Vous pouvez également utiliser la pagination via OFFSET et FETCH. Spécifiez le nombre de lignes à ignorer et les lignes à afficher.

Mais voici la mauvaise nouvelle.

L'ajout de ORDER BY à vos requêtes peut les ralentir. Et certaines autres mises en garde peuvent faire en sorte que ORDER BY "ne fonctionne pas". Vous ne pouvez pas les utiliser quand vous le souhaitez, car il pourrait y avoir des pénalités. Alors, qu'est-ce qu'on fait ?

Dans cet article, nous examinerons les choses à faire et à ne pas faire en utilisant ORDER BY. Chaque élément traitera d'un problème et une solution suivra.

Prêt ?

À faire dans SQL ORDER BY

1. Indexer la ou les colonnes SQL ORDER BY

Les index sont des recherches rapides. Et en avoir un dans les colonnes que vous utilisez dans la clause ORDER BY peut accélérer votre requête.

Commençons à utiliser ORDER BY dans une colonne sans index. Nous utiliserons AdventureWorks exemple de base de données. Avant d'exécuter la requête ci-dessous, désactivez le IX_SalesOrderDetail_ProductID index dans le SalesOrderDetail table. Ensuite, appuyez sur Ctrl-M et exécutez-le.


-- 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

Le code ci-dessus affichera les statistiques d'E/S dans l'onglet Messages de SQL Server Management Studio. Vous verrez le plan d'exécution dans un autre onglet.

SANS INDEX

Tout d'abord, obtenons les lectures logiques de STATISTICS IO. Consultez la figure 1.

Illustration 1 . Lectures logiques à l'aide de ORDER BY d'une colonne non indexée. (Formaté en utilisant statisticsparser.com )

Sans l'index, la requête a utilisé 1 313 lectures logiques. Et cette WorkTable ? Cela signifie que SQL Server a utilisé TempDB pour traiter le tri.

Mais que s'est-il passé dans les coulisses ? Examinons le plan d'exécution de la figure 2.

Illustration 2 . Plan d'exécution d'une requête utilisant ORDER BY d'une colonne non indexée.

Avez-vous vu cet opérateur Parallelism (Gather Streams) ? Cela signifie que SQL Server a utilisé plus d'un processeur pour traiter cette requête. La requête était suffisamment lourde pour nécessiter plus de processeurs.

Et si SQL Server utilisait TempDB et plus de processeurs ? C'est mauvais pour une simple requête.

AVEC UN INDEX

Comment cela se passera-t-il si l'index est réactivé ? Découvrons-le. Reconstruire l'index IX_SalesOrderDetail_ProductID . Ensuite, relancez la requête ci-dessus.

Vérifiez les nouvelles lectures logiques de la figure 3.

Illustration 3 . Nouvelles lectures logiques après reconstruction de l'index.

Ceci est vraiment mieux. Nous avons réduit de près de moitié le nombre de lectures logiques. Cela signifie que l'index l'a fait consommer moins de ressources. Et la table de travail ? C'est parti! Pas besoin d'utiliser TempDB .

Et le plan d'exécution ? Voir Figure 4.

Illustration 4 . Le nouveau plan d'exécution est plus simple lorsque l'index a été reconstruit.

Voir? Le plan est plus simple. Pas besoin de processeurs supplémentaires pour trier les mêmes 121 317 lignes.

Donc, l'essentiel est :Assurez-vous que les colonnes que vous utilisez pour ORDER BY sont indexées .

MAIS ET SI L'AJOUT D'UN INDEX A UN IMPACT SUR LES PERFORMANCES D'ÉCRITURE ?

Bonne question.

Si tel est le problème, vous pouvez vider une partie de la table source dans une table temporaire ou une table à mémoire optimisée . Ensuite, indexez cette table. Utilisez la même chose si plusieurs tables sont impliquées. Ensuite, évaluez les performances de requête de l'option que vous avez choisie. L'option la plus rapide sera la gagnante.

2. Limitez les résultats avec WHERE et OFFSET/FETCH

Utilisons une requête différente. Supposons que vous ayez besoin d'afficher des informations sur le produit avec des images dans une application. Les images peuvent rendre les requêtes encore plus lourdes. Ainsi, nous ne vérifierons pas seulement les lectures logiques, mais également les lectures logiques lob.

Voici le code.

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


Cela produira 97 vélos avec des images. Ils sont très difficiles à parcourir sur un appareil mobile.

ANALYSE

UTILISATION DE LA CONDITION WHERE MINIMALE SANS DÉCALAGE/FETCH

Voici combien de lectures logiques sont nécessaires pour récupérer 97 produits avec des images. Voir Figure 5.

Illustration 5 . Les lectures logiques et les lectures logiques lob lors de l'utilisation de ORDER BY sans OFFSET/FETCH et avec une condition WHERE minimale . (Remarque :statisticsparser.com n'a pas affiché les lectures logiques lob. La capture d'écran est modifié en fonction du résultat dans SSMS)

667 lectures logiques lob sont apparues en raison de la récupération d'images dans 2 colonnes. Pendant ce temps, 590 lectures logiques ont été utilisées pour le reste.

Voici le plan d'exécution de la figure 6 afin que nous puissions le comparer ultérieurement au meilleur plan.

Illustration 6 . Plan d'exécution utilisant ORDER BY sans OFFSET/FETCH et avec une condition WHERE minimale.

Il n'y a pas grand-chose d'autre à dire jusqu'à ce que nous voyions l'autre plan d'exécution.

UTILISATION DE LA CONDITION WHERE SUPPLÉMENTAIRE ET DE L'OFFSET/FETCH IN ORDER BY

Maintenant, ajustons la requête pour nous assurer qu'un minimum de données est renvoyé. Voici ce que nous allons faire :

  • Ajouter une condition sur la sous-catégorie de produit. Dans l'application d'appel, nous pouvons imaginer laisser l'utilisateur choisir également la sous-catégorie.
  • Ensuite, supprimez la sous-catégorie de produit dans la liste de colonnes SELECT et la liste de colonnes ORDER BY.
  • Enfin, ajoutez OFFSET/FETCH dans ORDER BY. Seuls 10 produits seront retournés et affichés dans l'application appelante.

Voici le code modifié.

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


Ce code s'améliorera encore si vous en faites une procédure stockée. Il aura également des paramètres comme le numéro de page et le nombre de lignes. Le numéro de page indique la page que l'utilisateur consulte actuellement. Améliorez encore cela en rendant le nombre de lignes flexible en fonction de la résolution de l'écran. Mais c'est une autre histoire.

Examinons maintenant les lectures logiques de la figure 7.

Illustration 7 . Moins de lectures logiques après avoir simplifié la requête. OFFSET/FETCH est également utilisé dans ORDER BY.

Ensuite, comparez la Figure 7 à la Figure 5. Les lectures logiques lob ont disparu. De plus, les lectures logiques ont une diminution notable car l'ensemble de résultats a également été réduit de 97 à 10.

Mais qu'est-ce que SQL Server a fait dans les coulisses ? Consultez le plan d'exécution de la figure 8.

Figure 8 . Un plan d'exécution plus simple après avoir simplifié la requête et ajouté OFFSET/FETCH dans ORDER BY.

Ensuite, comparez la Figure 8 avec la Figure 6. Sans examiner chaque opérateur, nous pouvons voir que ce nouveau plan est plus simple que le précédent.

La leçon? Simplifiez votre requête. Utilisez OFFSET/FETCH chaque fois que possible.

À ne pas faire dans SQL ORDER BY

Nous en avons fini avec ce que nous devons faire lorsque nous utilisons ORDER BY. Cette fois, concentrons-nous sur ce que nous devrions éviter.

3. Ne pas utiliser ORDER BY lors du tri par la clé d'index cluster

Parce que c'est inutile.

Montrons-le avec un exemple.

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


Ensuite, vérifions les lectures logiques des deux instructions SELECT de la figure 9.

Illustration 9 . 2 requêtes sur la table Person affichent les mêmes lectures logiques. L'un est avec ORDER BY, l'autre sans.

Les deux ont 17 lectures logiques. C'est logique car les 100 mêmes lignes sont renvoyées. Mais ont-ils le même projet ? Consultez la figure 10.

Figure 10 . Le même plan, que ORDER BY soit utilisé ou non lors du tri par clé d'index clusterisé.

Observez les mêmes opérateurs et le même coût de requête.

Mais pourquoi? Lors de l'indexation d'une ou plusieurs colonnes dans un index clusterisé, la table sera triée physiquement par la clé d'index clusterisé. Ainsi, même si vous ne triez pas selon cette clé, le résultat sera toujours trié.

En bout de ligne ? Pardonnez-vous de ne pas utiliser la clé d'index clusterisé dans des cas similaires en utilisant ORDER BY . Économisez votre énergie avec moins de frappes.

4. N'utilisez pas ORDER BY lorsqu'une colonne de chaîne contient des nombres

Si vous triez par une colonne de chaîne contenant des nombres, ne vous attendez pas à un ordre de tri comme les types de nombres réels. Sinon, vous allez avoir une grosse surprise.

Voici un exemple.


SELECT 
 NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY NationalIDNumber;


Vérifiez la sortie de la figure 11.

Figure 11 . Ordre de tri d'une colonne de chaîne contenant des nombres. La valeur numérique n'est pas suivie.

Dans la figure 11, l'ordre de tri lexicographique est suivi. Donc, pour résoudre ce problème, utilisez un CAST vers un entier.


SELECT 
 NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT)


Consultez la figure 12 pour la sortie fixe.

Figure 12 . CAST to INT a corrigé le tri d'une colonne de chaîne contenant des nombres.

Donc, au lieu de ORDER BY , utilisez ORDER BY CAST( AS INT).

5. Ne pas utiliser SELECT INTO #TempTable avec ORDER BY

Votre ordre de tri souhaité ne sera pas garanti dans la table temporaire cible. Voir la documentation officielle .

Prenons un code modifié de l'exemple précédent.


SELECT 
 NationalIDNumber
,JobTitle
,HireDate
INTO #temp
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT);

SELECT * FROM #temp;


La seule différence avec l'exemple précédent est la clause INTO. La sortie sera la même que dans la figure 11. Nous sommes de retour au carré 1 même si nous CAST la colonne à INT.

Vous devez créer une table temporaire à l'aide de CREATE TABLE. Mais incluez une colonne d'identité supplémentaire et faites-en une clé primaire. Ensuite, INSERT dans la table temporaire.

Voici le code fixe.


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;


Et la sortie sera la même que dans la figure 12. Ça marche !

Points à retenir sur l'utilisation de SQL ORDER BY

Nous avons couvert les pièges courants liés à l'utilisation de SQL ORDER BY. Voici un résumé :

À faire :

  • Indexer les colonnes ORDER BY,
  • Limiter les résultats avec WHERE et OFFSET/FETCH,

A ne pas faire :

  • N'utilisez pas ORDER BY lors du tri par la clé d'index groupée,
  • N'utilisez pas ORDER BY lorsqu'une colonne de chaîne contient des nombres. Au lieu de cela, CAST la colonne de chaîne en INT en premier.
  • N'utilisez pas SELECT INTO #TempTable avec ORDER BY. Au lieu de cela, créez d'abord la table temporaire avec une colonne d'identité supplémentaire.

Quels sont vos trucs et astuces pour utiliser ORDER BY ? Faites-nous savoir dans la section commentaires ci-dessous. Et si vous aimez cet article, partagez-le sur vos plateformes de médias sociaux préférées.