Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/77.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 为数据库中的所有表/列生成DDL/DML脚本_Sql_Sql Server_Sql Server 2008 - Fatal编程技术网

Sql 为数据库中的所有表/列生成DDL/DML脚本

Sql 为数据库中的所有表/列生成DDL/DML脚本,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,这主要是为了学习一些SQL Server概念而做的一个实验。假设以下场景: 我有一个生产数据库、一个开发数据库和一个测试数据库 开发数据库比测试数据库更新得更多,包含最近开发的几个新表和列 我也想更新测试数据库(使用这些新的表和列),但不想删除并重新创建该数据库(它包含有用的测试数据) 我在下面编写的脚本是针对“development”数据库执行的,因此它将为数据库的每一列生成一个带有条件的脚本。然后,应针对其他数据库使用脚本来更新它,并且条件应添加测试数据库尚未具有的任何列或表: DECL

这主要是为了学习一些SQL Server概念而做的一个实验。假设以下场景:

  • 我有一个生产数据库、一个开发数据库和一个测试数据库
  • 开发数据库比测试数据库更新得更多,包含最近开发的几个新表和列
  • 我也想更新测试数据库(使用这些新的表和列),但不想删除并重新创建该数据库(它包含有用的测试数据)
我在下面编写的脚本是针对“development”数据库执行的,因此它将为数据库的每一列生成一个带有条件的脚本。然后,应针对其他数据库使用脚本来更新它,并且条件应添加测试数据库尚未具有的任何列或表:

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,并且它正在引用自己的数据库,那么它也会抛出一个错误,因此我们删除引用。如果有对象查看不同的数据库,我们也必须将这些数据库作为引用添加。