nvarchar连接/索引/ nvarchar(最大)不可解释的行为
我今天在SQL Server(2008R2和2012)中遇到了一个非常奇怪的问题。 我试图建立一个串联使用连接与select
语句。
我找到了解决方法,但我真的很想知道这里发生了什么,以及为什么它没有给我预期的结果。 有人可以向我解释吗?
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函数或表达式应用于SELECT列表中的列而不是ORDER BY子句中。
有问题的查询不会将任何表达式应用于ORDER BY
子句中的列。
2005年的文章Ordering在SQL Server中保证...状态
出于向后兼容性的原因,SQL Server提供了对最顶级范围的SELECT @p = @p + 1 ... ORDER BY类型的分配的支持。
在连接按照预期工作的计划中,具有表达式[Expr1003] = Scalar Operator([@x]+[Expr1004])
的计算标量出现在排序之上。
在计划中,无法工作的计算标量出现在排序下面。 正如在2006年的这个连接项中所解释的那样,当表达式下面出现表达式@x = @x + [msg]
,它会针对每行进行评估,但所有评估都使用@x
的预分配值结束。 在2006年的另一个类似连接项目中,微软的回应提到“解决”了这个问题。
微软对此问题上的所有后续Connect项目的回复(并且还有很多)声明,这是不能保证的
例1
我们不保证连接查询的正确性(如使用变量赋值和特定顺序的数据检索)。 查询输出可以在SQL Server 2008中更改,具体取决于计划选择,表中的数据等。即使语法允许您编写混合使用变量赋值的有序行检索的SELECT语句,也不应该依赖此工作。
例2
你看到的行为是通过设计。 在使用ORDER BY子句的查询中使用赋值操作(本例中的连接)具有未定义的行为。 由于查询计划的变化,这可能会从发布版本更改为发布版本,甚至在特定的服务器版本中也会发生变化 即使有解决方法,您也无法依赖此行为。 有关更多详细信息,请参阅以下知识库文章
http://support.microsoft.com/kb/287515唯一的保证机制如下:
例3
你看到的行为实际上是通过设计。 这与SQL是一种集合操纵语言有关。 SELECT列表中的所有表达式(也包括分配)不能保证每个输出行只执行一次。 事实上,SQL查询优化器尽可能少地尝试执行它们。 当您根据表中的某些数据计算变量的值时,这会给出预期的结果,但是当您分配的值取决于同一变量的先前值时,结果可能非常出乎意料。 如果查询优化器将表达式移动到查询树中的不同位置,则可能会得到更少的评估次数(或者只是一次,如您的示例之一)。 这就是为什么我们不推荐使用“迭代”类型分配来计算聚合值。 我们发现基于XML的解决方法...对于客户来说通常效果很好
例4
即使没有ORDER BY,我们也不保证@var = @var +会为影响多行的任何语句生成连接值。 在查询执行过程中,表达式的右边可以进行一次或多次计算,而且我所说的行为是与计划相关的。
例5
使用SELECT语句进行变量赋值是一种专有语法(仅限于T-SQL),如果生成多行,则行为未定义或与计划相关。 如果您需要进行字符串连接,请使用SQLCLR聚合或FOR XML查询连接或其他关系方法。
看起来有点像这个帖子:VARCHAR(MAX)在连接字符串时表现怪异
结论如下:这种字符串连接方式通常有效,但不能保证。 关于类似问题的知识库文章中的官方行是“聚合串联查询的正确行为未定义。”
链接地址: http://www.djcxy.com/p/44419.html上一篇: nvarchar concatenation / index / nvarchar(max) inexplicable behavior