Quels sont les anti-modèles SQL les plus courants? [fermé]


232

Tous ceux qui travaillent avec des bases de données relationnelles ont appris (ou apprennent) que SQL est différent. Obtenir les résultats souhaités, et le faire efficacement, implique un processus fastidieux en partie caractérisé par l'apprentissage de paradigmes inconnus et la découverte que certains de nos modèles de programmation les plus familiers ne fonctionnent pas ici. Quels sont les antipatterns courants que vous avez vus (ou que vous avez commis)?


Il s'agit d'une question qui n'est pas conforme aux normes plus récentes concernant le type de question approprié pour le débordement de pile. Quand on lui a demandé, cela n'était peut-être pas vrai.
David Manheim

@casperOne n'y a-t-il pas une clause d '"importance historique" qui pourrait transformer cette question en acceptabilité?
Amy B

26
Je trouve triste que l'une des questions les plus utiles sur le site wohole soit fermée car non constructive.
HLGEM

11
@HLGEM Je suis totalement d'accord. Cette question est un parfait exemple de tout ce qui ne va pas avec StackExchange
Kevin Morse

1
Le sujet est absolument important et pertinent. Mais la question est trop ouverte, c'est pourquoi les réponses décrivent chacune un bugbear anti-pattern personnel d'un ingénieur individuel.
Shane

Réponses:


156

Je suis constamment déçu par la tendance de la plupart des programmeurs à mélanger leur logique d'interface utilisateur dans la couche d'accès aux données:

SELECT
    FirstName + ' ' + LastName as "Full Name",
    case UserRole
        when 2 then "Admin"
        when 1 then "Moderator"
        else "User"
    end as "User's Role",
    case SignedIn
        when 0 then "Logged in"
        else "Logged out"
    end as "User signed in?",
    Convert(varchar(100), LastSignOn, 101) as "Last Sign On",
    DateDiff('d', LastSignOn, getDate()) as "Days since last sign on",
    AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' +
        City + ', ' + State + ' ' + Zip as "Address",
    'XXX-XX-' + Substring(
        Convert(varchar(9), SSN), 6, 4) as "Social Security #"
FROM Users

Normalement, les programmeurs le font car ils ont l'intention de lier leur ensemble de données directement à une grille, et c'est tout simplement pratique d'avoir le format SQL Server côté serveur que le format sur le client.

Les requêtes comme celle illustrée ci-dessus sont extrêmement fragiles car elles couplent étroitement la couche de données à la couche d'interface utilisateur. En plus de cela, ce style de programmation empêche complètement les procédures stockées d'être réutilisables.


10
Un bon modèle affiche-enfant pour un couplage maximal sur le plus grand nombre possible de niveaux / couches d'abstraction.
dkretz

3
Cela peut ne pas être bon pour le découplage, mais pour des raisons de performances, j'ai souvent fait des choses comme ça, les modifications itératives effectuées par SQL Server sont plus rapides que celles effectuées par le code à mi-niveau. Je ne reçois pas de point de réutilisabilité - rien ne vous empêche d'exécuter le SP et de renommer les cols si vous le souhaitez.
Joe Pineda

54
Mon préféré est lorsque les gens intègrent du HTML et du javascript, par exemple SELECT '<a href=... onclick="">' + nom '</a>'
Matt Rogish

15
Avec des requêtes comme celle-ci, vous pouvez modifier la grille dans un site Web avec une simple instruction alter. Vous pouvez également modifier le contenu d'une exportation ou reformater une date dans un rapport. Cela rend les clients heureux et me fait gagner du temps. Alors merci, mais non merci, je vais m'en tenir à des requêtes comme celle-ci.
Andomar

4
@Matt Rogish - Jésus, quelqu'un fait ça?
Axarydax

118

Voici mon top 3.

Numéro 1. Échec de la spécification d'une liste de champs. (Modifier: pour éviter toute confusion: il s'agit d'une règle de code de production. Elle ne s'applique pas aux scripts d'analyse uniques - sauf si je suis l'auteur.)

SELECT *
Insert Into blah SELECT *

devrait être

SELECT fieldlist
Insert Into blah (fieldlist) SELECT fieldlist

Numéro 2. En utilisant un curseur et une boucle while, quand une boucle while avec une variable de boucle fera l'affaire.

DECLARE @LoopVar int

SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable)
WHILE @LoopVar is not null
BEGIN
  -- Do Stuff with current value of @LoopVar
  ...
  --Ok, done, now get the next value
  SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable
    WHERE @LoopVar < TheKey)
END

Numéro 3. DateLogic à travers les types de chaînes.

--Trim the time
Convert(Convert(theDate, varchar(10), 121), datetime)

Devrait être

--Trim the time
DateAdd(dd, DateDiff(dd, 0, theDate), 0)

J'ai récemment vu un pic de "Une requête vaut mieux que deux, d'accord?"

SELECT *
FROM blah
WHERE (blah.Name = @name OR @name is null)
  AND (blah.Purpose = @Purpose OR @Purpose is null)

Cette requête nécessite deux ou trois plans d'exécution différents selon les valeurs des paramètres. Un seul plan d'exécution est généré et bloqué dans le cache pour ce texte SQL. Ce plan sera utilisé quelle que soit la valeur des paramètres. Il en résulte des performances médiocres intermittentes. Il est préférable d'écrire deux requêtes (une requête par plan d'exécution prévu).


7
hmmm, je vais vous donner un +1 pour les points 2 et 3 seuls, mais les développeurs surpassent la règle 1. Elle a parfois sa place.
annakata

1
Quel est le raisonnement derrière # 1?
jalf

29
Lorsque vous utilisez select *, vous obtenez tout ce qui est dans le tableau. Ces colonnes peuvent changer de nom et d'ordre. Le code client repose fréquemment sur les noms et l'ordre. Tous les 6 mois, on me demande comment conserver l'ordre des colonnes lors de la modification d'une table. Si la règle était respectée, cela n'aurait pas d'importance.
Amy B

J'ai utilisé # 2 parfois, d'autres j'ai suivi la route du curseur (bien que j'enregistre d'abord les résultats de la requête sur une table var, ouvrez le curseur dessus). Je me suis toujours demandé si quelqu'un avait fait un test de performance des deux.
Joe Pineda

4
... mais bien sûr, les curseurs devraient presque toujours être un dernier recours, après avoir échoué à comprendre comment faire le travail avec SQL basé sur un ensemble. Une fois, j'ai passé environ 45 minutes à disséquer soigneusement un horrible et gigantesque curseur PL / SQL dans une procédure stockée (a dessiné des diagrammes de la chose pourrie), qui a rempli une grande table temporaire, puis a sélectionné le contenu de la table temporaire à l'appelant pour rendre un rapport. L'exécution a duré 8,5 minutes, sur un matériel substantiel. Après avoir schématisé le tout, j'ai pu le remplacer par une seule requête qui a renvoyé les mêmes résultats en moins de 2 secondes. Curseurs, mec ...
Craig

71
  • Champs de mot de passe lisibles par l'homme , egad. Explicite.

  • Utiliser LIKE contre des colonnes indexées , et je suis presque tenté de dire LIKE en général.

  • Recyclage des valeurs PK générées par SQL.

  • Surprise, personne n'a encore mentionné la table divine. Rien ne dit "organique" comme 100 colonnes de drapeaux de bits, de grandes chaînes et des entiers.

  • Ensuite, il y a le modèle «Les fichiers .ini me manquent» : stockage de fichiers CSV, de chaînes délimitées par des tuyaux ou d'autres données d'analyse requises dans de grands champs de texte.

  • Et pour MS SQL Server, l'utilisation de curseurs du tout . Il existe une meilleure façon d'effectuer une tâche de curseur donnée.

Modifié parce qu'il y en a tellement!


19
tort sur les curseurs, j'hésiterais à dire que faire quelque chose en particulier est 100% correct ou 100% faux
Shawn

4
Jusqu'à présent, chaque exemple de défense de curseur que j'ai vu utilise le mauvais outil pour le travail. Mais si vous ne connaissez que SQL, vous l'utilisez de manière inappropriée ou vous apprenez à écrire d'autres types de logiciels.
dkretz

3
@tuinstoel: Comment LIKE '% blah%' peut-il utiliser un index? L'indexation repose sur l'ordre et cet exemple recherche une position médiane aléatoire d'une chaîne. (Les index sont classés par le 1er caractère 1er, et donc regarder les 4 caractères du milieu donne un ordre pratiquement aléatoire ...)
MatBailie

12
Sur la plupart des serveurs de bases de données (au moins ceux que j'ai utilisés), LIKE peut utiliser des index .. tant qu'il s'agit d'une recherche de préfixe (LIKE 'xxx%') - c'est-à-dire tant que les caractères génériques ne le font pas. venir en premier dans la chaîne de recherche. Je pense que vous pourriez parler un peu à contre-courant ici.
Cowan

10
C'est comme si vous n'aimiez pas LIKE '%LIKE'.
Johan

62

Pas besoin de creuser profondément pour cela: ne pas utiliser de déclarations préparées.


3
Ouaip. Suivi de près dans le même contexte, selon mon expérience, avec "ne pas piéger les erreurs".
dkretz

1
@stesch: Ce n'est rien comparé à l'utilisation de vues et à une date de rapport variable. Les vues sont un contre-modèle si vous avez une date de rapport variable (je suppose que la plupart des applications en ont). J'ajouterais cela dans une réponse distincte, mais c'est fermé, malheureusement.
Stefan Steiger

56

Utilisation d'alias de table sans signification:

from employee t1,
department t2,
job t3,
...

Rend la lecture d'une grande instruction SQL beaucoup plus difficile qu'elle ne devrait l'être


49
alias? diable j'ai vu de vrais noms de colonnes comme ça
annakata

10
les alias laconiques sont OKAY. Si vous voulez un nom significatif, n'utilisez pas du tout d'alias.
Joel Coehoorn

43
Il n'a pas dit «laconique», il a dit «vide de sens». Dans mon livre, il n'y aurait rien de mal à utiliser e, d et j comme alias dans l'exemple de requête.
Robert Rossney

11
Absolument, Robert - e, d et j me conviendrait parfaitement.
Tony Andrews

8
J'utiliserais emp pour employé, dep pour département et job pour job (ou peut-être jb) :)
Andrei Rînea

53
var query = "select COUNT(*) from Users where UserName = '" 
            + tbUser.Text 
            + "' and Password = '" 
            + tbPassword.Text +"'";
  1. Confiance aveugle à l'utilisateur
  2. Ne pas utiliser de requêtes paramétrées
  3. Mots de passe en texte clair

Tout cela peut être utilement traité en utilisant une couche de base de données abstraite d'une sorte (de n'importe quel) type.
dkretz

@doofledorfer: D'accord, un niveau intermédiaire serait certainement mieux dans un cas comme celui-ci, en plus de fournir la mise en cache des résultats comme un bel effet secondaire.
Joe Pineda

Exemple génial. Si un développeur cherche à remplacer cela par une bonne solution, il est à mi-chemin de devenir un développeur SQL décent.
Steve McLeod

46

Mes bugbears sont les 450 tables d'accès de colonne qui ont été rassemblées par le fils de 8 ans du meilleur toiletteur pour chiens du directeur général et la table de recherche douteuse qui n'existe que parce que quelqu'un ne sait pas normaliser correctement une infrastructure de données.

En règle générale, cette table de recherche ressemble à ceci:

ID INT,
Nom NVARCHAR (132),
IntValue1 INT,
IntValue2 INT,
CharValue1 NVARCHAR (255),
CharValue2 NVARCHAR (255),
Date1 DATETIME,
Date2 DATETIME

J'ai perdu le compte du nombre de clients que j'ai vus qui ont des systèmes qui reposent sur des abominations comme celle-ci.


1
Pire encore, j'ai lu que dans la dernière version d'Access qui est réellement prise en charge automatiquement, ce qui, je le crains, encouragera davantage ce fétichisme de la colonne Value1, Value2, Value3 ...
Joe Pineda

Attendez - donc le fils de 8 ans est le fils du toiletteur?
barrypicker

28

Ceux que je déteste le plus sont

  1. Utilisation d'espaces lors de la création de tableaux, de sprocs, etc. J'ai rencontré ça) m'irrite vraiment.

  2. Données dénormalisées. Un tableau n'a pas besoin d'être parfaitement normalisé, mais lorsque je rencontre un tableau d'employés qui a des informations sur leur score d'évaluation actuel ou leur principal élément, il me dit que je devrai probablement créer un tableau séparé à un moment donné et essayez ensuite de les garder synchronisés. Je normaliserai d'abord les données, puis si je vois un endroit où la dénormalisation aide, je l'envisagerai.

  3. Surutilisation des vues ou des curseurs. Les vues ont un but, mais lorsque chaque table est enveloppée dans une vue, c'est trop. J'ai dû utiliser des curseurs à quelques reprises, mais en général, vous pouvez utiliser d'autres mécanismes pour cela.

  4. Accès. Un programme peut-il être un anti-modèle? Nous avons SQL Server à mon travail, mais un certain nombre de personnes utilisent l'accès en raison de sa disponibilité, de sa «facilité d'utilisation» et de sa «convivialité» pour les utilisateurs non techniques. Il y a trop de choses ici, mais si vous avez été dans un environnement similaire, vous savez.


2
# 4 - il y a un autre thread juste pour <a href=' stackoverflow.com/questions/327199/…> :).
dkretz

4
L'accès n'est PAS un SGBD. C'est un environnement RAD, avec un gestionnaire de base de données très simple inclus. SQL Server, Oracle et al. ne jamais le remplacer, à moins que vous ajoutez un VB comme langue et Crystal Reports comme l' installation.
Joe Pineda

26

utilisez SP comme préfixe du nom de la procédure de stockage car il recherchera d'abord dans l'emplacement des procédures système plutôt que dans les personnalisées.


1
Peut également être étendu à l'utilisation de tout autre préfixe commun pour toutes les procédures stockées, ce qui rend plus difficile la sélection d'une liste triée.
dkretz

7
+1 pour le commentaire doofledorfer !! J'ai beaucoup vu ça, je trouve ça idiot et ça rend la recherche d'un SP particulier très difficile !!! Également étendu à "vw_" pour les vues, "tbl_" pour les tables et autres, comme je les déteste!
Joe Pineda

1
Les préfixes peuvent être utiles si vous scriptez les objets dans des fichiers (par exemple: pour le contrôle de code source, les déploiements ou la migration)
Rick

1
Pourquoi diable serait-il utile de préfixer chaque procédure stockée avec sp ou usp? Cela rend juste plus difficile de parcourir la liste pour celle que vous voulez.
Ryan Lundy

25

Surutilisation des tables temporaires et des curseurs.


2
Une bonne preuve que "tout ce que je sais, ce sont des langages procéduraux".
dkretz

2
La surutilisation de quoi que ce soit est par définition indésirable. Un exemple spécifique où l'utilisation de tables / curseurs temporaires ne serait pas nécessaire serait utile.
Jace Rhea

6
La plupart du temps, je vois des tables temporaires sous-utilisées. avec SQL Server, vous obtenez souvent des gains de performances en faisant des choses avec un tas de tables temporaires au lieu d'une seule requête monolithique.
Cervo

24

Pour stocker les valeurs d'heure, seul le fuseau horaire UTC doit être utilisé. L'heure locale ne doit pas être utilisée.


3
Je n'ai toujours pas trouvé de bonne solution simple pour convertir de l'UTC en heure locale pour des dates dans le passé, lorsque l'heure d'été doit être prise en compte, avec des dates de changement variables selon les années et les pays, ainsi que toutes les exceptions au sein des pays. L'UTC ne vous évite donc pas la complexité de la conversion. Cependant, il est important d'avoir un moyen de connaître le fuseau horaire de chaque datetime stocké.
ckarras

1
@CsongorHalmai De nombreux endroits pratiquent l'heure d'été, de sorte que les valeurs horaires dans l'heure suivant le décalage peuvent être ambiguës.
Frank Schwieterman

C'est certainement vrai pour le présent et le passé, mais pour l'avenir, en particulier pour un avenir assez éloigné, des fuseaux horaires explicites sont souvent une nécessité. Si vous avez une option de 30 ans qui vient d'être écrite et expire en 2049-09-27T17: 00: 00, heure de New York, vous ne pouvez pas simplement supposer aveuglément que ce sera 21: 00: 00Z. Le Congrès américain pourrait bien changer les règles de l'heure d'été. Vous devez garder l'heure locale et le vrai fuseau horaire (America / New_York) séparés.
John Cowan

23

en utilisant @@ IDENTITY au lieu de SCOPE_IDENTITY ()

Cité de cette réponse :

  • @@ IDENTITY renvoie la dernière valeur d'identité générée pour n'importe quelle table de la session en cours, sur toutes les étendues. Vous devez être prudent ici, car il s'agit de plusieurs étendues. Vous pouvez obtenir une valeur à partir d'un déclencheur, au lieu de votre instruction actuelle.
  • SCOPE_IDENTITY renvoie la dernière valeur d'identité générée pour n'importe quelle table de la session en cours et la portée en cours. Généralement ce que vous souhaitez utiliser.
  • IDENT_CURRENT renvoie la dernière valeur d'identité générée pour une table spécifique dans une session et une étendue. Cela vous permet de spécifier de quelle table vous voulez que la valeur provienne, au cas où les deux ci-dessus ne seraient pas tout à fait ce dont vous avez besoin (très rare). Vous pouvez l'utiliser si vous souhaitez obtenir la valeur IDENTITY actuelle pour une table dans laquelle vous n'avez pas inséré d'enregistrement.

+1 très vrai, pourrait provoquer des bugs qui seraient difficiles à éliminer
Axarydax

23

Réutiliser un champ «mort» pour quelque chose auquel il n'était pas destiné (par exemple, stocker des données utilisateur dans un champ «Fax») - très tentant cependant comme solution rapide!


21
select some_column, ...
from some_table
group by some_column

et en supposant que le résultat sera trié par some_column. J'ai vu cela un peu avec Sybase où l'hypothèse est vraie (pour l'instant).


1
vote positif pour JAMAIS en supposant l'ordre de tri, simplement parce que c'est ainsi que cela s'est affiché dans l'outil de requête une fois
Joel Coehoorn

3
J'ai même vu cela signalé plus d'une fois comme un bug.
dkretz

6
dans MySQL, il est documenté pour trier. < dev.mysql.com/doc/refman/5.0/en/select.html >. Blâmez donc MySQL (encore une fois).
derobert

1
Dans Oracle, les résultats non triés correspondaient (presque) toujours au regroupement - jusqu'à la version 10G. Beaucoup de retouches pour les développeurs qui laissaient de côté l'ORDRE PAR!
Tony Andrews, le

1
J'étais même dans une classe de formation où cela a été déclaré comme un fait pour SQL Server. J'ai dû protester très fort. Pour enregistrer uniquement pour taper 20 caractères, vous comptez sur un comportement obscur ou non documenté.
erikkallen

20
SELECT FirstName + ' ' + LastName as "Full Name", case UserRole when 2 then "Admin" when 1 then "Moderator" else "User" end as "User's Role", case SignedIn when 0 then "Logged in" else "Logged out" end as "User signed in?", Convert(varchar(100), LastSignOn, 101) as "Last Sign On", DateDiff('d', LastSignOn, getDate()) as "Days since last sign on", AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' + City + ', ' + State + ' ' + Zip as "Address", 'XXX-XX-' + Substring(Convert(varchar(9), SSN), 6, 4) as "Social Security #" FROM Users

Ou, tout entasser en une seule ligne.


J'ai utilisé la requête d'un commentaire précédent, simplement parce que c'était la première instruction SQL dont je disposais.
Jasper Bekkers

17
  • La FROM TableA, TableB WHEREsyntaxe pour JOINS plutôt queFROM TableA INNER JOIN TableB ON

  • Le fait de supposer qu'une requête sera renvoyée est trié d'une certaine manière sans insérer de clause ORDER BY, simplement parce que c'est ainsi qu'il est apparu lors des tests dans l'outil de requête.


5
Mes administrateurs de base de données Oracle se plaignent toujours que j'utilise des «jointures ANSI», c'est-à-dire ce que vous présentez comme étant la bonne manière. Mais je continue de le faire, et je soupçonne qu'au fond, ils savent mieux.
Steve McLeod

1
Je soupçonne qu'Oracle souhaite que le SQL standard disparaisse. :-) De plus, vous ne pouvez pas mélanger des JOINS implicites et explicites (aka ANSI JOINs) dans MySQL 5 - cela ne fonctionne pas. C'est un autre argument pour les JION explicites.
staticsan

3
Je dirais que même A INNER JOIN B ON est un anti pattern. Je préfère A INNER JOIN B USING.
John Nilsson

Oracle prend désormais en charge la syntaxe ANSI, mais ils avaient auparavant cette syntaxe vraiment bizarre pour les jointures externes et il y a trop de gens qui l'utilisent encore.
Cervo

eh bien ... Oracle ne vous laissera toujours pas utiliser les jointures ANSI pour des vues matérialisées
actualisables

14

Apprendre le SQL au cours des six premiers mois de sa carrière et ne jamais rien apprendre d'autre au cours des 10 prochaines années. En particulier, ne pas apprendre ou utiliser efficacement les fonctionnalités de fenêtrage / analytique SQL. En particulier l'utilisation de over () et de la partition par.

Les fonctions de fenêtre, comme les fonctions d'agrégation, effectuent une agrégation sur un ensemble défini (un groupe) de lignes, mais plutôt que de renvoyer une valeur par groupe, les fonctions de fenêtre peuvent renvoyer plusieurs valeurs pour chaque groupe.

Voir O'Reilly SQL Cookbook Annexe A pour un bon aperçu des fonctions de fenêtrage.


12

Je dois mettre mon propre favori actuel ici, juste pour compléter la liste. Mon antipattern préféré ne teste pas vos requêtes .

Cela s'applique lorsque:

  1. Votre requête implique plusieurs tables.
  2. Vous pensez avoir une conception optimale pour une requête, mais ne vous embêtez pas à tester vos hypothèses.
  3. Vous acceptez la première requête qui fonctionne, sans aucun indice quant à son optimisation.

Et tout test exécuté contre des données atypiques ou insuffisantes ne compte pas. S'il s'agit d'une procédure stockée, mettez l'instruction de test dans un commentaire et enregistrez-la avec les résultats. Sinon, mettez-le dans un commentaire dans le code avec les résultats.


Une technique très utile pour un test T-SQL minimal: dans le fichier .SQL où vous définissez votre SP, UDF, etc., immédiatement après, créez un test de bloc comme IF 1 = 2 BEGIN (exemples de cas pour votre code, avec les résultats attendus comme commentaires) FIN
Joe Pineda

SQL Server analyse le code dans le bloc de test, même s'il n'est jamais exécuté. Ainsi, lorsque votre objet est modifié et reçoit plus de paramètres, ou de type différent, etc. ou qu'un objet dont il dépend est modifié, vous recevrez une erreur simplement en demandant un plan d'exécution!
Joe Pineda

Il n'est pas toujours possible de tester avec des données réelles. Souvent, le serveur de développement / serveur "test" est sous-payé et obtient une fraction du serveur en direct. Généralement, les tests sont désapprouvés contre le serveur en direct. Certains endroits sont meilleurs et disposent d'un serveur de test ou de transfert avec des données en direct.
Cervo

11

Abus de table temporaire.

Plus précisément ce genre de chose:

SELECT personid, firstname, lastname, age
INTO #tmpPeople
FROM People
WHERE lastname like 's%'

DELETE FROM #tmpPeople
WHERE firstname = 'John'

DELETE FROM #tmpPeople
WHERE firstname = 'Jon'

DELETE FROM #tmpPeople
WHERE age > 35

UPDATE People
SET firstname = 'Fred'
WHERE personid IN (SELECT personid from #tmpPeople)

Ne créez pas de table temporaire à partir d'une requête, uniquement pour supprimer les lignes dont vous n'avez pas besoin.

Et oui, j'ai vu des pages de code sous cette forme dans des bases de données de production.


1
+1, je suis d'accord. Bien que, j'ai trouvé au moins un ou deux cas où cette technique a amélioré les performances - les requêtes impliquées étaient pour le moins complexes.
2010

1
Vrai - ils ont une place, mais pas dans toutes les requêtes :)
geofftnz

1
Parfois, vous devez le faire si les conditions sont super compliquées. Certes, il peut être abusé à l'extrême. Mais plusieurs fois, une simple suppression est beaucoup plus simple que la logique pour obtenir le cas dans la requête initiale. Parfois aussi, si la clause n'est pas négociable, la requête initiale ralentira. Mais le faire sur la petite table temporaire est plus efficace. Et d'autres fois, vous continuez à ajouter des cas que les hommes d'affaires continuent d'ajouter après coup.
Cervo

9

Point de vue contraire: obsession excessive de la normalisation.

La plupart des systèmes SQL / RBDB offrent de nombreuses fonctionnalités (transactions, réplication) qui sont très utiles, même avec des données non normalisées. L'espace disque est bon marché, et parfois il peut être plus simple (code plus facile, temps de développement plus rapide) de manipuler / filtrer / rechercher les données récupérées, que d'écrire le schéma 1NF, et de gérer tous les tracas qu'il contient (jointures complexes, sous-sélections désagréables , etc).

J'ai trouvé que les systèmes sur-normalisés sont souvent une optimisation prématurée, en particulier pendant les premiers stades de développement.

(plus de réflexions à ce sujet ... http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/ )


22
Je pense que la non-normalisation est souvent une optimisation prématurée.
tuinstoel

Parfois c'est le cas, parfois non. Heureusement, il est souvent facile de tester, et différentes options fonctionnent avec différents besoins de base de données.
Gregg Lind

17
La normalisation ne se limite pas à l'économie d'espace disque. Il s'agit également de créer une source faisant autorité pour les données. Si les données ne sont stockées qu’un seul endroit, la cohérence n’est pas un sous-produit d’un codage soigneux, mais plutôt un sous-produit de la conception.
Grant Johnson

Stocker des données composées au format JSON est une chose: elles sont de plus en plus prises en charge et c'est un compromis conscient. L'utilisation de valeurs séparées par des virgules (ou autre) dans le but d'enregistrer une jointure est penny-sage et insensée.
John Cowan

Les solutions noSQL présentent un avantage en termes de performances au détriment des données en double en éliminant les recherches multi-tables. Met toute la normalisation sur sa tête. Dans certains exemples, les données sont collectées à plusieurs endroits pour garantir qu'un processus a le temps de réponse le plus rapide possible. Bien sûr, des questions sur les sources faisant autorité entrent en jeu.
barrypicker

9

Je viens de mettre celui-ci ensemble, basé sur certaines des réponses SQL ici sur SO.

C'est un contre-modèle sérieux de penser que les déclencheurs sont aux bases de données comme les gestionnaires d'événements le sont à la POO. Il y a cette perception que n'importe quelle ancienne logique peut être insérée dans des déclencheurs, pour être déclenchée lorsqu'une transaction (événement) se produit sur une table.

Pas vrai. L'une des grandes différences est que les déclencheurs sont synchrones - avec une vengeance, car ils sont synchrones sur une opération définie, et non sur une opération de ligne. Du côté de la POO, exactement le contraire - les événements sont un moyen efficace d'implémenter des transactions asynchrones.


8

Procédures ou fonctions stockées sans aucun commentaire ...


Et vues;) Fonctions true, à l'exception des fonctions table (= vues avec paramètres).
Stefan Steiger

7

1) Je ne sais pas que c'est un anti-modèle "officiel", mais je n'aime pas et j'essaye d'éviter les littéraux de chaîne comme valeurs magiques dans une colonne de base de données.

Un exemple de la table 'image' de MediaWiki:

img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", 
    "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
img_major_mime ENUM("unknown", "application", "audio", "image", "text", 
    "video", "message", "model", "multipart") NOT NULL default "unknown",

(Je remarque juste un boîtier différent, une autre chose à éviter)

Je conçois des cas tels que des recherches int dans les tableaux ImageMediaType et ImageMajorMime avec des clés primaires int.

2) conversion de date / chaîne qui repose sur des paramètres NLS spécifiques

CONVERT(NVARCHAR, GETDATE())

sans identificateur de format


Et pas de retrait syntaxique non plus. Argghh.
dkretz

2
Pourquoi est-ce mauvais? Sûrement, si vous essayez d'exprimer un ensemble de valeurs, cela fonctionne aussi bien qu'une table de recherche et correspond mieux au code qui l'appelle. Je préfère avoir une énumération dans mon code d'application qui correspond à une contrainte d'énumération dans ma base de données plutôt qu'une énumération dans mon code d'application qui correspond à des lignes spécifiques d'une table de recherche. Il semble juste plus propre.
Jack Ryan

@JackRyan: C'est mauvais car lorsque vous modifiez la liste d'énumérations plus tard, vous devez vous rappeler de la modifier à deux endroits maintenant. Il viole SEC . La base de données devrait être la seule source de vérité.
Gerrat

7

Sous-requêtes identiques dans une requête.


10
Malheureusement, parfois, vous ne pouvez tout simplement pas éviter cela - dans SQL 2000, il n'y avait pas de mot clé "WITH", et l'utilisation des FDU pour encapsuler les sous-requêtes courantes entraîne parfois des pénalités de performances, blâmez MS pour cela ...
Joe Pineda

Et bien, j'espère qu'ils arriveront à l'ajouter un de ces jours.
EvilTeach

Dans SQL 2000, vous pouvez utiliser des variables de table.
récursif

@recursive: vous ne pouvez pas avoir d'index sur une variable de table, ce qui la rendra souvent plus lente qu'une sous-requête. Cependant, vous pouvez utiliser une table temporaire avec des index personnalisés.
Rick

Cool, je travaille avec SQL depuis des années et je ne savais même pas qu'il existait des expressions de table communes (même si j'en aurais eu besoin). Maintenant oui! Merci!
sleske

7
  • La vue modifiée - Une vue qui est modifiée trop souvent et sans préavis ni raison. Le changement sera soit remarqué au moment le plus inapproprié, soit pire se trompera et ne sera jamais remarqué. Peut-être que votre application sera interrompue parce que quelqu'un a pensé à un meilleur nom pour cette colonne. En règle générale, les vues devraient étendre l'utilité des tables de base tout en conservant un contrat avec les consommateurs. Résolvez les problèmes mais n'ajoutez pas de fonctionnalités ou pire changement de comportement, pour cela créez une nouvelle vue. Pour atténuer, ne partagez pas les vues avec d'autres projets et utilisez les CTE lorsque les plateformes le permettent. Si votre boutique dispose d'un DBA, vous ne pouvez probablement pas changer de vue, mais toutes vos vues seront obsolètes et / ou inutiles dans ce cas.

  • Le! Paramed - Une requête peut-elle avoir plusieurs objectifs? Probablement, mais la prochaine personne qui le lira ne le saura pas avant une profonde méditation. Même si vous n'en avez pas besoin maintenant, il y a de fortes chances que vous le fassiez, même si c'est "juste" pour déboguer. L'ajout de paramètres réduit le temps de maintenance et garde les choses au SEC. Si vous avez une clause where, vous devez avoir des paramètres.

  • Le cas sans CAS -

    SELECT  
    CASE @problem  
      WHEN 'Need to replace column A with this medium to large collection of strings hanging out in my code.'  
        THEN 'Create a table for lookup and add to your from clause.'  
      WHEN 'Scrubbing values in the result set based on some business rules.'  
        THEN 'Fix the data in the database'  
      WHEN 'Formating dates or numbers.'   
        THEN 'Apply formating in the presentation layer.'  
      WHEN 'Createing a cross tab'  
        THEN 'Good, but in reporting you should probably be using cross tab, matrix or pivot templates'   
    ELSE 'You probably found another case for no CASE but now I have to edit my code instead of enriching the data...' END  

J'ai adoré ce troisième. Je l'utilise déjà localement ...
alphadogg

Merci pour les accessoires. :)
jason saldo

5

Les deux que je trouve le plus, et qui peuvent avoir un coût significatif en termes de performances sont:

  • Utiliser des curseurs au lieu d'une expression basée sur un ensemble. Je suppose que celui-ci se produit fréquemment lorsque le programmeur réfléchit de manière procédurale.

  • À l'aide de sous-requêtes corrélées, lorsqu'une jointure à une table dérivée peut faire le travail.


Je suis d'accord si vous voulez dire ce que je pense que vous voulez dire; bien qu'une sous-requête corrélée soit un type de table dérivée IIRC.
dkretz

1
Une table dérivée est une opération définie, tandis qu'une sous-requête corrélée s'exécute pour chaque ligne de la requête externe, ce qui la rend moins efficace (9 fois sur 10)
Mitch Wheat

Il y a quelques années, j'ai trouvé à ma grande surprise que SQL S. est en quelque sorte optimisé pour gérer les requêtes corrélées: pour les requêtes simples, vous obtenez le même plan d'exécution qu'avec une requête logiquement équivalente utilisant un JOIN! De plus, les requêtes corrélées qui mettent Oracle à genoux ne s'exécutent que lentement sur SQL S.!
Joe Pineda

C'est pourquoi je le teste toujours des deux côtés. Et je <i> fais </> d'habitude l'essayer dans les deux sens. En pratique, pour SQL Server de toute façon, j'ai généralement trouvé que le sq corrélé n'était pas plus lent.
dkretz

3
VEUILLEZ comprendre qu'une sous-requête corrélée et une jointure sont IDENTIQUES (dans la plupart des cas). Ce ne sont même pas des choses différentes qui sont optimisées les unes aux autres, mais juste des représentations textuelles différentes de la même opération.
erikkallen

5

Placer des éléments dans des tables temporaires, en particulier les personnes qui passent de SQL Server à Oracle, ont l'habitude de trop utiliser les tables temporaires. Utilisez simplement des instructions select imbriquées.


5

Les développeurs qui écrivent des requêtes sans avoir une bonne idée de ce qui rend les applications SQL (à la fois les requêtes individuelles et les systèmes multi-utilisateurs) rapides ou lentes. Cela comprend l'ignorance de:

  • stratégies de minimisation des E / S physiques, étant donné que le goulot d'étranglement de la plupart des requêtes est E / S et non CPU
  • impact de différents types d'accès au stockage physique (par exemple, de nombreuses E / S séquentielles seront plus rapides que de nombreuses petites E / S aléatoires, mais moins si votre stockage physique est un SSD!)
  • comment régler manuellement une requête si le SGBD produit un mauvais plan de requête
  • comment diagnostiquer les performances médiocres de la base de données, comment «déboguer» une requête lente et comment lire un plan de requête (ou EXPLAIN, selon le SGBD de votre choix)
  • stratégies de verrouillage pour optimiser le débit et éviter les blocages dans les applications multi-utilisateurs
  • importance du traitement par lots et d'autres astuces pour gérer le traitement des ensembles de données
  • conception de tables et d'index pour équilibrer au mieux l'espace et les performances (par exemple, couvrir les index, garder les index petits dans la mesure du possible, réduire les types de données à la taille minimale requise, etc.)

3

Utilisation de SQL comme package ISAM (méthode d'accès séquentiel indexé) glorifié. En particulier, imbriquer des curseurs au lieu de combiner des instructions SQL en une seule instruction, quoique plus volumineuse. Cela compte également comme «abus de l'optimiseur» car en fait, l'optimiseur ne peut pas faire grand-chose. Cela peut être combiné avec des déclarations non préparées pour une inefficacité maximale:

DECLARE c1 CURSOR FOR SELECT Col1, Col2, Col3 FROM Table1

FOREACH c1 INTO a.col1, a.col2, a.col3
    DECLARE c2 CURSOR FOR
        SELECT Item1, Item2, Item3
            FROM Table2
            WHERE Table2.Item1 = a.col2
    FOREACH c2 INTO b.item1, b.item2, b.item3
        ...process data from records a and b...
    END FOREACH
END FOREACH

La solution correcte (presque toujours) consiste à combiner les deux instructions SELECT en une seule:

DECLARE c1 CURSOR FOR
    SELECT Col1, Col2, Col3, Item1, Item2, Item3
        FROM Table1, Table2
        WHERE Table2.Item1 = Table1.Col2
        -- ORDER BY Table1.Col1, Table2.Item1

FOREACH c1 INTO a.col1, a.col2, a.col3, b.item1, b.item2, b.item3
    ...process data from records a and b...
END FOREACH

Le seul avantage de la version à double boucle est que vous pouvez facilement repérer les ruptures entre les valeurs du tableau 1 car la boucle interne se termine. Cela peut être un facteur dans les rapports de rupture de contrôle.

En outre, le tri dans l'application est généralement un non-non.


Le style, bien que pas cette syntaxe, est particulièrement répandu en PHP dans mon expérience.
dkretz

La syntaxe est en fait IBM Informix-4GL - mais elle est suffisamment claire pour ne pas avoir besoin de beaucoup d'explications (je pense). Et le style est répandu dans de nombreux programmes SQL, quel que soit le langage de programmation.
Jonathan Leffler

Hormis le fait que vous utilisez un antipattern bien connu (jointures implicites) pour illustrer votre antipattern, cela défait en quelque sorte le point.
Johan

Et bien sûr, l'utilisation de curseurs est un contre-modèle SQl. Pratiquement tous les curseurs peuvent être réécrits en tant qu'opérations basées sur des ensembles. Les quelques-uns qui ne peuvent pas sont du genre que seuls les administrateurs de bases de données avec des années d'expérience et qui comprennent comment les éléments internes de la base de données devraient écrire. Aucun développeur d'application ne devrait jamais avoir besoin d'écrire un curseur SQL.
HLGEM du

3

Utilisation de clés primaires comme substitut pour les adresses d'enregistrement et utilisation de clés étrangères comme substitut pour les pointeurs intégrés aux enregistrements.

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.