使用SQL串联与ORDER BY

Ale*_*hov 5 sql t-sql sql-server sql-server-2014

我糊涂了.你如何用ORDER BY解释变量连接中的这种差异?

declare @tbl table (id int);
insert into @tbl values (1), (2), (3);

declare @msg1 varchar(100) = '', @msg2 varchar(100) = '',
    @msg3 varchar(100) = '',    @msg4 varchar(100) = '';

select @msg1 = @msg1 + cast(id as varchar) from @tbl
order by id;

select @msg2 = @msg2 + cast(id as varchar) from @tbl
order by id+id;

select @msg3 = @msg3 + cast(id as varchar) from @tbl
order by id+id desc;

select TOP(100) @msg4 = @msg4 + cast(id as varchar) from @tbl
order by id+id;

select
    @msg1 as msg1,
    @msg2 as msg2,
    @msg3 as msg3,
    @msg4 as msg4;
Run Code Online (Sandbox Code Playgroud)

结果

msg1  msg2  msg3  msg4
----  ----  ----  ----
123   3     1     123  
Run Code Online (Sandbox Code Playgroud)

Jas*_*n W 5

正如许多人已经证实的那样,这不是将列中的所有行连接成变量的正确方法 - 即使在某些情况下它确实"有效".如果您想看一些替代方案,请查看此博客.

根据MSDN(适用于SQL Server 2008到2014和Azure SQL数据库),SELECT不应该用于分配局部变量.在备注中,它描述了当您使用SELECT它时,它会如何尝试表现.有趣的注意事项:

  • 虽然它通常只用于将单个值返回给变量,但当表达式是列的名称时,它可以返回多个值.
  • 当表达式返回多个值时,将为变量分配返回的最后一个值.
  • 如果没有返回值,则变量保留其原始值(此处不直接相关,但值得注意).

这里的前两点是关键 - 连接恰好起作用,因为SELECT @msg1 = @msg1 + cast(id as varchar)它本质上SELECT @msg1 += cast(id as varchar)是语法注释,+=是该表达式上可接受的复合赋值运算符.请注意,不应该期望继续支持此操作VARCHAR并进行字符串连接 - 仅仅因为它在某些情况下适用并不意味着它对于生产代码是可行的.

根本原因的底线是Compute Scalar,在select表达式上运行的是使用原始id列还是id列的表达式.您可能找不到任何有关优化程序为每个查询选择特定计划的原因的文档,但每个示例都突出显示允许从列计算msg值的不同用例(因此返回和连接多行)或表达式(因此只有最后一列).

  1. @ msg1是'123',因为Compute Scalar(变量赋值的逐行评估)发生在Sort.之后.这允许标量计算在id列上返回多个值,通过+=复合运算符将它们连接起来.我怀疑有特定文档的原因,但似乎优化器选择在标量计算之前进行排序,因为order by是一列而不是表达式.

  2. @ msg2是'3',因为它Compute Scalar是在排序之前完成的,这使得每行中的@ msg2只是(''+ id) - 所以从不连接,只是id的值.同样,可能没有任何文档为什么优化器选择了这个,但似乎因为order by是一个表达式,也许它需要在它可以排序之前作为标量计算的一部分按顺序执行(id + id).此时,原始列不再引用源列,但已由表达式替换.因此,正如MSDN所述,您的第一列指向表达式而不是列,因此行为将结果集的最后一个值分配给SELECT中的变量.由于您对ASC进行了排序,因此这里得到'3'.

  3. @ msg3为'1',原因与示例2相同,只是您订购了DESC.同样,这成为评估中的表达 - 而不是原始列,因此赋值获取DESC顺序的最后一个值,因此得到'1'.

  4. @ msg4再次为'123',因为该TOP操作强制进行初始标量评估,ORDER BY以便它可以确定您的前100条记录.这与示例2和3不同,在示例2和3中,标量计算包含order by和select计算,这些计算使每个示例成为表达式而不是返回原始列.示例4使TOP分离ORDER BY和SELECT计算,因此在应用SORT(TOP N SORT)之后,它会对SELECT列进行标量计算,此时您仍然在引用原始列(不是列的表达式,因此它返回多行,允许连接发生.

资料来源: