nvarchar连接/索引/ nvarchar(最大)莫名其妙的行为

我今天在SQL Server(2008R2和2012)中遇到了一个奇怪的问题。 我试图build立一个string使用串联与一个select语句。

我find了解决办法,但我真的很想知道这里发生了什么,为什么它不给我预期的结果。 有人可以向我解释吗?

http://sqlfiddle.com/#!6/7438a/1

根据要求,还有代码在这里:

 -- base table create table bla ( [id] int identity(1,1) primary key, [priority] int, [msg] nvarchar(max), [autofix] bit ) -- table without primary key on id column create table bla2 ( [id] int identity(1,1), [priority] int, [msg] nvarchar(max), [autofix] bit ) -- table with nvarchar(1000) instead of max create table bla3 ( [id] int identity(1,1) primary key, [priority] int, [msg] nvarchar(1000), [autofix] bit ) -- fill the three tables with the same values insert into bla ([priority], [msg], [autofix]) values (1, 'A', 0), (2, 'B', 0) insert into bla2 ([priority], [msg], [autofix]) values (1, 'A', 0), (2, 'B', 0) insert into bla3 ([priority], [msg], [autofix]) values (1, 'A', 0), (2, 'B', 0) ; declare @a nvarchar(max) = '' declare @b nvarchar(max) = '' declare @c nvarchar(max) = '' declare @d nvarchar(max) = '' declare @e nvarchar(max) = '' declare @f nvarchar(max) = '' -- I expect this to work and generate 'AB', but it doesn't select @a = @a + [msg] from bla where autofix = 0 order by [priority] asc -- this DOES work: convert nvarchar(4000) select @b = @b + convert(nvarchar(4000),[msg]) from bla where autofix = 0 order by [priority] asc -- this DOES work: without WHERE clause select @c = @c + [msg] from bla --where autofix = 0 order by [priority] asc -- this DOES work: without the order by select @d = @d + [msg] from bla where autofix = 0 --order by [priority] asc -- this DOES work: from bla2, so without the primary key on id select @e = @e + [msg] from bla2 where autofix = 0 order by [priority] asc -- this DOES work: from bla3, so with msg nvarchar(1000) instead of nvarchar(max) select @f = @f + [msg] from bla3 where autofix = 0 order by [priority] asc select @a as a, @b as b, @c as c, @d as d, @e as e, @f as f 

已经由VanDerNorth链接的知识库文章包括该行

聚合串联查询的正确行为是未定义的。

但通过提供一个似乎表明确定性行为是可能的解决方法,然后继续混浊水域。

为了从聚合连接查询中获得预期的结果,请将任何Transact-SQL函数或expression式应用于SELECT列表中的列而不是ORDER BY子句中。

有问题的查询不会将任何expression式应用于ORDER BY子句中的列。

2005年的文章Ordering在SQL Server中保证…状态

出于向后兼容性的原因,SQL Server提供了对最顶级范围的SELECT @p = @p + 1 … ORDER BYtypes赋值的支持。

在连接按照您的预期运行的计划中,具有expression式[Expr1003] = Scalar Operator([@x]+[Expr1004])的计算标量出现在sorting上方。

在计划中,工作失败的计算标量出现在sorting下面。 正如在2006年的这个连接项中所解释的那样,当@ @x = @x + [msg]expression式出现在sorting的下面时,它会针对每一行进行评估,但是所有评估都使用@x的预分配值结束。 在2006年的另一个类似的连接项目中 ,微软的回应是“解决”这个问题。

在这个问题上的所有后来的连接项目(有很多)的微软响应声明,这是不能保证

例1

我们不保证连接查询的正确性(如使用variables赋值和特定顺序的数据检索)。 查询输出可以在SQL Server 2008中根据计划select,表中的数据等进行更改。即使语法允许您编写混合使用variables赋值的有序行检索的SELECT语句,也不应该依赖此工作。

例2

你看到的行为是通过devise。 在使用ORDER BY子句的查询中使用赋值操作(本例中的连接)具有未定义的行为。 由于查询计划中的更改,这可能会从发行版本更改为发行版本,甚至在特定服务器版本中也会发生变化 即使有解决方法,也不能依赖此行为。 有关更多详细信息,请参见下面的知识库
http://support.microsoft.com/kb/287515唯一保证机制如下:;

  1. 使用游标以特定顺序循环行并连接值
  2. 用于ORDER BY的xml查询来生成连接的值
  3. 使用CLR聚合(这不适用于ORDER BY子句)

例3

你所看到的行为实际上是通过devise。 这与SQL是一种集合操纵语言有关。 SELECT列表中的所有expression式(也包括分配)不能保证每个输出行仅执行一次。 实际上,SQL查询优化器尽可能less地尝试执行它们。 当您根据表中的某些数据计算variables的值时,这会给出预期的结果,但是当您分配的值取决于同一个variables的前一个值时,结果可能是非常意外的。 如果查询优化器将expression式移动到查询树中的不同位置,则可能会得到更less的评估次数(或者只是一次,如同在一个示例中那样)。 这就是为什么我们不推荐使用“迭代”types分配来计算聚合值。 我们发现基于XML的解决方法…对于客户来说通常效果很好

例4

即使没有ORDER BY,我们也不保证@v​​ar = @var +会为影响多行的任何语句生成连接值。 在查询执行过程中,可以对expression式的右侧进行一次或多次求值,并且我所说的行为是与计划相关的。

例5

使用SELECT语句进行variables赋值是一种专有语法(仅限于T-SQL),如果生成多行,则行为是未定义的或与计划相关。 如果您需要进行string连接,请使用SQLCLR聚合或FOR XML查询连接或其他关系方法。

看起来有点像这个post: VARCHAR(MAX)在连接string时performance怪异

那里的结论: 这种string连接的方法通常工作,但不能保证。 有关类似问题的知识库文章中的官方行是:“聚合级联查询的正确行为是未定义的”。