Arjuna a écrit :
*SNIP* du blabla d'introduction, c'est à peu près la seule chose de juste dans tout le bordel
Regardons ce qu'il se passe sur une clé primaire.
-> Lorsqu'on ajoute une valeur, le SGBD va en premier rechercher la présence de cette valeur dans la table, puis l'insérer si elle n'existe pas. PUIS, étant donné qu'une clé primaire est systématiquement associée à un index unique, elle va vérifier l'existance de cet élément dans l'index unique, et l'ajouter si elle n'existe pas (ce qui est forcément le cas si on est allé jusque là).
Ca fait pas mal de trucs... Voyons voir ce que ça donne si on supprime cette contrainte, et qu'on ne garde que l'index unique.
-> Recherche de la valeur dans l'index. Si elle n'existe pas, insertion de la donnée, mise à jour de l'index, sinon, erreur.
On a fait 2 traîtements de moins.
=> Il ne faut pas utiliser de clé primaire sur une table. Un index unique suffit, et offre le même résultat en un temps plus rapide.
Deplus, les clé alternatives n'existant que rarement dans les SGBD, elles sont habituellement traîtées de cette façon, alors qu'il n'y a pas de raison de les différencier.
Bon, prenons le problème à l'envers. Qu'est-ce qu'une clefs primaire? C'est simple, tout d'abord, une clef primaire se doit d'être unique, ensuite, elle se doit de ne pas être NULL, enfin... rien, c'est tout! Une clef primaire n'est rien d'autre qu'une clef unique non nulle. Et par quoi notre ami nou propose-t-il de la remplacer? par une clef unique simple!!! soit une clef plus longue à parcourir parce qu'elle doit également tenir compte des éléments NULL dans son classement. en outre, dans la plupart des SGDB, les clefs primaires sont EXACTEMENT identiques aux clefs uniques avec contrainte NOT NULL si ce n'est qu'elles sont taggées du sigle "primary key"
Maintenant, penchons-nous sur la clé étrangère.
-> Lorsque j'insère une donnée dans la table "fille", je regarde si la donnée liée existe dans la table mère. Si elle existe, j'insère ma ligne, et si elle n'existe pas, je fait une erreur. Ceci peut prendre pas mal de temps si la table mère contient beaucoup de données, ou que le tuple servant de lien est gros.
Lorsque je supprime une donnée de la table "mère", je regarde si des données de la table fille y font référence. Si oui, alors je plante, sinon je supprime la ligne. Ceci est encore plus lent, puisque très généralement il y a plus de filles que de mères. Sans compter le fait que du côté mère, ce champ est forcément unique, tandis que côté fille, il ne l'est pas.
Jusqu'ici, c'est pass faux, mais encore une fois, c'est de la pure théorie
Quand j'ai un programme qui me permet de remplir un formulaire ou les données de la table mère sont listées dans une dropdown list, quel est l'intérêt d'aller vérifier qu'elle existent dans la table mère au moment de l'insertion ? Aucun, puisque ces données existes forcément !
Quel magnifique point de vue nombrilistique! Si je fais une dropdown list dans un formulaire html, RIEN, ABSOLUMENT RIEN, ne me garanti que l'utilisateur ne pourra pas simuler une requète http POST ou GET en mettant des valeurs bidons à la place. Et ce n'est qu'un cas de figure.
Et dans un traîtement de suppression d'une donnée de référence, pour éviter de planter si des données existent dans la table fille, on va de toute façon nettoyer cette dernière en premier. Ainsi, au moment du delete sur la table mère, on est certains qu'il ne peut pas y avoir de filles, vu qu'on vient de toutes les effacer. A nouveau, aucun intérêt.
Ouais ouais ouais, et les ON ACTION ... c'est pour les chiens. Mais tu en reparles plus bas (mal), laissons donc ce point ridicule à l'abandon.
=> Les clé étrangères ne servent donc à rien, et c'est au programme de s'assurer de l'intégrité des données lors de ces accès à la base. Il vaut donc mieu éviter de les utiliser, afin d'économiser les traîtements inutiles.
Bah non, CQFD
A noter deplus le problème du CASCADE CONSTRAINTS. Cette fabuleuse commande permet de supprimer une mère et toutes ses filles en une ligne.
Elle a aussi la formidable capacité à vider la moitiée des tables de la base en une seconde si on écrit mal la requête... Dans ce cas, le segment de rollback étant sous-dimensionné, ça plante, et on ne peut plus faire rollback. La catastrophe. Sans clé étrangère, aucun risque, le CASCADE CONSTRAINTS ne retrouvant pas de cheminement entre les table, ne pourra faire aucun dégat.
Tu évoques le problèe du rollback, ceci est vrai car les CASCADE CONSTRAINTS s'effectuent dans une transaction pour garantir l'intégrité de la DB. Maintenant, si tu supprimes ces contraintes, c'est donc que tu les remplaces par des traitement manuels. Or, ces traitement manuels doivent également s'exécuter dans la même transaction, autrement, tu ne garanties AUCUNEMENT la stabilité de ton SGDB lors d'une modification non atomique. Donc, soit tu le fais dans la même transaction et tu as exactement les mêmes problèmes de rollback, soit tu le fais en dehors de tout transaction et tu te retrouves avec un état instable qui peut durer suffisament longtemps pour perturber le reste du système.
-> Les triggers. Un trigger s'éxécute sur différents évèments et différentes actions sur différents objets. Un trigger est généralement utilisé sur une table (mais ça peut aussi bien être sur une vue pour les SGBD récents), avant ou après la modification de la table, sur une insertion, une suppression ou/et une mise à jour.
L'éxécution du trigger sera systématique lorsqu'un tel traîtement aura lieu sur son l'objet qu'il surveille. Ainsi, si un trigger s'occupe de mettre à jour le montant d'une commande en fonction des produits qui y sont rattachés, la moindre modification d'un libellé d'un produit dans la liste va déclencher le calcul, même si aucune quantité ni prix n'a été modifié.
Dans l'exemple que tu présentes ici, tu as visiblement déjà mal codé ton trigger. La moindre des choses à faire, quand on a des traitements lourds à faire comme celui que tu décris, c'est de vérifier que les colonnes impliquées ont bien été modifiées au sein du tuple -> Un trigger fonctionne dans sa propre sous-transaction
vrai
-> Un trigger peut tout à fait déclencher d'autres triggers
vrai
-> A savoir qu'une sous-transaction fonctionne comme de la récusrivité : c'est la transaction principale qui encaisse tout. On voit rapidement que pour certains trigger un rollback segment fault risque d'arriver, avec toutes les erreurs et incohérences que cela peut engendrer.
D'une part si tu en arrives à ce point c'est qu'il y a mauvaise getion du serveur. D'autre part, si tu échappes à cela, c'est que tu n'agis pas dans une transaction et donc que ce traitement, visiblement très couteux puisque mettant à mal le serveur, tu laisse ton systèem A NOUVEAU dans un état incohérent pendant un laps de temps non négligeable
-> Un trigger, étant écrit à la main, ne peut pas bénéficier des optimisation des PK et FK (qui sont en réalité des triggers automatiques), et se contenteront d'utiliser du code SQL et dérivés (PL/SQL ou T-SQL pour ne citer qu'eux), ce qui risque rapidement de prendre du temps.
Faux. Tout du moins avec un SGDB compétent. Tout trigger est destiné à remplacer l'équivalent qui serait réalisé manuellement, soit par du SQL, soit par un autre traitement. Là où le Trigger prend l'avantage, c'est qu'il est enrigistré sous forme de procédure stockée. Sous cette forme, le trigger a déjà été précompilé, il n'a quasiment plus besoin du parseur de Query (s'il ne peux pas s'en passer complètement) et a par avance effectué un query plan avantageux pour le traitement qu'il doit faire, ceci incluant l'utilsation d'autres Triggers comme les PK ET FK
=> La plus part des traîtements par trigger peuvent être évités en faisant attention à ce qu'on fait au niveau du programme qui utilise la base de données. Le calcul d'un total d'une commande notamment, n'a rien à faire dans la base, il a sa place dans le programme, d'autant plus que c'est plus évident de modifier un programme pour y rajouter des frais de port ou la TVA que dans un trigger...
Faux, encore une fois tu as un regards trop nombriliste sur la chose. D'une part, si le calcul du prix est fluctuant, le stocker dans la DB est une erreur de modèlisation, d'autre part, les triggers sont nettement moins limités à la petite utilisation dans laquelle tu sembles vouloir les confiner. Par exemple, j'utilise les triggers pour modifier l'es relations d'appartenance d'object et de liaisons qu'ils ont au sein de la DB en fonction de l'évolution des données incomplètes qui y sont rentrées (données biologiques oblige). C'est autrement plus complexe qu'un calcul de prix et nettement plus rapide que le même traitment manuel au sein d'une transaction
En bref, les triggers sont à éviter autant que possible. Les seuls qui sont vraiment acceptables, sont ceux qui vont s'occuper de générer un identifiant (pour les bases qui n'ont pas de fonction native).
Faux, CQFD
On crééra alors un trigger "on before insert" qui s'occupera d'aller récupérer la valeur d'une séquence par exemple. Sorti de ça, les tests comme exclusion de champs, surtout lorsque le trigger se lève sur un traîtement fréquent, et lorsque ses traîtements sont longs, doivent impérativement déportés dans l'application.
*soupir*
Voilà. C'était juste parceque j'avais rien à faire et que j'ai pas de question à poser, et y'a pas de réponse auxquelles répondre, donc j'étalle ma science, ça peu toujours servir si un jour une personne se pose la question
Tien, voila un autre pot de confiture, tu en auras besoin. Personnellement, si je suivais la moitié des conseils que tu donnes dans ce post, je pourrais me faire virer pour incompétence. Alors, je suppose que chez GE, s'il se retrouve avec une DB en rade dans un état incohérent, ca ne les déranges pas, mais moi, si j'ai des biologistes qui font des traitements de plusieurs jours de calculs sur la DB et qu'A UN SEUL MOMENT, elle se retrouve dans ce type d'état, c'est tout leur boulot qui est foutu.
|