0

我正在尝试构建一个脚本,该脚本将删除特定模式中的所有外键(我正在创建一个新数据库,该数据库需要编写脚本到其他几个位置,一个测试环境和一个生产环境)。拥有这三个独立环境的最简单方法是在“开发”上使用我的“CREATE”脚本,然后在以后通过我们的其他环境进行分阶段。这要求(作为一种可能的解决方案)“CREATE”脚本的开头是一种If Exists(...) DROP....

我已经解决了我遇到的问题,但希望有人可以解释我所看到的行为(见下文)。

此代码有效(我得到一个将所有记录作为它们自己的 SQL 语句的语句):

DECLARE @SQL VARCHAR(MAX) = ''
DECLARE @Schema VARCHAR(100) = 'SchemaName'
SELECT 
    @SQL = @SQL
         + 'ALTER TABLE ' + tpa.name + CHAR(13) + CHAR(10)
         + CHAR(9) + 'DROP CONSTRAINT ' + fk.name +';' + CHAR(13) + CHAR(10)
FROM 
    sys.foreign_keys AS fk
JOIN sys.foreign_key_columns AS fkc 
    ON fkc.constraint_object_id = fk.object_id
JOIN sys.tables AS tpa
    ON fk.parent_object_id = tpa.object_id
JOIN sys.columns AS cpa 
    ON tpa.object_id = cpa.object_id AND 
       fkc.parent_column_id = cpa.column_id
JOIN sys.tables AS tref
    ON fkc.parent_object_id = tref.object_id
JOIN sys.columns AS cref 
    ON tref.object_id = cref.object_id AND 
       fkc.referenced_column_id = cref.column_id
WHERE 
    SCHEMA_NAME(fk.schema_id) = @Schema

PRINT @SQL 

此代码不起作用(它只返回记录集的最后一行):

DECLARE @SQL VARCHAR(MAX) = ''
DECLARE @Schema VARCHAR(100) = 'SchemaName'
SELECT 
    @SQL = @SQL
         + 'ALTER TABLE ' + tpa.name + CHAR(13) + CHAR(10)
         + CHAR(9) + 'DROP CONSTRAINT ' + fk.name +';' + CHAR(13) + CHAR(10)
FROM 
    sys.foreign_keys AS fk
JOIN sys.foreign_key_columns AS fkc 
    ON fkc.constraint_object_id = fk.object_id
JOIN sys.tables AS tpa
    ON fk.parent_object_id = tpa.object_id
JOIN sys.columns AS cpa 
    ON tpa.object_id = cpa.object_id AND 
       fkc.parent_column_id = cpa.column_id
JOIN sys.tables AS tref
    ON fkc.parent_object_id = tref.object_id
JOIN sys.columns AS cref 
    ON tref.object_id = cref.object_id AND 
       fkc.referenced_column_id = cref.column_id
WHERE 
    SCHEMA_NAME(fk.schema_id) = @Schema
ORDER BY 
     OBJECT_NAME(fk.parent_object_id)
    ,OBJECT_NAME(fk.referenced_object_id)
    ,fk.name
PRINT @SQL

违规行似乎是OBJECT_NAME(fk.referenced_object_id)ORDER BY注释中解决了问题,但我不明白为什么它会失败。我的怀疑是某些东西是天生的或某种东西在做一个小组。

即使包括所有列ORDER BY似乎也无济于事:

DECLARE @SQL VARCHAR(MAX) = ''
DECLARE @Schema VARCHAR(100) = 'UserMgmt'
SELECT 
    @SQL = @SQL
         + 'ALTER TABLE ' + tpa.name + CHAR(13) + CHAR(10)
         + CHAR(9) + 'DROP CONSTRAINT ' + fk.name +';' + CHAR(13) + CHAR(10)
         + '--' + OBJECT_NAME(fk.referenced_object_id) + ', ' + OBJECT_NAME(fk.parent_object_id) + CHAR(13) + CHAR(10)
FROM 
    sys.foreign_keys AS fk
JOIN sys.foreign_key_columns AS fkc 
    ON fkc.constraint_object_id = fk.object_id
JOIN sys.tables AS tpa
    ON fk.parent_object_id = tpa.object_id
JOIN sys.columns AS cpa 
    ON tpa.object_id = cpa.object_id AND 
       fkc.parent_column_id = cpa.column_id
JOIN sys.tables AS tref
    ON fkc.parent_object_id = tref.object_id
JOIN sys.columns AS cref 
    ON tref.object_id = cref.object_id AND 
       fkc.referenced_column_id = cref.column_id
WHERE 
    SCHEMA_NAME(fk.schema_id) = @Schema
ORDER BY 
     OBJECT_NAME(fk.parent_object_id)
    ,OBJECT_NAME(fk.referenced_object_id)
    ,fk.name
PRINT @SQL

但是,更改JOIN引用表的语句确实可以做到这一点:

DECLARE @SQL VARCHAR(MAX) = ''
DECLARE @Schema VARCHAR(100) = 'UserMgmt'
SELECT 
    @SQL = @SQL
         + 'ALTER TABLE ' + tpa.name + CHAR(13) + CHAR(10)
         + CHAR(9) + 'DROP CONSTRAINT ' + fk.name +';' + CHAR(13) + CHAR(10)
         + '--' + OBJECT_NAME(fk.referenced_object_id) + ', ' + OBJECT_NAME(fk.parent_object_id) + CHAR(13) + CHAR(10)
FROM 
    sys.foreign_keys AS fk
JOIN sys.foreign_key_columns AS fkc 
    ON fkc.constraint_object_id = fk.object_id
JOIN sys.tables AS tpa
    ON fk.parent_object_id = tpa.object_id
JOIN sys.columns AS cpa 
    ON tpa.object_id = cpa.object_id AND 
       fkc.parent_column_id = cpa.column_id
JOIN sys.tables AS tref
    ON fk.referenced_object_id = tref.object_id
JOIN sys.columns AS cref 
    ON tref.object_id = cref.object_id AND 
       fkc.referenced_column_id = cref.column_id
WHERE 
    SCHEMA_NAME(fk.schema_id) = @Schema
ORDER BY 
     OBJECT_NAME(fk.parent_object_id)
    ,OBJECT_NAME(fk.referenced_object_id)
    ,fk.name
PRINT @SQL

谁能解释这种行为?

4

1 回答 1

1

原因正如 Martin Smith 指出的那样:聚合字符串连接没有定义的行为。

解决方案是使用光标而不是假装你不是。您还应该 (a) 不VARCHAR用于元数据 - 它全部存储为 Unicode,因此您应该使用SYSNAME/NVARCHAR(b)QUOTENAME在实体名称周围使用,以防它们是保留字或其他无效标识符 (c) 一次性获取架构的 ID 而不是内联.

DECLARE @schemaID INT = SCHEMA_ID(N'SchemaName');

DECLARE @sql NVARCHAR(MAX) = N'', @tpaname SYSNAME, @fkname SYSNAME;

DECLARE c CURSOR LOCAL FAST_FORWARD FOR
SELECT tpa.name, fk.name FROM sys.foreign_keys AS fk
INNER JOIN sys.foreign_key_columns AS fkc ON fkc.constraint_object_id = fk.object_id
INNER JOIN sys.tables AS tpa ON fk.parent_object_id = tpa.object_id
INNER JOIN sys.columns AS cpa ON tpa.object_id = cpa.object_id AND 
       fkc.parent_column_id = cpa.column_id
INNER JOIN sys.tables AS tref ON fkc.parent_object_id = tref.object_id
INNER JOIN sys.columns AS cref ON tref.object_id = cref.object_id AND 
       fkc.referenced_column_id = cref.column_id
WHERE fk.schema_id = @SchemaID
ORDER BY 
     OBJECT_NAME(fk.parent_object_id)
    ,OBJECT_NAME(fk.referenced_object_id)
    ,fk.name;

OPEN c;

FETCH c INTO @tpaname, @fkname;

WHILE @@FETCH_STATUS <> -1;
BEGIN
  SET @sql += N'ALTER TABLE ' + QUOTENAME(@tpaname) + CHAR(13) + CHAR(10)
         + CHAR(9) + 'DROP CONSTRAINT ' + QUOTENAME(@fkname) +';' + CHAR(13) + CHAR(10)
  FETCH c INTO @tpaname, @fkname;
END

CLOSE c; DEALLOCATE c;

PRINT @sql;
于 2013-10-08T15:13:55.853 回答