nvarchar连接/索引/nvarchar(max)的不可解释行为
nvarchar连接/索引/nvarchar(max)的不可解释行为
今天在SQL Server(包括2008R2和2012)中遇到了一个非常奇怪的问题。我正在尝试使用连接和select
语句来构建一个字符串。
我已经找到了解决办法,但我真的很想了解这里发生了什么,为什么它没有给我预期的结果。有人可以解释一下吗?
http://sqlfiddle.com/#!6/7438a/1
根据请求,这里也有代码:
-- 基本表 create table bla ( [id] int identity(1,1) primary key, [priority] int, [msg] nvarchar(max), [autofix] bit ) -- 没有id列的主键表 create table bla2 ( [id] int identity(1,1), [priority] int, [msg] nvarchar(max), [autofix] bit ) -- nvarchar(1000)替换为max的表 create table bla3 ( [id] int identity(1,1) primary key, [priority] int, [msg] nvarchar(1000), [autofix] bit ) -- 将三个表填充相同的值 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) = '' -- 我期望这个工作并生成'AB',但它没有 select @a = @a + [msg] from bla where autofix = 0 order by [priority] asc -- 这个工作:转换为nvarchar(4000) select @b = @b + convert(nvarchar(4000),[msg]) from bla where autofix = 0 order by [priority] asc -- 这个工作:没有WHERE子句 select @c = @c + [msg] from bla --where autofix = 0 order by [priority] asc -- 这个工作:没有ORDER BY子句 select @d = @d + [msg] from bla where autofix = 0 --order by [priority] asc -- 这个工作:来自bla2,所以没有id上的主键 select @e = @e + [msg] from bla2 where autofix = 0 order by [priority] asc -- 这个工作:来自bla3,所以使用了msg的nvarchar(1000)而不是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
在SQL Server中,使用nvarchar类型的变量进行字符串拼接时,会出现一些无法解释的行为。有时它能正常工作,但有时却失败了,这取决于执行计划的选择。
问题的根源在于使用不受支持的方法对行进行字符串拼接。这种方法并没有得到官方文档的支持,因此无法保证其行为的一致性。这可能导致字符串拼接的结果在不同的执行计划下产生不同的结果。
为了解决这个问题,可以使用以下几种方法来确保字符串拼接的正确性。
对于SQL Server 2017及以上版本,可以使用STRING_AGG函数来实现字符串拼接:
SELECT = STRING_AGG([msg], '') WITHIN GROUP (ORDER BY [priority] ASC) FROM bla WHERE autofix = 0
对于SQL Server 2005及以上版本,可以使用FOR XML PATH函数来实现字符串拼接:
SELECT = (SELECT [msg] + '' FROM bla WHERE autofix = 0 ORDER BY [priority] ASC FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)')
这些方法都是可靠的,可以保证字符串拼接的正确性。
关于这个问题,微软的官方回复是:这种字符串拼接的行为是未定义的,不能保证其正确性。在SQL Server的查询计划中,对于变量的赋值操作(比如字符串拼接)并不保证每一行都会执行一次。查询优化器会尽量减少这种操作的执行次数,这就导致了一些意外的结果。
因此,在进行字符串拼接时,应该使用官方支持的方法,如使用游标、FOR XML查询或CLR聚合函数等。这些方法都能够保证字符串拼接的正确性。
总之,对于nvarchar类型的字符串拼接,在SQL Server中存在一些无法解释的行为。为了避免出现问题,应该使用官方支持的方法来实现字符串拼接,并避免使用不受支持的方法。
nvarchar concatenation / index / nvarchar(max) inexplicable behavior 的问题是关于在字符串连接时出现的不确定行为。根据一篇Stack Overflow的帖子,官方对于类似问题的解释是“对于聚合连接查询的正确行为是未定义的”。这个问题的出现可能是因为SQL Server没有对字符串连接的行为进行明确的定义,所以在使用这种方法时出现了不确定的结果。
虽然该问题在SQL Server 2000和7.0中存在,但官方并没有对此进行修复。这是因为官方从未保证过字符串连接的行为,所以不能依赖于它。如果想要替代的方法,可以参考一篇名为“Concatenating Row Values in Transact-SQL”的文章。
nvarchar字符串的连接在SQL Server中可能会出现不确定的行为,并且官方并没有对此进行明确的定义。要解决这个问题,可以考虑使用替代的方法来进行字符串连接。