Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Odczytaj wzorzec char,double,int z ciągu w sql

Ostateczna wersja (mam nadzieję):

Ponieważ serwer sql 2008 nie obsługuje order by w klauzuli over funkcji agregujących, dodałem kolejne cte, aby dodać indeks wiersza zamiast sum Użyłem w poprzedniej wersji:

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

Cała reszta jest taka sama jak w poprzedniej wersji.

Aktualizacja

Pierwszą rzeczą, jaką zrobiłem, jest zmiana funkcji dzielenia ciągów na funkcję opartą na tabeli ze zbiorami, tak abym mógł łatwo dodać do niej numer wiersza. Tak więc, jeśli nie masz jeszcze tabeli statystyk, utwórz jeden .Jeśli zadajesz sobie pytanie, co to jest tablica z zestawieniami i dlaczego jej potrzebujesz, przeczytaj artykuł Jeffa Modena :

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

Następnie utwórz funkcję dzielenia ciągów w oparciu o tabelę tally (zaczerpniętą z artykułu Aarona, ale z dodaną kolumną indeksu wiersza):

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

Teraz, sztuczka, której użyłem, jest bardzo podobna do poprzedniej, tylko teraz dodałem do pierwszego cte nową kolumnę, którą nazwałem RowIndex, to w zasadzie suma ciągów na podstawie wiersza indeks wszystkich wierszy:

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

Dało mi to następujący wynik:

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

Jak widać, mam teraz numer dla każdego wiersza, więc od teraz to proste:

;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

To dało mi następujący wynik:

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

Jak widać, przedmioty są teraz posortowane według oryginalnej kolejności w ciągu. Dzięki za wyzwanie, Minęło trochę czasu, odkąd miałem przyzwoity :-)

Pierwsza próba

Cóż, najpierw musisz podzielić ten ciąg na tabelę. W tym celu należy użyć funkcji zdefiniowanej przez użytkownika. Możesz wybrać ten, który najbardziej Ci odpowiada z Split strings właściwy sposób – lub następny najlepszy sposób artykuł.

Do tej demonstracji wybrałem użycie SplitStrings_XML .

Więc najpierw utwórz funkcję:

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

Teraz zadeklaruj i zainicjuj zmienną:

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

Następnie utwórz 4 wspólne wyrażenia tabelowe - jeden dla wszystkich elementów, jeden dla łańcuchów, jeden dla podwójnych i jeden dla liczb całkowitych. Zwróć uwagę na użycie row_number() funkcja - zostanie później wykorzystana do połączenia wszystkich wyników razem:

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

Następnie wybierz opcję łączenia wszystkich tych wspólnych wyrażeń tabelowych. Zwróć uwagę na użycie COALESCE wbudowana funkcja zwracająca tylko te wiersze, w których występuje co najmniej jedna wartość:

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

Wyniki:

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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Dlaczego nie ma funkcji okienkowych w klauzulach where?

  2. Jak nadać uprawnienia (tabela) w SQL Server 2008?

  3. Jak znaleźć format daty używany w bieżącej sesji w SQL Server (T-SQL)

  4. Jak mogę ręcznie wstawić tożsamość?

  5. tsql zwraca tabelę z funkcji lub procedury magazynu