2017-09-20 2 views
1

Wir verwenden eine kleine SQL-Funktion, die die Zeichenfolge durch ein Trennzeichen teilt und diese Werte in einer Tabelle zurückgibt.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 
); 

Die Frage ist, ob dies die Reihenfolge der Elemente aus der Zeichenfolge ändern kann?

Ex.

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

Kann diese Rückkehr

1 
5 
3 
4 
2 

statt

1 
2 
3 
4 
5 

?

WEITERE INFORMATIONEN: Nach Monaten des normalen Arbeitens, fanden wir einen Fehler in einer unserer Komponenten und die einzige Quelle, die wir finden konnten und die diesen Fehler verursachen könnte, ist das erwähnte Verfahren. Es änderte irgendwie die Reihenfolge des String-Arrays, das 65 000 Elemente enthielt (die Gesamtlänge des Strings mit Trennzeichen betrug 65 000 * 11). Wir haben versucht, denselben Fehler auf demselben SQL-Server zu reproduzieren, aber ohne Glück. Ihre Kommentare und Antworten haben diesen Sproc schuldiger gemacht.

+3

Alles, was die 'ORDER BY' Klausel ist, ist es zu definieren, was 'ROW_NUMBER()' Werte den Zeilen zugewiesen sind. Da jedoch allen Zeilen innerhalb dieser Klausel derselbe Wert zugewiesen wird, sind die zugewiesenen Zeilennummern in keiner Weise garantiert. Und nichts davon garantiert, dass * die Reihenfolge beeinflusst wird, in der Ergebnisse zurückgegeben werden *. –

+1

Ich stimme @Damien_The_Unbeliever zu. Das Fehlen einer logischen Reihenfolge könnte zu einer beliebigen Reihenfolge führen.Ich hatte eine Abfrage, die jahrelang Daten in der von mir gewünschten Reihenfolge zurückgegeben hatte, und nach einem SQL Server-Upgrade begann sie, sie in einer anderen Reihenfolge bereitzustellen, weil es keine explizite Reihenfolge gab. Wir haben unsere Funktionen so ähnlich gemacht, dass eine genaue Zeilennummerierung möglich ist. nicht effizient, aber korrekt. – UnhandledExcepSean

+2

XQuery * verarbeitet Elemente in der richtigen Reihenfolge. Wenn Sie dies also robuster machen möchten, implementieren Sie einen Zähler darin (XQuery verfügt ebenfalls über native Funktionen zur Knotenzählung, aber SQL Server implementiert diese nicht). Die XML-Knoten haben eine Reihenfolge. Die 'ROW_NUMBER()' nicht. In der Praxis weiß ich nicht, ob Sie jemals beobachten könnten, dass der Optimierer hier außer Kontrolle gerät - wenn es möglich ist, dann wahrscheinlich für einen großen Satz, wenn ein paralleler Plan gewählt wird, aber Ihre Strings wahrscheinlich zu klein sind, um jemals ausgelöst zu werden Das. –

Antwort

1

Im Allgemeinen gibt eine select-Anweisung immer eine unsortierte Ergebnismenge zurück. Die allgemeine Antwort auf Ihre Frage lautet also: Ja, Ihre SELECT-Anweisung kann eine beliebige Reihenfolge von Elementen zurückgeben. Dies würde dem SQL-Standard entsprechen. (Und besser nie zu etwas anderem vertrauen.)

Aber: Sie setzen eine implizite Reihenfolge innerhalb Ihrer Funktion. Diese Reihenfolge ergibt sich aus der Reihenfolge der -Elemente innerhalb des XML-Fragments. Dieser XML-Code wird in der Reihenfolge verarbeitet, in der Sie den in XML konvertierten String erstellt haben. Dies wird verwendet, wenn die rechte Seite Ihres Kreuzes angewendet wird (Sie können hier nicht verwenden und brauchen keine Reihenfolge). Die Antwort auf Ihre Frage lautet also: Nein, Ihre SELECT-Anweisung gibt immer die angegebene Reihenfolge der Elemente zurück.

+2

Ich weiß, dass XML in der Reihenfolge der Elemente verarbeitet wird, aber ich habe nie etwas gelesen, das explizit angibt, dass die Daten in dieser Reihenfolge zurückgegeben werden. Wenn die Reihenfolge der Daten wichtig ist, ist eine explizite Bestellung der einzig sichere Weg. https://blogs.msdn.microsoft.com/conor_cunningham_msft/2008/08/27/no-seatbelt-expecting-order-without-order-by/ –

+2

Dies ist nicht korrekt, aus dem Grund Sean spezifiziert. Insbesondere bedeutet "CROSS APPLY" keine Bestellung. * Nothing * in T-SQL impliziert die Reihenfolge außer 'ORDER BY', aber dann natürlich nicht' ORDER BY (SELECT NULL) 'wie hier, da dies noch nichtdeterministisch ist. Selbst wenn die Anweisung erneut mit einer 'ORDER BY Id 'umschlossen würde, würde dies letztendlich nichts garantieren, da das innere Rowset eine nichtdeterministische Reihenfolge hat (und nichts kann * eine deterministische Ordnung geben). In * practice * wird eine solche Abfrage * fast sicher * Zeilenverarbeitung in der Reihenfolge haben, aber es gibt keine * Garantie *. –

+0

@Jeroen: Ich denke, wir sind uns alle einig, dass es in SQL für eine gegebene Reihenfolge keine Garantie gibt, solange es keine explizite 'Bestellung von' gibt. In der gegebenen SQL gibt es eine Reihenfolge innerhalb der XML-Elemente. Innerhalb von XML-Elementen gibt es per Definition eine Reihenfolge (nicht so bei XML-Attributen). Die Verarbeitung dieser XML-Elemente muss also diese Reihenfolge beibehalten, es sei denn, Sie würden einige Informationen verpassen (bwt. Es gibt keine Möglichkeit, eine Bestellung in Prozession-XML-Knoten zu deklarieren, da sie "nach Struktur" deklariert ist). – Christian4145

1

Einige weitere Informationen, die nicht in den Kommentaren passt:

Die minimale Erklärung für diese Frage so etwas wie

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); 

Die Frage dazu reduziert sein würde: Könnte das Ergebnis in dieser Reihenfolge sein ?

2 
1 

Der Ausführungsplan dieser Abfrage wie folgt aussieht:

|--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 

Vielleicht gibt es einen Grund für die Bestellung der Elemente innerhalb der verschachtelten Schleife mit Daten aus den XML-Reader kommen, aber das ist nicht sicher und es konnte keine Dokumentation gefunden werden.

Und selbst keine Dokumentation konnte zu der Frage gefunden werden: Wie kann man die Reihenfolge beibehalten, ohne die XML-Struktur zu ändern (d. H. Einen Sortierschlüssel innerhalb des XML hinzufügen)?