2017-09-20 10 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 

できますか?

詳細情報 数か月の正常な作業の後、私たちのコンポーネントの1つにバグがあり、このバグの原因となる可能性のある唯一のソースが記載されています。それは何とか65000要素を含む文字列配列の順序を変更しました(区切り文字を含む文字列の全長は65000 * 11でした)。私たちは、同じSQLサーバ上で同じエラーを再現しようとしましたが、運がありませんでした。あなたのコメントと答えは、このsprocをより有罪にしました。

+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内の要素の順序によって生成されます。このXMLは、XMLに変換された文字列を作成した順序で処理されます。これはクロス・アプリケーションの右側として使用されます(あなたはここでは何も注文する必要はありません)。したがって、あなたの質問への答えは:いいえ、あなたのselectステートメントは、常に要素の指定された順序を返します。

+2

私はXMLが要素の順に処理されることを知っていますが、データがその順序で返されると明示的に述べているものは一度も読んでいません。明示的な注文よりもデータの順序が重要な場合は、唯一の安全な方法です。 https://blogs.msdn.microsoft.com/conor_cunningham_msft/2008/08/27/no-seatbelt-expecting-order-without-order-by/ –

+2

Seanが指定した理由で、これは正しくありません。特に、「CROSS APPLY」は注文を意味しない。 * T-SQLの何も*はORDER BY以外の順序を意味しますが、ここではORDER BY(SELECT NULL)ではなく、明らかに非決定論的です。たとえその文が 'ORDER BY Id 'で再びラップされたとしても、内部行セットが非決定論的順序を持ち(決定論的順序を与えることはできません)、最終的に何も保証されません。 * practice *では、このようなクエリは*ほぼ確実に*インオーダーの行処理を行いますが、*保証はありません。 –

+0

@ Jeroen:明示的な 'order by'がない限り、指定された注文についてSQLには保証がないということにすべて同意したと思います。与えられたSQLには、XML要素内に順序があります。 XML要素の中には定義による注文があります(XML属性ではそうではありません)。したがって、このXML要素の処理は、情報が欠落している場合を除いて、その順序を維持する必要があります(bwt。行列構造で宣言されているように、行列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内にソートキーを追加する)という質問にはドキュメントが見つかりませんでしたか?