Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Baca char, double, int pattern dari string di sql

Versi final (saya harap):

Karena sql server 2008 tidak mendukung urutan dalam klausa lebih dari fungsi agregat, saya telah menambahkan cte lain untuk menambahkan indeks baris alih-alih sum Saya telah menggunakan di versi sebelumnya:

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

Selebihnya sama dengan versi sebelumnya.

Perbarui

Hal pertama yang saya lakukan adalah mengubah fungsi pemisahan string menjadi fungsi berdasarkan tabel penghitungan, sehingga saya dapat dengan mudah menambahkan nomor baris ke dalamnya. Jadi, jika Anda belum memiliki tabel penghitungan, buat satu .Jika Anda bertanya pada diri sendiri apa itu tabel penghitungan dan mengapa Anda membutuhkannya, baca artikel ini oleh 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

Kemudian, buat fungsi pemisahan string berdasarkan tabel penghitungan (diambil dari artikel Aaron tetapi menambahkan kolom indeks baris):

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

Sekarang, Trik yang saya gunakan sangat mirip dengan yang sebelumnya, hanya sekarang saya telah menambahkan ke kolom pertama cte kolom baru yang saya sebut RowIndex, itu pada dasarnya adalah total jumlah string, berdasarkan baris indeks semua baris:

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

Ini memberi saya hasil ini:

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

Seperti yang Anda lihat, saya sekarang memiliki nomor untuk setiap baris, jadi mulai sekarang sederhana:

;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

Itu memberi saya hasil ini:

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

Seperti yang Anda lihat, item sekarang diurutkan berdasarkan urutan asli dalam string. Terima kasih atas tantangannya, Sudah lama sejak saya memiliki yang layak :-)

Percobaan pertama

Nah, pertama-tama Anda harus membagi string itu menjadi sebuah tabel. Untuk melakukan itu, Anda harus menggunakan fungsi yang ditentukan pengguna. Anda dapat memilih yang paling cocok untuk Anda dari Split strings dari Aaron Bertrand cara yang benar – atau cara terbaik berikutnya artikel.

Untuk demonstrasi ini saya telah memilih untuk menggunakan SplitStrings_XML .

Jadi pertama, buat fungsinya:

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

Sekarang, deklarasikan dan inisialisasi variabel:

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

Kemudian, Buat 4 ekspresi tabel umum - satu untuk semua item, satu untuk string, satu untuk ganda dan satu untuk bilangan bulat. Perhatikan penggunaan row_number() fungsi - itu akan digunakan nanti untuk menggabungkan semua hasil bersama:

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

Kemudian, pilih dari menggabungkan semua ekspresi tabel umum ini. Perhatikan penggunaan COALESCE fungsi bawaan untuk hanya mengembalikan baris di mana setidaknya ada satu nilai:

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

Hasil:

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. Apa cara paling efisien untuk menormalkan teks dari kolom menjadi tabel?

  2. Tampilan Pertama SQL Server 2022 - 5 Fitur Baru Teratas (Bonus 5 Fitur)

  3. Tidak dapat menggunakan nama Alias ​​​​dalam klausa WHERE tetapi dapat di ORDER BY

  4. Menyimpan gambar di SQL Server?

  5. Mengatasi SQL Server Database Terjebak dalam Masalah Mode Tersangka Secara Efisien