Hier, je discutais avec un programmeur "hobby" (je suis moi-même un programmeur professionnel). Nous avons découvert une partie de son travail et il dit qu'il interroge toujours toutes les colonnes de sa base de données (même sur le code / serveur de production).
J'ai essayé de le convaincre de ne pas le faire, mais je n'ai pas encore eu autant de succès. À mon avis, un programmeur ne devrait interroger que ce qui est réellement nécessaire pour des raisons de "joliesse", d'efficacité et de trafic. Est-ce que je me trompe avec ma vue?
Réponses:
Réfléchissez à ce que vous récupérez et à la manière dont vous les liez aux variables de votre code.
Pensez maintenant à ce qui se passe lorsque quelqu'un met à jour le schéma de la table pour ajouter (ou supprimer) une colonne, même si vous ne l'utilisez pas directement.
L'utilisation de select * lorsque vous saisissez des requêtes à la main convient, pas lorsque vous écrivez des requêtes pour du code.
la source
Changements de schéma
foo
, et qu'une autre table de la requête ajoute une colonnefoo
, la façon dont cela est traité peut entraîner des problèmes lors de la tentative d'obtention de la colonne de droitefoo
.Dans les deux cas, une modification de schéma peut entraîner des problèmes lors de l'extraction des données.
Déterminez ensuite si une colonne en cours d'utilisation est supprimée de la table. Le
select * from ...
fonctionne toujours mais des erreurs quand essayant de tirer les données sur l'ensemble des résultats. Si la colonne est spécifiée dans la requête, la requête sera erreur en donnant au lieu une indiciation claire quant à quoi et où est le problème.Surcharge de données
Certaines colonnes peuvent avoir une quantité importante de données associées. Sélectionner en arrière
*
va extraire toutes les données. Oui, voici quevarchar(4096)
c'est sur 1000 lignes que vous avez sélectionnées en vous donnant une possibilité supplémentaire de 4 mégaoctets de données dont vous n'avez pas besoin, mais qui sont quand même envoyées sur le réseau.En ce qui concerne le changement de schéma, varchar peut ne pas exister lors de la création initiale de la table, mais maintenant, il existe.
Défaut de transmettre l'intention
Lorsque vous sélectionnez retour
*
et obtenez 20 colonnes mais n'en avez besoin que de 2, vous ne communiquez pas l'intention du code. En regardant la requête qui fait,select *
on ne sait pas quelles sont les parties importantes de celle-ci. Puis-je modifier la requête pour qu'elle utilise plutôt cet autre plan afin de l'accélérer en n'incluant pas ces colonnes? Je ne sais pas parce que l'intention de ce que la requête retourne n'est pas claire.Regardons quelques violons SQL qui explorent un peu plus ces changements de schéma .
Tout d’abord, la base de données initiale: http://sqlfiddle.com/#!2/a67dd/1
DDL:
SQL:
Et de retour les colonnes que vous obtenez sont
oneid=1
,data=42
,twoid=2
etother=43
.Maintenant, que se passe-t-il si j'ajoute une colonne à la première table? http://sqlfiddle.com/#!2/cd0b0/1
Et comme avant mes résultats de la même requête sont
oneid=1
,data=42
,twoid=2
etother=foo
.Un changement dans l'une des tables perturbe les valeurs de a
select *
et soudainement votre liaison de 'other' à un int va lancer une erreur et vous ne savez pas pourquoi.Si au lieu de cela votre instruction SQL était
Le changement de table 1 n'aurait pas perturbé vos données. Cette requête s'exécute de la même manière avant et après le changement.
Indexage
Lorsque vous faites un,
select * from
vous tirez toutes les lignes de toutes les tables qui correspondent aux conditions. Même les tables qui vous intéressent vraiment. Cela signifie que plus de données sont transférées, mais un autre problème de performances se cache plus loin dans la pile.Index. (lié à SO: Comment utiliser index dans une instruction select? )
Si vous extrayez un grand nombre de colonnes, l'optimiseur de plan de base de données peut ne pas utiliser un index, car vous devrez quand même extraire toutes ces colonnes. Cela prendrait plus de temps d'utiliser l'index, puis toutes les colonnes de la requête. que ce serait juste de faire un scan complet de la table.
Si vous êtes sélectionnez le, par exemple, le nom d'un utilisateur (que vous faire beaucoup et ont donc un indice sur elle), la base de données peut faire un balayage d' index unique ( index postgres wiki uniquement scan , mysql analyse complète tableau vs complet scan d'index , Index seule analyse: Éviter le tableau d' accès ).
Il y a pas mal d'optimisations pour ne lire que des index, si possible. Les informations peuvent être extraites plus rapidement sur chaque page d’index, car vous en tirez moins aussi - vous n’ajoutez pas toutes les autres colonnes pour le
select *
. Il est possible qu'une analyse d'index seulement renvoie des résultats de l'ordre de 100 fois plus rapidement (source: Select * is bad ).Cela ne veut pas dire qu'une analyse d'index complète est excellente, c'est toujours une analyse complète - mais c'est mieux qu'une analyse de table complète. Une fois que vous commencez à rechercher toutes les conséquences
select *
négatives sur les performances, vous continuez à en trouver de nouvelles.Lecture connexe
la source
select *
?Autre préoccupation: s'il s'agit d'une
JOIN
requête et que vous récupérez les résultats dans un tableau associatif (comme cela pourrait être le cas en PHP), cela risque de provoquer des bogues.Le truc c'est que
foo
a des colonnesid
etname
bar
a des colonnesid
etaddress
,SELECT * FROM foo JOIN bar ON foo.id = bar.id
devinez ce qui se passe quand quelqu'un ajoute une colonne
name
à labar
table.Le code cessera soudainement de fonctionner correctement, car maintenant la
name
colonne apparaît deux fois dans les résultats et si vous stockez les résultats dans un tableau, les données de secondname
(bar.name
) écraseront le premiername
(foo.name
)!C'est un bug assez méchant parce que c'est très peu évident. Cela peut prendre un certain temps, et il est impossible que la personne qui ajoute une autre colonne à la table ait pu anticiper un tel effet secondaire indésirable.
(Histoire vraie).
Donc, n'utilisez pas
*
, contrôlez les colonnes que vous récupérez et utilisez des alias, le cas échéant.la source
SELECT
clause. Nous espérons que le nom n'est pas unique. BTW Je ne pense pas que ce soit si rare dans les systèmes avec des bases de données volumineuses. Comme je l'ai dit, une fois, j'ai passé une ou deux heures à la recherche de ce bogue dans un gros fichier de code PHP. Et j'ai trouvé un autre cas tout à l'heure: stackoverflow.com/q/17715049/168719Interroger chaque colonne peut être parfaitement légitime, dans de nombreux cas.
Toujours interroger chaque colonne n'est pas.
Cela demande plus de travail à votre moteur de base de données, qui doit fouiller et scruter ses métadonnées internes pour déterminer les colonnes à traiter avant de pouvoir réellement obtenir les données et les vous renvoyer. Bien sûr, ce n’est pas la plus grosse surcharge au monde, mais les catalogues système peuvent être un goulot d’étranglement appréciable.
Cela représente plus de travail pour votre réseau, car vous retirez un grand nombre de champs alors que vous ne voulez peut-être qu’un ou deux d’entre eux. Si quelqu'un [d'autre] va et ajoute quelques dizaines de champs supplémentaires, qui contiennent tous de gros morceaux de texte, votre débit passe soudainement au sol - sans raison apparente. Cela est aggravé si votre clause "where" n'est pas particulièrement bonne et que vous retirez également un grand nombre de lignes, ce qui risque de générer beaucoup de données qui traversent le réseau (c'est-à-dire que cela va être lent).
Cela demande plus de travail à votre application, car elle doit extraire et stocker toutes ces données supplémentaires dont elle ne s’occupe probablement pas.
Vous courez le risque que les colonnes changent leur ordre. OK, vous ne devriez pas avoir à vous soucier de cela (et vous ne le ferez pas si vous ne sélectionnez que les colonnes dont vous avez besoin), mais si vous les récupérez toutes en même temps et que quelqu'un d'autre décide de réorganiser l'ordre des colonnes dans le tableau. , cette exportation CSV soigneusement conçue que vous donnez aux comptes dans le couloir va tout à coup à la poterie - encore une fois, sans raison apparente.
Au fait, j'ai déjà dit "quelqu'un d'autre" plusieurs fois, ci-dessus. Rappelez-vous que les bases de données sont intrinsèquement multi-utilisateurs; vous ne pouvez pas avoir le contrôle sur eux que vous pensez avoir.
la source
TOP
limitation; Je ne sais pas à quel point c'est important si le code en lit autant qu'il veut en afficher et ensuite se débarrasser de la requête. Je pense que les réponses aux requêtes sont traitées un peu paresseusement, bien que je ne connaisse pas les détails. En tout cas, je pense qu'au lieu de dire "n'est pas légitime", il serait préférable de dire "... est légitime dans beaucoup moins"; En gros, je résumerais les cas légitimes comme ceux où l'utilisateur aurait une meilleure idée de ce qui est significatif que le programmeur.La réponse courte est: cela dépend de la base de données utilisée. Les bases de données relationnelles sont optimisées pour extraire les données dont vous avez besoin de manière rapide, fiable et atomique . Sur des jeux de données volumineux et des requêtes complexes, il est beaucoup plus rapide et probablement plus sûr que SELECTing * et effectue l'équivalent de jointures du côté "code". Les magasins de valeurs clés ne disposent peut-être pas de telles fonctionnalités ou ne sont pas suffisamment matures pour être utilisés en production.
Cela dit, vous pouvez toujours renseigner la structure de données que vous utilisez avec SELECT * et utiliser le reste dans le code, mais vous rencontrerez des goulots d'étranglement si vous souhaitez évoluer.
La comparaison la plus proche est le tri des données: vous pouvez utiliser quicksort ou bubblesort et le résultat sera correct. Mais ne sera pas optimisé, et aura certainement des problèmes lorsque vous introduisez la concurrence et que vous devez trier de manière atomique.
Bien sûr, il est moins coûteux d’ajouter de la RAM et des processeurs que d’investir dans un programmeur capable de faire des requêtes SQL et ayant même une compréhension vague de ce qu’est un JOIN.
la source
Customer customer = this._db.Customers.Where( “it.ID = @ID”, new ObjectParameter( “ID”, id ) ).First();
Voir «Le temps qu'il faut pour prendre une infraction» à la page 2.var cmd = db.CreateCommand(); cmd.CommandText = "SELECT TOP 1 * FROM Customers WHERE ID = @ID"; cmd.Parameters.AddWithValue("@ID", id); var result = cmd.ExecuteReader();
.... et vous pouvez ensuite créer un client à partir de chaque ligne. LINQ bat le pantalon de ça.var customer = _db.Customers.Where(it => it.id == id).First();
.OMI, c'est à propos d'être explicite vs implicite. Lorsque j'écris du code, je veux que cela fonctionne parce que je l'ai fait fonctionner, et pas seulement parce que toutes les pièces se trouvent juste là. Si vous interrogez tous les enregistrements et que votre code fonctionne, vous aurez tendance à aller de l'avant. Plus tard, si quelque chose change et que votre code ne fonctionne plus, c’est une douleur royale de déboguer de nombreuses requêtes et fonctions à la recherche d’une valeur qui devrait être présente et les seules références référencées sont *.
Également dans une approche à plusieurs niveaux, il est toujours préférable d'isoler les perturbations de schéma de base de données au niveau des données. Si votre couche de données passe * dans la logique métier et très probablement dans la couche de présentation, vous développez votre portée de débogage de manière exponentielle.
la source
select *
est bien pire!parce que si la table reçoit de nouvelles colonnes, vous obtenez toutes celles-ci même lorsque vous n'en avez pas besoin. avec
varchars
cela peut devenir beaucoup de données supplémentaires qui doivent voyager à partir de la DBCertaines optimisations de base de données peuvent également extraire les enregistrements de longueur non fixe dans un fichier séparé pour accélérer l’accès aux parties de longueur fixe.
la source
À part les frais généraux, ce que vous voulez éviter en premier lieu, je dirais qu'en tant que programmeur, vous ne dépendez pas de l'ordre des colonnes défini par l'administrateur de la base de données. Vous sélectionnez chaque colonne même si vous en avez besoin à tous.
la source
Je ne vois aucune raison pour laquelle vous ne devriez pas utiliser le but pour lequel il est construit - récupérer toutes les colonnes d'une base de données. Je vois trois cas:
Une colonne est ajoutée à la base de données et vous la souhaitez également dans le code. a) Avec * échouera avec un message approprié. b) Sans * fonctionnera, mais ne fera pas ce que vous attendez, ce qui est très mauvais.
Une colonne est ajoutée à la base de données et vous ne la voulez pas dans le code. a) avec * échouera; cela signifie que * ne s'applique plus car sa sémantique signifie "tout récupérer". b) Sans * fonctionnera.
Une colonne est supprimée Le code échouera de toute façon.
Le cas le plus courant est le cas 1 (puisque vous avez utilisé *, ce qui signifie tout ce que vous voulez probablement tout); sans * vous pouvez avoir un code qui fonctionne bien mais ne fait pas ce qui est attendu - beaucoup, bien pire - ce code qui échoue avec un message d'erreur approprié .
Je ne prends pas en considération le code qui extrait les données de colonne en fonction de leur index, ce qui est sujet aux erreurs, à mon avis. C'est beaucoup plus logique de le récupérer en fonction du nom de la colonne.
la source
Select *
était plus destiné à faciliter les requêtes ad-hoc et non à développer des applications. Ou pour une utilisation dans des constructions statistiques tellesselect count(*)
que celles qui permettent au moteur de requête de décider si un index doit être utilisé, quel index utiliser, etc., et vous ne renvoyez aucune donnée de colonne réelle. Ou pour une utilisation dans des clauses telles quewhere exists( select * from other_table where ... )
, qui est à nouveau une invitation au moteur de requête à choisir seul le chemin le plus efficace et la sous-requête est uniquement utilisée pour contraindre les résultats de la requête principale. Etc.select *
a la sémantique de récupérer toutes les colonnes; si votre application en a vraiment besoin, je ne vois aucune raison de ne pas l'utiliser. Pouvez-vous indiquer une référence (Oracle, IBM, Microsoft, etc.) qui indique que le but pour lequel laselect *
construction a été construite n'est pas de récupérer toutes les colonnes?select *
existe pour récupérer toutes les colonnes ... comme une fonctionnalité pratique, pour les requêtes ad-hoc, pas parce que c'est une excellente idée dans un logiciel de production. Les raisons sont déjà assez bien couvertes dans les réponses de cette page, c’est pourquoi je n’ai pas créé ma propre réponse détaillée: •) Problèmes de performances, regroupement répétitif de données sur le réseau que vous n’utilisez jamais, •) problèmes liés au repliement de colonnes, •) Echecs d'optimisation du plan de requête (échec dans certains cas d'utilisation des index), •) E / S serveur inefficaces dans les cas où la sélection limitée aurait pu utiliser uniquement des index, etc.select *
dans une application de production réelle, mais la nature d’un cas marginal est qu’il ne s’agit pas d’ un cas courant . :-)select *
; ce que je disais si vous avez vraiment besoin de toutes les colonnes, je ne vois pas pourquoi vous ne devriez pas utiliserselect *
; bien que peu de scénarios doivent exister où toutes les colonnes sont nécessaires.Pensez-y de cette façon ... si vous interrogez toutes les colonnes d'une table qui ne contient que quelques chaînes ou champs numériques, cela représente un total de 100 000 données. Mauvaise pratique, mais ça va marcher. Ajoutez maintenant un seul champ contenant, par exemple, une image ou un document Word de 10 Mo. maintenant, votre requête performante commence immédiatement et mystérieusement à mal fonctionner, simplement parce qu'un champ a été ajouté à la table ... vous n'avez peut-être pas besoin de cet énorme élément de données, mais parce que vous l'avez fait,
Select * from Table
vous l'obtenez quand même.la source