2014-03-30 17 views
8

Wir haben ein mittelgroßes SSDT-Projekt (~ 100 Tabellen), das für Dutzende verschiedener Datenbankinstanzen bereitgestellt wird. Als Teil unseres Build-Prozesses generieren wir eine .dacpac-Datei. Wenn wir dann bereit sind, eine Datenbank zu aktualisieren, generieren wir ein Publish-Skript und führen es gegen die Datenbank aus. Einige db-Instanzen werden zu unterschiedlichen Zeitpunkten aktualisiert, daher ist es wichtig, dass wir einen strukturierten Prozess für diese Upgrades und Versionierungen haben.Wie können benutzerdefinierte Datenmigrationen und statische/Referenzdaten in ein SSDT-Projekt einbezogen werden?

Der Großteil des generierten Migrationsskripts besteht darin, Prozeduren, Funktionen, Indizes und strukturelle Änderungen sowie einige in einem Post-Deployment-Skript enthaltene Datenskripte zu löschen und (neu) zu erstellen. Es sind diese zwei datenbezogene Artikel Ich mag gerne wissen, wie man am besten im Rahmen des Projekts strukturieren:

  1. Benutzerdefinierte Datenmigrationen erforderlich zwischen den Versionen

  2. Statische oder Referenzdaten

Benutzerdefinierte Datenmigrationen zwischen den Versionen erforderlich

Manchmal möchten wir eine einmalige Daten mi durchführen Als Teil eines Upgrades kann ich mir nicht vorstellen, dass dies am besten in unser SSDT-Projekt integriert werden kann. Zum Beispiel habe ich kürzlich eine neue Bitspalte dbo.Charge.HasComments hinzugefügt, um (redundante) abgeleitete Daten basierend auf einer anderen Tabelle zu enthalten, und wird über Trigger synchron gehalten. Eine lästige aber notwendige Leistungsverbesserung (nur nach sorgfältiger Überlegung hinzugefügt & Messung). Im Rahmen des Upgrades das Skript die notwendigen ALTER TABLE und CREATE TRIGGER Aussagen Publish-SSDT erzeugt werden enthalten, aber ich möchte auch diese Spalte aktualisieren, basierend auf Daten in einer anderen Tabelle:

update dbo.Charge 
set HasComments = 1 
where exists (select * 
       from dbo.ChargeComment 
       where ChargeComment.ChargeId = Charge.ChargeId) 
and HasComments = 0 

Was ist der beste Weg, diese Daten schließen Migrationsskript in meinem SSDT-Projekt?

Derzeit ich jede dieser Arten von Migrationen in einer separaten Datei, die in der Post-Deployment-Skript enthalten ist, so dass meine Post-Deployment-Skript endet wie folgt aussehen:

-- data migrations 
:r "data migration\Update dbo.Charge.HasComments if never populated.sql" 
go 
:r "data migration\Update some other new table or column.sql" 
go 

Ist dies der richtige Weg um es zu tun, oder gibt es eine Möglichkeit, SSDT und seine Versionsverfolgung besser zu verknüpfen, sodass diese Skripts nicht einmal ausgeführt werden, wenn das SSDT-Publish für eine Datenbank ausgeführt wird, die bereits eine neuere Version hat. Ich könnte meinen eigenen Tisch haben, um zu verfolgen, welche Migrationen durchgeführt wurden, aber ich würde es vorziehen, nicht zu rollen, wenn es eine Standardmethode dafür gibt.

Statische oder Referenzdaten

Einige der Datenbanktabellen enthalten, was wir statische oder Referenzdaten nennen, z.B. Liste der möglichen Zeitzonen, Einstellungstypen, Währungen, verschiedene "Typ" -Tabellen usw. Derzeit füllen wir diese mit einem separaten Skript für jede Tabelle, die als Teil des Post-Deployment-Skripts ausgeführt wird. Jedes statische Datenskript fügt alle 'korrekten' statischen Daten in eine Tabellenvariable ein und fügt dann die statische Datentabelle bei Bedarf ein/aktualisiert/löscht sie. Abhängig von der Tabelle kann es sinnvoll sein, nur existierende Datensätze einzufügen oder nur einzufügen und zu löschen, nicht aber zu aktualisieren.So sieht jedes Skript etwas wie folgt aus:

-- table listing all the correct static data 
declare @working_data table (...) 

-- add all the static data that should exist into the working table 
insert into @working_data (...) select null, null null where 1=0 
union all select 'row1 col1 value', 'col2 value', etc... 
union all select 'row2 col1 value', 'col2 value', etc... 
... 

-- insert any missing records in the live table 
insert into staticDataTableX (...) 
select * from @working_data 
where not exists (select * from staticDataTableX 
        where [... primary key join on @working_data...]) 

-- update any columns that should be updated 
update staticDataTableX 
set ... 
from staticDataTableX 
inner join @working_data on [... primary key join on @working_data...] 

-- delete any records, if appropriate with this sort of static data 
delete from staticDataTableX 
where not exists (select * from staticDataTableX 
        where [... primary key join on @working_data...]) 

und dann meine Post-Deployment-Skript hat einen Abschnitt wie folgt aus:

-- static data. each script adds any missing static/reference data: 
:r "static_data\settings.sql" 
go 
:r "static_data\other_static_data.sql" 
go 
:r "static_data\more_static_data.sql" 
go 

Gibt es eine bessere oder mehrere herkömmliche Art und Weise solche statischen Datenskripten zu strukturieren als Teil eines SSDT-Projekts?

+1

Das sieht für mich richtig aus. Jamie Thomson hat in seinem Blog einige Ideen, wie man eine "Version" -Tabelle pflegen kann, auf die man sich beziehen kann. Sie können auch eine Tabelle erstellen, um anzugeben, welche Skripts ausgeführt wurden, wenn Sie die Skripts später nicht wiederholen möchten. Du müsstest Code dafür schreiben, aber es würde funktionieren. Leider scheint das Versionsnummern-Tracking in SSDT zu diesem Zeitpunkt etwas zu fehlen. –

+0

@Rory: Haben Sie jemals Fortschritte gemacht oder eine funktionierende Lösung gefunden? Meine derzeitige Vermutung ist zu sehen, ob die Version des DACPAC während der Post-Deployment-Skriptausführung verfügbar ist, und dann die Versionsnummern am Anfang jedes Datenskripts zu überprüfen. – DonBecker

+0

Wir fuhren mit dem oben beschriebenen Muster fort. Ich denke, wir haben eine Tabelle hinzugefügt, um zu protokollieren, welche Migrationen ausgeführt wurden, um Datenmigrationen zu bewältigen, die wir nur einmal ausführen möchten und die nicht aus der Datenbankstruktur ermittelt werden können. Das Muster, das ich oben beschrieben habe, funktioniert gut genug, obwohl es ein wenig chaotisch ist, die statischen Datendateien zu erstellen und zu verwalten. – Rory

Antwort

3

zu verfolgen, ob das Feld bereits initialisiert wurde, versuchen, eine erweiterte Eigenschaft hinzugefügt, wenn der Initialisierungsbefehl ausgeführt wird (es kann auch die Notwendigkeit für den Initialisierungsvorgang zu bestimmen, verwendet werden):

die erweiterte Eigenschaft hinzufügen :

EXEC sys.sp_addextendedproperty 
@name = N'EP_Charge_HasComments', 
@value = N'Initialized', 
@level0type = N'SCHEMA', @level0name = dbo, 
@level1type = N'TABLE', @level1name = Charge, 
@level2type = N'COLUMN', @level2name = HasComments; 

für die erweiterte Eigenschaft zu prüfen:

SELECT objtype, objname, name, value 
FROM fn_listextendedproperty (NULL, 
    'SCHEMA', 'dbo', 
    'TABLE', 'Charge', 
    'COLUMN', 'HasComments'); 

für Referenzdaten, versuchen Sie eine MERGE verwenden. Es ist viel sauberer als die dreifachen Abfragen, die Sie verwenden.

MERGE INTO staticDataTableX AS Target 
USING (
VALUES 
     ('row1_UniqueID', 'row1_col1_value', 'col2_value'), 
     ('row2_UniqueID', 'row2_col1_value', 'col2_value'), 
     ('row3_UniqueID', 'row3_col1_value', 'col2_value'), 
     ('row4_UniqueID', 'row4_col1_value', 'col2_value') 


    ) AS Source (TableXID, col1, col2) 
     ON Target.TableXID = Source.TableXID 
WHEN MATCHED THEN 
    UPDATE SET 
     Target.col1 = Source.col1, 
     Target.col2 = Source.col2 

WHEN NOT MATCHED BY TARGET THEN 
    INSERT (TableXID, col1, col2) 
    VALUES (Source.TableXID, Source.col1, Source.col2) 

WHEN NOT MATCHED BY SOURCE THEN 
    DELETE; 
+1

Funktioniert das mit DAC-Datenbanken? Ich glaube nicht, dass Extended Props für DAC/DACPAC unterstützt werden. – DonBecker