2010-02-04 18 views
5

Nous effectuons une migration de base de données vers SQL Server et, pour prendre en charge une application héritée, nous avons défini des vues sur la table SQL Server contenant les données attendues. Cependant, nous avons maintenant des problèmes avec les déclencheurs INSTEAD OF INSERT définis sur ces vues, lorsque les champs peuvent avoir des valeurs par défaut.Utilisation des valeurs par défaut dans un déclencheur INSTEAD OF INSERT

Je vais essayer de donner un exemple.

Une table de la base de données comporte 3 champs, a, b et c. c est flambant neuf, l'application legacy ne le sait pas, donc nous avons aussi une vue avec 2 champs, a et b.

Lorsque l'application héritée tente d'insérer une valeur dans son point de vue, nous utilisons un déclencheur INSTEAD OF INSERT pour rechercher la valeur qui devrait aller dans le champ c, quelque chose comme ceci:

INSERT INTO realTable(a, b, c) SELECT Inserted.a, Inserted.b, Calculated.C FROM... 

(Les détails de la recherche n'est pas pertinente.)

Ce déclencheur fonctionne bien, sauf si le champ b a une valeur par défaut. En effet, si la requête

INSERT INTO legacyView(a) VALUES (123) 

est exécuté, puis dans la gâchette, Inserted.b est NULL, et non b de la valeur par défaut. Maintenant, j'ai un problème, parce que je ne peux pas dire la différence de la requête ci-dessus, ce qui mettrait la valeur par défaut en b, et ceci:

INSERT INTO legacyView(a,b) VALUES (123, NULL) 

Même si b était non nulle, je ne sais pas comment écrire la requête INSERT dans le trigger de telle sorte que si une valeur a été fournie pour b, elle est utilisée dans le trigger, mais sinon la valeur par défaut est utilisée à la place.

EDIT: ajouté que je préfère ne pas dupliquer les valeurs par défaut dans le déclencheur. Les valeurs par défaut sont déjà dans le schéma de base de données, j'espère que je pourrais juste les utiliser directement.

+0

Sachant que tous vos champs avec des valeurs par défaut ne sont pas NULL est énorme! J'aurais pu avoir une solution pour vous si j'avais su cette information. – ErikE

+0

Désolé Emtucifor, je ne savais pas à l'époque que j'ai posté la question que nous n'avions pas de valeurs par défaut sur les champs Nullable. Quelle aurait été votre solution? Si c'est bon, je peux encore l'augmenter et peut-être changer la réponse acceptée! –

Répondre

1

Paul: J'ai résolu celui-ci; finalement. Bit d'une solution sale et peut-être pas au goût de tout le monde, mais je suis tout à fait nouveau à SQL Server et les choses semblables

Dans la gâchette Instead_of_INSERT:

  1. Copie la structure de table virtuelle Inséré de données à un table temporaire:

    SELECT * INTO aTempInserted FROM Inserted WHERE 1=2 
    
  2. créer une vue de déterminer les contraintes par défaut de table sous-jacente (à partir des tables du système) de la vue et de les utiliser pour construire des déclarations qui en double les contraintes dans la table temporaire:Utilisez un curseur pour parcourir l'ensemble récupéré et exécuter chaque instruction. Cela vous laisse avec une table temporaire avec les mêmes valeurs par défaut que la table à insérer.

  3. Insérer défaut enregistrement dans la table temporaire (tous les champs sont nullable comme créés à partir de la table virtuelle Inséré):

    INSERT INTO aTempInserted DEFAULT VALUES 
    
  4. Copiez les enregistrements de la table virtuelle Inséré dans la table sous-jacente de la vue (où ils ont été insérés à l'origine, si le déclencheur ne l'avait pas empêché), en rejoignant la table temporaire pour fournir des valeurs par défaut. Cela nécessite l'utilisation de la fonction COALESCE de sorte que seules les valeurs sont initialisés non ravitaillées:

    INSERT INTO realTable([a], [b], 
          SELECT COALESCE(I.[a], T.[a]), 
            COALESCE(I.[a], T.[b]) 
          FROM Inserted  AS I, 
            aTempInserted AS T 
    
  5. Supprimez la table temporaire

+0

C'est presque là - vous ne pouvez toujours pas dire la différence (à l'étape 5) entre une valeur NULL parce que la valeur n'a pas été spécifiée (d'où la valeur par défaut) et une valeur NULL car une valeur NULL a été spécifiée Par défaut, ceci est seulement un problème si vous avez un champ nullable avec une valeur par défaut, ce que je ne pense pas que nous faisons, donc cette réponse est assez bonne pour être acceptée –

+0

C'est vrai, c'est une approche assez compliquée, mais peut être amené à fonctionner lorsque d'autres jointures/traitements sont requis dans les instructions d'insertion. –

1

Quelques idées:

  • Si l'application héritée spécifie les listes de colonnes pour INSERTs et nommer des colonnes plutôt que d'utiliser SELECT *, alors ne pouvez pas vous lier simplement une valeur par défaut à la colonne c et laisser l'application utiliser votre table originale (modifiée)? Si vous pouviez faire en sorte que l'application héritée utilise une vue ou une table différente pour ses INSERTs que pour SELECT ou DELETE, vous pouvez mettre les valeurs par défaut requises sur cette table et utiliser un after-trigger régulier pour vous déplacer les nouvelles colonnes sur la table réelle.

  • Que diriez-vous de laisser la table d'origine seule et d'ajouter vos colonnes supplémentaires dans une table distincte qui a une relation 1-1 avec l'original? Créez ensuite une vue qui combine ces deux tables et placez un ou plusieurs déclencheurs appropriés sur cette nouvelle vue pour gérer toutes les opérations de données réparties entre les deux tables. Je réalise que cela a des implications sur les performances, mais c'est peut-être la seule façon de contourner le problème. Ce serait un cas idéal pour une vue matérialisée, ce qui ralentirait les mises à jour mais rendrait le résultat exactement comme une table pour les lectures. (Les vues matérialisées se prêtent mieux aux jointures internes et ne nécessitent aucune agrégation.)

  • J'ai rencontré un problème similaire dans lequel je ne pouvais pas faire la différence et les colonnes ignorées dans un déclencheur UPDATE à la place sur une vue.J'ai finalement fait un déclencheur à la place de INSERT sur la vue pour convertir les insertions en mises à jour (si la clé existait déjà c'était une mise à jour, sinon c'était une insertion). Bien que cela ne vous aide pas directement, cela pourrait stimuler certaines idées pour vous ou pour d'autres.

+0

Merci pour ces commentaires, je ne peux pas pointer sur quelque chose en particulier qui a aidé, mais cela nous a fait sentir beaucoup moins comme si nous allions frapper un mur de briques! –

0

Qu'en est-il en utilisant quelque chose comme ça ???:

insert into realtable 
values inserted.a, isnull(inserted.b, DEFAULT), computedC 
from inserted