5

我正在使用选择查询和变量来连接查询中的一些字符串,如下所示:

DECLARE @sConcat nvarchar(max)
SET @sConcat = ''
SELECT @sConcat = @sConcat + '[' + SomeColumn + ']'
 FROM SomeTable
 ORDER BY SomeColumn

在我的特殊情况下,我将FROM子句更改为派生表,它只是将整数列转换为 nvarchar,因此我不必每次在连接中出现时都将其转换。

进行此更改后,连接的结果只是表中的一个值。将 CAST 移动到外部查询或删除ORDER BY子句时,结果符合预期。

这一切都可以用下面的 t-sql 来重现。

BEGIN TRAN

-- Create a dummy table and populate it with some values
CREATE TABLE TTest (
    TTest_ID int IDENTITY(1,1) NOT NULL,
    TTest_Text varchar(8) NOT NULL
 CONSTRAINT PK_TTest PRIMARY KEY CLUSTERED (
    TTest_Id ASC
 )
) ON [PRIMARY]

INSERT INTO TTest (TTest_Text) VALUES ('A')
INSERT INTO TTest (TTest_Text) VALUES ('B')
INSERT INTO TTest (TTest_Text) VALUES ('C')
INSERT INTO TTest (TTest_Text) VALUES ('D')
INSERT INTO TTest (TTest_Text) VALUES ('E')
INSERT INTO TTest (TTest_Text) VALUES ('F')
INSERT INTO TTest (TTest_Text) VALUES ('G')
INSERT INTO TTest (TTest_Text) VALUES ('H')

-- Create a string with the ID values of each row in brackets
DECLARE @sConcat nvarchar(max)

-- First attempt, produces the result '[8]' which is not what I expected
SET @sConcat = ''
SELECT @sConcat = @sConcat + '[' + TTest_ID + ']'
 FROM  (SELECT CAST(TTest_ID AS nvarchar(100)) AS TTest_ID, TTest_Text
         FROM TTest) TTestBis
 ORDER BY TTestBis.TTest_ID ASC

PRINT @sConcat

-- Second attempt, with cast in the outer query, 
-- produces the expected result '[1][2][3][4][5][6][7][8]'
SET @sConcat = ''
SELECT @sConcat = @sConcat + '[' + CAST(TTest_ID AS nvarchar(100)) + ']'
 FROM  (SELECT TTest_ID, TTest_Text
         FROM TTest) TTestBis
 ORDER BY TTestBis.TTest_ID ASC

PRINT @sConcat

-- Third attempt, same as first but without ORDER BY, 
-- also produces the expected result '[1][2][3][4][5][6][7][8]'
SET @sConcat = ''
SELECT @sConcat = @sConcat + '[' + TTest_ID + ']'
 FROM  (SELECT CAST(TTest_ID AS nvarchar(100)) AS TTest_ID, TTest_Text
         FROM TTest) TTestBis

PRINT @sConcat

ROLLBACK

为什么 SQL Server 会以这种方式运行?对我来说这没有意义。我在 SQL Server 2005、2008 和 2008R2 上重现了这一点。

编辑

这个问题确实是重复的。迄今为止给出的最佳答案似乎是 Martin Smith在这个问题中给出的答案。所以你可以投票关闭。

4

2 回答 2

1

这个问题确实是重复的。可以在以下 stackoverflow 问题中找到全面的答案:nvarchar concatenation / index / nvarchar(max) inexplicable behavior

本文提供了几种在 SELECT 查询中连接字符串问题的有效解决方案:https ://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql /

于 2013-08-29T11:38:26.470 回答
-1

哇!乍一看,您可能会认为这是一个错误。经过大量分析,我的意见如下

我在 SQL Server 2012 开发人员版本中进行了所有测试。让我们去查询计划学校。

-- 典型 order by = sort operation
select * from TTest
order by TTest_Text desc;


在此处输入图像描述

-- 代码 1
声明 @sConcat nvarchar(max) = '';
SELECT @sConcat = @sConcat + '[' + D1.TTest_ID2 + ']'
FROM
(SELECT CAST(TTest_ID AS nvarchar(100)) AS TTest_ID2, TTest_Text FROM TTest) D1
ORDER BY D1.TTest_ID2;
打印@sConcat;


在此处输入图像描述

这会产生看起来正确但结果错误的计划。

-- 代码 2
声明 @sConcat nvarchar(max) = '';
SELECT @sConcat = @sConcat + '[' + CAST(D2.TTest_ID AS nvarchar(100)) + ']'
FROM (SELECT TTest_ID, TTest_Text FROM TTest) D2
ORDER BY D2.TTest_ID ASC;
打印@sConcat;

在此处输入图像描述

这产生了一个没有排序的计划,错误的计划,但正确的结果。

我认为您确实需要考虑 SQL 解释请求的逻辑方式。

1 - FROM
2 - WHERE
3 - GROUP BY
4 - 拥有
5 - SELECT
6 - ORDER BY

因此,首先计算派生查询。由于以下限制,我们无法订购子查询:

ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效,除非还指定了 TOP、OFFSET 或 FOR XML。

有人会认为(查询优化器/事务管理器)会计算字符串,然后注意到它无法排序,因为您有 N 行但有 1 个结果。

简而言之,人们希望这两个查询要么正常工作,要么失败。因为他们没有得到相同的计划,所以一个查询有效而另一个查询无效。

于 2013-08-12T21:04:48.643 回答