Domanda

Backstory

Al lavoro dove abbiamo intenzione di deprecando una colonna chiave naturale in una delle nostre tabelle primarie. Il progetto si compone di oltre 100 applicazioni che puntano a questa tabella / colonna; 400+ stored procedure che fanno riferimento direttamente questa colonna; e una vasta gamma di tabelle comuni tra queste applicazioni anche Riferimenti questa colonna.

Il Big Bang e ripartire da metodi di lavoro vengono fuori dal quadro. Stiamo andando a deprecare questa colonna una sola applicazione alla volta, certifica le modifiche e passare a quello successivo ... e abbiamo ottenuto un obiettivo bersaglio lungo per rendere questo sforzo pratico.

Il problema che ho è che molte di queste applicazioni hanno condiviso le stored procedure e tabelle. Se ho completamente convertire tutti di tabelle / stored procedure di applicazione Un'applicazione B e C sarà spezzato fino convertito. Questi a loro volta possono rompersi applicazioni D, E, F ... ecc. Ho già una strategia attuata per le classi di codice e stored procedure, la parte mi sono bloccato su è stato transizione del database.

Ecco un base esempio di ciò che abbiamo:

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

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

E noi stiamo puntando ora solo per lo stato di transizione in questo modo:

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

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

L'idea che è durante le applicazioni non-migrati fase di transizione e le stored procedure saranno ancora in grado di accedere a tutti i dati appropriati e quelli nuovi possono cominciare a spingere per le colonne corrette - Una volta completata la migrazione di tutte le procedure e le applicazioni memorizzate possiamo finalmente eliminare le colonne aggiuntive.

Ho voluto utilizzare i trigger di SQL Server per intercettare automaticamente ogni nuovo inserimento / aggiornamento e di fare qualcosa di simile a quanto segue su ciascuna delle tabelle interessate:

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

Domanda

I 2 enormi problemi che sto avendo finora sono:

  1. Non riesco a fare un "dopo INSERT", perché i vincoli NULL renderanno l'inserto sicuro.
  2. Il "codice impossibile" che ho citato è come mi piacerebbe delega in modo pulito la query originale; Se la query originale ha x, y, z colonne in esso o semplicemente x, mi Preferirei lo stesso grilletto per fare queste. E se posso aggiungere / eliminare un'altra colonna, mi piacerebbe il grilletto a funzionare.

Chiunque ha un esempio di codice in cui questo potrebbe essere possibile, o addirittura una soluzione alternativa per mantenere queste colonne riempite correttamente quando anche uno solo dei valori viene passato SQL?

È stato utile?

Soluzione 3

Dopo aver dormito sul problema, questa sembra essere la soluzione più generica / riutilizzabile potevo venire con all'interno della sintassi SQL. Funziona bene anche se entrambe le colonne hanno un vincolo NOT NULL, anche se non si fa riferimento alla colonna "altro" a tutti nel vostro inserto.

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

Altri suggerimenti

affare complicato ...

OK, prima di tutto: questo trigger sarà non il lavoro in molte circostanze:

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

Il grilletto può essere chiamato con un set di righe nel Inserted pseudo-table - quale stai andando a raccogliere qui ?? Hai bisogno di scrivere il trigger in modo tale che funzionerà anche quando si ottiene 10 righe della tabella Inserted. Se un'istruzione SQL inserti 10 righe, il trigger sarà non essere licenziato volte dieci - uno per ogni riga - ma solo una volta per l'intera partita - è necessario prendere in conto!

Secondo punto: vorrei provare a rendere i campi IDENTITY del ID - poi faranno sempre avere un valore - anche per le applicazioni "legacy". Queste applicazioni "vecchi" dovrebbero fornire una chiave eredità, invece - così si dovrebbe andare bene lì. L'unico problema che vedo e non sanno come gestire questi sono gli inserti da un app già convertito - non forniscono una chiave di legacy "vecchio stile", così? In caso contrario - quanto velocemente si fa a bisogno di avere una tale chiave?

Quello che sto pensando sarebbe un "lavoro di pulizia" che avrebbe eseguito sopra il tavolo e ottenere tutte le righe con una chiave NULL legacy e quindi fornire un valore significativo per esso. Rendere questo una stored procedure regolari ed eseguirlo ogni esempio giorno, quattro ore e 30 minuti - qualunque sia adatta alle vostre esigenze. Allora non avere a che fare con i trigger e tutti i limiti che hanno.

Non sarebbe possibile fare delle modifiche dello schema 'bigbang', ma creare le viste sopra la parte superiore di quelle tabelle che 'nascondere' il cambiamento?

Credo che si potrebbe trovare si sta semplicemente mettendo fuori le rotture a un secondo momento: "Stiamo andando a deprecare questa colonna una sola applicazione alla volta" - potrebbe essere la mia ingenuità, ma non riesco a vedere come che sia mai andare al lavoro.

Di certo, un disastro peggiore può verificarsi quando diverse applicazioni stanno facendo le cose in modo diverso?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top