Question

Backstory

Au travail où nous prévoyons de déprécier une colonne clé naturelle dans l'une de nos tables primaires. Le projet consiste en plus de 100 applications qui pointent vers cette table / colonne; 400+ procédures stockées qui font référence à cette colonne directement; et un vaste éventail de tableaux communs entre ces applications qui font référence également cette colonne.

Le Big Bang et à partir de méthodes Scratch sont hors de l'image. Nous allons désapprouver cette colonne une application à la fois, de certifier les modifications et passer à la prochaine ... et nous avons un long objectif cible de faire cet effort pratique.

Le problème que j'ai est que beaucoup de ces applications ont partagé des procédures et des tables stockées. Si je convertir complètement toutes les tables de l'application A / procédures stockées application B et C sera brisé jusqu'à converti. Ceux-ci peuvent à leur tour casser des applications D, E, F ... Etc. J'ai déjà une stratégie mise en place pour les classes de code et procédures stockées, la partie que je suis coincé sur est l'état de transition de la base de données.

Voici un base exemple de ce que nous avons:

Users
---------------------------
Code          varchar(32) natural key

Access
---------------------------
UserCode      varchar(32) foreign key
AccessLevel   int

Et nous visons maintenant juste pour l'état de transition comme ceci:

Users
---------------------------
Code          varchar(32) 
Id            int         surrogate key

Access
---------------------------
UserCode      varchar(32)   
UserID        int         foreign key      
AccessLevel   int

L'idée étant dans les applications migrées de l'ONU phase de transition et les procédures stockées seront toujours en mesure d'accéder à toutes les données appropriées et de nouvelles peuvent commencer à pousser les colonnes correctes - Une fois la migration terminée pour toutes les procédures stockées et applications nous pouvons enfin déposer les colonnes supplémentaires.

Je voulais utiliser les déclencheurs de SQL Server pour automatiquement intercepter toute nouvelle insertion / mise à jour et de faire quelque chose comme ce qui suit sur chacune des tables affectées:

CREATE TRIGGER tr_Access_Sync
ON Access
INSTEAD OF INSERT(, UPDATE)
AS
BEGIN
  DIM @code as Varchar(32)
  DIM @id as int

  SET @code = (SELECT inserted.code FROM inserted)
  SET @id = (SELECT inserted.code FROM inserted)

  -- This is a migrated application; find the appropriate legacy key
  IF @code IS NULL AND @id IS NOT NULL
     SELECT Code FROM Users WHERE Users.id = @id

  -- This is a legacy application; find the appropriate surrogate key
  IF @id IS NULL AND @code IS NOT NULL
     SELECT Code FROM Users WHERE Users.id = @id

  -- Impossible code:
  UPDATE inserted SET inserted.code=@code, inserted.id=@id
END

Question

Les 2 énormes problèmes que je vais avoir à ce jour sont:

  1. Je ne peux pas faire une « APRÈS INSERT » car les contraintes de NULL feront l'insert échouer.
  2. Le « code impossible » je l'ai mentionné comment je voudrais pouvoir proprement la requête initiale; Si la requête initiale a x, y, z colonnes en elle ou tout simplement x, je voudrais idéalement le même déclencheur pour faire ces derniers. Et si j'ajouter / supprimer une autre colonne, je voudrais que le déclencheur de rester fonctionnel.

Quelqu'un a un exemple de code où cela pourrait être possible, ou même une solution de rechange pour le maintien de ces colonnes correctement remplies, même si une seule des valeurs est passé à SQL?

Était-ce utile?

La solution 3

Après avoir dormi sur le problème, cela semble être la solution la plus / générique réutilisable que je pourrais trouver au sein de la syntaxe SQL. Il fonctionne très bien, même si les deux colonnes ont une contrainte NOT NULL, même si vous ne faites pas référence la colonne « autres » du tout dans votre insert.

CREATE TRIGGER tr_Access_Sync
ON Access
INSTEAD OF INSERT
AS 
BEGIN

    /*-- Create a temporary table to modify because "inserted" is read-only */
    /*-- "temp" is actually "#temp" but it throws off stackoverflow's syntax highlighting */
    SELECT * INTO temp FROM inserted

    /*-- If for whatever reason the secondary table has it's own identity column */
    /*-- we need to get rid of it from our #temp table to do an Insert later with identities on */
    ALTER TABLE temp DROP COLUMN oneToManyIdentity

    UPDATE temp 
    SET 
        UserCode = ISNULL(UserCode, (SELECT UserCode FROM Users U WHERE U.UserID = temp.UserID)),
        UserID = ISNULL(UserID, (SELECT UserID FROM Users U WHERE U.UserCode = temp.UserCode))

    INSERT INTO Access SELECT * FROM temp

END

Autres conseils

Tricky ... business

OK, tout d'abord: ce déclencheur pas travail dans de nombreuses circonstances:

SET @code = (SELECT inserted.code FROM inserted)
SET @id = (SELECT inserted.code FROM inserted)

Le déclencheur peut être appelé avec un ensemble de lignes dans la pseudo-table Inserted - que l'on allez-vous choisir ici ?? Vous devez écrire votre déclencheur de telle façon que cela fonctionnera même si vous obtenez 10 lignes dans la table Inserted. Si une commande insère SQL 10 lignes, votre déclencheur pas être tiré dix fois - un pour chaque ligne - mais seulement une fois pour le lot entier - vous devez prendre cela en compte!

Deuxième point: Je voudrais essayer de faire les champs IDENTITY de l'ID - alors ils obtiennent toujours une valeur - même pour les applications « anciens ». Ces applications « anciens » devraient fournir une clé de l'héritage à la place - donc vous devriez être bien là. Le seul problème que je vois et ne savent pas comment vous gérez ces sont des inserts à partir d'une application déjà convertie - Offrent-ils une clé de l'héritage « de style ancien » aussi? Dans le cas contraire - comment rapidement vous devez avoir une telle clé?

Qu'est-ce que je pense à serait un « travail de nettoyage » qui fonctionnerait sur la table et obtenir toutes les lignes avec une clé héritage NULL et fournir une valeur significative pour elle. Faire une procédure régulière stockée et exécuter chaque exemple jour, quatre heures, 30 minutes - ce qui convient à vos besoins. Ensuite, vous ne devez pas traiter avec des déclencheurs et toutes les limitations qu'ils ont.

Ce ne serait pas possible de faire les modifications du schéma de la bigbang 'mais créer des vues sur le dessus de ces tables « cacher » le changement?

Je pense que vous pourriez vous trouver êtes tout simplement dépouillant le casse à un point plus tard dans le temps: « Nous allons désapprouver cette colonne une application à la fois » - il pourrait être ma naïveté, mais je ne vois pas comment qui va jamais au travail.

Certes, pire désordre peut se produire lorsque des applications sont en train de faire les choses différemment?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top