2017-09-20 68 views
1

我们使用一个小的sql函数,它通过一些分隔符分割字符串,并将这些值返回到表中。ORDER BY(SELECT NULL)

ALTER FUNCTION [shark].[SplitStrings] 
(
    @List  VARCHAR(MAX), 
    @Delimiter VARCHAR(255) 
) 
RETURNS TABLE 
AS 
    RETURN (SELECT [Item], ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS [Id] FROM 
     (SELECT Item = x.i.value('(./text())[1]', 'varchar(max)') 
     FROM (SELECT [XML] = CONVERT(XML, '<i>' 
     + REPLACE(@List, @Delimiter, '</i><i>') + '</i>').query('.') 
     ) AS a CROSS APPLY [XML].nodes('i') AS x(i)) AS y 
     WHERE Item IS NOT NULL 
); 

问题是,如果这可以改变从字符串中的元素的顺序?

Ex。

SELECT * FROM [shark].[SplitStrings] ('1,2,3,4,5', ',') 

可这一回的

1 
5 
3 
4 
2 

代替

1 
2 
3 
4 
5 

更多信息: 经过几个月的正常工作,我们在其中一个组件中发现了一个错误,并且唯一的来源是我们可以找到并且可能导致此错误的是上述过程。它以某种方式改变了包含65 000个元素的字符串数组的顺序(带分隔符的字符串的总长度为65 000 * 11)。我们试图在同一个sql服务器上重现相同的错误,但没有任何运气。你的意见和答案使这个问题更加有罪。

+3

所有'ORDER BY'子句的用途是定义将什么'ROW_NUMBER()'值分配给行。但是由于所有行在该子句中都被赋予相同的值,所以分配的行号无法得到保证。并且这些保证都不影响*返回结果的顺序*。 –

+1

我同意@Damien_The_Unbeliever。缺乏逻辑顺序可能导致任何顺序。我有一个查询按照我想要的顺序返回了多年的数据,在SQL Server升级后,它开始以不同的顺序提供它,因为没有明确的顺序。我们使得我们的函数具有相似的循环来提供准确的行编号;没有效率,但是正确。 – UnhandledExcepSean

+2

XQuery *按顺序处理元素,所以如果想要使其更加健壮,请在其中实现一个计数器(XQuery也具有本地节点计数函数,但SQL Server不会实现它们)。 XML节点确实有一个订单。 'ROW_NUMBER()'没有。在实践中,我不知道你是否可以观察到优化器在这里轮流出现 - 如果可能的话,那么可能对于大集合,如果选择了并行计划,但是你的字符串可能太小而不能触发那。 –

回答

1

通常,select语句将始终返回未排序的结果集。所以对你的一般回答是:是的,你的select语句可以返回元素的任何顺序。这将确认SQL标准。 (最好不要相信别的。)

但是:你在函数中放置了一个隐式顺序。此订单由XML-Fragment中的-elements顺序生成。该XML按照您构建转换为XML的字符串的顺序进行处理。这被用作交叉应用的右侧(你不能使用,也不需要这里的任何命令)。所以你的问题的答案是:不,你的select语句将总是返回元素的给定顺序。

+2

我知道XML是按照元素的顺序处理的,但我从来没有读过任何明确声明数据将按照该顺序返回的内容。如果数据的顺序比明确的顺序重要,那么这是唯一安全的方法。 https://blogs.msdn.microsoft.com/conor_cunningham_msft/2008/08/27/no-seatbelt-expecting-order-without-order-by/ –

+2

这是不正确的,因为肖恩指定的原因。特别是'CROSS APPLY'意味着没有订单。 *除了'ORDER BY'外,T-SQL中没有任何*表示顺序,但显然不是这里使用的'ORDER BY(SELECT NULL)',因为这仍然是不确定的。即使语句被'ORDER BY Id'重新包装,这最终也不会保证,因为内部行集具有非确定性顺序(并且没有任何东西可以给它确定性顺序)。在*练习*中,像这样的查询几乎肯定会按顺序进行行处理,但没有*保证*。 –

+0

@Jeroen:我认为我们都同意,只要没有明确的'order by',就没有给定的顺序的SQL保证。在给定的SQL中,XML元素中有一个顺序。在XML元素中,根据定义确实有顺序(而不是XML属性)。所以处理这个XML元素必须保持这个顺序,除非你会错过一些信息(bwt。没有办法在procession XML-nodes中声明一个命令,因为它是按结构声明的)。 – Christian4145

1

一些更多的信息,不适合在评论:

对这个问题的最小的语句会是这样的

SELECT x.i.value('(./text())[1]', 'varchar(100)') as Item 
FROM 
    (SELECT CONVERT(XML, '<i>1</i><i>2</i>') as [XML]) AS a 
CROSS APPLY [XML].nodes('i') AS x(i); 

的问题简化为这样:可能的结果是这个顺序?

2 
1 

此查询的执行计划是这样的:

|--Compute Scalar(DEFINE:([Expr1011]=[Expr1010])) 
    |--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1000], XML Reader with XPath filter.[id])) 
     |--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1000])) 
     | |--Constant Scan(VALUES:((CONVERT(xml,'<i>1</i><i>2</i>',0)))) 
     | |--Filter(WHERE:(STARTUP EXPR([Expr1000] IS NOT NULL))) 
     |   |--Table-valued function 
     |--Stream Aggregate(DEFINE:([Expr1010]=MIN(CASE WHEN [Expr1000] IS NULL THEN NULL ELSE CASE WHEN datalength(XML Reader with XPath filter.[value])>=(128) THEN CONVERT_IMPLICIT(varchar(100),XML Reader with XPath filter.[lvalue],0) ELSE CONVERT_IMPLICIT(varchar(100),XML Reader with XPath filter.[value],0) END END))) 
      |--Top(TOP EXPRESSION:((1))) 
        |--Compute Scalar(DEFINE:([Expr1009]=0x58)) 
         |--Filter(WHERE:(XML Reader with XPath filter.[id]=getancestor(XML Reader with XPath filter.[id],(1)))) 
          |--Table-valued function 

也许有与数据从XML阅读器传来的嵌套循环内的元素进行排序的理由,但这不是当然,没有关于这方面的文件可以找到。

甚至没有关于这个问题的文档:如何在不改变XML结构的情况下正确保持排序(即在XML中添加一个sortkey)?