Pourquoi SELECT * est-il considéré comme dangereux?


256

Pourquoi est-ce une SELECT *mauvaise pratique? Cela ne signifierait-il pas moins de code à modifier si vous ajoutiez une nouvelle colonne que vous vouliez?

Je comprends que SELECT COUNT(*)c'est un problème de performances sur certaines bases de données, mais que faire si vous vouliez vraiment chaque colonne?


30
SELECT COUNT(*)être mauvais est incroyablement vieux et obsolète . Pour plus d'informations sur SELECT *- voir: stackoverflow.com/questions/1960036/…
OMG Ponies

8
SELECT COUNT(*)donne une réponse différente de SELECT COUNT(SomeColumn)sauf si la colonne est une colonne NOT NULL. Et l'optimiseur peut donner SELECT COUNT(*)un traitement spécial - et le fait généralement. Notez également que le WHERE EXISTS(SELECT * FROM SomeTable WHERE ...)traitement des cas spéciaux est donné.
Jonathan Leffler

3
@Michael Mrozek, en fait c'est l'inverse de la question. Je demande si cela a toujours été nocif, et non s'il n'a jamais été nocif.
Theodore R. Smith

1
@Bytecode Ninja: spécifiquement, MySQL avec le moteur MyISAM a une optimisation pour COUNT (*): mysqlperformanceblog.com/2007/04/10/count-vs-countcol
Piskvor a quitté le bâtiment le

Réponses:


312

Il y a vraiment trois raisons principales:

  • Inefficacité dans la transmission des données au consommateur. Lorsque vous sélectionnez *, vous récupérez souvent plus de colonnes de la base de données que votre application n'en a vraiment besoin pour fonctionner. Cela entraîne le déplacement de plus de données du serveur de base de données vers le client, ce qui ralentit l'accès et augmente la charge sur vos machines, tout en prenant plus de temps pour parcourir le réseau. Cela est particulièrement vrai lorsque quelqu'un ajoute de nouvelles colonnes à des tables sous-jacentes qui n'existaient pas et n'étaient pas nécessaires lorsque les consommateurs d'origine codaient leur accès aux données.

  • Problèmes d'indexation. Envisagez un scénario dans lequel vous souhaitez régler une requête à un niveau élevé de performances. Si vous deviez utiliser * et qu'il renvoyait plus de colonnes que vous n'en aviez réellement besoin, le serveur devrait souvent exécuter des méthodes plus coûteuses pour récupérer vos données qu'il ne le ferait autrement. Par exemple, vous ne seriez pas en mesure de créer un index qui couvrirait simplement les colonnes de votre liste SELECT, et même si vous l'avez fait (y compris toutes les colonnes [ frisson ]), le gars suivant qui est venu et a ajouté une colonne au sous-jacent La table entraînerait l'optimiseur à ignorer votre index de couverture optimisé, et vous constateriez probablement que les performances de votre requête chuteraient considérablement sans raison apparente.

  • Problèmes contraignants. Lorsque vous sélectionnez *, il est possible de récupérer deux colonnes du même nom à partir de deux tables différentes. Cela peut souvent planter votre consommateur de données. Imaginez une requête qui joint deux tables, qui contiennent toutes deux une colonne appelée "ID". Comment un consommateur saurait-il lequel était lequel? SELECT * peut également confondre les vues (au moins dans certaines versions de SQL Server) lorsque les structures de table sous-jacentes changent - la vue n'est pas reconstruite et les données qui reviennent peuvent être absurdes . Et le pire, c'est que vous pouvez prendre soin de nommer vos colonnes comme vous le souhaitez, mais le prochain gars qui viendra pourrait n'avoir aucun moyen de savoir qu'il doit s'inquiéter d'ajouter une colonne qui entrera en collision avec votre déjà développé des noms.

Mais tout n'est pas mauvais pour SELECT *. Je l'utilise généreusement pour ces cas d'utilisation:

  • Requêtes ad hoc. Lorsque vous essayez de déboguer quelque chose, en particulier sur une table étroite avec laquelle je ne suis peut-être pas familier, SELECT * est souvent mon meilleur ami. Cela m'aide à voir ce qui se passe sans avoir à faire de nombreuses recherches sur les noms des colonnes sous-jacentes. Cela devient un «plus» plus grand plus les noms de colonne sont longs.

  • Lorsque * signifie "une rangée". Dans les cas d'utilisation suivants, SELECT * est très bien, et les rumeurs selon lesquelles c'est un tueur de performances ne sont que des légendes urbaines qui pouvaient avoir une certaine validité il y a de nombreuses années, mais pas maintenant:

    SELECT COUNT(*) FROM table;

    dans ce cas, * signifie "compter les lignes". Si vous deviez utiliser un nom de colonne au lieu de *, il compterait les lignes où la valeur de cette colonne n'était pas nulle . COUNT (*), pour moi, fait vraiment ressortir le concept selon lequel vous comptez les lignes , et vous évitez les cas de bord étranges causés par les NULLs éliminés de vos agrégats.

    Il en va de même avec ce type de requête:

    SELECT a.ID FROM TableA a
    WHERE EXISTS (
        SELECT *
        FROM TableB b
        WHERE b.ID = a.B_ID);
    

    dans toute base de données digne de ce nom, * signifie simplement "une ligne". Peu importe ce que vous mettez dans la sous-requête. Certaines personnes utilisent l'ID de b dans la liste SELECT, ou elles utiliseront le numéro 1, mais l'OMI ces conventions sont à peu près absurdes. Ce que vous voulez dire, c'est "compter la rangée", et c'est ce que * signifie. La plupart des optimiseurs de requêtes sont suffisamment intelligents pour le savoir. (Bien que pour être honnête, je sais seulement que cela est vrai avec SQL Server et Oracle.)


17
L'utilisation de "SELECT id, name" est aussi probable que "SELECT *" pour sélectionner deux colonnes du même nom dans deux tables différentes lors de l'utilisation de jointures. Le préfixe avec le nom de la table résout le problème dans les deux cas.
Michał Tatarynowicz

1
Je sais que c'est plus ancien, mais c'est ce qui a été tiré pendant la recherche sur Google, donc je demande. "Quand * signifie" une rangée ". Dans les cas d'utilisation suivants, SELECT * est très bien, et les rumeurs selon lesquelles c'est un tueur de performance ne sont que des légendes urbaines ..." avez-vous des références ici? Cette déclaration est-elle due au fait que le matériel est plus puissant (si tel est le cas, cela ne signifie pas qu'il n'est pas inefficace simplement que vous êtes moins susceptible de le remarquer). Je n'essaye pas de deviner en soi, je me demande simplement d'où vient cette déclaration.
Jared

6
En ce qui concerne les références, vous pouvez examiner les plans de requête - ils sont identiques dans les cas où vous avez un "*" dans la sous-requête par rapport à lorsque vous sélectionnez une colonne. Ils sont identiques car l'optimiseur basé sur les coûts "reconnaît" que sémantiquement, vous parlez de n'importe quelle ligne qui satisfait aux critères - ce n'est pas une question de matériel ou de vitesse.
Dave Markle

4
Un autre avantage de l'utilisation *est que dans certaines situations, il peut mieux tirer parti des systèmes de cache de MySQL. Si vous utilisez un grand nombre de semblables selectrequêtes qui demandent différents noms de colonnes ( select A where X, select B where X, ...) en utilisant un select * where Xpermettra au cache de traiter un plus grand nombre de requêtes qui peut entraîner une augmentation substantielle des performances. Il s'agit d'un scénario spécifique à l'application, mais il convient de le garder à l'esprit.
Ben D

2
Plus de 8 ans plus tard, mais je veux ajouter un point sur l'ambiguïté qui n'a pas été mentionné. Travailler avec plus de 200 tables dans une base de données et avoir un mélange de conventions de dénomination. Lors de l'examen du code qui interagit avec les résultats de la requête, SELECT *force les développeurs à examiner le ou les schémas de table impliqués, afin de déterminer les colonnes affectées / disponibles, comme dans un foreachou serialize. La tâche de regarder à plusieurs reprises les schémas pour suivre ce qui se passe augmentera inévitablement le temps total impliqué à la fois dans le débogage et le développement de code associé.
fyrye

91

Le caractère astérisque, "*", dans l'instruction SELECT est un raccourci pour toutes les colonnes des tables impliquées dans la requête.

Performance

La *sténographie peut être plus lente car:

  • Tous les champs ne sont pas indexés, forçant une analyse complète de la table - moins efficace
  • Ce que vous enregistrez pour envoyer SELECT *par câble risque une analyse complète du tableau
  • Renvoyer plus de données que nécessaire
  • Le renvoi de colonnes de fin à l'aide d'un type de données de longueur variable peut entraîner une surcharge de recherche

Entretien

Lors de l'utilisation SELECT *:

  • Une personne qui ne connaît pas la base de code serait obligée de consulter la documentation pour savoir quelles colonnes sont renvoyées avant de pouvoir apporter des modifications compétentes. Rendre le code plus lisible, minimiser l'ambiguïté et le travail nécessaire pour les personnes qui ne le connaissent pas permet d'économiser plus de temps et d'efforts à long terme.
  • Si le code dépend de l'ordre des colonnes, SELECT *masquera une erreur en attente de se produire si l'ordre d'une colonne a été modifié.
  • Même si vous avez besoin de chaque colonne au moment où la requête est écrite, cela pourrait ne pas être le cas à l'avenir
  • l'utilisation complique le profilage

Conception

SELECT *est un anti-motif :

  • Le but de la requête est moins évident; les colonnes utilisées par l'application sont opaques
  • Il enfreint la règle de modularité sur l'utilisation d'un typage strict dans la mesure du possible. Explicite est presque universellement meilleur.

Quand faut-il utiliser "SELECT *"?

Il est acceptable de l'utiliser SELECT *lorsqu'il existe un besoin explicite pour chaque colonne des tables impliquées, par opposition à chaque colonne qui existait lorsque la requête a été écrite. La base de données étendra en interne le * dans la liste complète des colonnes - il n'y a pas de différence de performances.

Sinon, répertoriez explicitement toutes les colonnes à utiliser dans la requête, de préférence lorsque vous utilisez un alias de table.


20

Même si vous souhaitez sélectionner chaque colonne maintenant, vous ne voudrez peut-être pas sélectionner chaque colonne après que quelqu'un a ajouté une ou plusieurs nouvelles colonnes. Si vous écrivez la requête avec SELECT *vous, vous risquez à un moment donné que quelqu'un ajoute une colonne de texte, ce qui ralentit l'exécution de votre requête, même si vous n'avez pas réellement besoin de cette colonne.

Cela ne signifierait-il pas moins de code à modifier si vous ajoutiez une nouvelle colonne que vous vouliez?

Les chances sont que si vous voulez réellement utiliser la nouvelle colonne, vous devrez de toute façon apporter beaucoup d'autres modifications à votre code. Vous ne faites qu'économiser , new_column- juste quelques caractères de frappe.


21
Surtout si cette nouvelle colonne est un BLOB de trois mégaoctets
Matti Virkkunen

2
@Matti - Mais j'espère qu'ils y réfléchiront plus que "Hé, laisse tomber une énorme colonne BLOB sur cette table!" . (Oui un imbécile espère que je sais, mais un gars ne peut-il pas rêver?)
ChaosPandion

5
La performance est un aspect, mais il y a souvent aussi un aspect de correction: la forme du résultat projeté avec *peut changer de façon inattendue et cela peut faire des ravages dans l'application elle-même: les colonnes référencées par ordinal (par exemple sqldatareader.getstring (2)) récupèrent soudainement une colonne différente , tout INSERT ... SELECT *se cassera et ainsi de suite et ainsi de suite.
Remus Rusanu du

2
@chaos: mettre des blobs sur les tables ne va pas vraiment nuire à vos performances ... À moins que vous n'utilisiez SELECT * ... ;-)
Dave Markle

2
Vous ne devez pas vous soucier des performances jusqu'à ce qu'elles causent de vrais problèmes. Et il SELECT *ne s'agit pas non plus de sauvegarder quelques caractères. Il s'agit de gagner des heures de débogage car il est facile d'oublier de spécifier de nouvelles colonnes ajoutées.
Lewis

4

Si vous nommez les colonnes dans une instruction SELECT, elles seront renvoyées dans l'ordre spécifié et peuvent ainsi être référencées en toute sécurité par un index numérique. Si vous utilisez "SELECT *", vous pouvez finir par recevoir les colonnes dans une séquence arbitraire et ne pouvez donc utiliser les colonnes que par leur nom en toute sécurité. À moins que vous ne sachiez à l'avance ce que vous voudrez faire avec une nouvelle colonne ajoutée à la base de données, l'action correcte la plus probable est de l'ignorer. Si vous allez ignorer de nouvelles colonnes ajoutées à la base de données, il n'y a aucun avantage à les récupérer.


« peut donc être référencé par index numérique en toute sécurité » , mais qui serait assez stupide pour toujours essayer de faire référence à une colonne par index numérique au lieu du nom de ce !? C'est un anti-modèle bien pire que d'utiliser select * dans une vue.
MGOwen

@MGOwen: Utiliser select *puis utiliser les colonnes par index serait horrible, mais utiliser select X, Y, Zou select A,B,Cpuis passer le lecteur de données résultant au code qui s'attend à faire quelque chose avec les données des colonnes 0, 1 et 2 semble une manière parfaitement raisonnable de permettre au même code d'agir sur X, Y, Z ou A, B, C. Notez que les indices des colonnes dépendent de leur emplacement dans l'instruction SELECT, plutôt que de leur ordre dans la base de données.
supercat

3

Dans de nombreuses situations, SELECT * provoquera des erreurs au moment de l'exécution dans votre application, plutôt qu'au moment de la conception. Il masque la connaissance des changements de colonne ou des mauvaises références dans vos applications.


1
Alors, comment la dénomination des colonnes aide-t-elle? Dans SQL Server, les requêtes existantes, incorporées dans du code ou des SP, ne se plaindront pas tant qu'elles ne s'exécuteront pas, même si vous avez nommé les colonnes. Les nouveaux échoueront lorsque vous les testerez, mais il vous faudra beaucoup de temps pour rechercher les SP affectés par les changements de table. À quel genre de situations faites-vous référence qui seraient détectées au moment de la conception?
ChrisA

3

Si vous voulez vraiment chaque colonne, je n'ai pas vu de différence de performance entre select (*) et nommer les colonnes. Le pilote pour nommer les colonnes peut être simplement d'être explicite sur les colonnes que vous attendez de voir dans votre code.

Souvent, cependant, vous ne voulez pas que chaque colonne et la sélection (*) peuvent entraîner un travail inutile pour le serveur de base de données et des informations inutiles devant être transmises sur le réseau. Il est peu probable que cela cause un problème notable à moins que le système ne soit fortement utilisé ou que la connectivité réseau soit lente.


3

Considérez-le comme réduisant le couplage entre l'application et la base de données.

Pour résumer l'aspect «odeur de code»:
SELECT *crée une dépendance dynamique entre l'application et le schéma. Restreindre son utilisation est un moyen de rendre la dépendance plus définie, sinon une modification de la base de données risque davantage de faire planter votre application.


3

Si vous ajoutez des champs à la table, ils seront automatiquement inclus dans toutes vos requêtes où vous les utilisez select *. Cela peut sembler pratique, mais cela rendra votre application plus lente car vous récupérez plus de données que vous n'en avez besoin, et cela bloquera votre application à un moment donné.

Il y a une limite pour la quantité de données que vous pouvez récupérer dans chaque ligne d'un résultat. Si vous ajoutez des champs à vos tables afin qu'un résultat finisse par dépasser cette limite, vous obtenez un message d'erreur lorsque vous essayez d'exécuter la requête.

C'est le genre d'erreurs difficiles à trouver. Vous effectuez un changement à un endroit et il explose à un autre endroit qui n'utilise pas du tout les nouvelles données. Il peut même s'agir d'une requête moins fréquemment utilisée, de sorte qu'il faut un certain temps avant que quelqu'un l'utilise, ce qui rend encore plus difficile la connexion de l'erreur au changement.

Si vous spécifiez les champs que vous souhaitez dans le résultat, vous êtes à l'abri de ce type de dépassement de surcharge.



2

Référence tirée de cet article.

N'allez jamais avec "SELECT *",

Je n'ai trouvé qu'une seule raison d'utiliser "SELECT *"

Si vous avez des exigences particulières et créé un environnement dynamique lors de l'ajout ou de la suppression d'une colonne, gérer automatiquement par le code d'application. Dans ce cas particulier, vous n'avez pas besoin de modifier le code de l'application et de la base de données, ce qui affectera automatiquement l'environnement de production. Dans ce cas, vous pouvez utiliser «SELECT *».


1

En règle générale, vous devez adapter les résultats de votre SELECT * ...dans des structures de données de différents types. Sans spécifier dans quel ordre les résultats arrivent, il peut être difficile de tout aligner correctement (et les champs plus obscurs sont beaucoup plus faciles à manquer).

De cette façon, vous pouvez ajouter des champs à vos tables (même au milieu) pour diverses raisons sans casser le code d'accès sql dans toute l'application.


1

L'utilisation SELECT *lorsque vous n'avez besoin que de quelques colonnes signifie beaucoup plus de données transférées que vous n'en avez besoin. Cela ajoute du traitement sur la base de données et augmente la latence lors de la transmission des données au client. Ajoutez à cela qu'il utilisera plus de mémoire lors du chargement, dans certains cas beaucoup plus, comme les gros fichiers BLOB, c'est principalement une question d'efficacité.

En plus de cela, cependant, il est plus facile de voir en regardant la requête quelles colonnes sont chargées, sans avoir à rechercher ce qui est dans la table.

Oui, si vous ajoutez une colonne supplémentaire, ce serait plus rapide, mais dans la plupart des cas, vous voudrez / devrez modifier votre code à l'aide de la requête pour accepter les nouvelles colonnes de toute façon, et il est possible que vous obteniez celles que vous ne faites pas '' t vouloir / attendre peut causer des problèmes. Par exemple, si vous saisissez toutes les colonnes, puis comptez sur l'ordre dans une boucle pour attribuer des variables, puis en ajouter une, ou si les ordres des colonnes changent (vu que cela se produit lors de la restauration à partir d'une sauvegarde), cela peut tout jeter.

C'est également le même type de raisonnement pour lequel si vous faites un, INSERTvous devez toujours spécifier les colonnes.


1

Je ne pense pas qu'il puisse vraiment y avoir une règle générale pour cela. Dans de nombreux cas, j'ai évité SELECT *, mais j'ai également travaillé avec des cadres de données où SELECT * était très bénéfique.

Comme pour tout, il y a des avantages et des coûts. Je pense qu'une partie de l'équation avantages / coûts est simplement le contrôle que vous avez sur les infrastructures de données. Dans les cas où le SELECT * fonctionnait bien, les structures de données étaient étroitement contrôlées (il s'agissait de logiciels de vente au détail), il n'y avait donc pas beaucoup de risques que quelqu'un se faufile dans un immense champ BLOB dans une table.


1

La sélection avec le nom de colonne augmente la probabilité que le moteur de base de données puisse accéder aux données à partir des index plutôt que d'interroger les données de la table.

SELECT * expose votre système à des changements de performances et de fonctionnalités inattendus dans le cas où votre schéma de base de données change parce que vous allez ajouter de nouvelles colonnes à la table, même si votre code n'est pas prêt à utiliser ou à présenter ces nouvelles données.


1

Il y a aussi une raison plus pragmatique: l'argent. Lorsque vous utilisez une base de données cloud et que vous devez payer pour les données traitées, il n'y a aucune explication pour lire les données que vous rejeterez immédiatement.

Par exemple: BigQuery :

Tarification des requêtes

La tarification des requêtes fait référence au coût d'exécution de vos commandes SQL et des fonctions définies par l'utilisateur. BigQuery facture les requêtes en utilisant une métrique: le nombre d'octets traités.

et Contrôlez la projection - Évitez SELECT * :

Meilleure pratique: contrôler la projection - Recherchez uniquement les colonnes dont vous avez besoin.

La projection fait référence au nombre de colonnes lues par votre requête. La projection de colonnes en excès entraîne des E / S supplémentaires (gaspillées) et une matérialisation (écriture des résultats).

L'utilisation de SELECT * est le moyen le plus coûteux d'interroger des données. Lorsque vous utilisez SELECT *, BigQuery effectue une analyse complète de chaque colonne du tableau.


0

Comprenez vos besoins avant de concevoir le schéma (si possible).

En savoir plus sur les données, 1) l'indexation 2) le type de stockage utilisé, 3) le moteur ou les fonctionnalités du fournisseur; c'est-à-dire ... mise en cache, capacités en mémoire 4) types de données 5) taille de la table 6) fréquence de la requête 7) charges de travail associées si la ressource est partagée 8) Test

A) Les exigences varieront. Si le matériel ne peut pas prendre en charge la charge de travail attendue, vous devez réévaluer comment fournir les exigences dans la charge de travail. Concernant la colonne d'addition au tableau. Si la base de données prend en charge les vues, vous pouvez créer une vue indexée (?) Des données spécifiques avec les colonnes nommées spécifiques (vs sélectionner '*'). Passez régulièrement en revue vos données et votre schéma pour vous assurer de ne jamais tomber dans le syndrome "Garbage-in" -> "Garbage-out".

En supposant qu'il n'y a pas d'autre solution; vous pouvez prendre en compte les éléments suivants. Il existe toujours plusieurs solutions à un problème.

1) Indexation: La sélection * exécutera un scan de table. En fonction de divers facteurs, cela peut impliquer une recherche de disque et / ou un conflit avec d'autres requêtes. Si la table est polyvalente, assurez-vous que toutes les requêtes sont performantes et exécutez-les au-dessous de votre heure cible. S'il y a une grande quantité de données et que votre réseau ou autre ressource n'est pas réglé; vous devez en tenir compte. La base de données est un environnement partagé.

2) type de stockage. C'est-à-dire: si vous utilisez des SSD, un disque ou une mémoire. Les temps d'E / S et la charge sur le système / processeur varient.

3) Le DBA peut-il régler la base de données / tables pour des performances plus élevées? En supposant que pour quelque raison que ce soit, les équipes ont décidé que la sélection «*» est la meilleure solution au problème; la base de données ou la table peut-elle être chargée en mémoire. (Ou une autre méthode ... peut-être que la réponse a été conçue pour répondre avec un délai de 2 à 3 secondes? --- pendant qu'une publicité est diffusée pour gagner les revenus de l'entreprise ...)

4) Commencez par la ligne de base. Comprenez vos types de données et comment les résultats seront présentés. Types de données plus petits, le nombre de champs réduit la quantité de données renvoyées dans l'ensemble de résultats. Cela laisse des ressources disponibles pour d'autres besoins du système. Les ressources système ont généralement une limite; «toujours» travailler en dessous de ces limites pour assurer la stabilité et un comportement prévisible.

5) taille de la table / des données. sélectionner «*» est courant avec les petites tables. Ils tiennent généralement en mémoire et les temps de réponse sont rapides. Encore une fois ... revoyez vos besoins. Planifier le fluage des fonctionnalités; planifiez toujours les besoins actuels et futurs.

6) Fréquence des requêtes / requêtes. Soyez conscient des autres charges de travail sur le système. Si cette requête se déclenche toutes les secondes et que la table est minuscule. L'ensemble de résultats peut être conçu pour rester dans le cache / la mémoire. Cependant, si la requête est un processus par lots fréquent avec des gigaoctets / téraoctets de données ... il est préférable de consacrer des ressources supplémentaires pour éviter que d'autres charges de travail ne soient affectées.

7) Charges de travail connexes. Comprenez comment les ressources sont utilisées. Le réseau / système / base de données / table / application est-il dédié ou partagé? Quelles sont les parties prenantes? Est-ce pour la production, le développement ou l'AQ? Est-ce une "solution miracle" temporaire? Avez-vous testé le scénario? Vous serez surpris du nombre de problèmes pouvant exister sur le matériel actuel aujourd'hui. (Oui, les performances sont rapides ... mais la conception / les performances sont toujours dégradées.) Le système doit-il effectuer 10 000 requêtes par seconde contre 5 à 10 requêtes par seconde? Le serveur de base de données est-il dédié ou exécute-t-il d'autres applications de surveillance sur la ressource partagée? Certaines applications / langues; Les O / S consommeront 100% de la mémoire, provoquant divers symptômes / problèmes.

8) Test: testez vos théories et comprenez autant que possible. Votre problème de sélection «*» peut être un gros problème, ou il peut être quelque chose dont vous n'avez même pas besoin de vous inquiéter.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.