2013-10-08 64 views
0

我試圖構建一個腳本,將所有外鍵放在特定模式中(我正在創建一個新的數據庫,它需要腳本到其他幾個位置,一個測試環境和一個生產)。擁有這三個獨立環境的最簡單方法是在「開發」中使用我的「CREATE」腳本,然後在以後通過其他環境進行處理。這需要(作爲一種可能的解決方案)「CREATE」腳本的開始是If Exists(...) DROP...ORDER BY更改字符串串聯SQL

我已經解決了我遇到的問題,但希望能有人解釋我看到的行爲(見下文)。

此代碼工作(我得到了所有記錄作爲自己的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 

出錯行似乎是在ORDER BYOBJECT_NAME(fk.referenced_object_id)作爲評論說出來解決問題,但我不明白爲什麼它的失敗。我的懷疑是有些事情是由內在或某事組成的。

即使包括從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

的可能重複[爲什麼SQL Server會忽略在指定的ORDER BY子句時字符串連接vaules(http://stackoverflow.com/questions/5538187/why-sql-server-ignores-vaules-in-string -concatenation-when-order-by-clause-speci) –

+0

這裏的順序仍然會導致它失敗,即使在select語句中指定了列時也是如此。但是,更改JOIN子句似乎允許ORDER BY工作。看我的編輯。 – Jon

回答

1

原因是as Martin Smith pointed out:聚合字符串連接沒有定義的行爲。

解決方案是使用遊標,而不是假裝你沒有。您還應該:(a)不要使用VARCHAR作爲元數據 - 它們全部存儲爲Unicode,因此您應該在實體名稱周圍使用SYSNAME/NVARCHAR(b)使用QUOTENAME以防止它們是保留字或其他無效標識符(c)一次獲取模式的標識而不是內聯。

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;