2013-02-28 94 views
10

我今天在SQL Server(2008R2和2012)中遇到了一个奇怪的问题。我试图建立一个字符串使用串联结合select声明。nvarchar concatenation/index/nvarchar(max)莫名其妙的行为

我找到了解决方法,但我真的很想了解这里发生了什么,以及为什么它不会给我预期的结果。有人可以向我解释吗?

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 
+0

这是一个很好的例子,但是你能否包含一些重现问题所需的代码? SQLFiddle非常有用,但代码不应该存在* only * there。 – 2013-02-28 15:00:08

+0

你是什么意思?这是SQL中的问题,而不是其他地方..对吗? – bartlaarhoven 2013-02-28 15:10:50

+0

我的意思是你在SQLfiddle上有repro,但是它在代码块中。 – 2013-02-28 15:12:19

回答

23

KB article已经VanDerNorth链接不包括线

以总级联查询正确的行为是不确定的 。

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

为了从聚集 级联查询达到了预期的效果,在SELECT列表中,而不是在ORDER BY子句中应用任何的Transact-SQL函数或表达式 列。

有问题的查询不适用于ORDER BY子句中的列的任何表达式。

2005年的文章Ordering guarantees in SQL Server...确实状态

为了向后兼容的原因,SQL Server将在最顶层的 范围提供了类型SELECT @p = @p + 1 ... ORDER的 任务支持。

在按照您预期的方式进行连接的计划中,带有表达式[Expr1003] = Scalar Operator([@x]+[Expr1004])的计算标量出现在排序之上。

在计划中,计算标量出现在排序下面。如2006年this connect item中所解释的,当@x = @x + [msg]表达式出现在排序下方时,将针对每行评估,但所有评估均使用预分配值@x结束。在2006年的another similar Connect Item中,微软的回应是“解决”这个问题。

微软回应关于这个问题的所有后来的连接项目(同时有很多)状态,这是根本无法保证

Example 1

我们不上的正确性任何保证串联 查询(如使用变量分配和 特定顺序的数据检索)。查询输出可以在SQL Server 2008 中进行更改,具体取决于计划选择,表中的数据等。您不应该使用 依赖于此工作,即使语法允许您使用 编写一个SELECT语句,该语句会混合有序行检索 变量赋值。

Example 2

您所看到的现象是由设计。在具有ORDER BY子句的查询中使用赋值操作 (在此示例中连接)具有 未定义的行为。由于查询计划中的更改,这可能会在发布之间发生变化,甚至在特定的服务器版本中可能会更改为 。 即使有解决方法,也不能依赖此行为。见 以下知识库文章了解更多详情:
http://support.microsoft.com/kb/287515的唯一保证 机制如下:

  1. 使用游标循环通过特定的顺序行和串联值
  2. 用于XML的查询与ORDER BY生成连接值
  3. 使用CLR聚合(这不会与治安工作BY子句)

Example 3

您看到的行为实际上是通过设计。这与 SQL是一个集合操纵语言。 SELECT 列表中的所有表达式(也包括分配)不保证为每个输出行只执行一次 。事实上,SQL查询 优化器尽可能少地尝试执行它们。这个 将根据表中的一些数据计算变量 的值时得到预期的结果,但当您分配的值取决于同一变量的先前值时, 结果可能是相当意想不到的。如果查询优化器将 表达式移动到查询树中的不同位置,则可能会得到 评估次数更少(或者只是一次,如您的示例之一)。 是我们不推荐使用 计算总计值的“迭代”类型分配的原因。我们发现基于XML的解决方法......通常为 客户

Example 4

工作得很好,即使没有ORDER BY,我们不保证@v​​ar = @var + 将产生影响多行的任何声明 合并的值。表达式的右边可以在查询执行期间被评估一次或多次,并且如我所说的行为是与计划相关的。

Example 5

与SELECT语句中的变量赋值是一种专有的语法 (T-SQL只),其中的行为是未定义或计划依赖如果 多个行生产。如果您需要执行字符串连接 ,请使用SQLCLR聚合或FOR XML查询级联或其他关系方法。

2

似乎有点喜欢这个岗位:VARCHAR(MAX) acting weird when concatenating string

的结论: 这种方法来字符串连接确实平时工作但不能保证。 The official line in the KB article对于类似的问题是“聚合级联查询的正确行为未定义”。

+0

嗯。谢谢。但它并不真正让我满意,“未定义的行为”。接下来,您引用的知识库文章适用于SQL Server 2000和7.0;现在不应该这样解决吗? – bartlaarhoven 2013-02-28 15:43:26

+2

@bartlaarhoven - 没有什么可以解决的,因为行为从未得到保证,所以你不应该依赖它。有关替代方法,请参阅[在Transact-SQL中连接行值](https://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/)。 – 2013-03-01 11:29:28

相关问题