0

特定のスキーマ内のすべての外部キーを削除するスクリプトを作成しようとしていました (他のいくつかの場所、テスト環境、および運用環境にスクリプトを作成する必要がある新しいデータベースの作成に取り組んでいます)。これら 3 つの個別の環境を作成する最も簡単な方法は、"開発" で "CREATE" スクリプトを使用し、後日、他の環境を介してステージングすることです。これには、(考えられる解決策の 1 つとして)「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 を 1 回取得します.

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 に答える