web-dev-qa-db-fra.com

Quel est un bon cas d'utilisation pour SELECT * dans le code de production?

Par habitude, je n'utilise jamais SELECT * dans le code de production (je l'utilise uniquement avec des requêtes de rebut ad-hoc, généralement lors de l'apprentissage du schéma d'un objet). Mais je suis tombé sur un cas maintenant où je suis tenté de l'utiliser, mais je me sentirais bon marché si je le faisais.

Mon cas d'utilisation est à l'intérieur d'une procédure stockée où une table temporaire locale est créée qui doit toujours correspondre à la table sous-jacente utilisée pour la créer, chaque fois que la procédure stockée s'exécute. La table temporaire est remplie beaucoup plus tard, donc un hack rapide pour créer la table temporaire sans être verbeux serait SELECT * INTO #TempTable FROM RealTable WHERE 1 = 0 spécialement pour une table avec des centaines de colonnes.

Si le consommateur de ma procédure stockée est indépendant des jeux de résultats dynamiques, puis-je rencontrer des problèmes avec la vente de mes services à SELECT *?

33
J.D.

Je déteste généralement SELECT * dans le code de production, et j'ai été dans une situation où son utilisation a conduit à des quantités massives de retouches plus tard. Votre cas semble cependant être une utilisation équitable.

L'endroit où je trouve SELECT * comme un must - et son cousin diabolique "INSERT INTO tbl" sans liste de colonnes - est dans une situation d'archivage, où les lignes sont déplacées vers une autre table qui doit avoir la même structure.

INSERT INTO SalesOrderArchive  -- Note no column list
SELECT *
  FROM SalesOrder
 WHERE OrderDate < @OneYearAgo

DELETE FROM SalesOrder
 WHERE OrderDate < @OneYearAgo

Si une nouvelle colonne est ajoutée à SalesOrder à l'avenir, mais pas à SalesOrderArchive, l'INSERT échouera. Ce qui sonne mal, mais c'est en fait une très bonne chose! Parce que l'alternative est bien pire. Si toutes les colonnes étaient répertoriées sur l'INSERT et le SELECT, l'INSERT réussirait, ainsi que la suppression suivante (qui est en fait "DELETE *"). Le code de production qui réussit n'attire aucune attention, et il peut s'écouler longtemps avant que quelqu'un remarque que la nouvelle colonne n'est pas archivée, mais supprimée silencieusement.

48
Rusty

À quoi sert select * en production?

OMI seulement des choses comme ça:

par exemple

create table #foo(a int, b int, c int, d int)
...
select * from #foo

ou

with q as
(
  select a, b, c
  from ...
)
select *
from q

c'est-à-dire lorsque le * est lié à une liste de colonnes explicite déclarée dans le même lot et est utilisée uniquement pour éviter de répéter plusieurs fois la liste de colonnes.

Quand le * fait référence à une table ou à une vue, il existe quelques complexités désagréables concernant les métadonnées mises en cache, par exemple ici . Et cela ne vous évite pas vraiment de taper, car SSMS vous permettra de glisser-déposer la liste complète des colonnes.

34

Une utilisation valide de SELECT * se produit lorsqu'il est introduit par EXISTS.

WHERE EXISTS (SELECT * FROM ...

Comme toutes les utilisations de SELECT *, cela a pour effet secondaire indésirable (et inutile ici) d'empêcher WITH SCHEMABINDING d'être ajouté si le code est à l'intérieur, par exemple une fonction.

Questions et réponses connexes: EXISTS (SELECT 1 ...) vs EXISTS (SELECT * ...) L'un ou l'autre?

Voir également:

20
user195591

Je vais être en gras et le dire: il y a des cas d'utilisation pour SELECT * dans le code de production. Chaque fois que vous pouvez dire "Je veux que toutes les modifications apportées à la table soient immédiatement reflétées dans une modification de mon résultat sortie "est un cas pour le faire.

Permettez-moi de vous donner quelques exemples:

Cas d'utilisation n ° 1 - Créez une vue qui reflète la table principale, sauf qu'elle filtre les données sensibles réservées à l'entreprise.

Dans ce cas, vous souhaitez que la vue soit SELECT *. La sortie est supposée être un reflet de la table principale. Avoir la disposition de sortie correspondant à la disposition du tableau est une fonctionnalité, pas un bug.

...

Cas d'utilisation # 2 - Créez un proc stocké qui copie certains enregistrements que vous êtes sur le point de mettre à jour dans une table d'attente temporaire, au cas où le processus rencontrerait des problèmes (c'est un peu floconneux).

Dans ce cas, avoir une sortie qui ne correspond pas à la table elle-même est terrible - cela signifie que vous ne pourrez peut-être pas utiliser les données de la table d'attente temporaire pour revenir à votre point de départ.

7
Kevin

À quoi sert select * en production?

Vraiment, il n'y en a pas. Dans le développement de la manière que vous avez mentionnée (sélectionnez top n *), cela a du sens, mais sinon, développe de mauvaises habitudes et pourrait entraîner des problèmes.

Vous avez abordé le point principal sur la raison pour laquelle vous ne devriez pas l'utiliser, et c'est la structure de la table réelle qui pourrait changer. Créer une table temporaire comme vous l'avez mentionné est très bien, mais où cela pourrait vous arriver plus tard dans votre proc, après avoir rempli la table temporaire avec quelques commandes INSERT, vous retournez le jeu de résultats avec SELECT *. Maintenant, votre application finale récupérera les colonnes auxquelles elle n'est pas attendue.

Vous pouvez utiliser SELECT ... INTO #TEMP FROM ... pour créer également la table temporaire à la volée. Il est difficile de savoir ce qui convient le mieux à votre situation.

La plupart des autres problèmes avec SELECT * ne semble pas avoir d'importance pour votre cas d'utilisation

  • Création d'une vue (peut se casser lorsque la table change)
  • Problèmes contraignants
  • Analyse de table (puisque vous voulez en fait toutes les colonnes)
  • Récupérer les colonnes inutiles (puisque vous semblez toutes les vouloir à nouveau) et leur position ordinale
6
scsimon

Ma réponse à votre question est "Cas d'utilisation la plupart/nombreux"

Vos cas d'utilisation soulignent pourquoi * n'est pas horrible. Votre schéma va changer. Pourquoi vous configurer pour toucher chaque single SP parce que vous devez ajouter un champ?

Si vous ajoutez un champ, pourquoi mon interface utilisateur s'en soucierait-elle? Si vous supprimez un champ, peu importe si vous utilisez * ou un CSV, vous introduisez un changement de rupture.

"Si le consommateur de ma procédure stockée est indépendant des ensembles de résultats dynamiques, ..."

Ici se trouve le vrai problème avec l'utilisation de select *. Si votre consommateur est mal écrit, vous pourriez rencontrer des problèmes. Par exemple, si vous mettez en forme des champs [13] comme date ...

Certains cas où je voudrais éviter de sélectionner *:

Atténuation de la latence - Mon consommateur est distant et il n'a besoin que d'un sous-ensemble relativement petit de champs dans les tables.

jointures - Requête complexe impliquant plusieurs tables. Le consommateur n'a probablement pas besoin de tous les champs de toutes les tables. En fait, il existe des colonnes de clés en double qui devraient probablement être omises.

Je sais que mes consommateurs écrivent un mauvais code - Si vous développez une API publique, je suppose que c'est possible que vous souhaitiez honorer OpenClose et créer une nouvelle méthode api à chaque fois que l'ensemble de champs change. Pour moi, cela semble être une mauvaise approche de ce problème ...

Comme pour tout ce qui est programmé, il y a un temps et un lieu. Il y a toujours la tour d'ivoire. Écoutez leurs pensées avec un esprit ouvert. Décidez-vous ensuite.

1
greg

cela dépend du mécanisme de défaillance que vous préférez.

Si, lorsqu'une nouvelle colonne est ajoutée, elle peut toujours être ignorée en toute sécurité (par exemple: sélectionnez le prénom, le nom des clients où id = @ x), puis n'utilisez jamais select * - plus vous touchez de colonnes, moins il y a de chance qu'un index utile peut être trouvé/créé.

Si lorsqu'une nouvelle colonne est ajoutée, sa suppression silencieuse serait mauvaise (tout type de réplication de données), puis utilisez une sélection non spécifiée (insérez-la dans archiveTable select * à partir de la table où isOld = true) L'échec du code ici n'est pas un bogue, il est une fonctionnalité - en particulier dans le cas où vous contrôlez à la fois la base de données et le code lisant la table.

Inverser les mécanismes d'échec ici est mauvais - vous ne voulez pas que votre recherche de nom échoue si vous ajoutez une nouvelle couleur préférée à la personne.

vous ne voulez pas ignorer l'archivage de la couleur préférée de la personne si la colonne a été ajoutée après avoir écrit le code d'archivage.

Une note sur les jointures - le serveur SQL sautera la lecture des colonnes non utilisées plus tard

select firtname,email from (select * from profile where isArchived=false)p join (select * from emailaddresses where mostRecentSend < ago(6days))e on p.id=e.profileId

ne touche rien d'autre que les colonnes prénom, identifiant et e-mail.

0
Andrew Hill