Erreurs de développement de base de données commises par des développeurs d'applications [clôturé]


566

Quelles sont les erreurs courantes de développement de base de données commises par les développeurs d'applications?


Réponses:


1002

1. Ne pas utiliser d'indices appropriés

C'est relativement facile mais ça arrive tout le temps. Les clés étrangères doivent avoir des index dessus. Si vous utilisez un champ dans un, WHEREvous devriez (probablement) avoir un index dessus. Ces index doivent souvent couvrir plusieurs colonnes en fonction des requêtes que vous devez exécuter.

2. Ne pas appliquer l'intégrité référentielle

Votre base de données peut varier ici, mais si votre base de données prend en charge l'intégrité référentielle - ce qui signifie que toutes les clés étrangères sont garanties pour pointer vers une entité qui existe - vous devez l'utiliser.

Il est assez fréquent de voir cet échec sur les bases de données MySQL. Je ne crois pas que MyISAM le supporte. InnoDB le fait. Vous trouverez des personnes qui utilisent MyISAM ou celles qui utilisent InnoDB mais ne l'utilisent pas de toute façon.

Plus ici:

3. Utilisation de clés primaires naturelles plutôt que de substitution (techniques)

Les clés naturelles sont des clés basées sur des données significatives de l'extérieur qui sont (ostensiblement) uniques. Les exemples courants sont les codes de produit, les codes d'État à deux lettres (États-Unis), les numéros de sécurité sociale, etc. Les clés primaires de substitution ou techniques sont celles qui n'ont absolument aucune signification en dehors du système. Ils sont inventés uniquement pour identifier l'entité et sont généralement des champs auto-incrémentés (SQL Server, MySQL, autres) ou des séquences (notamment Oracle).

À mon avis, vous devez toujours utiliser des clés de substitution. Ce problème est apparu dans ces questions:

Il s'agit d'un sujet quelque peu controversé sur lequel vous n'obtiendrez pas d'accord universel. Bien que vous puissiez trouver certaines personnes, qui pensent que les clés naturelles sont OK dans certaines situations, vous ne trouverez aucune critique des clés de substitution autre que sans doute inutile. C'est un petit inconvénient si vous me demandez.

N'oubliez pas que même les pays peuvent cesser d'exister (par exemple, la Yougoslavie).

4. Rédaction de requêtes nécessitant DISTINCT de travailler

Vous le voyez souvent dans les requêtes générées par ORM. Regardez la sortie du journal d'Hibernate et vous verrez toutes les requêtes commencer par:

SELECT DISTINCT ...

C'est un peu un raccourci pour vous assurer de ne pas renvoyer de lignes en double et d'obtenir ainsi des objets en double. Vous verrez parfois des gens faire cela aussi. Si vous le voyez trop, c'est un vrai drapeau rouge. Pas çaDISTINCT n'est pas mauvais ou n'a pas d'applications valides. C'est le cas (sur les deux points), mais ce n'est pas un substitut ou un intervalle pour écrire des requêtes correctes.

De Pourquoi je déteste DISTINCT :

À mon avis, les choses commencent à mal tourner quand un développeur crée une requête substantielle, joint des tables et soudain, il se rend compte qu'il semble qu'il obtienne des lignes en double (ou même plus) et sa réponse immédiate ... sa «solution» à ce «problème» est de jeter sur le mot-clé DISTINCT et POOF tous ses problèmes disparaissent.

5. Favoriser l'agrégation plutôt que les jointures

Une autre erreur courante des développeurs d'applications de base de données est de ne pas réaliser combien d'agrégation plus coûteuse (c'est-à-dire la GROUP BYclause) peut être comparée aux jointures.

Pour vous donner une idée de l'étendue de ce phénomène, j'ai écrit plusieurs fois sur ce sujet ici et j'ai été beaucoup moins bien noté. Par exemple:

De l' instruction SQL - «rejoindre» vs «grouper et avoir» :

Première requête:

SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3

Temps de requête: 0,312 s

Deuxième requête:

SELECT t1.userid
FROM userrole t1
JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2
JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3
AND t1.roleid = 1

Temps de requête: 0,016 s

C'est vrai. La version join que j'ai proposée est vingt fois plus rapide que la version agrégée.

6. Ne pas simplifier les requêtes complexes via les vues

Tous les fournisseurs de bases de données ne prennent pas en charge les vues, mais pour ceux qui le font, ils peuvent grandement simplifier les requêtes s'ils sont utilisés judicieusement. Par exemple, sur un projet, j'ai utilisé un modèle Party générique pour CRM. Il s'agit d'une technique de modélisation extrêmement puissante et flexible, mais elle peut conduire à de nombreuses jointures. Dans ce modèle, il y avait:

  • Parti : personnes et organisations;
  • Rôle du parti : ce que ces partis ont fait, par exemple l'employé et l'employeur;
  • Relation entre les rôles des parties : comment ces rôles sont liés les uns aux autres.

Exemple:

  • Ted est une personne, étant un sous-type de parti;
  • Ted a de nombreux rôles, dont l'un est employé;
  • Intel est une organisation, étant un sous-type d'une partie;
  • Intel a de nombreux rôles, dont l’employeur;
  • Intel emploie Ted, ce qui signifie qu'il existe une relation entre leurs rôles respectifs.

Il y a donc cinq tables jointes pour relier Ted à son employeur. Vous supposez que tous les employés sont des personnes (pas des organisations) et vous fournissez cette vue d'aide:

CREATE VIEW vw_employee AS
SELECT p.title, p.given_names, p.surname, p.date_of_birth, p2.party_name employer_name
FROM person p
JOIN party py ON py.id = p.id
JOIN party_role child ON p.id = child.party_id
JOIN party_role_relationship prr ON child.id = prr.child_id AND prr.type = 'EMPLOYMENT'
JOIN party_role parent ON parent.id = prr.parent_id = parent.id
JOIN party p2 ON parent.party_id = p2.id

Et soudain, vous avez une vue très simple des données que vous voulez, mais sur un modèle de données très flexible.

7. Entrée non désinfectante

C'est énorme. Maintenant, j'aime PHP mais si vous ne savez pas ce que vous faites, il est très facile de créer des sites vulnérables aux attaques. Rien ne résume mieux que l' histoire des petites tables Bobby .

Les données fournies par l'utilisateur via des URL, des données de formulaire et des cookies doivent toujours être traitées comme hostiles et nettoyées. Assurez-vous d'obtenir ce que vous attendez.

8. Ne pas utiliser de déclarations préparées

Les instructions préparées sont lorsque vous compilez une requête moins les données utilisées dans les insertions, les mises à jour et les WHEREclauses, puis que vous les fournissez plus tard. Par exemple:

SELECT * FROM users WHERE username = 'bob'

contre

SELECT * FROM users WHERE username = ?

ou

SELECT * FROM users WHERE username = :username

en fonction de votre plateforme.

J'ai vu des bases de données mises à genoux en faisant cela. Fondamentalement, chaque fois qu'une base de données moderne rencontre une nouvelle requête, elle doit la compiler. S'il rencontre une requête déjà vue, vous donnez à la base de données la possibilité de mettre en cache la requête compilée et le plan d'exécution. En effectuant beaucoup de requêtes, vous donnez à la base de données la possibilité de comprendre et d'optimiser en conséquence (par exemple, en épinglant la requête compilée en mémoire).

L'utilisation d'instructions préparées vous donnera également des statistiques significatives sur la fréquence d'utilisation de certaines requêtes.

Les instructions préparées vous protégeront également mieux contre les attaques par injection SQL.

9. Pas assez normalisé

La normalisation de la base de données est essentiellement le processus d'optimisation de la conception de la base de données ou de la façon dont vous organisez vos données en tables.

Cette semaine, j'ai rencontré du code dans lequel quelqu'un avait implosé un tableau et l'avait inséré dans un seul champ d'une base de données. Normaliser cela reviendrait à traiter l'élément de ce tableau comme une ligne distincte dans une table enfant (c'est-à-dire une relation un-à-plusieurs).

Cela est également apparu dans la meilleure méthode pour stocker une liste d'ID utilisateur :

J'ai vu dans d'autres systèmes que la liste est stockée dans un tableau PHP sérialisé.

Mais le manque de normalisation se présente sous plusieurs formes.

Plus:

10. Normaliser trop

Cela peut sembler une contradiction avec le point précédent, mais la normalisation, comme beaucoup de choses, est un outil. C'est un moyen pour une fin et non une fin en soi. Je pense que de nombreux développeurs oublient cela et commencent à traiter un "moyen" comme une "fin". Les tests unitaires en sont un excellent exemple.

J'ai déjà travaillé sur un système qui avait une énorme hiérarchie pour les clients qui ressemblait à:

Licensee ->  Dealer Group -> Company -> Practice -> ...

de sorte que vous deviez joindre environ 11 tables avant de pouvoir obtenir des données significatives. C'était un bon exemple de normalisation prise trop loin.

Plus précisément, une dénormalisation minutieuse et réfléchie peut avoir d'énormes avantages en termes de performances, mais vous devez être très prudent lorsque vous effectuez cette opération.

Plus:

11. Utilisation d'arcs exclusifs

Un arc exclusif est une erreur courante lorsqu'une table est créée avec deux ou plusieurs clés étrangères où une et une seule d'entre elles peuvent être non nulles. Grosse erreur. D'une part, il devient d'autant plus difficile de maintenir l'intégrité des données. Après tout, même avec l'intégrité référentielle, rien n'empêche la définition de deux ou plusieurs de ces clés étrangères (malgré les contraintes de vérification complexes).

D' un guide pratique à la conception de bases de données relationnelles :

Nous avons fortement déconseillé la construction exclusive d'arc dans la mesure du possible, pour la bonne raison qu'ils peuvent être difficiles à écrire du code et poser plus de difficultés de maintenance.

12. Ne pas faire du tout d'analyse des performances sur les requêtes

Le pragmatisme règne en maître, en particulier dans le monde des bases de données. Si vous vous en tenez aux principes au point qu'ils sont devenus un dogme, vous avez probablement commis des erreurs. Prenons l'exemple des requêtes agrégées ci-dessus. La version agrégée peut sembler "agréable" mais ses performances sont lamentables. Une comparaison des performances aurait dû mettre fin au débat (mais ce ne fut pas le cas), mais plus précisément: jaillir de telles opinions mal informées est en premier lieu ignorant, voire dangereux.

13. Dépendance excessive à l'égard de UNION ALL et en particulier des constructions UNION

Une UNION en termes SQL concatène simplement des ensembles de données congrus, ce qui signifie qu'ils ont le même type et le même nombre de colonnes. La différence entre eux est que UNION ALL est une simple concaténation et devrait être préférée dans la mesure du possible alors qu'un UNION fera implicitement un DISTINCT pour supprimer les tuples en double.

Les UNIONS, comme DISTINCT, ont leur place. Il y a des applications valides. Mais si vous vous retrouvez à en faire beaucoup, en particulier dans les sous-requêtes, vous faites probablement quelque chose de mal. Cela pourrait être un cas de mauvaise construction de requête ou d'un modèle de données mal conçu vous obligeant à faire de telles choses.

Les UNIONs, en particulier lorsqu'ils sont utilisés dans des jointures ou des sous-requêtes dépendantes, peuvent paralyser une base de données. Essayez de les éviter autant que possible.

14. Utilisation des conditions OR dans les requêtes

Cela peut sembler inoffensif. Après tout, les ET sont OK. OU devrait être OK trop à droite? Faux. Fondamentalement, une condition ET restreint l'ensemble de données tandis qu'une condition OU le fait croître , mais pas d'une manière qui se prête à l'optimisation. Particulièrement lorsque les différentes conditions OR peuvent se croiser, forçant ainsi l'optimiseur à effectuer efficacement une opération DISTINCT sur le résultat.

Mauvais:

... WHERE a = 2 OR a = 5 OR a = 11

Meilleur:

... WHERE a IN (2, 5, 11)

Votre optimiseur SQL peut maintenant transformer efficacement la première requête en seconde. Mais ce n'est peut-être pas le cas. Ne le fais pas.

15. Ne pas concevoir leur modèle de données pour se prêter à des solutions performantes

C'est un point difficile à quantifier. Il est généralement observé par son effet. Si vous vous retrouvez à écrire des requêtes noueuses pour des tâches relativement simples ou que les requêtes pour trouver des informations relativement simples ne sont pas efficaces, alors vous avez probablement un mauvais modèle de données.

À certains égards, ce point résume tous les précédents, mais il s'agit plus d'un récit édifiant que faire des choses comme l'optimisation des requêtes se fait souvent en premier, alors qu'il devrait l'être en deuxième. Avant tout, vous devez vous assurer d'avoir un bon modèle de données avant d'essayer d'optimiser les performances. Comme l'a dit Knuth:

L'optimisation prématurée est la racine de tout Mal

16. Utilisation incorrecte des transactions de base de données

Toutes les modifications de données pour un processus spécifique doivent être atomiques. C'est-à-dire que si l'opération réussit, elle le fait pleinement. S'il échoue, les données restent inchangées. - Il ne devrait pas y avoir de possibilité de modifications «à moitié effectuées».

Dans l'idéal, la manière la plus simple d'y parvenir est que la conception complète du système s'efforce de prendre en charge toutes les modifications de données via des instructions INSERT / UPDATE / DELETE uniques. Dans ce cas, aucune gestion de transaction spéciale n'est nécessaire, car votre moteur de base de données doit le faire automatiquement.

Cependant, si des processus nécessitent que plusieurs instructions soient exécutées en tant qu'unité pour conserver les données dans un état cohérent, un contrôle de transaction approprié est nécessaire.

  • Commencez une transaction avant la première instruction.
  • Validez la transaction après la dernière instruction.
  • En cas d'erreur, annulez la transaction. Et très NB! N'oubliez pas de sauter / abandonner toutes les instructions qui suivent après l'erreur.

Il est également recommandé de prêter une attention particulière aux subtilités de la façon dont votre couche de connectivité de base de données et le moteur de base de données interagissent à cet égard.

17. Ne pas comprendre le paradigme «basé sur les ensembles»

Le langage SQL suit un paradigme spécifique adapté à des types spécifiques de problèmes. Malgré diverses extensions spécifiques au fournisseur, le langage a du mal à gérer les problèmes banals dans des langues comme Java, C #, Delphi, etc.

Ce manque de compréhension se manifeste de plusieurs manières.

  • Imposer de manière inappropriée trop de logique procédurale ou impérative à la base de données.
  • Utilisation inappropriée ou excessive de curseurs. Surtout quand une seule requête suffit.
  • Supposant à tort que les déclencheurs se déclenchent une fois par ligne affectés dans les mises à jour à plusieurs lignes.

Déterminez une répartition claire des responsabilités et efforcez-vous d'utiliser l'outil approprié pour résoudre chaque problème.


9
Sur les déclarations MySQL sur les clés étrangères, vous avez raison de dire que MyISAM ne les prend pas en charge, mais vous impliquez que le simple fait d'utiliser MyISAM est une mauvaise conception. J'ai utilisé MyISAM parce qu'InnoDB ne prend pas en charge les recherches FullText, et je ne pense pas que ce soit déraisonnable.
Derek H

1
Je dois poser des questions sur # 6. L'utilisation de vues comme celle-ci est l'une de mes choses préférées à faire, mais j'ai récemment appris, à ma grande horreur, qu'avec les index MySQL sur les tables sous-jacentes, il n'est obéi que si la structure de la vue permet l'utilisation de l'algorithme de fusion. Sinon, une table temporaire est utilisée et tous vos index sont inutiles. C'est encore plus alarmant lorsque vous réalisez qu'un tas d'opérations est à l'origine de ce comportement. C'est un excellent moyen de transformer une requête de 0,01 seconde en une requête de 100 secondes. Est-ce que quelqu'un d'autre ici a de l'expérience avec ça? Vérifiez les liens dans mon prochain commentaire.
Peter Bailey

5
Pas du tout d'accord avec le point 3. Oui, les pays peuvent cesser d'exister, mais le code de pays continuera de représenter la même chose. Idem avec les codes de devise ou les États américains. Il est stupide d'utiliser une clé de substitution dans ces cas et crée plus de surcharge dans vos requêtes car vous devez inclure une jointure supplémentaire. Je dirais qu'il est plus sûr de dire que vous devriez probablement utiliser un substitut pour les données spécifiques à l'utilisateur (donc pas les pays, les devises et les États américains).
Thomas

1
RE: # 11 La contrainte de vérification nécessaire pour appliquer l'intégrité des données est triviale. Il existe d'autres raisons pour éviter cette conception, mais la nécessité d'une contrainte de vérification «complexe» n'en fait pas partie.
Thomas

2
Avec le n ° 3, vous n'êtes pas honnête. Il y a plus d'inconvénients à la clé artificielle que «vous n'en aurez peut-être pas besoin». Plus précisément, l'utilisation d'une clé naturelle vous donnera la possibilité de contrôler l'ordre dans lequel les données de votre table sont écrites sur le disque. Si vous savez comment votre table sera interrogée, vous pouvez l'indexer afin que les lignes auxquelles vous accédez simultanément se retrouvent dans la même page. En outre, vous pouvez appliquer l'intégrité des données à l'aide d'un index composite unique. Si vous en avez besoin, vous devrez l'ajouter en plus de votre index de clé artificielle. Si cet index composite est votre clé, c'est 2 oiseaux tués avec une pierre.
Shane H

110

Erreurs de conception et de programmation des bases de données commises par les développeurs

  • Conception et utilisation égoïstes de bases de données. Les développeurs traitent souvent la base de données comme leur magasin d'objets persistant personnel sans tenir compte des besoins des autres parties prenantes dans les données. Cela s'applique également aux architectes d'applications. La mauvaise conception de la base de données et l'intégrité des données compliquent la tâche des tiers travaillant avec les données et peuvent augmenter considérablement les coûts du cycle de vie du système. Les rapports et les SIG ont tendance à être un mauvais cousin dans la conception des applications et ne sont effectués qu'après coup.

  • Abus de données dénormalisées. L'excès de données dénormalisées et la tentative de les conserver dans l'application est une recette pour les problèmes d'intégrité des données. Utilisez la dénormalisation avec parcimonie. Ne pas vouloir ajouter une jointure à une requête n'est pas une excuse pour dénormaliser.

  • Peur d'écrire SQL. SQL n'est pas sorcier et est en fait assez bon pour faire son travail. Les couches de mappage O / R sont assez bonnes pour effectuer 95% des requêtes qui sont simples et s'intègrent bien dans ce modèle. Parfois, SQL est la meilleure façon de faire le travail.

  • Politiques Dogmatic «No Stored Procedures». Peu importe si vous pensez que les procédures stockées sont mauvaises, ce type d'attitude dogmatique n'a pas sa place dans un projet logiciel.

  • Ne pas comprendre la conception de la base de données. La normalisation est votre amie et ce n'est pas sorcier. La jonction et la cardinalité sont des concepts assez simples - si vous êtes impliqué dans le développement d'applications de base de données, il n'y a vraiment aucune excuse pour ne pas les comprendre.


2
On pourrait faire valoir que les transactions devraient être effectuées dans une base de données et des rapports transactionnels et que le SIG devrait être effectué dans une base de données d'analyse distincte. Par conséquent, vous obtenez le meilleur des deux mondes et tout le monde est heureux (sauf le pauvre mug qui doit écrire le script de transformation des données pour construire ce dernier à partir du premier).
Chris Simpson

Pas seulement la mauvaise tasse écrivant l'ETL - toute personne utilisant des données du système, les données de mauvaise qualité dans l'application MIS qui est encadrée parce que plusieurs relations clés ne sont pas réellement enregistrées à la source, toute personne impliquée dans les chutes de réconciliation sans fin qui s'ensuivent. de la mauvaise qualité des données.
ConcernedOfTunbridgeWells

Je ne pourrais pas être plus en désaccord avec le premier point. Les bases de données sont pour la persistance, elles ne sont pas pour la communication inter-processus. Il existe presque toujours de meilleures solutions à ce problème. À moins qu'il n'y ait une exigence explicite pour cela, vous DEVRIEZ absolument traiter la base de données comme si personne, sauf votre application, ne l'utilisera jamais. Même s'il existe une exigence explicite, faites une analyse de l'histoire de l'utilisateur et des causes profondes et vous découvrirez très souvent une bien meilleure façon de remplir l'intention du demandeur. Là encore, je travaille dans une entreprise où l'expression CQRS est quelque peu courante
George Mauer

3
Exemple trivial: J'ai un système d'administration des polices d'assurance et j'ai besoin de charger l'état de 5 millions de sinistres dans un système de réassurance cédée pour calculer les recouvrements potentiels. Les systèmes sont d'anciens packages COTS client-serveur, conçus pour s'interfacer avec des systèmes mainframe encore plus anciens. Les deux doivent être rapprochés à des fins de contrôle financier. Ce travail est effectué une fois par mois. Selon votre logique, j'écrirais une série d'histoires utilisateur définissant les exigences et demanderais aux fournisseurs de citer l'ajout d'un wrapper de service Web à leurs produits existants.
ConcernedOfTunbridgeWells

2
Votre DBA est alors paresseux ou incompétent.
ConcernedOfTunbridgeWells

80
  1. Ne pas utiliser le contrôle de version sur le schéma de base de données
  2. Travailler directement sur une base de données active
  3. Ne pas lire et comprendre les concepts de base de données plus avancés (index, index clusterisés, contraintes, vues matérialisées, etc.)
  4. Ne pas tester l'évolutivité ... les données de test de seulement 3 ou 4 lignes ne vous donneront jamais la vraie image de la vraie performance en direct

1
J'appuie, fortement, # 1 et # 2. Chaque fois que je modifie la base de données, je vide son schéma et le versionne; J'ai trois bases de données configurées, une de développement, une de mise en scène et une en direct - RIEN n'est jamais "testé" sur la base de données en direct !!
Ixmatus

Chez Red Gate, nous avons pris des mesures pour améliorer votre premier point avec SQL Source Control! D'après les conversations que j'ai eues au cours de mes recherches, je pense que les gens ne développent plus contre des bases de données de production, mais souvent des correctifs "d'urgence" sont faits qui retrouvent généralement leur chemin vers les environnements de développement, ce qui est un autre problème.
David Atkinson

46

Surutilisation et / ou dépendance des procédures stockées.

Certains développeurs d'applications voient les procédures stockées comme une extension directe du code intermédiaire / frontal. Cela semble être un trait commun chez les développeurs de piles Microsoft (j'en suis un, mais j'ai grandi) et produit de nombreuses procédures stockées qui exécutent une logique métier complexe et un traitement de flux de travail. C'est beaucoup mieux fait ailleurs.

Les procédures stockées sont utiles lorsqu'il a été réellement prouvé qu'un certain facteur technique réel nécessite leur utilisation (par exemple, les performances et la sécurité).

J'ai récemment dû aider à maintenir et à améliorer une grande application de bureau Delphi dont 70% de la logique et des règles métier ont été implémentées dans 1400 procédures stockées SQL Server (le reste dans les gestionnaires d'événements de l'interface utilisateur). Ce fut un cauchemar, principalement dû à la difficulté d'introduire des tests unitaires efficaces dans TSQL, au manque d'encapsulation et aux outils médiocres (débogueurs, éditeurs).

En travaillant avec une équipe Java dans le passé, j'ai rapidement découvert que souvent l'opposé complet se vérifie dans cet environnement. Un architecte Java m'a dit un jour: "La base de données est pour les données, pas pour le code.".

De nos jours, je pense que c'est une erreur de ne pas considérer du tout les proc stockés, mais ils devraient être utilisés avec parcimonie (pas par défaut) dans des situations où ils offrent des avantages utiles (voir les autres réponses).


4
Les procédures stockées ont tendance à devenir un îlot de mal dans tout projet où elles sont utilisées, ainsi certains développeurs font une règle "Pas de procédures stockées". Il semble donc qu'il y ait un conflit ouvert entre eux. Votre réponse est un bon argument pour savoir quand choisir réellement une façon ou l'autre.
Warren P

Avantages: sécurité - vous n'avez pas à donner aux applications la possibilité de "supprimer * de ..."; réglages - les administrateurs de bases de données peuvent modifier les requêtes sans avoir à recompiler / déployer l'application entière; analyse - il est facile de recompiler un tas de procs après un changement de modèle de données pour s'assurer qu'ils sont toujours valides; et, enfin, considérant que SQL est exécuté par le moteur de base de données (pas votre application), le concept de "base de données est pour les données, pas pour le code" est juste retardé.
NotMe

Donc, vous intègreriez votre logique métier dans l'interface utilisateur, où elle était dissociée des données manipulées? Cela ne semble pas être une bonne idée, d'autant plus que la manipulation des données est plus efficace lorsqu'elle est effectuée par le serveur de base de données plutôt que par des allers-retours à partir de l'interface utilisateur. Cela signifie également qu'il est plus difficile de contrôler l'application, car vous ne pouvez pas vous fier à la base de données qui contrôle ses données et potentiellement avoir différentes versions d'une interface utilisateur avec différentes manipulations de données. Pas bon. Je ne laisse rien toucher mes données, sauf par le biais d'une procédure stockée.
David T.Macknet

S'il est nécessaire de séparer la logique métier de l'interface utilisateur, des architectures à plusieurs niveaux peuvent être utilisées. Ou, une bibliothèque avec des objets métier et une logique, utilisée par différentes applications / UI. Les procédures stockées verrouillent vos données / logique métier dans une base de données spécifique, la modification d'une base de données dans ce cas est très coûteuse. Et le coût énorme est mauvais.
trop

@too: Changer une base de données dans la plupart des cas est très coûteux. Peu importe l'idée de perdre les performances et les fonctions de sécurité d'un SGBD particulier. De plus, des niveaux supplémentaires ajoutent de la complexité et diminuent les performances et des couches supplémentaires sont liées à votre langue particulière. Enfin, il est plus probable que la langue utilisée changera qu'un serveur de base de données.
2011

41

Problème numéro un? Ils ne testent que sur des bases de données de jouets. Ils n'ont donc aucune idée que leur SQL va ramper lorsque la base de données sera volumineuse, et quelqu'un doit venir le réparer plus tard (ce son que vous pouvez entendre est mon grincement de dents).


2
La taille de la base de données est pertinente, mais un problème plus important est la charge - même si vous testez sur un ensemble de données réel, vous ne testez pas les performances de vos requêtes lorsque la base de données est sous une charge de production, ce qui peut être une véritable révélation.
davidcl

Je dirais que la taille de la base de données est plus importante que la charge. J'ai vu à plusieurs reprises qu'il manquait des index cruciaux - il n'y a jamais eu de problème de performances pendant les tests, car toute la base de données tient en mémoire
Danubian Sailor


28

Mauvaise performance causée par des sous-requêtes corrélées

La plupart du temps, vous voulez éviter les sous-requêtes corrélées. Une sous-requête est corrélée si, dans la sous-requête, il existe une référence à une colonne de la requête externe. Lorsque cela se produit, la sous-requête est exécutée au moins une fois pour chaque ligne renvoyée et peut être exécutée plusieurs fois si d'autres conditions sont appliquées après l'application de la condition contenant la sous-requête corrélée.

Pardonnez l'exemple artificiel et la syntaxe Oracle, mais disons que vous vouliez trouver tous les employés qui ont été embauchés dans l'un de vos magasins depuis la dernière fois que le magasin a réalisé moins de 10000 $ de ventes en une journée.

select e.first_name, e.last_name
from employee e
where e.start_date > 
        (select max(ds.transaction_date)
         from daily_sales ds
         where ds.store_id = e.store_id and
               ds.total < 10000)

La sous-requête dans cet exemple est corrélée à la requête externe par le store_id et serait exécutée pour chaque employé de votre système. Une façon d'optimiser cette requête consiste à déplacer la sous-requête vers une vue en ligne.

select e.first_name, e.last_name
from employee e,
     (select ds.store_id,
             max(s.transaction_date) transaction_date
      from daily_sales ds
      where ds.total < 10000
      group by s.store_id) dsx
where e.store_id = dsx.store_id and
      e.start_date > dsx.transaction_date

Dans cet exemple, la requête dans la clause from est désormais une vue en ligne (là encore une syntaxe spécifique à Oracle) et n'est exécutée qu'une seule fois. Selon votre modèle de données, cette requête s'exécutera probablement beaucoup plus rapidement. Il fonctionnerait mieux que la première requête à mesure que le nombre d'employés augmentait. La première requête pourrait en fait mieux fonctionner s'il y avait peu d'employés et de nombreux magasins (et peut-être que de nombreux magasins n'avaient pas d'employés) et que la table daily_sales était indexée sur store_id. Ce n'est pas un scénario probable, mais montre comment une requête corrélée pourrait éventuellement être plus performante qu'une alternative.

J'ai vu des développeurs juniors corréler les sous-requêtes à plusieurs reprises et cela a généralement eu un impact sévère sur les performances. Cependant, lorsque vous supprimez une sous-requête corrélée, assurez-vous de consulter le plan d'explication avant et après pour vous assurer que les performances ne sont pas moins bonnes.


1
Grand point, et pour souligner l'un de vos points connexes - testez vos modifications. Apprenez à utiliser des plans d'explication (et voyez ce que la base de données fait réellement pour exécuter votre requête, et ce qu'elle coûte), faites vos tests sur un grand ensemble de données et ne rendez pas votre SQL trop complexe et illisible / impossible à maintenir pour une optimisation cela n'améliore pas réellement les performances réelles.
Rob Whelan

21

D'après mon expérience:
ne communique pas avec les administrateurs de base de données expérimentés.


17

Utiliser Access au lieu d'une "vraie" base de données. Il existe de nombreuses bases de données petites et même gratuites, comme SQL Express , MySQL et SQLite, qui fonctionneront et évolueront beaucoup mieux. Les applications doivent souvent évoluer de manière inattendue.


16

Oublier de mettre en place des relations entre les tables. Je me souviens avoir dû nettoyer tout cela lorsque j'ai commencé à travailler chez mon employeur actuel.


14

Utiliser Excel pour stocker (d'énormes quantités de) données.

J'ai vu des entreprises détenir des milliers de lignes et utiliser plusieurs feuilles de calcul (en raison de la limite de 65535 lignes sur les versions précédentes d'Excel).


Excel est bien adapté aux rapports, à la présentation des données et à d'autres tâches, mais ne doit pas être traité comme une base de données.


14

Je voudrais ajouter: privilégier le code "élégant" au code hautement performant. Le code qui fonctionne le mieux contre les bases de données est souvent moche aux yeux du développeur d'applications.

Croire ce non-sens sur l'optimisation prématurée. Les bases de données doivent tenir compte des performances dans la conception d'origine et dans tout développement ultérieur. La performance est de 50% de la conception de la base de données (40% est l'intégrité des données et les 10 derniers% est la sécurité) à mon avis. Les bases de données qui ne sont pas construites de bas en haut pour fonctionner fonctionneront mal une fois que les utilisateurs réels et le trafic réel seront placés contre la base de données. L'optimisation prématurée ne signifie pas aucune optimisation! Cela ne signifie pas que vous devez écrire du code qui fonctionnera presque toujours mal parce que vous le trouverez plus facile (curseurs par exemple qui ne devraient jamais être autorisés dans une base de données de production, sauf si tout le reste a échoué). Cela signifie que vous n'avez pas besoin de chercher à éliminer cette dernière petite performance jusqu'à ce que vous en ayez besoin. On sait beaucoup de choses sur les meilleures performances des bases de données,


2
+1 - La programmation de la base de données consiste à optimiser le comportement des composants mécaniques. Notez, cependant, que Knuth dit que l'optimisation prématurée est la racine de tous les maux environ 97% du temps (ou des mots à cet effet). La conception de bases de données est un domaine dans lequel vous devez vraiment y penser dès le départ.
ConcernedOfTunbridgeWells

2
Ahem ... ce dont vous parlez, c'est d'une optimisation qui n'est pas prématurée. Une certaine considération de l'utilisation réelle est requise dès le début dans la conception de la base de données (et la conception de l'application également) La règle de Knuth n'est en fait pas triviale à suivre, car vous devez décider ce qui est prématuré et ce qui ne l'est pas - cela revient vraiment à "ne pas effectuer d'optimisations sans données". Les premières décisions liées aux performances dont vous parlez contiennent des données - certaines conceptions fixeront des limites inacceptables aux performances futures, et vous pouvez les calculer.
Rob Whelan

13

Ne pas utiliser de requêtes paramétrées. Ils sont assez pratiques pour arrêter l' injection SQL .

Il s'agit d'un exemple spécifique de non-désinfection des données d'entrée, mentionné dans une autre réponse.


3
Sauf que l'entrée de désinfection est incorrecte. La désinfection implique de la placer quelque part où elle peut être dangereuse. Paramétrer signifie le garder complètement hors du chemin du mal.
Dustin

12

Je déteste quand les développeurs utilisent des instructions select imbriquées ou même des fonctions retournent le résultat d'une instruction select dans la partie "SELECT" d'une requête.

Je suis en fait surpris de ne voir cela nulle part ailleurs ici, peut-être que je l'ai ignoré, bien que @adam ait un problème similaire indiqué.

Exemple:

SELECT
    (SELECT TOP 1 SomeValue FROM SomeTable WHERE SomeDate = c.Date ORDER BY SomeValue desc) As FirstVal
    ,(SELECT OtherValue FROM SomeOtherTable WHERE SomeOtherCriteria = c.Criteria) As SecondVal
FROM
    MyTable c

Dans ce scénario, si MyTable renvoie 10000 lignes, le résultat est comme si la requête venait d'exécuter 20001 requêtes, car elle devait exécuter la requête initiale et interroger chacune des autres tables une fois pour chaque ligne de résultat.

Les développeurs peuvent s'en tirer dans un environnement de développement où ils ne renvoient que quelques lignes de données et les sous-tables ne contiennent généralement qu'une petite quantité de données, mais dans un environnement de production, ce type de requête peut devenir exponentiellement coûteux car plus des données sont ajoutées aux tableaux.

Un meilleur exemple (pas nécessairement parfait) serait quelque chose comme:

SELECT
     s.SomeValue As FirstVal
    ,o.OtherValue As SecondVal
FROM
    MyTable c
    LEFT JOIN (
        SELECT SomeDate, MAX(SomeValue) as SomeValue
        FROM SomeTable 
        GROUP BY SomeDate
     ) s ON c.Date = s.SomeDate
    LEFT JOIN SomeOtherTable o ON c.Criteria = o.SomeOtherCriteria

Cela permet aux optimiseurs de base de données de mélanger les données ensemble, plutôt que d'effectuer une nouvelle requête sur chaque enregistrement de la table principale et je trouve généralement que lorsque je dois corriger le code où ce problème a été créé, je finis généralement par augmenter la vitesse des requêtes de 100% ou plus tout en réduisant simultanément l'utilisation du processeur et de la mémoire.


12

Pour les bases de données SQL:

  1. Ne pas tirer parti des INDEX CLUSTERED ou ne pas choisir la ou les colonnes incorrectes pour CLUSTER.
  2. Ne pas utiliser un type de données SERIAL (numéro automatique) en tant que CLÉ PRIMAIRE pour se joindre à une CLÉ ÉTRANGÈRE (INT) dans une relation de table parent / enfant.
  3. Ne PAS METTRE À JOUR LES STATISTIQUES sur une table lorsque de nombreux enregistrements ont été INSÉRÉS ou SUPPRIMÉS.
  4. Ne pas réorganiser (c'est-à-dire décharger, supprimer, recréer, charger et réindexer) les tables lorsque de nombreuses lignes ont été insérées ou supprimées (certains moteurs conservent physiquement les lignes supprimées dans une table avec un indicateur de suppression.)
  5. Ne pas profiter de FRAGMENT ON EXPRESSION (si pris en charge) sur les grandes tables qui ont des taux de transaction élevés.
  6. Choisir le mauvais type de données pour une colonne!
  7. Ne pas choisir un nom de colonne correct.
  8. Ne pas ajouter de nouvelles colonnes à la fin du tableau.
  9. Ne pas créer d'index appropriés pour prendre en charge les requêtes fréquemment utilisées.
  10. créer des index sur des colonnes avec peu de valeurs possibles et créer des index inutiles.
    ... plus à ajouter.

1
Un petit problème: 2) est en fait une mauvaise pratique. Je vois où vous voulez en venir - vous voulez un index unique sur ce numéro automatique et l'utiliser comme clé de substitution. Mais la clé primaire ne doit pas être un numéro automatique, car ce n'est pas ce qu'est une clé primaire EST: une clé primaire est "de quoi parle l'enregistrement", qui (sauf pour des choses comme les transactions de vente) n'est PAS le numéro automatique, mais un bit unique d'informations sur l'entité modélisée.
David T.Macknet

la raison principale de l'utilisation de la numérotation automatique pour la clé primaire et la clé étrangère est de garantir qu'une jointure parent-enfant peut être conservée indépendamment des modifications dans les autres colonnes. l'utilisation d'une clé primaire différente, comme le nom du client ou d'autres données, peut être risquée!
Frank R.

@David: Je suis corrigé! .. il n'est pas nécessaire d'utiliser le numéro automatique comme clé primaire, on peut toujours avoir une colonne série indexée dans le parent, rejoindre le substitut chez l'enfant pour garantir que la relation ne sera pas rompue, tout en ayant un autre colonne comme un primaire significatif pour localiser la ligne!
Frank R.

C'est un problème de sémantique, en fin de compte ... et Microsoft préfère que les clés primaires soient dénuées de sens plutôt que significatives. Les débats autour de lui font rage, mais je tombe dans le camp "significatif". :)
David T.Macknet

9
  • Ne pas prendre de sauvegarde avant de corriger un problème dans la base de données de production.

  • Utilisation de commandes DDL sur des objets stockés (comme des tables, des vues) dans des procédures stockées.

  • Peur d'utiliser un proc stocké ou peur d'utiliser des requêtes ORM partout où celle-ci est plus efficace / appropriée à utiliser.

  • Ignorer l'utilisation d'un profileur de base de données, qui peut vous dire exactement en quoi votre requête ORM est finalement convertie et donc vérifier la logique ou même le débogage lorsque vous n'utilisez pas ORM.


8

Ne fait pas le bon niveau de normalisation . Vous voulez vous assurer que les données ne sont pas dupliquées et que vous divisez les données en différentes selon vos besoins. Vous devez également vous assurer que vous ne suivez pas la normalisation trop loin car cela nuirait aux performances.


Jusqu'où est trop loin? Si aucune donnée n'est dupliquée, comment pouvez-vous aller plus loin?
finnw

La normalisation est un équilibre entre la suppression des données redondantes et l'augmentation de la flexibilité par rapport à une baisse des performances et une complexité accrue. Trouver le bon équilibre demande de l'expérience et change avec le temps. Voir en.wikipedia.org/wiki/Database_normalization pour savoir quand dénormaliser
Nathan Voxland

8

Traiter la base de données comme un simple mécanisme de stockage (c.-à-d. Bibliothèque de collections glorifiées) et donc subordonnée à leur application (en ignorant les autres applications qui partagent les données)


Un corollaire à cela est de décharger trop de travail de requête vers l'application au lieu de le conserver dans la base de données à laquelle il appartient. LINQ est particulièrement mauvais à ce sujet.
3Dave

8
  • Rejeter un ORM comme Hibernate pour des raisons comme "c'est trop magique" ou "pas dans ma base de données".
  • S'appuyer trop sur un ORM comme Hibernate et essayer de le chausse-pied là où il n'est pas approprié.

8

1 - Utilisation inutile d'une fonction sur une valeur dans une clause where avec le résultat de l'index non utilisé.

Exemple:

where to_char(someDate,'YYYYMMDD') between :fromDate and :toDate

au lieu de

where someDate >= to_date(:fromDate,'YYYYMMDD') and someDate < to_date(:toDate,'YYYYMMDD')+1

Et dans une moindre mesure: ne pas ajouter d'index fonctionnels aux valeurs qui en ont besoin ...

2 - Ne pas ajouter de contraintes de vérification pour garantir la validité des données. L'optimiseur de requêtes peut utiliser des contraintes et elles aident vraiment à garantir que vous pouvez faire confiance à vos invariants. Il n'y a tout simplement aucune raison de ne pas les utiliser.

3 - Ajout de colonnes non normalisées aux tables par pure paresse ou pression du temps. Les choses ne sont généralement pas conçues de cette façon, mais évoluent vers cela. Le résultat final, sans faute, est une tonne de travail essayant de nettoyer le gâchis lorsque vous êtes mordu par l'intégrité des données perdues dans les évolutions futures.

Pensez à cela, une table sans données est très bon marché à repenser. Une table avec quelques millions d'enregistrements sans intégrité ... pas si bon marché à repenser. Ainsi, faire la bonne conception lors de la création de la colonne ou du tableau est amorti en pique.

4 - pas tellement sur la base de données en soi mais vraiment ennuyeux. Ne pas se soucier de la qualité du code de SQL. Le fait que votre SQL soit exprimé en texte ne permet pas de masquer la logique dans des tas d'algorithmes de manipulation de chaînes. Il est parfaitement possible d'écrire du SQL dans du texte d'une manière qui soit réellement lisible par votre collègue programmeur.


7

Cela a déjà été dit, mais: index, index, index . J'ai vu tellement de cas d'applications Web d'entreprise peu performantes qui ont été corrigées en faisant simplement un peu de profilage (pour voir quelles tables étaient souvent touchées), puis en ajoutant un index sur ces tables. Cela ne nécessite même pas beaucoup de connaissances en écriture SQL, et le gain est énorme.

Évitez la duplication des données comme la peste. Certaines personnes préconisent qu'un peu de duplication ne nuira pas et améliorera les performances. Hé, je ne dis pas que vous devez torturer votre schéma en troisième forme normale, jusqu'à ce qu'il soit si abstrait que même les DBA ne savent pas ce qui se passe. Comprenez simplement que chaque fois que vous dupliquez un ensemble de noms, de codes postaux ou de codes d'expédition, les copies ne seront plus synchronisées les unes avec les autres. Cela va arriver. Et puis vous vous donnerez un coup de pied en exécutant le script de maintenance hebdomadaire.

Et enfin: utilisez une convention de dénomination claire, cohérente et intuitive. De la même manière qu'un morceau de code bien écrit doit être lisible, un bon schéma ou une requête SQL doit être lisible et vous dire pratiquement ce qu'il fait, même sans commentaires. Vous vous remercierez dans six mois, lorsque vous devrez faire l'entretien sur les tables. "SELECT account_number, billing_date FROM national_accounts"est infiniment plus facile à utiliser que "SELECT ACCNTNBR, BILLDAT FROM NTNLACCTS".


Si vous les configurez correctement, ils ne le feront pas, mais cela implique l'utilisation de déclencheurs auxquels de nombreuses personnes sont allergiques.
HLGEM

6

Ne pas exécuter une requête SELECT correspondante avant d'exécuter la requête DELETE (en particulier sur les bases de données de production)!


5

L'erreur la plus courante que j'ai vue depuis vingt ans: ne pas planifier à l'avance. De nombreux développeurs créeront une base de données et des tables, puis modifieront et développeront continuellement les tables au fur et à mesure de la création des applications. Le résultat final est souvent un gâchis et inefficace et difficile à nettoyer ou à simplifier plus tard.


1
Je peux imaginer les horreurs qui s'ensuivent dans ces situations ... Les bases de données sans schéma sont bien mieux adaptées au prototypage rapide et au développement itératif, mais comme tout le reste, une telle flexibilité s'accompagne de divers compromis.
Zsolt Török

4

a) Valeurs de requête de codage en dur dans la chaîne
b) Placement du code de requête de base de données dans l'action "OnButtonPress" dans une application Windows Forms

J'ai vu les deux.


4
"Mettre le code de requête DB dans l'action" OnButtonPress "dans une application Windows Form" Quelle est l'erreur de base de données ici?
récursif

@recursive: c'est une énorme vulnérabilité d'injection SQL. N'importe qui peut envoyer du SQL arbitraire à votre serveur et il sera exécuté mot pour mot.
Bill Karwin

D'accord avec @recursive. Ceux-ci n'ont vraiment rien à voir avec les problèmes de base de données.
p.campbell

b) est une erreur d'architecture. Bien sûr, le codage des requêtes directement dans votre application est de toute façon une mauvaise idée.
3Dave

4

Ne pas accorder suffisamment d'attention à la gestion des connexions aux bases de données dans votre application. Ensuite, vous découvrez l'application, l'ordinateur, le serveur et le réseau est obstrué.


4
  1. Penser qu'ils sont des administrateurs de base de données et des modélisateurs / concepteurs de données lorsqu'ils n'ont aucun endoctrinement formel d'aucune sorte dans ces domaines.

  2. Penser que leur projet ne nécessite pas de DBA parce que tout cela est facile / trivial.

  3. Échec de la distinction correcte entre le travail qui doit être effectué dans la base de données et le travail qui doit être effectué dans l'application.

  4. Ne pas valider les sauvegardes ou ne pas sauvegarder.

  5. Incorporation de SQL brut dans leur code.



3

Ne pas comprendre le modèle de concurrence des bases de données et comment cela affecte le développement. Il est facile d'ajouter des index et de modifier les requêtes après coup. Cependant, les applications conçues sans tenir compte des hotspots, des conflits de ressources et du bon fonctionnement (en supposant que ce que vous venez de lire est toujours valide!) Peuvent nécessiter des modifications importantes au sein de la base de données et du niveau d'application pour être corrigées ultérieurement.


3

Ne pas comprendre comment un SGBD fonctionne sous le capot.

Vous ne pouvez pas conduire correctement un bâton sans comprendre comment fonctionne un embrayage. Et vous ne pouvez pas comprendre comment utiliser une base de données sans comprendre que vous écrivez vraiment dans un fichier sur votre disque dur.

Plus précisément:

  1. Savez-vous ce qu'est un index cluster? Avez-vous pensé à cela lorsque vous avez conçu votre schéma?

  2. Savez-vous comment utiliser correctement les index? Comment réutiliser un index? Savez-vous ce qu'est un indice de couverture?

  3. Tellement génial, vous avez des index. Quelle est la taille d'une ligne dans votre index? Quelle sera la taille de l'indice lorsque vous aurez beaucoup de données? Est-ce que cela rentrera facilement dans la mémoire? Sinon c'est inutile comme index.

  4. Avez-vous déjà utilisé EXPLAIN dans MySQL? Génial. Maintenant, soyez honnête avec vous-même: avez-vous compris même la moitié de ce que vous avez vu? Non, tu ne l'as probablement pas fait. Répare ça.

  5. Comprenez-vous le cache de requête? Savez-vous ce qui rend une requête non cachable?

  6. Utilisez-vous MyISAM? Si vous avez besoin d'une recherche en texte intégral, MyISAM est de toute façon de la merde. Utilisez Sphinx. Passez ensuite à Inno.


2
Une meilleure analogie pourrait être que l'on ne peut pas correctement dépanner une transmission manuelle sans comprendre un embrayage. Beaucoup de gens conduisent correctement un changement de vitesse sans savoir comment fonctionne un embrayage.
Michael Easter

3
  1. Utilisation d'un ORM pour effectuer des mises à jour groupées
  2. Sélection de plus de données que nécessaire. Encore une fois, généralement effectué lors de l'utilisation d'un ORM
  3. Faire des sqls en boucle.
  4. Ne pas avoir de bonnes données de test et remarquer une dégradation des performances uniquement sur les données en direct.
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.