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

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

Je comprends que SELECT COUNT(*) est un problème de performance sur certaines bases de données, mais que se passe-t-il si vous voulez vraiment chaque colonne?

Il y a vraiment trois raisons principales:

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

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

  • Problèmes de liaison. 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 faire planter votre consommateur de données. Imaginez une requête qui joint deux tables, chacune contenant une colonne appelée “ID”. Comment un consommateur pourrait-il savoir qui était qui? 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 insensées . Et le pire, c’est que vous pouvez prendre soin de nommer vos colonnes comme vous voulez, mais le prochain qui arrive peut ne pas savoir qu’il doit se soucier d’append 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. En essayant de déboguer quelque chose, surtout 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 un tas de recherches sur les noms des colonnes sous-jacentes. Cela devient un plus “plus” plus les noms de colonnes sont longs.

  • Quand * signifie “une ligne”. Dans les cas d’utilisation suivants, SELECT * est très bien, et les rumeurs selon lesquelles il s’agirait d’un tueur de performance ne sont que des légendes urbaines qui ont peut-être eu une certaine validité il ya plusieurs années, mais ne le font 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 (*), à mon avis, fait réellement apparaître le concept que vous comptez les lignes , et vous évitez les effets de bord étranges provoqués par l’élimination des valeurs NULL de vos agrégats.

    Même chose 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 firebase database vaut son sel, * signifie simplement “une ligne”. Peu importe ce que vous mettez dans la sous-requête. Certaines personnes utilisent l’identifiant de b dans la liste SELECT, ou utilisent le numéro 1, mais ces conventions ne sont pas sensées. Ce que vous voulez dire, c’est “compter la ligne”, 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 le sais que pour SQL Server et Oracle.)

Le caractère astérisque “*” de l’instruction SELECT est un raccourci pour toutes les colonnes de la ou 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 parsing complète de la table – moins efficace
  • Ce que vous enregistrez pour envoyer SELECT * au fil du fil risque un scan complet de la table
  • Renvoyer plus de données que nécessaire
  • Le renvoi des colonnes de fin à l’aide du type de données de longueur variable peut entraîner une surcharge de la recherche

Entretien

Lorsque vous utilisez SELECT * :

  • Quelqu’un qui ne connaît pas 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, en minimisant l’ambiguïté et le travail nécessaire aux personnes peu familiarisées avec le code, permet de gagner du temps et des efforts à long terme.
  • Si le code dépend de l’ordre des colonnes, SELECT * masquera une erreur en attente si une table a été modifiée.
  • Même si vous avez besoin de chaque colonne au moment de l’écriture de la requête, cela pourrait ne pas être le cas à l’avenir
  • l’utilisation complique le profilage

Conception

SELECT * est un anti-pattern :

  • Le but de la requête est moins évident. les colonnes utilisées par l’application sont opaques
  • Il rompt la règle de modularité concernant l’utilisation d’un typage ssortingct dans la mesure du possible. Explicit est presque universellement meilleur.

Quand “SELECT *” doit-il être utilisé?

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 lors de l’écriture de la requête. La firebase database étendra en interne le * dans la liste complète des colonnes – il n’y a pas de différence de performance.

Sinon, listez explicitement chaque colonne à utiliser dans la requête – de préférence en utilisant un alias de table.

Même si vous souhaitez sélectionner chaque colonne maintenant, vous ne souhaiterez peut-être pas sélectionner chaque colonne après avoir ajouté une ou plusieurs nouvelles colonnes. Si vous écrivez la requête avec SELECT * vous prenez le risque qu’à un moment donné, quelqu’un ajoute une colonne de texte pour que votre requête s’exécute plus lentement, 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?

Les chances sont que si vous voulez réellement utiliser la nouvelle colonne, vous devrez apporter d’autres modifications à votre code. Vous ne faites que sauvegarder , new_column – juste quelques caractères de saisie.

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 index numérique. Si vous utilisez “SELECT *”, vous risquez de recevoir les colonnes dans une séquence arbitraire et ne pouvez donc utiliser les colonnes qu’en toute sécurité par leur nom. À moins que vous ne sachiez à l’avance ce que vous voulez faire avec toute nouvelle colonne ajoutée à la firebase database, l’action correcte la plus probable est de l’ignorer. Si vous ignorez les nouvelles colonnes ajoutées à la firebase database, il n’y a aucun avantage à les récupérer.

Dans de nombreuses situations, SELECT * provoquera des erreurs lors de l’exécution dans votre application, plutôt qu’au moment du design. Il cache la connaissance des modifications de colonne ou des références erronées dans vos applications.

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

Bien souvent, vous ne voulez pas que chaque colonne et la sélection (*) peuvent entraîner un travail inutile pour le serveur de firebase database et des informations inutiles doivent être transmises sur le réseau. Il est peu probable que le problème soit perceptible à moins que le système ne soit trop utilisé ou que la connectivité du réseau soit lente.

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

La quantité de données que vous pouvez récupérer dans chaque ligne d’un résultat est limitée. 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 en un seul endroit, et ça explose ailleurs que dans les nouvelles données. Cela peut même être une requête moins fréquemment utilisée, de sorte que cela prend 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 voulez dans le résultat, vous êtes à l’abri de ce type de dépassement de capacité.

Considérez-le comme réduisant le couplage entre l’application et la firebase database.

Pour résumer l’aspect “odeur de code”:
SELECT * crée une dépendance dynamic entre l’application et le schéma. Restreindre son utilisation est un moyen de mieux définir la dépendance, sinon une modification de la firebase database est plus susceptible de faire planter votre application.

Généralement, vous devez adapter les résultats de votre SELECT * ... aux 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 les champs plus obscurs sont beaucoup plus faciles à rater).

De cette façon, vous pouvez append des champs à vos tables (même au milieu d’eux) pour diverses raisons sans casser le code d’access SQL sur toute l’application.

L’utilisation de SELECT * lorsque vous n’avez besoin que de deux colonnes signifie plus de données transférées que nécessaire. Cela ajoute un traitement à la firebase database et augmente la latence à l’obtention des données sur le client. Ajoutez à cela le fait qu’il utilisera plus de mémoire lorsqu’il sera chargé, dans certains cas, beaucoup plus, comme les fichiers BLOB volumineux, il s’agira surtout d’efficacité.

En plus de cela, il est plus facile de voir, en regardant la requête, quelles colonnes sont en cours de chargement, sans devoir chercher ce qu’il y a dans la table.

Oui, si vous ajoutez une colonne supplémentaire, cela sera plus rapide, mais dans la plupart des cas, vous voudrez / devrez changer votre code en utilisant la requête pour accepter les nouvelles colonnes de toute façon, et il est possible que vous en obteniez. Je veux / attendre peut causer des problèmes. Par exemple, si vous récupérez toutes les colonnes, utilisez l’ordre dans une boucle pour atsortingbuer des variables, puis en append une, ou si les commandes de colonne changent (ce qui se produit lors d’une restauration à partir d’une sauvegarde).

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

Je ne pense pas qu’il puisse vraiment y avoir une règle générale à cet égard. Dans de nombreux cas, j’ai évité SELECT *, mais j’ai également travaillé avec des frameworks 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 le niveau de 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 (c’était un logiciel de vente au détail), donc il n’y avait pas beaucoup de risque que quelqu’un lance un énorme champ BLOB dans une table.

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

Ne jamais aller avec “SELECT *”,

Je n’ai trouvé qu’une seule raison d’utiliser “SELECT *”

Si vous avez des exigences particulières et créé un environnement dynamic lorsque vous ajoutez ou supprimez une colonne, vous gérez automatiquement le code de l’application. Dans ce cas particulier, vous n’avez pas besoin de modifier le code de l’application et de la firebase database, ce qui affecte automatiquement l’environnement de production. Dans ce cas, vous pouvez utiliser “SELECT *”.

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; à savoir … 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 varient. 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. En ce qui concerne la colonne d’addition au tableau. Si la firebase database 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 (en sélectionnant «*»). Révisez périodiquement vos données et votre schéma pour vous assurer que vous ne rencontrez jamais 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: Le sélecteur * exécutera un balayage de tables. 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écutées en deçà des heures ciblées. 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 firebase database est un environnement partagé.

2) type de stockage. Ie: si vous utilisez des SSD, un disque ou de la mémoire. Les temps d’E / S et la charge sur le système / processeur varient.

3) L’administrateur de firebase database peut-il ajuster la firebase database / les tables pour obtenir de meilleures performances? En supposant que pour une raison quelconque, les équipes ont décidé que le choix «*» était la meilleure solution au problème; le DB ou la table peut-il être chargé 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é joue pour gagner les revenus de l’entreprise …)

4) Commencez à la ligne de base. Comprenez vos types de données et comment les résultats seront présentés. Des types de données plus petits, un nombre de champs réduit 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 sont généralement limitées; «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 correspondent généralement à la mémoire et les temps de réponse sont rapides. Encore une fois … examinez vos besoins. Planifier le fluage des fonctionnalités planifier toujours pour les besoins actuels et futurs possibles.

6) fréquence des requêtes / requêtes. Soyez conscient des autres charges de travail sur le système. Si cette requête est déclenchée toutes les secondes et que la table est minuscule. Le jeu de résultats peut être conçu pour restr dans le cache / la mémoire. Toutefois, si la requête est un traitement par lots fréquent avec des giga-octets / téraoctets de données … vous pouvez mieux dédier des ressources supplémentaires pour vous assurer que les autres charges de travail ne sont pas affectées.

7) charges de travail connexes. Comprendre comment les ressources sont utilisées. Le réseau / système / firebase database / table / application est-il dédié ou partagé? Qui sont les parties prenantes? Est-ce 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 de voir combien de problèmes peuvent 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 10K par seconde et 5-10 requêtes par seconde? Si le serveur de firebase database est dédié ou exécute d’autres applications, la surveillance s’exécute sur la ressource partagée. Quelques applications / langages; Les S / O consumnt 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 select ‘*’ peut être un gros problème, ou il peut s’agir de quelque chose dont vous n’avez même pas besoin de vous soucier.

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

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

Il y a aussi une raison plus pragmatique: l’argent. Lorsque vous utilisez une firebase database cloud et que vous devez payer pour des données traitées, il n’y a aucune explication à lire les données que vous écarterez immédiatement.

Par exemple: BigQuery :

Prix ​​de la requête

Le prix de la requête fait référence au coût de l’exécution de vos commandes SQL et des fonctions définies par l’utilisateur. BigQuery charge les requêtes en utilisant une mésortingque: le nombre d’octets traités.

et projection de contrôle – Evitez SELECT * :

Meilleure pratique: Projection de contrôle – Interrogez uniquement les colonnes dont vous avez besoin.

Projection fait référence au nombre de colonnes lues par votre requête. La projection de colonnes excédentaires entraîne des E / S et une matérialisation supplémentaires (gaspillage) (é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 parsing complète de chaque colonne du tableau.