web-dev-qa-db-fra.com

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

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

Je comprends que SELECT COUNT(*) est un problème de performances sur certaines bases de données, mais qu’en est-il si vous vouliez vraiment toutes les colonnes?

214
Theodore R. Smith

Il y a vraiment trois raisons principales:

  • Inefficacité dans le transfert de données vers le consommateur. Lorsque vous sélectionnez SELECT *, vous récupérez souvent plus de colonnes de la base de données que votre application n’a réellement besoin de fonctionner. Cela entraîne le transfert d'un plus grand nombre de données du serveur de base de données au client, ce qui ralentit l'accès et augmente la charge sur vos ordinateurs, ainsi que de prendre plus de temps pour voyager sur le réseau. Cela est particulièrement vrai lorsque quelqu'un ajoute aux tables sous-jacentes de nouvelles colonnes qui n'existaient pas et qui n'étaient pas nécessaires lorsque les consommateurs d'origine codaient leurs accès aux données.

  • Problèmes d'indexation Envisagez un scénario dans lequel vous souhaitez ajuster une requête à un niveau de performance élevé. Si vous utilisiez * et renvoyiez plus de colonnes que nécessaire, le serveur devra souvent appliquer des méthodes plus coûteuses pour extraire vos données que ce n’aurait été autrement. Par exemple, vous ne pourriez pas créer d’index couvrant simplement les colonnes de votre liste SELECT et même si vous le faisiez (y compris toutes les colonnes [ frissonnent ]), le Le type suivant qui ajoute une colonne à la table sous-jacente oblige l'optimiseur à ignorer votre index de couverture optimisé et vous constaterez probablement que les performances de votre requête chuteront considérablement sans raison apparente.

  • Problèmes de reliure. Lorsque vous sélectionnez *, il est possible d'extraire deux colonnes du même nom de deux tables différentes. Cela peut souvent bloquer votre consommateur de données. Imaginez une requête qui joint deux tables, toutes deux contenant une colonne appelée "ID". Comment un consommateur peut-il savoir lequel est lequel? SELECT * peut également confondre les vues (du 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 renvoyées peuvent être absurdes . Et le pire, c’est que vous pouvez prendre soin de nommer vos colonnes comme vous le souhaitez, mais le prochain type qui arrive risque de ne pas savoir s’il doit s’inquiéter de l’ajout d’une colonne qui entrera en collision avec votre version déjà développée. 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 que je ne connais peut-être pas, SELECT * est souvent mon meilleur ami. Cela m'aide simplement à voir ce qui se passe sans avoir à faire beaucoup de recherches sur les noms des colonnes sous-jacentes. Cela devient un "plus" plus gros, plus les noms de colonnes sont longs.

  • Quand * signifie "une ligne". Dans les cas d'utilisation suivants, SELECT * suffit, et les rumeurs voulant que ce soit un tueur à la performance ne sont que des légendes urbaines peut avoir eu une certaine validité il y a plusieurs années, mais ne le faites pas maintenant:

    SELECT COUNT(*) FROM table;
    

    dans ce cas, * signifie "compter les lignes". Si vous utilisiez un nom de colonne au lieu de *, , les lignes pour lesquelles la valeur de cette colonne n'était pas nulle seraient comptées. COUNT (*), pour moi, indique vraiment que vous comptez des lignes , et vous évitez ainsi que d'étranges cas Edge ne soient générés par l'élimination de NULL dans 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 de sélection, ou bien le numéro 1, mais ces conventions ne sont pas insensées. 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 assez intelligents pour le savoir. (Bien que pour être honnête, je ne connaisse que avec SQL Server et Oracle.)

290
Dave Markle

Le caractère astérisque "*" de l'instruction SELECT est un raccourci pour toutes les colonnes de la ou des table (s) impliquées dans la requête. 

Performance

Le raccourci * peut être plus lent pour les raisons suivantes:

  • Tous les champs ne sont pas indexés, ce qui impose une analyse complète de la table - moins efficace
  • Ce que vous enregistrez pour envoyer SELECT * sur le réseau risque de donner une analyse complète de la table
  • Renvoyer plus de données que nécessaire
  • Le renvoi de colonnes de fin utilisant un type de données de longueur variable peut entraîner un temps système supplémentaire pour la recherche.

Entretien

Si vous utilisez SELECT *:

  • Quelqu'un qui ne connaît pas bien la base de code serait obligé 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 non familiarisées avec le code, économise 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 d'une table a été modifié.
  • Même si vous avez besoin de chaque colonne au moment de l'écriture de la requête, cela pourrait ne plus être le cas à l'avenir
  • l'utilisation complique le profilage

Conception

SELECT * est unanti-pattern:

  • 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é relative à l'utilisation d'un typage strict chaque fois que cela est possible. Explicit est presque universellement meilleur. 

Quand faut-il utiliser "SELECT *"?

Il est acceptable d'utiliser SELECT * lorsqu'il existe un besoin explicite pour chaque colonne de la ou des tables impliquées, par opposition à chaque colonne qui existait au moment de l'écriture de la requête. La base de données développera 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 en utilisant un alias de table.

84
OMG Ponies

Même si vous vouliez sélectionner chaque colonne maintenant, vous ne voudriez peut-être pas sélectionner chaque colonne après que quelqu'un ait ajouté une ou plusieurs nouvelles colonnes. Si vous écrivez la requête avec SELECT *, vous prenez le risque qu'à un moment donné, quelqu'un puisse ajouter une colonne de texte qui ralentisse 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 la nouvelle colonne que vous vouliez?

Il est probable que si vous voulez réellement utiliser la nouvelle colonne, vous devrez de toute façon apporter d’autres modifications à votre code. Vous ne sauvegardez que , new_column - seulement quelques caractères de frappe.

18
Mark Byers

Si vous nommez les colonnes dans une instruction SELECT, elles seront renvoyées dans l'ordre spécifié et pourront donc être référencées en toute sécurité par un index numérique. Si vous utilisez "SELECT *", vous risquez de recevoir les colonnes dans un ordre arbitraire et vous ne pourrez donc utiliser les colonnes en toute sécurité que par nom. Si vous ne savez pas à l'avance ce que vous voudrez faire avec toute nouvelle colonne ajoutée à la base de données, l'action correcte la plus probable consiste à l'ignorer. Si vous ignorez les nouvelles colonnes ajoutées à la base de données, leur extraction ne présente aucun avantage.

4
supercat

Voyez cela comme une réduction du 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. Limiter son utilisation est un moyen de mieux définir la dépendance, sinon une modification de la base de données risque davantage de provoquer le blocage de votre application. 

3
Kelly S. French

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

3
Andrew Lewis

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 simplement être explicite sur les colonnes que vous vous attendez à voir dans votre code.

Cependant, il arrive souvent que vous ne vouliez pas que toutes les colonnes et que select (*) entraîne un travail inutile pour le serveur de base de données et vous oblige à transmettre des informations inutiles sur le réseau. Il est peu probable qu'un problème perceptible apparaisse à moins que le système soit fortement utilisé ou que la connectivité du réseau soit lente.

3
brabster

Si vous ajoutez des champs à la table, ils seront automatiquement inclus dans toutes vos requêtes pour lesquelles vous utilisez select *. Cela peut sembler pratique, mais cela ralentira votre application à mesure que vous récupérez plus de données que nécessaire, et votre application plantera à un moment donné.

Il y a une limite au nombre de données que vous pouvez récupérer dans chaque ligne d'un résultat. Si vous ajoutez des champs à vos tables de sorte qu'un résultat dépasse la 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 les nouvelles données du tout. 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 de votre choix dans le résultat, vous évitez ce type de surcharge.

3
Guffa

Généralement, vous devez adapter les résultats de votre SELECT * ... à des structures de données de différents types. Sans spécifier l'ordre dans lequel les résultats arrivent, il peut être difficile de tout aligner correctement (et il est beaucoup plus facile de rater des champs plus obscurs).

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

1
jkerian

Utiliser SELECT * lorsque vous n'avez besoin que de quelques colonnes signifie que beaucoup plus de données transférées que nécessaire. Cela ajoute un traitement à 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 lorsqu'il sera chargé, dans certains cas beaucoup plus, comme les gros fichiers BLOB, il s'agit principalement d'efficacité.

En plus de cela, cependant, il est plus facile de voir quelles sont les colonnes en cours de chargement dans la requête, sans avoir à rechercher le contenu de la table.

Oui, si vous ajoutez une colonne supplémentaire, ce sera plus rapide, mais dans la plupart des cas, vous voudrez/devez changer votre code en utilisant la requête pour accepter les nouvelles colonnes de toute façon, et il est possible que vous n'en obteniez pas. Vouloir/attendre peut causer des problèmes. Par exemple, si vous saisissez toutes les colonnes, puis vous vous fiez à l'ordre dans une boucle pour affecter des variables, puis en ajoutez une, ou si les ordres des colonnes changent (si cela se produit lors de la restauration à partir d'une sauvegarde), tout peut être perdu.

C’est aussi le même type de raisonnement. Par conséquent, si vous faites une INSERT, vous devez toujours spécifier les colonnes.

1
Tarka

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

Comme pour toutes choses, il y a des avantages et des coûts. Je pense qu'une partie de l'équation avantages/coûts est à quel point vous avez le contrôle 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 grand risque que quelqu'un introduise un énorme champ BLOB dans une table. 

1
JMarsch

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

Ne jamais aller avec "SELECT *",

J'ai trouvé une seule raison d'utiliser "SELECT *"

Si vous avez des exigences particulières et un environnement dynamique créé lors de l'ajout ou de la suppression d'une colonne, gérez-le automatiquement par code d'application. Dans ce cas particulier, il n’est pas nécessaire 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
Anvesh

RAISONS POUR LESQUELLES NE PAS UTILISER SELECT * FROM TABLE: -

  1. I/O inutile

  2. Augmenter le trafic réseau

  3. Vues fragiles

  4. Conflit dans les requêtes de jointure

  5. Plus de mémoire d'application

  6. Risqué lors de la copie de données

  7. Dépend de l'ordre des colonnes

Toujours utiliser les noms de colonnes vous aidera toujours dans une base de données à grande échelle.

0
shaurya uppal

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

En savoir plus sur les données, 1) indexation 2) type de stockage utilisé, 3) moteur ou fonctionnalités du fournisseur; c'est-à-dire ... la mise en cache, les capacités en mémoire 4) types de données 5) taille de la table 6) fréquence d'interrogation 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 la manière de fournir les exigences de la charge de travail. En ce qui concerne la colonne addition à la table. 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 (par opposition à sélectionner '*'). Examinez périodiquement 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 ait pas d'autre solution; vous pouvez prendre en compte les éléments suivants. Il y a toujours plusieurs solutions à un problème.

1) Indexation: La sélection * exécutera une analyse de table. En fonction de divers facteurs, cela peut impliquer une recherche de disque et/ou des conflits avec d'autres requêtes. Si la table est multi-usages, assurez-vous que toutes les requêtes sont performantes et exécutez en-dessous de vos délais cibles. 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 un disque SSD, un disque ou de la mémoire. Les temps d'E/S et la charge sur le système/la CPU varieront.

3) L'administrateur de base de données peut-il optimiser la performance de la base de données/des tables? Pour une raison quelconque, les équipes ont décidé que le choix '*' était la meilleure solution au problème; le DB ou la table peuvent-ils être chargés en mémoire? (Ou une autre méthode ... peut-être que la réponse a été conçue pour réagir 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. Les types de données plus petits, le nombre de champs réduisent la quantité de données renvoyée dans le jeu 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électionnez '*' est commun avec les petites tables. Ils tiennent généralement dans la mémoire et les temps de réponse sont rapides. Encore une fois .... revoir vos besoins. Planifier le fluage des fonctionnalités; planifiez toujours pour les besoins actuels et futurs possibles. 

6) Fréquence de requête/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 petite. L'ensemble de résultats peut être conçu pour rester en cache/mémoire. Toutefois, si la requête est un processus de traitement par lots fréquent avec des gigaoctets/téraoctets de données ... vous feriez mieux de dédier des ressources supplémentaires pour vous assurer que les autres charges de travail ne sont pas concernées.

7) Charges de travail associées. Comprendre comment les ressources sont utilisées. Le réseau/système/base de données/table/application est-il dédié ou partagé? Qui sont les intervenants? Est-ce que c'est pour la production, le développement ou l'assurance qualité? Est-ce une "solution rapide" temporaire? Avez-vous testé le scénario? Vous serez surpris du nombre de problèmes pouvant exister sur le matériel actuel. (Oui, les performances sont rapides ... mais la conception/les performances sont toujours dégradées.) Le système doit-il exécuter des requêtes de 10 000 requêtes par seconde au lieu de 5 à 10 requêtes par seconde? Le serveur de base de données est-il dédié, ou d'autres applications, la surveillance s'exécute-t-elle sur la ressource partagée? Quelques applications/langues; Les systèmes d'exploitation consommeront 100% de la mémoire, provoquant divers symptômes/problèmes.

8) Test: Testez vos théories et comprenez-en le plus possible. Votre numéro '*' peut être un problème important ou peut-être même une chose dont vous n'avez même pas à vous soucier.

0
kllee

Il y a aussi une raison plus pragmatique: l'argent. Lorsque vous utilisez une base de données en nuage et que vous devez payer pour les données traitées, rien n’explique que vous lisiez immédiatement des données.

Par exemple: BigQuery :

Prix ​​de la requête

La tarification des requêtes fait référence au coût d'exécution des 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ôle de la projection - Évitez SELECT * :

Meilleure pratique: Contrôle de la projection - Ne recherchez que 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 entrées/sorties (gaspillage) et une matérialisation (résultats d’écriture) supplémentaires.

Utiliser 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
Lukasz Szozda

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

SELECT * expose votre système à des performances et à des modifications de fonctionnalité inattendues dans le cas où votre schéma de base de données change, car de nouvelles colonnes seront ajoutées à la table, même si votre code n'est pas prêt à utiliser ou à présenter ces nouvelles données.

0
Aradhana Mohanty