【问题标题】:Order by when concating a varchar. Does not work as expected连接 varchar 时按顺序排列。没有按预期工作
【发布时间】:2012-04-13 21:29:18
【问题描述】:

我不知道以前是否有人问过这个问题。但是我在连接 varchars 时遇到了问题。

让我解释一下:

我有这张桌子:

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

然后我通常像这样连接varchar:

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

但是这个返回:

'[5]'

我觉得这很奇怪。谁能解释一下原因?

我知道连接 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

因为它会给出这样的异常信息:

Msg 1033, Level 15, State 1, Line 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

因为这会引发这样的异常:

Msg 141, Level 15, State 1, Line 9 变量的值不能与数据检索结合 操作。

【问题讨论】:

  • 你能解释一下这个场景的原因吗?通常,当您在文本字段中存储数字时,您做错了。此外,您必须绝对肯定该表不会增长到 100.000 行。
  • 我只想在动态Pivot 中进行此操作。该表是静态的,除了一个表明它被禁用的标志。女巫我没有包括在例子中。该表是我无法更改的遗留系统的一部分。它用于客户需要的报告。
  • 为什么不能使用第二种解决方案?它按预期工作。
  • 我只是知道为什么这不起作用。这样我就可以避免如果我需要order by。我只是认为它必须是它不起作用的原因。

标签: sql sql-server-2008 tsql sql-order-by concatenation


【解决方案1】:

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

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

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

排序在计算标量之后而不是之前应用。

将字符串与 order by 连接的安全方法是使用 for xml

【讨论】:

    【解决方案2】:

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

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

    它返回 1,然而

    DECLARE @x INT
    SET @x = 0
    SELECT  @x =@x + 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]

    【讨论】:

    • 现在这可能是一个愚蠢的问题。但是你能改变顺序,让Compute ScalarSort之前优先吗?我同意替代解决方案可能更好。也在维护方面。
    • 一句话,没有。您可以使用表提示来影响某些执行计划,但一般来说,执行计划路径是内部 SQL 服务器的东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-15
    • 2016-03-25
    相关资源
    最近更新 更多