【问题标题】:ORDER BY changes String Concatenation SQLORDER BY 更改字符串连接 SQL
【发布时间】:2013-10-15 13:55:39
【问题描述】:

我正在尝试构建一个脚本,该脚本将删除特定架构中的所有外键(我正在创建一个新数据库,该数据库需要脚本化到其他几个位置、一个测试环境和一个生产环境)。拥有这三个独立环境的最简单方法是在“开发”上使用我的“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

违规行似乎是ORDER BY 中的OBJECT_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

谁能解释这种行为?

【问题讨论】:

标签: sql-server tsql sql-order-by string-concatenation


【解决方案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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-26
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    • 2012-04-22
    • 1970-01-01
    相关资源
    最近更新 更多