Étude de l’instruction SELECT

Effectué

Transact-SQL ou T-SQL est un dialecte du langage SQL ANSI standard utilisé par les produits et services Microsoft SQL. Il est similaire au SQL standard. Nous allons essentiellement nous concentrer sur l’instruction SELECT, qui a de loin le plus d’options et de variantes parmi les instructions DML.

Commençons par étudier une vue d’ensemble de la façon dont une instruction SELECT est traitée. L’ordre dans lequel une instruction SELECT est écrite n’est pas l’ordre dans lequel elle est évaluée et traitée par le moteur de base de données SQL Server.

Considérez la requête suivante :

SELECT OrderDate, COUNT(OrderID) AS Orders
FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate
HAVING COUNT(OrderID) > 1
ORDER BY OrderDate DESC;

La requête se compose d’une instruction SELECT, composée de plusieurs clauses, chacune d’elles définissant une opération spécifique qui doit être appliquée aux données récupérées. Avant d’examiner l’ordre d’exécution des opérations, observons brièvement ce que fait cette requête, bien que les détails des différentes clauses ne soient pas couverts dans ce module.

La clause SELECT retourne la colonne OrderDate et le nombre de valeurs OrderID, en lui affectant le nom (ou alias) Orders :

SELECT OrderDate, COUNT(OrderID) AS Orders

La clause FROM identifie la table qui est la source des lignes de la requête. Dans ce cas, il s’agit de la table Sales.SalesOrder :

FROM Sales.SalesOrder

La clause WHERE filtre les lignes en dehors des résultats, en conservant uniquement les lignes qui répondent à la condition spécifiée, dans ce cas les commandes dont l’état est « shipped » :

WHERE Status = 'Shipped'

La clause GROUP BY prend les lignes qui répondent à la condition de filtre et les regroupe par OrderDate, afin que toutes les lignes ayant la même OrderDate soient considérées comme un groupe unique, et qu’une ligne soit retournée pour chaque groupe :

GROUP BY OrderDate

Une fois les groupes formés, la clause HAVING filtre les groupes en fonction de son propre prédicat. Seules les dates avec plusieurs commandes seront incluses dans les résultats :

HAVING COUNT(OrderID) > 1

Dans le cadre de l’affichage de l’aperçu de cette requête, la clause finale est l’ordre de tri, qui trie la sortie dans l’ordre décroissant d’OrderDate :

ORDER BY OrderDate DESC;

Maintenant que vous avez vu ce que fait chaque clause, examinons l’ordre dans lequel SQL Server les évalue réellement :

  1. La clause FROM est évaluée en premier, afin de fournir les lignes sources pour le reste de l’instruction. Une table virtuelle est créée et passée à l’étape suivante.
  2. La clause WHERE est ensuite évaluée, et filtre les lignes de la table source qui correspondent à un prédicat. La table virtuelle filtrée est passée à l’étape suivante.
  3. GROUP BY vient ensuite, en organisant les lignes de la table virtuelle en fonction des valeurs uniques trouvées dans la liste GROUP BY. Une nouvelle table virtuelle est créée, contenant la liste des groupes, et est passée à l’étape suivante. À partir de ce stade dans le déroulement des opérations, seules les colonnes de la liste GROUP BY ou les fonctions d’agrégation peuvent être référencées par d’autres éléments.
  4. La clause HAVING est ensuite évaluée, en filtrant les groupes entiers en fonction de son prédicat. La table virtuelle créée à l’étape 3 est filtrée et transmise à l’étape suivante.
  5. La clause SELECT s’exécute enfin, en déterminant les colonnes qui s’affichent dans les résultats de la requête. Étant donné que la clause SELECT est évaluée après les autres étapes, tous les alias de colonne (dans notre exemple, Orders) créés ne peuvent pas être utilisés dans la clause GROUP BY ou HAVING.
  6. La clause ORDER BY est la dernière à s’exécuter, en triant les lignes comme déterminé par sa liste de colonnes.

Pour appliquer cette compréhension à notre exemple de requête, voici l’ordre logique au moment de l’exécution de l’instruction SELECT ci-dessus :

FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate 
HAVING COUNT(OrderID) > 1
SELECT OrderDate, COUNT(OrderID) AS Orders
ORDER BY OrderDate DESC;

Toutes les clauses possibles ne sont pas requises dans chaque instruction SELECT que vous écrivez. La seule clause obligatoire est la clause SELECT, qui peut être utilisée seule dans certains cas. En règle générale, une clause FROM est également incluse pour identifier la table interrogée. En outre, Transact-SQL comporte d’autres clauses qui peuvent être ajoutées.

Comme vous l’avez vu, vous n’écrivez pas les requêtes T-SQL dans le même ordre que celui dans lequel elles sont évaluées logiquement. L’ordre d’évaluation au moment de l’exécution détermine les données disponibles pour les clauses, car une clause a uniquement accès aux informations déjà rendues disponibles à partir d’une clause déjà traitée. Pour cette raison, il est important de comprendre l’ordre de traitement logique réel lors de l’écriture de requêtes.

Sélection de toutes les colonnes

La clause SELECT est souvent appelée la liste de SELECT, car elle répertorie les valeurs à retourner dans les résultats de la requête.

La forme la plus simple d’une clause SELECT consiste à utiliser l’astérisque (*) pour retourner toutes les colonnes. Lorsqu’il est utilisé dans les requêtes T-SQL, il est appelé étoile. Bien que SELECT * soit adapté aux tests rapides, vous devez éviter de l’utiliser en production pour les raisons suivantes :

  • Les modifications apportées à la table qui ajoutent ou réorganisent les colonnes seront reflétées dans les résultats de la requête, ce qui peut entraîner une sortie inattendue pour les applications ou les rapports qui utilisent la requête.
  • Le retour de données qui ne sont pas nécessaires peut ralentir vos requêtes et entraîner des problèmes de performances si la table source contient un grand nombre de lignes.

Par exemple, l’exemple suivant extrait toutes les colonnes de la table Production.Product (hypothétique).

SELECT * FROM Production.Product;

Le résultat de cette requête est un ensemble de lignes qui contient toutes les colonnes de toutes les lignes de la table, ce qui peut ressembler à ceci :

ProductID

Nom

ProductNum

Couleur

StandardCost

ListPrice

Taille

Poids

ProductCatID

680

Cadre route HL - Noir, 58

FR-R92B-58

Noir

1059.31

1431.5

58

1016,04

18

706

Cadre route HL - Rouge, 58

FR-R92R-58

Rouge

1059.31

1431.5

58

1016,04

18

707

Casque Sport-100, Rouge

HL-U509-R

Rouge

13,0863

34,99

35

708

Casque Sport-100, Noir

HL-U509

Noir

13,0863

34,99

35

...

...

...

...

...

...

...

...

...

Sélection de colonnes spécifiques

Une liste de colonnes explicite vous permet de contrôler exactement quelles colonnes sont retournées et dans quel ordre. Chaque colonne dans le résultat aura le nom de la colonne comme en-tête.

Par exemple, considérez la requête suivante, qui utilise de nouveau la table hypothétique Production.Product.

SELECT ProductID, Name, ListPrice, StandardCost
‎FROM Production.Product;

Cette fois, les résultats incluent uniquement les colonnes spécifiées :

ProductID

Nom

ListPrice

StandardCost

680

Cadre route HL - Noir, 58

1431.5

1059.31

706

Cadre route HL - Rouge, 58

1431.5

1059.31

707

Casque Sport-100, Rouge

34,99

13,0863

708

Casque Sport-100, Noir

34,99

13,0863

...

...

...

...

Sélection d’expressions

En plus de récupérer des colonnes stockées dans la table spécifiée, une clause SELECT peut effectuer des calculs et des manipulations, qui utilisent des opérateurs pour combiner des colonnes et des valeurs ou plusieurs colonnes. Le résultat du calcul ou de la manipulation doit être un résultat à valeur unique (scalaire) qui apparaît dans le résultat sous forme d’une colonne distincte.

Par exemple, la requête suivante comprend deux expressions :

SELECT ProductID,
      Name + '(' + ProductNumber + ')',
  ListPrice - StandardCost
FROM Production.Product;

Les résultats de cette requête peuvent se présenter comme suit :

ProductID

680

HL Road Frame - Black, 58(FR-R92B-58)

372.19

706

HL Road Frame - Red, 58(FR-R92R-58)

372.19

707

Sport-100 Helmet, Red(HL-U509-R)

21.9037

708

Sport-100 Helmet, Black(HL-U509)

21.9037

...

...

...

Il y a quelques points intéressants à noter sur ces résultats :

  • Les colonnes retournées par les deux expressions n’ont pas de nom de colonne. En fonction de l’outil que vous utilisez pour envoyer votre requête, un nom de colonne manquant peut être indiqué par un en-tête de colonne vide, un indicateur « no column name » littéral ou un nom par défaut comme column1. Nous verrons comment spécifier un alias pour le nom de colonne dans la requête plus loin dans cette section.
  • La première expression utilise l’opérateur + pour concaténer les valeurs de chaîne (basées sur des caractères), tandis que la deuxième expression utilise l’opérateur - pour soustraire une valeur numérique d’une autre. Lorsqu’il est utilisé avec des valeurs numériques, l’opérateur + effectue une addition. En clair, il est important de comprendre les types de données des colonnes que vous incluez dans les expressions. Nous discuterons des types de données dans la section suivante.

Spécification d’alias de colonne

Vous pouvez spécifier un alias pour chaque colonne retournée par la requête SELECT, soit comme alternative au nom de la colonne source, soit pour attribuer un nom à la sortie d’une expression.

Par exemple, voici la même requête que précédemment, mais avec des alias spécifiés pour chacune des colonnes :

SELECT ProductID AS ID,
      Name + '(' + ProductNumber + ')' AS ProductName,
  ListPrice - StandardCost AS Markup
FROM Production.Product;

Les résultats de cette requête incluent les noms de colonne spécifiés :

id

ProductName

balisage

680

HL Road Frame - Black, 58(FR-R92B-58)

372.19

706

HL Road Frame - Red, 58(FR-R92R-58)

372.19

707

Sport-100 Helmet, Red(HL-U509-R)

21.9037

708

Sport-100 Helmet, Black(HL-U509)

21.9037

...

...

...

Notes

Le mot clé AS est facultatif lors de la spécification d’un alias, mais il est conseillé de l’inclure pour clarification.

Mise en forme des requêtes

Vous pouvez noter les exemples de cette section qui vous permettent de mettre en forme votre code de requête de manière flexible. Par exemple, vous pouvez écrire chaque clause (ou l’intégralité de la requête) sur une seule ligne, ou la diviser sur plusieurs lignes. Dans la plupart des systèmes de base de données, le code n’est pas sensible à la casse, et certains éléments du langage T-SQL sont facultatifs (y compris le mot clé AS mentionné précédemment, et même le point-virgule à la fin d’une instruction).

Tenez compte des recommandations suivantes pour rendre votre code T-SQL facile à lire (et, par conséquent, plus facile à comprendre et à déboguer !) :

  • Mettez en majuscules les mots clés T-SQL, comme SELECT, FROM, AS, etc. La capitalisation des mots clés est une convention couramment utilisée qui facilite la recherche de chaque clause d’une instruction complexe.
  • Commencez une nouvelle ligne pour chaque clause principale d’une instruction.
  • Si la liste SELECT contient plus de quelques colonnes, expressions ou alias, envisagez de répertorier chaque colonne sur sa propre ligne.
  • Mettez en retrait les lignes contenant des sous-clauses ou des colonnes pour clarifier le code qui appartient à chaque clause principale.