Endgültige Version (hoffe ich):
Da SQL Server 2008 order by in der over-Klausel von Aggregatfunktionen nicht unterstützt, habe ich ein weiteres cte hinzugefügt, um den Zeilenindex anstelle von sum
hinzuzufügen Ich habe in der vorherigen Version verwendet:
;WITH cteAllRows as
(
SELECT Item,
ItemIndex,
CASE WHEN ISNUMERIC(Item) = 0 THEN 'String'
WHEN ISNUMERIC(Item) = 1 AND CHARINDEX('.', Item) > 0 THEN 'Double'
WHEN ISNUMERIC(Item) = 1 AND CHARINDEX('.', Item) = 0 THEN 'Integer'
END As DataType
FROM dbo.SplitStrings_Numbers(@string, ',')
), cteAll as
(
SELECT Item,
DataType,
ItemIndex,
(
SELECT COUNT(*)
FROM cteAllRows tInner
WHERE tInner.DataType = 'String'
AND tInner.ItemIndex <= tOuter.ItemIndex
) As RowIndex
FROM cteAllRows tOuter
)
Der Rest ist derselbe wie in der vorherigen Version.
Aktualisieren
Das erste, was ich getan habe, war, die String-Split-Funktion in eine Funktion zu ändern, die auf einer Strichliste basiert, sodass ich ihr einfach die Zeilennummer hinzufügen kann. Wenn Sie also noch keine Strichliste haben, eine erstellen .Wenn Sie sich fragen, was eine Strichliste ist und warum Sie sie brauchen, Lesen Sie diesen Artikel von Jeff Moden :
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Tally
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Tally ADD CONSTRAINT PK_NumbersTest PRIMARY KEY CLUSTERED (Number)
GO
Erstellen Sie dann eine String-Split-Funktion basierend auf der Tally-Tabelle (aus Aarons Artikel, aber mit hinzugefügter Zeilenindexspalte):
CREATE FUNCTION dbo.SplitStrings_Numbers
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = SUBSTRING(@List, Number, CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number),
ROW_NUMBER() OVER (ORDER BY Number) As ItemIndex
FROM dbo.Tally
WHERE Number <= CONVERT(INT, LEN(@List))
AND SUBSTRING(@Delimiter + @List, Number, LEN(@Delimiter)) = @Delimiter
);
GO
Nun, der Trick, den ich verwendet habe, ist dem vorherigen sehr ähnlich, nur habe ich jetzt dem ersten cte eine neue Spalte hinzugefügt, die ich RowIndex genannt habe, das ist im Grunde eine laufende Summe der Anzahl der Strings, basierend auf der Zeile Index aller Zeilen:
SELECT Item,
CASE WHEN ISNUMERIC(Item) = 0 THEN 'String'
WHEN ISNUMERIC(Item) = 1 AND CHARINDEX('.', Item) > 0 THEN 'Double'
WHEN ISNUMERIC(Item) = 1 AND CHARINDEX('.', Item) = 0 THEN 'Integer'
END As DataType,
SUM(CASE WHEN ISNUMERIC(Item) = 0 THEN 1 END) OVER(ORDER BY ItemIndex) As RowIndex
FROM dbo.SplitStrings_Numbers(@string, ',')
Es gab mir dieses Ergebnis:
Item DataType RowIndex
---------- -------- -----------
ddd String 1
1.5 Double 1
1 Integer 1
eee String 2
2.3 Double 2
0 Integer 2
fff String 3
1.2 Double 3
ggg String 4
6.123 Double 4
1 Integer 4
Wie Sie sehen können, habe ich jetzt eine Nummer für jede Zeile, also ist es von nun an einfach:
;WITH cteAll as
(
SELECT Item,
CASE WHEN ISNUMERIC(Item) = 0 THEN 'String'
WHEN ISNUMERIC(Item) = 1 AND CHARINDEX('.', Item) > 0 THEN 'Double'
WHEN ISNUMERIC(Item) = 1 AND CHARINDEX('.', Item) = 0 THEN 'Integer'
END As DataType,
SUM(CASE WHEN ISNUMERIC(Item) = 0 THEN 1 END) OVER(ORDER BY ItemIndex) As RowIndex
FROM dbo.SplitStrings_Numbers(@string, ',')
), cteString AS
(
SELECT Item, RowIndex
FROM cteAll
WHERE DataType = 'String'
), cteDouble AS
(
SELECT Item, RowIndex
FROM cteAll
WHERE DataType = 'Double'
), cteInteger AS
(
SELECT Item, RowIndex
FROM cteAll
WHERE DataType = 'Integer'
)
SELECT T1.Item As [String],
T2.Item As [Double],
T3.Item As [Integer]
FROM dbo.Tally
LEFT JOIN cteString T1 ON T1.RowIndex = Number
LEFT JOIN cteDouble T2 ON t2.RowIndex = Number
LEFT JOIN cteInteger T3 ON t3.RowIndex = Number
WHERE COALESCE(T1.Item, T2.Item, T3.Item) IS NOT NULL
Das gab mir dieses Ergebnis:
String Double Integer
---------- ---------- ----------
ddd 1.5 1
eee 2.3 0
fff 1.2 NULL
ggg 6.123 1
Wie Sie sehen können, sind die Elemente jetzt nach der ursprünglichen Reihenfolge in der Zeichenfolge sortiert. Danke für die Herausforderung. Es ist eine Weile her, seit ich eine anständige hatte :-)
Erster Versuch
Nun, zuerst müssen Sie diese Zeichenfolge in eine Tabelle aufteilen. Dazu sollten Sie eine benutzerdefinierte Funktion verwenden. Sie können die für Sie am besten geeignete aus Aaron Bertrands Split strings the auswählen richtigen Weg – oder den nächstbesten Weg Artikel.
Für diese Demonstration habe ich mich für die Verwendung von SplitStrings_XML
entschieden .
Erstellen Sie also zuerst die Funktion:
CREATE FUNCTION dbo.SplitStrings_XML
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(@List, @Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
GO
Deklarieren und initialisieren Sie nun die Variable:
declare @string nvarchar(max) = 'ddd,1.5,1,eee,2.3,0,fff,1.2,ggg,6.123,1'
Erstellen Sie dann 4 allgemeine Tabellenausdrücke
- eine für alle Elemente, eine für Strings, eine für Doubles und eine für Integer. Beachten Sie die Verwendung von row_number()
Funktion - sie wird später verwendet, um alle Ergebnisse zusammenzufügen:
;WITH AllItems as
(
SELECT Item, ROW_NUMBER() OVER(ORDER BY (select null)) as rn
FROM dbo.SplitStrings_XML(@string, ',')
)
, Strings as
(
SELECT Item as StringItem, ROW_NUMBER() OVER(ORDER BY (select null)) as rn
FROM dbo.SplitStrings_XML(@string, ',')
WHERE ISNUMERIC(Item) = 0
), Doubles as
(
SELECT Item as DoubleItem, ROW_NUMBER() OVER(ORDER BY (select null)) as rn
FROM dbo.SplitStrings_XML(@string, ',')
WHERE ISNUMERIC(Item) = 1 AND CHARINDEX('.', Item) > 0
), Integers as
(
SELECT Item as IntegerItem, ROW_NUMBER() OVER(ORDER BY (select null)) as rn
FROM dbo.SplitStrings_XML(@string, ',')
WHERE ISNUMERIC(Item) = 1 AND CHARINDEX('.', Item) = 0
)
Wählen Sie dann eine Verknüpfung all dieser allgemeinen Tabellenausdrücke aus. Beachten Sie die Verwendung von COALESCE
eingebaute Funktion, um nur Zeilen zurückzugeben, in denen mindestens ein Wert vorhanden ist:
SELECT StringItem, DoubleItem, IntegerItem
FROM AllItems A
LEFT JOIN Strings S ON A.rn = S.rn
LEFT JOIN Doubles D ON A.rn = D.rn
LEFT JOIN Integers I ON A.rn = I.rn
WHERE COALESCE(StringItem, DoubleItem, IntegerItem) IS NOT NULL
Ergebnisse:
StringItem DoubleItem IntegerItem
---------- ---------- -----------
ddd 1.5 1
eee 2.3 0
fff 1.2 1
ggg 6.123 NULL