sql >> Database >  >> RDS >> Sqlserver

Lees char,double,int patroon van string in sql

Definitieve versie (hoop ik):

Aangezien sql server 2008 order by niet ondersteunt in de over-clausule van aggregatiefuncties, heb ik nog een cte toegevoegd om de rij-index toe te voegen in plaats van de sum Ik heb in de vorige versie gebruikt:

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

De rest is hetzelfde als de vorige versie.

Bijwerken

Het eerste dat ik heb gedaan, is de functie voor het splitsen van tekenreeksen wijzigen in een functie op basis van een tallytabel, zodat ik er gemakkelijk het rijnummer aan kan toevoegen. Dus, als je nog geen tally-tabel hebt, maak er een .Als je jezelf afvraagt ​​wat een tallytabel is en waarom je die nodig hebt, lees dit artikel van 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

Maak vervolgens een tekenreekssplitsingsfunctie op basis van de tallytabel (overgenomen uit het artikel van Aaron maar de rij-indexkolom toegevoegd):

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

Nu, de truc die ik heb gebruikt lijkt erg op de vorige, alleen heb ik nu aan de eerste cte een nieuwe kolom toegevoegd die ik RowIndex heb genoemd, dat is in feite een lopend totaal van het aantal strings, gebaseerd op de rij index van alle rijen:

 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, ',')

Het gaf me dit resultaat:

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

Zoals je kunt zien, heb ik nu een nummer voor elke rij, dus vanaf nu is het eenvoudig:

;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

Dat gaf me dit resultaat:

String     Double     Integer
---------- ---------- ----------
ddd        1.5        1
eee        2.3        0
fff        1.2        NULL
ggg        6.123      1

Zoals je kunt zien, zijn de items nu gesorteerd op de oorspronkelijke volgorde in de string. Bedankt voor de uitdaging, het is een tijdje geleden dat ik een fatsoenlijke heb gehad :-)

Eerste poging

Nou, eerst moet je die string in een tabel splitsen. Om dat te doen, moet u een door de gebruiker gedefinieerde functie gebruiken. Je kunt degene kiezen die het beste bij je past uit Aaron Bertrand's Split strings the juiste manier – of de op één na beste manier artikel.

Voor deze demonstratie heb ik ervoor gekozen om de SplitStrings_XML . te gebruiken .

Maak dus eerst de functie:

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

Declareer en initialiseer nu de variabele:

declare @string nvarchar(max) = 'ddd,1.5,1,eee,2.3,0,fff,1.2,ggg,6.123,1'

Maak vervolgens 4 algemene tabeluitdrukkingen - één voor alle items, één voor strings, één voor doubles en één voor gehele getallen. Let op het gebruik van de row_number() functie - deze wordt later gebruikt om alle resultaten samen te voegen:

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

Maak vervolgens een keuze uit het samenvoegen van al deze veelvoorkomende tabeluitdrukkingen. Let op het gebruik van de COALESCE ingebouwde functie om alleen rijen te retourneren waar ten minste één waarde aanwezig is:

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

Resultaten:

StringItem  DoubleItem  IntegerItem
----------  ----------  -----------
ddd         1.5         1
eee         2.3         0
fff         1.2         1
ggg         6.123       NULL


  1. MySQL sluit rij uit

  2. Waarom SQL Server waarden negeert bij het samenvoegen van tekenreeksen wanneer de ORDER BY-clausule is opgegeven

  3. Hoe niet-bewerkbare/gegenereerde code in netbeans te veranderen

  4. Sleutel, waarde extraheren uit json-objecten in Postgres