2012-03-30 76 views
2

我不知道这个问题是否曾被问过。但是在拼接varchars时遇到问题。按字母顺序排列varchar时排序。不按预期方式工作

让我解释一下:

我有这个表:

CREATE TABLE Table1 
(
    [Field] [varchar](10) NOT NULL 
) 
INSERT INTO Table1 
VALUES('1'),('2'),('3'),('4'),('5'),('TITLE') 

而且我喜欢的输出是这样的:

'[TITLE],[1],[2],[3],[4],[5]' 

我想要的 'TITLE' 是为了首先1,2,3,4,5

所以这个查询将返回有序的结果。我没有更多的数字那么 'TITLE'

SELECT 
    * 
FROM 
    Table1 
ORDER BY 
    LEN([Field]) DESC, 
    [Field] ASC 

随后的长度我通常Concat的是这样的VARCHAR:

DECLARE @cols VARCHAR(MAX) 
SELECT @cols = COALESCE(@cols + ','+QUOTENAME([Field]), 
        QUOTENAME([Field])) 
FROM 
    Table1 
ORDER BY 
    LEN([Field]) DESC, 
    [Field] ASC 

但这种回报:

'[5]' 

其中我发现真的很奇怪。有人可以解释为什么吗?

我知道有一个替代解决方案来concate一个varchar。就像这样:

DECLARE @cols VARCHAR(MAX) 
SELECT @cols=STUFF 
(
    (
     SELECT 
      ',' +QUOTENAME([Field]) 
     FROM 
      Table1 
     ORDER BY 
      LEN([Field]) DESC, 
      [Field] ASC 
     FOR XML PATH('') 
    ) 
,1,1,'') 

这将返回我的预期的结果是这样的:

'[TITLE],[1],[2],[3],[4],[5]' 

编辑

建议,@cols是在开始时为空不能吧。因为我删除了order by。就像这样:

DECLARE @cols VARCHAR(MAX) 
SELECT @cols = COALESCE(@cols + ','+QUOTENAME([Field]), 
        QUOTENAME([Field])) 
FROM 
    Table1 

我的结果会是这样的:

'[1],[2],[3],[4],[5],[TITLE]' 

EDIT1

这是行不通的:

DECLARE @cols VARCHAR(MAX) 
SELECT @cols = COALESCE(@cols + ','+QUOTENAME([Field]), 
        QUOTENAME([Field])) 
FROM(
    SELECT [Field] 
    FROM 
     Table1 
    ORDER BY 
     LEN([Field]) DESC, 
     [Field] ASC 
) AS t 

因为它会给一个异常消息像这样:

消息1033,级别15,状态1,行17 ORDER BY子句是 视图,内联函数,派生表,子查询和公用表 表达式无效,除非TOP或FOR XML还指定。

EDIT2

就像我的预期。我不能做到这一点:

SELECT @cols, @cols = COALESCE(@cols + ','+QUOTENAME([Field]), 
        QUOTENAME([Field])) 
FROM 
    Table1 
ORDER BY 
    LEN([Field]) DESC, 
    [Field] ASC 

监守这将提高这样一个例外:

消息141,级别15,状态1,行9 SELECT语句,一个 值分配给一个变量必须不能与数据检索 操作相结合。

+0

你能解释一下这个场景的推理吗?通常当你在一个文本字段中存储数字时,你做错了。此外,你必须绝对肯定,该表不会增长到100.000行。 – SWeko 2012-03-30 07:56:50

+0

我想在一个动态的'Pivot'中做到这一点。该表格是静态的,除了一个表示它被禁用的标志。女巫我没有包括在这个例子中。该表是我无法更改的遗留系统的一部分。这是客户要求的报告。 – Arion 2012-03-30 07:59:57

+0

为什么你不能使用你的第二个解决方案?它按预期工作。 – SWeko 2012-03-30 08:12:00

回答

2

你可以看看这个文章PRB: Execution Plan and Results of Aggregate Concatenation Queries Depend Upon Expression Location

的ANSI SQL-92规范要求由 一个ORDER BY引用子句任何列匹配结果集,由存在于SELECT列表中列 定义。将表达式应用于ORDER BY子句的成员 时,结果列不在 SELECT列表中公开,导致未定义的行为。

当您在order by子句中使用表达式时,会得到不同的执行计划。

enter image description here

排序被计算标量,而不是之前之后施加。

串联字符串的安全方法是使用for xml

+0

谢谢你。这是接受的答案。 – Arion 2012-03-30 08:37:36

3

我不确定,但我认为答案是在查询的执行顺序。我试过下面的代码:

DECLARE @x INT 
SET @x = 0 
SELECT @x [email protected] + 1 
FROM 
    Table1 
ORDER BY 
    LEN(Field) DESC, Field ASC 
SELECT @x 

,并返回1,但

DECLARE @x INT 
SET @x = 0 
SELECT @x [email protected] + 1 
FROM 
    Table1 
SELECT @x 

返回6,这充其量是奇数。

然而,从第一个查询的执行计划可以看出,事件的顺序是:

Table Scan -> Compute Scalar -> Sort -> Select 

所以我最好的猜测是,结果被首先计算,然后排序,你结束了一些中间值。

恕我直言,这是滥用SQL服务器执行引擎,基本上是未定义的行为。即使你设法在这个SQL服务器上工作,也不知道服务包是否会为你打破它。我会选择你所描述的第二种解决方案,这很直接,而且容易维护。


关于EDIT1:指定SELECT TOP 100 PERCENT采取“视图中没有顺序”的护理差错,但是,SQL Server做一些魔术,你还是落得[1],[2],[3],[4],[5],[TITLE]

+0

现在这可能是一个愚蠢的问题。但是你可以改变顺序吗,所以'Sortute'之前的'Compute Scalar'首先是优先级?我同意替代解决方案可能会更好。也在维护方面。 – Arion 2012-03-30 08:27:07

+0

总之,没有。您可以使用表提示来影响某些执行计划,但通常情况下,执行计划路径是内部SQL服务器。 – SWeko 2012-03-30 08:35:08