2016-05-13 1 views
1

Dies ist meist ein Experiment, das ich gemacht habe, um einige SQL Server-Konzepte zu lernen. Angenommen, das folgende Szenario:Erstellen von DDL/DML-Skripten für alle Tabellen/Spalten in einer Datenbank

  • Ich habe eine Produktionsdatenbank, eine Entwicklungsdatenbank und eine Testdatenbank;
  • Die Entwicklungsdatenbank ist aktueller als die Testdatenbank und enthält mehrere neue Tabellen und Spalten, die kürzlich entwickelt wurden.
  • Ich möchte die Testdatenbank als auch aktualisiert werden (mit diesen neuen Tabellen und Spalten), aber lieber nicht löschen und neu erstellen, dass die DB

Das Skript (es nützliche Testdaten enthält) I unten geschrieben ist Wird für die Datenbank "development" ausgeführt, sodass ein Skript mit Bedingungen für jede Spalte der Datenbank generiert wird. Das Skript sollte dann gegen die andere Datenbank verwendet werden, um sie zu aktualisieren und die Bedingungen sollten unabhängig Spalte oder Tabelle hinzufügen, dass die Testdatenbank nicht bereits haben:

DECLARE @CURRENT_COLUMN nvarchar(100) 
DECLARE @COLUMN_LITERAL nvarchar(100) 
DECLARE @CURRENT_DEFAULT nvarchar(20) 
DECLARE @CURRENT_DATATYPE nvarchar(100) 
DECLARE @CURRENT_SCHEMA nvarchar(100) 
DECLARE @SQLA nvarchar(max) 
DECLARE @SQLB nvarchar(max) 
DECLARE @CURRENT_TABLE nvarchar(100) 
DECLARE @COMPUTED smallint 
SET @COMPUTED = 0 
PRINT ' 
DECLARE @SQL nvarchar(max) 
' 
DECLARE CUR_SCHEMA CURSOR FOR 
SELECT TABLE_SCHEMA from INFORMATION_SCHEMA.TABLES 

OPEN CUR_SCHEMA 

    FETCH NEXT FROM CUR_SCHEMA 
    INTO @CURRENT_SCHEMA 

WHILE @@FETCH_STATUS = 0 
BEGIN 


DECLARE CUR_TAB CURSOR FOR 
SELECT ist.TABLE_NAME from INFORMATION_SCHEMA.TABLES ist 
WHERE ist.TABLE_SCHEMA = @CURRENT_SCHEMA 
AND EXISTS (
     SELECT TOP 1 name 
     FROM sys.tables 
     where name = ist.TABLE_NAME) 
ORDER BY ist.TABLE_NAME 

OPEN CUR_TAB 

    FETCH NEXT FROM CUR_TAB 
    INTO @CURRENT_TABLE 


WHILE @@FETCH_STATUS = 0 
BEGIN 

     PRINT ' 
     IF OBJECT_ID('''[email protected]_TABLE+''') IS NULL 
     BEGIN 
     SET @SQL = '' 
     CREATE TABLE [' + @CURRENT_TABLE +'] (placeholder bit)'' 
     EXEC sp_executesql @SQL 
     END 
     ' 

DECLARE CUR CURSOR FOR 
SELECT COLUMN_NAME, DATA_TYPE 
FROM INFORMATION_SCHEMA.COLUMNS 
WHERE TABLE_NAME = @CURRENT_TABLE 
AND TABLE_SCHEMA = @CURRENT_SCHEMA 
ORDER BY ORDINAL_POSITION asc 

OPEN CUR 

    FETCH NEXT FROM CUR 
    INTO @CURRENT_COLUMN, @CURRENT_DATATYPE 
    SET @COLUMN_LITERAL = '[' + @CURRENT_COLUMN + ']' 

WHILE @@FETCH_STATUS = 0 
BEGIN 

    SET @SQLB = '' 
    SET @COMPUTED = 0 

    /* Check if column is computed */ 

     IF (SELECT is_computed FROM sys.columns 
     WHERE object_id = OBJECT_ID(@CURRENT_TABLE) 
     AND name = @CURRENT_COLUMN) = 1 
     BEGIN 
     SET @SQLB = @SQLB + 'IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc 
     INNER JOIN sys.tables st ON st.object_id = sc.object_id 
     INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id 
     WHERE sc.Name = ''' + @CURRENT_COLUMN + ''' 
     AND st.Object_ID = OBJECT_ID('''+ @CURRENT_TABLE+ ''') 
     AND ss.name = ''' + @CURRENT_SCHEMA + ''') 
BEGIN 
ALTER TABLE ' + @CURRENT_SCHEMA + '.[' + @CURRENT_TABLE + '] 
     ADD ' + @CURRENT_COLUMN + ' AS ' + 
     (SELECT definition FROM sys.computed_columns 
     WHERE object_id = OBJECT_ID(@CURRENT_TABLE) 
     AND name = @CURRENT_COLUMN) 

     SET @COMPUTED = 1 

     END 

     /* Check for identity */ 

     IF (SELECT is_identity FROM sys.columns WHERE object_id = OBJECT_ID(@CURRENT_TABLE) 
     AND name = @CURRENT_COLUMN) = 1 
     BEGIN 
     SET @SQLB = @SQLB + ' IDENTITY (' + 
     CAST((SELECT IDENT_SEED(@CURRENT_SCHEMA + '.[' + @CURRENT_TABLE + ']')) AS VARCHAR(4)) + ',' + 
     CAST((SELECT IDENT_INCR(@CURRENT_SCHEMA + '.[' + @CURRENT_TABLE + ']')) AS VARCHAR(4)) + ')' 
     END 

     /* Check if NULL is allowed */ 

     IF (SELECT sc.is_nullable from sys.columns sc 
     INNER JOIN sys.tables st ON st.object_id = sc.object_id 
     INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id 
     INNER JOIN sys.types sp ON sp.system_type_id = sc.system_type_id 
     WHERE st.name = @CURRENT_TABLE 
     AND sc.name = @CURRENT_COLUMN 
     AND ss.name = @CURRENT_SCHEMA 
     AND sp.name = @CURRENT_DATATYPE 
    ) = 0 
     BEGIN 
     SET @SQLB = @SQLB + ' NOT NULL' 
     END 
     ELSE SET @SQLB = @SQLB + ' NULL' 

     /* Check for defaults */ 

     IF (SELECT COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS 
     WHERE COLUMN_NAME = @CURRENT_COLUMN 
     AND TABLE_SCHEMA = @CURRENT_SCHEMA 
     AND TABLE_NAME = @CURRENT_TABLE) IS NOT NULL 
     BEGIN 
     SET @CURRENT_DEFAULT = ' DEFAULT ' + (SELECT COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS 
     WHERE COLUMN_NAME = @CURRENT_COLUMN 
     AND TABLE_SCHEMA = @CURRENT_SCHEMA 
     AND TABLE_NAME = @CURRENT_TABLE) 

     END 
ELSE SET @CURRENT_DEFAULT = '' 


IF @CURRENT_DATATYPE in ('date','datetime2','datetime','time', 
     'smalldatetime','datetimeoffset','text','ntext', 
     'varchar','char','nchar','nvarchar') 

    BEGIN 

     /* Check for date related data types */ 

    IF @CURRENT_DATATYPE in ('date','datetime2','datetime','time', 
     'smalldatetime','datetimeoffset') 
     BEGIN 
     SET @SQLA = ' 
    IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc 
    INNER JOIN sys.tables st ON st.object_id = sc.object_id 
    INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id 
    WHERE sc.Name = ''' + @CURRENT_COLUMN + ''' 
    AND st.Object_ID = OBJECT_ID('''+ @CURRENT_TABLE+ ''') 
    AND ss.name = ''' + @CURRENT_SCHEMA + ''') 
     BEGIN 
     ALTER TABLE ' + @CURRENT_SCHEMA + '.['+ @CURRENT_TABLE + '] 
     ADD '[email protected]_LITERAL+'' + ' ' + ''[email protected]_DATATYPE+' '[email protected]_DEFAULT 

     END 

     /* Check for MAX column length */ 

    IF (SELECT sc.max_length FROM sys.columns sc 
INNER JOIN sys.tables st ON st.object_id = sc.object_id 
INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id 
INNER JOIN sys.types sp ON sp.system_type_id = sc.system_type_id 
WHERE st.name = @CURRENT_TABLE 
AND sc.name = @CURRENT_COLUMN 
AND ss.name = @CURRENT_SCHEMA 
AND sp.name = @CURRENT_DATATYPE) = -1 

BEGIN 

SET @SQLA = ' 
    IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc 
    INNER JOIN sys.tables st ON st.object_id = sc.object_id 
    INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id 
    WHERE sc.Name = ''' + @CURRENT_COLUMN + ''' 
    AND st.Object_ID = OBJECT_ID('''+ @CURRENT_TABLE+ ''') 
    AND ss.name = ''' + @CURRENT_SCHEMA + ''') 
     BEGIN 
     ALTER TABLE ' + @CURRENT_SCHEMA + '.['+ @CURRENT_TABLE + '] 
     ADD '[email protected]_LITERAL+'' + ' ' + ''[email protected]_DATATYPE+'(MAX)'+' ' + @CURRENT_DEFAULT 
END 

     /* Check for string data types */ 

ELSE IF @CURRENT_DATATYPE in ('varchar','char','nchar','nvarchar') 
BEGIN 
SET @SQLA = ' 
    IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc 
    INNER JOIN sys.tables st ON st.object_id = sc.object_id 
    INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id 
    WHERE sc.Name = ''' + @CURRENT_COLUMN + ''' 
    AND st.Object_ID = OBJECT_ID('''+ @CURRENT_TABLE+ ''') 
    AND ss.name = ''' + @CURRENT_SCHEMA + ''') 
     BEGIN 
     ALTER TABLE ' + @CURRENT_SCHEMA + '.[' + @CURRENT_TABLE + '] 
     ADD '[email protected]_LITERAL+'' + ' ' + ''[email protected]_DATATYPE+'' 
     + '(' + 
     CAST( 
     (SELECT 
     CASE WHEN @CURRENT_DATATYPE IN ('nchar', 'nvarchar') THEN MAX(sc.max_length)/2 
     ELSE MAX(sc.max_length) END AS 'max_length' FROM sys.columns sc 
     INNER JOIN sys.tables st ON st.object_id = sc.object_id 
     INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id 
     INNER JOIN sys.types sp ON sp.system_type_id = sc.system_type_id 
     WHERE st.name = @CURRENT_TABLE 
     AND sc.name = @CURRENT_COLUMN 
     AND ss.name = @CURRENT_SCHEMA 
     AND sp.name = @CURRENT_DATATYPE 
     ) 
     AS VARCHAR(10)) +')'[email protected]_DEFAULT 

END 

    /* Check for text and ntext types (no column width) */ 

ELSE IF @CURRENT_DATATYPE in ('text','ntext') 
BEGIN 
SET @SQLA = ' 
    IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc 
    INNER JOIN sys.tables st ON st.object_id = sc.object_id 
    INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id 
    WHERE sc.Name = ''' + @CURRENT_COLUMN + ''' 
    AND st.Object_ID = OBJECT_ID('''+ @CURRENT_TABLE+ ''') 
    AND ss.name = ''' + @CURRENT_SCHEMA + ''') 
     BEGIN 
     ALTER TABLE ' + @CURRENT_SCHEMA + '.[' + @CURRENT_TABLE + '] 
     ADD '[email protected]_LITERAL+'' + ' ' + ''[email protected]_DATATYPE+' '[email protected]_DEFAULT 

END 


    END 
ELSE 

     /* Check for decimal and numeric types */ 

IF @CURRENT_DATATYPE in ('decimal','numeric') 
    BEGIN 


SET @SQLA = ' 
    IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc 
    INNER JOIN sys.tables st ON st.object_id = sc.object_id 
    INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id 
    WHERE sc.Name = ''' + @CURRENT_COLUMN + ''' 
    AND st.Object_ID = OBJECT_ID('''+ @CURRENT_TABLE+ ''') 
    AND ss.name = ''' + @CURRENT_SCHEMA + ''') 
     BEGIN 
     ALTER TABLE ' + @CURRENT_SCHEMA + '.[' + @CURRENT_TABLE + '] 
     ADD '[email protected]_LITERAL+'' + ' ' + ''[email protected]_DATATYPE+''+'(' + CAST((SELECT MIN(NUMERIC_PRECISION) FROM INFORMATION_SCHEMA.COLUMNS 
     WHERE TABLE_NAME = @CURRENT_TABLE 
     AND COLUMN_NAME = @CURRENT_COLUMN 
     AND TABLE_SCHEMA = @CURRENT_SCHEMA 
     AND DATA_TYPE = @CURRENT_DATATYPE 
     ) AS VARCHAR(10)) + ',' + 

     CAST((SELECT MIN(NUMERIC_SCALE) FROM INFORMATION_SCHEMA.COLUMNS 
     WHERE TABLE_NAME = @CURRENT_TABLE 
     AND COLUMN_NAME = @CURRENT_COLUMN 
     AND DATA_TYPE = @CURRENT_DATATYPE 
     ) AS VARCHAR(10)) + ')'+ @CURRENT_DEFAULT 

    END 
ELSE 
    BEGIN 
SET @SQLA = ' 
    IF NOT EXISTS(SELECT TOP 1 sc.name FROM sys.columns sc 
    INNER JOIN sys.tables st ON st.object_id = sc.object_id 
    INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id 
    WHERE sc.Name = ''' + @CURRENT_COLUMN + ''' 
    AND st.Object_ID = OBJECT_ID('''+ @CURRENT_TABLE+ ''') 
    AND ss.name = ''' + @CURRENT_SCHEMA + ''') 
     BEGIN 
     ALTER TABLE ' + @CURRENT_SCHEMA + '.[' + @CURRENT_TABLE + '] 
     ADD '[email protected]_LITERAL+'' + ' ' + ''[email protected]_DATATYPE+''[email protected]_DEFAULT 


END 

IF @COMPUTED = 0 
BEGIN 

    PRINT @SQLA + @SQLB + ' 
    END 
    ' 


END 

    FETCH NEXT FROM CUR 
    INTO @CURRENT_COLUMN, @CURRENT_DATATYPE 
    SET @COLUMN_LITERAL = '[' + @CURRENT_COLUMN + ']' 

END 
CLOSE CUR; 
DEALLOCATE CUR; 

PRINT ' 
    IF EXISTS 
    (SELECT TOP 1 sc.name FROM sys.columns sc 
    INNER JOIN sys.tables st ON st.object_id = sc.object_id 
    INNER JOIN sys.schemas ss ON ss.schema_id = st.schema_id 
    WHERE sc.Name = ''placeholder'' 
    AND st.Object_ID = OBJECT_ID('''+ @CURRENT_TABLE+ ''') 
    AND ss.name = ''' + @CURRENT_SCHEMA + ''') 
      BEGIN 
      ALTER TABLE '[email protected]_SCHEMA+'.['[email protected]_TABLE+'] DROP COLUMN [placeholder] 
      END 

      ' 

    FETCH NEXT FROM CUR_TAB 
    INTO @CURRENT_TABLE 

END 
CLOSE CUR_TAB 
DEALLOCATE CUR_TAB 

END 
CLOSE CUR_SCHEMA 
DEALLOCATE CUR_SCHEMA 

Fragen:

  • konnte mein Verwendung einer "Platzhalter" -Spalte vermieden werden? (Ich habe es hinzugefügt, weil ich keine leeren Spalten erstellen konnte, falls sie nicht existierten).
  • Wird die Verwendung von drei Cursorn akzeptiert? Ich glaube, das könnte vereinfacht werden, vielleicht mit temporären Tabellen oder Tabellenvariablen.
  • Ist mein Ansatz, um außergewöhnliche Formatierungsfälle (wie die numerische Datentypdefinition oder die maximale Spaltenzeichenlänge) kohärent zu fangen?
  • Ist das Skript vollständig korrekt? Ich habe es intensiv mit einer Kopie einer echten Datenbank getestet und auch das erzeugte Skript mit einer leeren Datenbank getestet, und es scheint die erwarteten Ergebnisse hervorgebracht zu haben.
  • Ist die Anzahl der Variablen übermäßig groß? Ist irgendeine meiner Variablen irrelevant?
  • Ist die Verwendung von INFORMATION_SCHEMA und Systemtabellen akzeptabel? (Ich habe INFORMATION_SCHEMA einige Male verwendet, um zu vermeiden, dass die Tabellenverbindung übermäßig ist).
  • Verwende ich Cursor korrekt?
  • Würden Sie einen anderen Ansatz für einen Abschnitt meines Skripts vorschlagen?

Danke, und tut mir leid, dass ich so viele Fragen gestellt habe. Antworten Sie nur einen oder einige, wenn Sie nicht alle beantworten möchten!

** NOTES **

  • Ich schrieb dies für eine SQL Server 2008-Datenbank, aber Sie Alternativen für neuere Versionen hinweisen könnten mein Wissen zu verbessern
  • Ich weiß, dass dieser Skript doesn‘ t replizierte gespeicherte Prozeduren, Trigger und andere Dinge, aber das könnte später automatisch mit SSMS gescriptet werden, also habe ich nur Spalteneigenschaften in das Skript aufgenommen.
+1

Oder Sie könnten nur kaufen so etwas wie SQL von redgate vergleichen, die für Sie all dies tun wird. Der Code, den Sie gepostet haben, scheint eine Menge wirklich wichtiger Dinge wie Einschränkungen, Fremdschlüssel, Primärschlüssel, Indizes zu vermissen. Es hat auch einige ernste Probleme mit Schemas. Wenn Sie in zwei Schemas den gleichen Tabellennamen haben, ist Ihr Code zum Abrufen der Spalten durcheinander, da Sie das Schema nicht angeben. –

+0

Ich weiß über sqlcompare (kaufte es nie). Ich habe das Skript zu einem Experiment gemacht, und wie ich bereits erwähnte, interessieren mich Dinge wie Fremdschlüssel, Primärschlüssel und Indizes nicht, weil SSMS das Skript verwenden kann und ich einfach sein Skript darüber laufen lassen könnte. Bei Schemas erzwingt der erste Cursor ** CUR_SCHEMA ** die Iteration jedes Schemas und jeder Tabelle in diesem Schema (oder soll es zumindest noch einmal testen). – Renato

+0

Nein, es wird nicht funktionieren. Ihre Abfrage "SELECT COLUMN_NAME, DATA_TYPE aus INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @CURRENT_TABLE ORDER VON ORDINAL_POSITION asc" wird Zeilen aus allen Tabellen mit demselben Namen zurückgeben, unabhängig davon, in welchem ​​Schema sie sich befindet. –

Antwort

1

Also würde ich die Verwendung der Sql Server Data Tools und Datenbank-Projekt vorschlagen.Sie können ein Schema der vorhandenen Datenbank importieren oder ein neues Datenbankprojekt erstellen. Sie können es mit dem TFS oder Git verknüpfen, Ihrer bevorzugten Quellcodeverwaltung. In diesem Fall benötigen Sie kein separates SQL-Skript zum Generieren des Schemas. Wenn Sie sich entscheiden, von SSDT zur Schemaentwicklung überzugehen, wird die Codebasis immer das letzte bekannte Schema haben. Sie können dann Skripte für die Bereitstellung der neuen Codebasis und SSDT generieren oder Visual Studio wird die SQL out, ich würde dringend empfehlen, diese Skripts vor der Bereitstellung zu prod zu überprüfen. Es ist auch möglich, Änderungen an Zieldatenbanken über dieses Tool zu veröffentlichen.

SSDT

+1

Ich habe vergessen zu erwähnen, dass Sie in den Eigenschaften angeben können, auf welche SQL Server-Version Ihr Projekt abzielt, also 2008 oder 2012 oder 2014 oder 2016. Das Projekt sucht dann nach einem Code, der in einem gegebenen Code verwendet werden kann Version von SQL Server. Alternative dazu ist das Redgate –

+0

Danke. Ich werde es sicherlich versuchen! Sie würden jedoch das abfragebasierte Schema-Kopieren nicht empfehlen? – Renato

+0

@Dariusz Ich werde diese Lösung mit Ausnahme der Tabellen größer als ein paar TB (sie brauchen Skripte, so dass Sie die beste Methode, sonst dauert es zu lange zu implementieren) –

Verwandte Themen