我正在尝试构建一个可以删除特定模式中所有外键的脚本(我正在创建一个新数据库,需要将脚本编写到其他几个位置,一个测试环境和一个生产环境)。拥有这三个独立环境的最简单方法是在“开发”中使用我的“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
任何人都可以解释这种行为吗?
答案 0 :(得分:1)
原因是as Martin Smith pointed out:聚合字符串连接没有定义的行为。
解决方案是使用光标而不是假装你不是。您还应该(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;