SELECT子句中的+ =;常量vs列

时间:2019-02-27 06:39:52

标签: sql sql-server tsql sql-server-2017

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

    select中的
  • +=充当聚合器(“连接所有行中的值”),当右边是常数时。
  • 当右侧的列名时,select中的
  • +=充当“只是设置值”。 (同样,这将导致其他列的汇总行为)

所以我的问题是:

  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)

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

(目前,我的目标是完全了解这种行为,所以我不会无意间踩到它。任何有关搜索正确文档/关键字的提示都会有所帮助)

3 个答案:

答案 0 :(得分:1)

它位于wrong place in the documentation中,所以您没有找到它就不足为奇了:

  

请勿在SELECT语句中使用变量来连接值(即,计算聚合值)。可能会出现意外的查询结果。因为,SELECT列表中的所有表达式(包括赋值)不一定对每个输出行只运行一次

最好寻找不同的字符串连接方式。如果您的版本支持它,请选择使用STRING_AGG。对于早期版本,Aaron Bertrand提供了good set of options(向Panagiotis Kanavos推荐了providing the link的技巧)

答案 1 :(得分:0)

好吧,我发现的发现很有趣:没有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

答案 2 :(得分:-1)

您应该使用Order by constraint_name而不是Order by 1,因为您正在将值分配给变量。它不是select语句。