Sql 为数据库中的所有表/列生成DDL/DML脚本
这主要是为了学习一些SQL Server概念而做的一个实验。假设以下场景:Sql 为数据库中的所有表/列生成DDL/DML脚本,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,这主要是为了学习一些SQL Server概念而做的一个实验。假设以下场景: 我有一个生产数据库、一个开发数据库和一个测试数据库 开发数据库比测试数据库更新得更多,包含最近开发的几个新表和列 我也想更新测试数据库(使用这些新的表和列),但不想删除并重新创建该数据库(它包含有用的测试数据) 我在下面编写的脚本是针对“development”数据库执行的,因此它将为数据库的每一列生成一个带有条件的脚本。然后,应针对其他数据库使用脚本来更新它,并且条件应添加测试数据库尚未具有的任何列或表: DECL
- 我有一个生产数据库、一个开发数据库和一个测试数据库李>
- 开发数据库比测试数据库更新得更多,包含最近开发的几个新表和列李>
- 我也想更新测试数据库(使用这些新的表和列),但不想删除并重新创建该数据库(它包含有用的测试数据)
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('''+@CURRENT_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 '+@COLUMN_LITERAL+'' + ' ' + ''+@CURRENT_DATATYPE+' '+@CURRENT_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 '+@COLUMN_LITERAL+'' + ' ' + ''+@CURRENT_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 '+@COLUMN_LITERAL+'' + ' ' + ''+@CURRENT_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)) +')'+@CURRENT_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 '+@COLUMN_LITERAL+'' + ' ' + ''+@CURRENT_DATATYPE+' '+@CURRENT_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 '+@COLUMN_LITERAL+'' + ' ' + ''+@CURRENT_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 '+@COLUMN_LITERAL+'' + ' ' + ''+@CURRENT_DATATYPE+''+@CURRENT_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 '+@CURRENT_SCHEMA+'.['+@CURRENT_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
问题:
- 我是否可以避免使用“占位符”列?(我添加它是因为我无法创建空列,以防它们不存在)
- 是否接受使用三个游标?我相信这可以简化,也许可以使用临时表或表变量
- 我捕获异常格式情况(如数字数据类型定义或最大列字符长度)的方法是否具有内聚性
- 脚本完全正确吗?我针对一个真实数据库的副本对它进行了深入测试,还针对一个空白数据库对它生成的脚本进行了测试,似乎已经产生了预期的结果
- 我使用的变量数量是否过多?我的任何变量都不相关吗
- 是否可以同时使用信息\u模式和系统表?(我曾多次使用信息\u模式来避免过度的表连接)
- 我是否正确使用游标
- 你能为我的剧本的某些部分提出不同的方法吗
- 我是为SQLServer2008数据库写这篇文章的,但您可以指出更新版本的替代方案,以帮助提高我的知识
- 我知道这个脚本不会复制存储过程、触发器和其他东西,但是可以在以后使用SSM自动编写脚本,所以我只在脚本中包含列属性
因此,我建议使用Sql Server数据工具和数据库项目。它允许您导入现有数据库的架构或创建新的数据库项目。您可以将其链接到TFS或Git,这是您对源代码管理的首选项。在这种情况下,您不必使用单独的SQL脚本来生成模式。如果您决定从SSDT转移到模式开发,那么代码库将始终具有最新的已知模式。然后可以生成用于部署新代码库的脚本,SSDT或visualstudio将计算出SQL,我强烈建议在部署到prod之前检查这些脚本。 还可以从该工具将更改发布到目标数据库
或者您可以从redgate购买类似sql compare的东西,它将为您完成所有这些。您发布的代码似乎遗漏了很多真正重要的东西,如约束、外键、主键和索引。它在模式方面也有一些严重的问题。如果在两个模式中有相同的表名,那么获取列的代码将全部混淆,因为您没有指定模式。我知道sqlcompare(但从未购买过)。我把这个脚本作为一个实验,正如我指出的,我并不真正关心外键、主键和索引之类的东西,因为SSM可以编写脚本,我可以在上面运行它的脚本。关于模式,第一个游标CUR_模式强制对该模式中的每个模式和每个表进行迭代(或者至少应该对其进行进一步测试)。不,它不起作用。您的查询“SELECT COLUMN_NAME,DATA_TYPE from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=@CURRENT_TABLE ORDER BY ORDINAL_POSITION asc”将返回所有具有相同名称的表中的行,而不管它位于哪个架构中。您是对的,我忘了在那里为@CURRENT_SCHEMA变量添加条件,现在我已经包含了它。谢谢我忘了提到,在属性中,您可以指定项目的目标SQL Server版本,因此可以是2008年、2012年、2014年或2016年。然后,Project将查找可以或不能在给定版本的SQL Server中使用的代码。除此之外还有红门谢谢。我一定要试一试!但是,您不建议基于查询的模式复制,那么?@Dariusz我支持该解决方案,但大于几TB的表除外(它们需要脚本,因此您可以获得最佳方法,否则部署时间太长)我尝试了SSDT,但是对于某些引用了不可访问数据库的数据库对象,存在很多问题。我不能忽略这些引用“问题”而复制模式?你是对的。你不应该忽视参考问题,如果可能的话,应该调查它们。我们所发现的是,在一些数据库中有很多对象,这些对象引用了不存在的存储过程、视图或字段,因此sst允许我们识别和修复这些金块。此外,如果对象的名称是treepart,并且它正在引用自己的数据库,那么它也会抛出一个错误,因此我们删除引用。如果有对象查看不同的数据库,我们也必须将这些数据库作为引用添加。