+= 在 SELECT 子句中;常量与列

+= in SELECT clause; constant vs column

我在 MS SQL Server 2017 中观察到一些奇怪的行为。

所以我的问题是:

  1. 为什么 @c1 结果只包含最后一行的值,即使使用 += 也是如此?
  2. 为什么 @c2 受到 @c1+=->= 变化的影响?

版本 1:

BEGIN
    DECLARE
        @c1 NVARCHAR(MAX) = N'',
        @c2 NVARCHAR(MAX) = N'';

    SELECT
        @c1 = constraint_name, -- version-1
        @c2 += '+'
    FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
    ORDER BY 1 DESC
    ;

    PRINT '@c1=' + @c1;
    PRINT '@c2=' + @c2;
END
;

版本 1 结果:

@c1 = fk_abcde
@c2 = ++++++++++++++++++++++++++++++++++++++++++
(`@c2` result is aggregation of many rows; one plus for each row)

版本 2:

BEGIN
    DECLARE
        @c1 NVARCHAR(MAX) = N'',
        @c2 NVARCHAR(MAX) = N'';

    SELECT
        @c1 += constraint_name, -- version-2
        @c2 += '+'
    FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
    ORDER BY 1 DESC
    ;

    PRINT '@c1=' + @c1;
    PRINT '@c2=' + @c2;
END
;

版本 2 结果:

@c1 = fk_abcde
@c2 = +
(`@c2` is just value assigned from last processed row)

这感觉很奇怪 - 有点像错误。 我找不到任何关于此的文档。 doc on '+= string' 根本没有在 select 查询中提及 += 用法。

(目前我的目标是完全理解这个行为,所以我不会不小心踩到它。任何向右 documentation/keywords 搜索的提示都会有所帮助)

您应该使用 Order by constraint_name 而不是 Order by 1,因为您正在为变量赋值。它不是 select 声明。

好吧,我的发现很有趣:没有 ORDER BY 查询运行一致且符合预期。

但是当加上 ORDER BY 因果关系时,我得到的结果和你一样。

我的建议是使用 CTE 进行排序 - 不幸的是,CTE 或子查询中不允许排序,但解决方法是使用 TOP 关键字,它允许我们在这种情况下进行排序。

参见下面的脚本:

;with cte as (
    select top 100 percent table_schema
    from information_schema.columns
    order by 1
)
-- This is more reliable
select @c1 = table_schema, @c2 += '+' from cte

它在 wrong place in the documentation 中,所以您找不到它也就不足为奇了:

Don't use a variable in a SELECT statement to concatenate values (that is, to compute aggregate values). Unexpected query results may occur. Because, all expressions in the SELECT list (including assignments) aren't necessarily run exactly once for each output row

最好寻找不同的字符串连接方法。如果你的版本支持,选择使用STRING_AGG。对于早期版本,Aaron Bertrand 提供了 good set of options (hat tip to Panagiotis Kanavos for )