Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Lesen Sie das Muster char, double, int aus einer Zeichenfolge in SQL

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