web-dev-qa-db-fra.com

Est-ce que select * est toujours un gros no-no sur SQL Server 2012?

À l'époque d'antan, il était considéré comme un grand non-faire select * from table Ou select count(*) from table à cause des performances.

Est-ce toujours le cas dans les versions ultérieures de SQL Server (j'utilise 2012, mais je suppose que la question s'appliquerait à 2008-2014)?

Edit: Puisque les gens semblent me critiquer légèrement ici, je regarde cela d'un point de vue de référence/académique, pas si c'est la "bonne" chose à faire (ce qui bien sûr n'est pas )

41
Piers Karsenbarg

Si vous SELECT COUNT(*) FROM TABLE qui ne renvoie qu'une seule ligne (le nombre), est relativement léger et est le moyen d'obtenir cette donnée.

Et SELECT * n'est pas un non-non physique, dans la mesure où il est légal et autorisé.

Cependant, le problème avec SELECT * est que vous pouvez provoquer beaucoup plus de mouvements de données. Vous opérez sur chaque colonne du tableau. Si votre SELECT ne comprend que quelques colonnes, vous pourrez peut-être obtenir votre réponse à partir d'un ou de plusieurs index, ce qui réduit les E/S et également l'impact sur le cache du serveur.

Donc, Oui il est déconseillé comme pratique générale car cela gaspille vos ressources.

Le seul véritable avantage de SELECT * ne tape pas tous les noms de colonne. Mais à partir de SSMS, vous pouvez utiliser le glisser-déposer pour obtenir les noms de colonne dans votre requête et supprimer ceux dont vous n'avez pas besoin.

ne analogie: Si quelqu'un utilise SELECT * lorsqu'ils n'ont pas besoin de chaque colonne, utiliseraient-ils SELECT sans WHERE (ou une autre clause limitative) lorsqu'ils n'ont pas besoin de chaque ligne?

50
RLF

En plus de la réponse déjà fournie, je pense qu'il convient de souligner que les développeurs sont souvent trop paresseux lorsqu'ils travaillent avec des ORM modernes tels que Entity Framework. Alors que les DBA font de leur mieux pour éviter SELECT *, les développeurs écrivent souvent l'équivalent sémantique, par exemple, en c # Linq:

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User").ToList();

Essentiellement, cela se traduirait par ce qui suit:

SELECT * FROM MyTable WHERE FirstName = 'User'

Il existe également des frais généraux supplémentaires qui n'ont pas déjà été couverts. Il s'agit des ressources nécessaires pour traiter chaque colonne de chaque ligne vers l'objet correspondant. De plus, pour chaque objet conservé en mémoire, cet objet doit être nettoyé. Si vous ne sélectionnez que les colonnes dont vous avez besoin, vous pouvez facilement économiser plus de 100 Mo de RAM. Bien que ce ne soit pas un montant massif en soi, c'est l'effet cumulatif de la collecte des ordures, etc., qui est le côté client des coûts.

Alors oui, pour moi au moins, c'est et sera toujours un grand non. Nous devons également nous renseigner sur les coûts "cachés" de cette démarche.

Addendum

Voici un exemple d'extraction uniquement des données dont vous avez besoin, comme demandé dans les commentaires:

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User")
                             .Select(entity => new { entity.FirstName, entity.LastNight });
24
Stuart Blackler

Performance: Une requête avec SELECT * ne sera probablement jamais une requête de couverture ( Explication de conversation simple , Explication de débordement de pile ).

A l'épreuve du temps: votre requête peut renvoyer les sept colonnes aujourd'hui, mais si quelqu'un ajoute cinq colonnes au cours de l'année suivante, alors dans un an, votre requête renvoie douze colonnes, gaspillant IO et CPU.

Indexation: si vous souhaitez que vos vues et fonctions table participent à l'indexation dans SQL Server, ces vues et fonctions doivent être créées avec la liaison de schémas, ce qui interdit l'utilisation de SELECT *.

Meilleure pratique : n'utilisez jamais SELECT * Dans le code de production.

Pour les sous-requêtes, je préfère WHERE EXISTS ( SELECT 1 FROM … ).

Modifier: Pour répondre au commentaire de Craig Young ci-dessous, utiliser "SELECT 1" dans une sous-requête n'est pas une "'optimisation" - c'est pour que je puisse me tenir devant ma classe et dire "don n'utilisez pas SELECT *, sans exception! "

La seule exception à laquelle je peux penser est celle où le client effectue une sorte d'opération de tableau croisé dynamique et nécessite toutes les colonnes présentes et futures.

Je pourrais accepter une exception concernant les CTE et les tables dérivées, bien que je veuille voir des plans d'exécution.

Notez que je considère COUNT(*) une exception à cela parce que c'est une utilisation syntaxique différente de "*".

13
Greenstone Walker

Dans SQL Server 2012 (ou toute version à partir de 2005), l'utilisation de SELECT *... N'est qu'un problème de performances possible dans l'instruction SELECT de niveau supérieur d'une requête.

Ce n'est donc PAS un problème dans les vues (*), dans les sous-requêtes, dans les clauses EXIST, dans les CTE, ni dans SELECT COUNT(*).. etc., etc. Notez que cela est probablement aussi vrai pour Oracle et DB2, et peut-être PostGres (pas sûr), mais il est très probable que cela reste un problème dans de nombreux cas pour MySql.

Pour comprendre pourquoi (et pourquoi cela peut toujours être un problème dans un SELECT de niveau supérieur), il est utile de comprendre pourquoi cela a déjà été un problème, car l'utilisation de SELECT *.. Signifie "retour TOUTES les colonnes ". En général, cela renverra beaucoup plus de données que vous ne le souhaitez, ce qui peut évidemment entraîner beaucoup plus d'E/S, à la fois sur le disque et sur le réseau.

Ce qui est moins évident, c'est que cela limite également les index et les plans de requête qu'un optimiseur SQL peut utiliser, car il sait qu'il doit finalement renvoyer toutes les colonnes de données. S'il peut savoir à l'avance que vous ne voulez que certaines colonnes, il peut souvent utiliser des plans de requête plus efficaces en tirant parti des index qui n'ont que ces colonnes. Heureusement, il existe un moyen de le savoir à l'avance, qui vous permet de spécifier explicitement les colonnes que vous souhaitez dans la liste des colonnes. Mais lorsque vous utilisez "*", vous renoncez à cela au profit de "donnez-moi tout, je trouverai ce dont j'ai besoin".

Oui, il y a également une utilisation supplémentaire du processeur et de la mémoire pour traiter chaque colonne, mais c'est presque toujours mineur par rapport à ces deux choses: le disque supplémentaire important et la bande passante réseau requise pour les colonnes dont vous n'avez pas besoin, et avoir à utiliser moins plan de requête optimisé car il doit inclure chaque colonne.

Alors qu'est-ce qui a changé? Fondamentalement, les optimiseurs SQL ont réussi à incorporer une fonctionnalité appelée "optimisation de colonne" qui signifie simplement qu'ils peuvent désormais comprendre dans les sous-requêtes de niveau inférieur si vous prévoyez d'utiliser une colonne dans les niveaux supérieurs de la requête.

Le résultat est que cela n'a plus d'importance si vous utilisez 'SELECT * ..' dans les niveaux inférieurs/internes d'une requête. Au lieu de cela, ce qui compte vraiment, c'est ce qui se trouve dans la liste des colonnes du SELECT de niveau supérieur. À moins que vous n'utilisiez SELECT *.. En haut, alors encore une fois, vous devez supposer que vous voulez TOUTES des colonnes, et ne pouvez donc pas utiliser la colonne optimisations efficacement.

(* - notez qu'il existe un problème de liaison mineur et différent dans les vues avec * où elles n'enregistrent pas toujours la modification dans les listes de colonnes lorsque "*" est utilisé. Il existe d'autres façons de résoudre ce problème et n'affecte pas les performances.)

10
RBarryYoung

Il y a une autre petite raison de ne pas utiliser SELECT *: si l'ordre des colonnes renvoyées change, votre application se cassera ... si vous avez de la chance. Si vous ne l'êtes pas, vous aurez un bug subtil qui pourrait ne pas être détecté pendant longtemps. L'ordre des champs dans une table est un détail d'implémentation qui ne doit jamais être pris en compte par les applications, car la seule fois où il est même visible est si vous utilisez un SELECT *.

5
Jon of All Trades

Il est physiquement et problématiquement autorisé d'utiliser select * from table, cependant, c'est une mauvaise idée. Pourquoi?

Tout d'abord, vous constaterez que vous renvoyez des colonnes dont vous n'avez pas besoin (ressources importantes).

Deuxièmement, cela prendra plus de temps sur une grande table que de nommer les colonnes, car lorsque vous sélectionnez *, vous sélectionnez en fait les noms de colonne dans la base de données et vous dites "donnez-moi les données associées aux colonnes qui ont des noms dans cette autre liste" . " Bien que cela soit rapide pour le programmeur, imaginez faire cette recherche sur l'ordinateur d'une banque qui pourrait avoir littéralement des centaines de milliers de recherches en une minute.

Troisièmement, cela rend la tâche plus difficile pour le développeur. À quelle fréquence devez-vous basculer entre SSMS et VS pour obtenir tous les noms de colonne?

Quatrièmement, c'est un signe de programmation paresseuse et je ne pense pas qu'un développeur voudrait cette réputation.

3
CharlieHorse

Cela peut être un problème si vous mettez le Select * ... code dans un programme, car, comme indiqué précédemment, la base de données peut changer au fil du temps et avoir plus de colonnes que ce à quoi vous vous attendiez lorsque vous avez écrit la requête. Cela peut entraîner un échec du programme (dans le meilleur des cas) ou le programme peut continuer sur sa lancée et corrompre certaines données car il examine les valeurs de champ qu'il n'a pas été écrit pour gérer. En bref, le code de production doit TOUJOURS spécifier les champs à renvoyer dans le SELECT.

Cela dit, j'ai moins de problèmes lorsque le Select * fait partie d'une clause EXISTS, car tout ce qui va être renvoyé au programme est un booléen indiquant le succès ou l'échec de la sélection. D'autres peuvent être en désaccord avec cette position et je respecte leur opinion à ce sujet. Il PEUT être légèrement moins efficace de coder Select * que pour coder 'Select 1' dans une clause EXISTS, mais je ne pense pas qu'il y ait de danger de corruption des données, de toute façon.

3
Mark Ross

Beaucoup de réponses pourquoi select * est faux, donc je couvrirai quand je pense que c'est bon ou du moins OK.

1) Dans un EXISTS, le contenu de la partie SELECT de la requête est ignoré, vous pouvez donc même écrire SELECT 1/0 et ce ne sera pas une erreur. EXISTS vérifie simplement que certaines données retourneraient et retournerait un booléen basé sur cela.

IF EXISTS(
    SELECT * FROM Table WHERE X=@Y
)

2) Cela pourrait déclencher une tempête de feu, mais j'aime utiliser select * dans mes déclencheurs de table d'historique. Par select *, il empêche la table principale d'obtenir une nouvelle colonne sans ajouter la colonne à la table d'historique et en provoquant une erreur immédiatement lorsqu'elle est insérée/mise à jour/supprimée dans la table principale. Cela a empêché de nombreuses fois les développeurs d'ajouter des colonnes et a oublié de l'ajouter à la table d'historique.

2
UnhandledExcepSean