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.
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. –
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
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. –