Hinzufügen einer ROW_NUMBER () ohne Spalte zu ORDER BY?

7

Also habe ich auf einem gerade arbeite Code Golf Puzzle und Notwendigkeit , eine INT „Nummer“ Spalte hinzufügen n zu einem Ergebnis , während die aktuelle Reihenfolge beibehalten wird .

Angenommen, meine Quelldaten sind:

SELECT value
FROM STRING_SPLIT('one,two,three,four,five', ',')

Dies gibt die Artikel in der ursprünglichen (gewünschten) Reihenfolge zurück:

value
-----
one
two
three
four
five

Wenn ich versuche zu verwenden ROW_NUMBER()oder RANK()gezwungen bin, eine anzugeben ORDER BY, valueist dies die einzige rechtliche Wahl:

SELECT value, n = ROW_NUMBER() OVER(ORDER BY value)
FROM STRING_SPLIT('one,two,three,four,five',',')

Dies wird jedoch (wie erwartet) valuealphabetisch sortiert, anstatt es in der gewünschten ursprünglichen Reihenfolge zu belassen:

value   n
------ ---
five    1
four    2
one     3
three   4
two     5

Das Verknüpfen mit einer Zahlentabelle funktioniert nicht, da WHEREich ohne Klausel einen vollständigen äußeren Join erhalte.

Das Beste, was ich mir einfallen lassen konnte, war die Verwendung einer temporären Tabelle mit einem Identitätsfeld:

CREATE TABLE #argg (n INT IDENTITY(1,1), v VARCHAR(99))

INSERT #argg 
SELECT value v
FROM STRING_SPLIT('one,two,three,four,five',',')

SELECT *
FROM #argg

DROP TABLE #argg

aber das ist wirklich lang und nervig. Irgendwelche besseren Ideen?

BradC
quelle

Antworten:

10

Der kanonische Weg, dies zu tun, ist der folgende : ROW_NUMBER() OVER(ORDER BY (SELECT NULL)). Wenn Sie Golf spielen, können Sie Folgendes ausprobieren:

SELECT value, n = ROW_NUMBER() OVER(ORDER BY (SELECT 1))
FROM STRING_SPLIT('one,two,three,four,five',',')

Es funktioniert für den einfachen Fall, den Sie in der Frage gepostet haben:

Geben Sie hier die Bildbeschreibung ein

Ich sollte sagen, dass es keine dokumentierte Garantie dafür gibt, dass der ROW_NUMBER()Wert in der genauen Reihenfolge liegt, die Sie erwarten. Aber es ist Code Golf, also scheint das gut genug zu sein.

Joe Obbish
quelle
6

Sie können sich nicht darauf verlassen, dass Sie Werte in der Reihenfolge Ihrer ursprünglichen Zeichenfolge INSERTgenerieren IDENTITY. Das ist vielleicht das, was Sie beobachten, aber das ist nur ein glücklicher Zufall und sicherlich nicht garantiert.

Folgendes funktioniert, wenn Sie keine Duplikate haben:

DECLARE @str varchar(255) = 'one,two,three,four,five';

SELECT value, n = ROW_NUMBER() OVER
  (ORDER BY CHARINDEX(',' + value + ',', ',' + @str + ','))
FROM STRING_SPLIT('one,two,three,four,five', ',')
ORDER BY n;

Für Code Golf vielleicht:

DECLARE @s char(99)='one,two,three,four,five';
SELECT value,n=RANK() OVER(ORDER BY CHARINDEX(','+value+',',','+@s+','))
FROM STRING_SPLIT('one,two,three,four,five', ',')ORDER BY n

Wenn Sie Duplikate haben, wird es viel komplexer. Einige Ideen hier vielleicht:

Aaron Bertrand
quelle
Ziemlich klug, aber ja, die Dups werden ein Problem sein. Ich hatte noch nie STRING_SPLITRückkehrelemente in einer anderen Reihenfolge als der offensichtlichen, aber vielleicht ist es mit einigen verrückten langen Saiten möglich. Vielen Dank.
BradC
5

Eine andere Option ist die Verwendung einer Sequenz:

DROP SEQUENCE IF EXISTS dbo.S;
CREATE SEQUENCE dbo.S START WITH 1;

SELECT value, NEXT VALUE FOR dbo.S 
FROM STRING_SPLIT('one,two,three,four,five', ',');

Ausgabe:

╔═══════╦═══╗
 one    1 
 two    2 
 three  3 
 four   4 
 five   5 
╚═══════╩═══╝

Es ist ärgerlich, dass STRING_SPLITohne integrierte Nummerierungsoption implementiert wurde. Stimmen Sie für Änderungen unter https://feedback.azure.com/forums/908035-sql-server/suggestions/32902852-string-split-is-not-feature-complete ab

Paul White 9
quelle
Whoa, wusste nichts über Sequenzen, ich werde diese auf jeden Fall überprüfen! Und ja, auch das als zusätzliche zurückgegebene Spalte von STRING_SPLITwäre absolut ideal!
BradC
2

Fügen Sie einfach eine weitere Alternative aus Ihrer Frage hinzu. Aber ich denke, die Bestellung ist nicht garantiert,

SELECT value v,IDENTITY(INT,1,1) AS n
INTO #argg
FROM STRING_SPLIT('one,two,three,four,five',',')

SELECT * FROM #argg
Biju Jose
quelle
Das ist ein ziemlich cooler Trick, ich wusste nicht, dass du das so machen kannst IDENTITY()! INTOUm weiter zu klären, ist das erforderlich, damit das funktioniert, sonst bekommt manThe IDENTITY function can only be used when the SELECT statement has an INTO clause.
BradC