Bagaimana cara membagi string sehingga saya dapat mengakses item x?

493

Menggunakan SQL Server, bagaimana cara membagi string sehingga saya dapat mengakses item x?

Ambil string "Hello John Smith". Bagaimana saya bisa membagi string dengan ruang dan mengakses item di indeks 1 yang seharusnya mengembalikan "John"?

GateKiller
sumber
3
Lihat stackoverflow.com/questions/314824/... juga
Jarrod Dixon
5
built-in pada sql server 2016 msdn.microsoft.com/en-us/library/mt684588.aspx
Tim Abell
4
Jawaban tertinggi di sini adalah - setidaknya bagi saya - cukup kuno dan agak ketinggalan zaman. Lokasi prosedur, loop, rekursi, CLR, fungsi, banyak baris kode ... Mungkin menarik untuk membaca jawaban "aktif" untuk menemukan lebih banyak pendekatan terbaru .
Shnugo
Saya telah menambahkan jawaban baru dengan lebih banyak pendekatan terkini: stackoverflow.com/a/49669994/632604
Gorgi Rankovski
Coba Dapatkan elemen ke-n dari daftar -> portosql.wordpress.com/2019/05/27/enesimo-elemento-lista
José Diz

Jawaban:

191

Anda dapat menemukan solusi dalam Fungsi Ditentukan Pengguna SQL untuk Parse a Delimited String bermanfaat (dari Proyek Kode ).

Anda dapat menggunakan logika sederhana ini:

Declare @products varchar(200) = '1|20|3|343|44|6|8765'
Declare @individual varchar(20) = null

WHILE LEN(@products) > 0
BEGIN
    IF PATINDEX('%|%', @products) > 0
    BEGIN
        SET @individual = SUBSTRING(@products,
                                    0,
                                    PATINDEX('%|%', @products))
        SELECT @individual

        SET @products = SUBSTRING(@products,
                                  LEN(@individual + '|') + 1,
                                  LEN(@products))
    END
    ELSE
    BEGIN
        SET @individual = @products
        SET @products = NULL
        SELECT @individual
    END
END
Jonesinator
sumber
1
mengapa SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( RTRIM( LTRIM( @p_SourceText)))dan tidak SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( @p_SourceText)?
Beth
12
@GateKiller Solusi ini tidak mendukung Unicode & menggunakan numeric kode keras (18,3) yang tidak membuatnya menjadi fungsi "dapat digunakan kembali" yang layak.
Filip De Vos
4
Ini berfungsi tetapi mengalokasikan banyak memori dan buang-buang CPU.
jjxtra
2
Pada SQL Server 2016, sekarang ada fungsi built-in STRING_SPLITyang akan membagi string dan mengembalikan hasil tabel satu kolom yang bisa Anda gunakan dalam SELECTpernyataan atau di tempat lain.
qJake
Sayang sekali orang-orang yang bekerja untuk saya tidak pada 2016. Tapi, saya akan mengingatnya kalau-kalau mereka bisa mendapatkan memimpin dari sepatu mereka. Solusi hebat untuk sementara. Saya menerapkannya sebagai fungsi dan menambahkan pembatas sebagai argumen.
Brandon Griffin
355

Saya tidak percaya SQL Server memiliki fungsi split bawaan, jadi selain UDF, satu-satunya jawaban lain yang saya tahu adalah membajak fungsi PARSENAME:

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 2) 

PARSENAME mengambil string dan membaginya pada karakter periode. Dibutuhkan angka sebagai argumen kedua, dan angka itu menentukan segmen string mana yang akan kembali (bekerja dari belakang ke depan).

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 3)  --return Hello

Masalah yang jelas adalah ketika string sudah mengandung tanda titik. Saya masih berpikir menggunakan UDF adalah cara terbaik ... ada saran lain?

Nathan Bedford
sumber
102
Terima kasih Saul ... Saya harus menunjukkan bahwa solusi ini benar-benar solusi buruk untuk pengembangan nyata. PARSENAME hanya mengharapkan empat bagian, jadi menggunakan string dengan lebih dari empat bagian menyebabkannya mengembalikan NULL. Solusi UDF jelas lebih baik.
Nathan Bedford
33
Ini adalah retasan yang hebat, dan juga membuat saya menangis bahwa sesuatu seperti ini diperlukan untuk sesuatu yang sangat sederhana dalam bahasa nyata.
Factor Mystic
36
Untuk membuat indeks berfungsi dengan cara yang "benar", yaitu, mulai dari 1, saya telah membajak pembajakan Anda dengan REVERSE: REVERSE (PARSENAME (REPLACE (REPLACE ('Hello John Smith'), '', '.') , 1)) - Pengembalian Hello
NothingsImpossible
3
@FactorMystic First Normal Form mengharuskan Anda untuk tidak meletakkan banyak nilai dalam satu bidang. Secara harfiah aturan pertama dari RDBMS. Sebuah SPLIT()fungsi ini tidak disertakan karena mendorong desain database miskin, dan database akan pernah dioptimalkan untuk menggunakan data yang disimpan dalam format ini. RDBMS tidak berkewajiban membantu pengembang melakukan hal-hal bodoh yang telah dirancang untuk tidak ditangani. Jawaban yang benar akan selalu "Menormalkan database Anda seperti yang kami katakan 40 tahun yang lalu." SQL atau RDBMS tidak dapat disalahkan untuk desain yang buruk.
Bacon Bits
8
@BaconBits sementara saya setuju secara teori, dalam praktiknya alat seperti ini berguna ketika menormalkan desain yang buruk yang dihasilkan oleh seseorang yang datang sebelum Anda.
Tim Abell
110

Pertama, buat fungsi (menggunakan CTE, ekspresi tabel umum tidak jauh dengan kebutuhan untuk tabel temp)

 create function dbo.SplitString 
    (
        @str nvarchar(4000), 
        @separator char(1)
    )
    returns table
    AS
    return (
        with tokens(p, a, b) AS (
            select 
                1, 
                1, 
                charindex(@separator, @str)
            union all
            select
                p + 1, 
                b + 1, 
                charindex(@separator, @str, b + 1)
            from tokens
            where b > 0
        )
        select
            p-1 zeroBasedOccurance,
            substring(
                @str, 
                a, 
                case when b > 0 then b-a ELSE 4000 end) 
            AS s
        from tokens
      )
    GO

Kemudian, gunakan sebagai tabel apa saja (atau modifikasi agar sesuai dengan proc tersimpan Anda yang sudah ada) seperti ini.

select s 
from dbo.SplitString('Hello John Smith', ' ')
where zeroBasedOccurance=1

Memperbarui

Versi sebelumnya akan gagal untuk string input yang lebih panjang dari 4000 karakter. Versi ini menangani batasan:

create function dbo.SplitString 
(
    @str nvarchar(max), 
    @separator char(1)
)
returns table
AS
return (
with tokens(p, a, b) AS (
    select 
        cast(1 as bigint), 
        cast(1 as bigint), 
        charindex(@separator, @str)
    union all
    select
        p + 1, 
        b + 1, 
        charindex(@separator, @str, b + 1)
    from tokens
    where b > 0
)
select
    p-1 ItemIndex,
    substring(
        @str, 
        a, 
        case when b > 0 then b-a ELSE LEN(@str) end) 
    AS s
from tokens
);

GO

Penggunaannya tetap sama.

vzczc
sumber
14
Ini elegan tetapi hanya berfungsi untuk 100 elemen karena batas kedalaman rekursi.
Pking
4
@ Pking, tidak, defaultnya adalah 100(untuk mencegah infinite loop). Gunakan petunjuk MAXRECURSION untuk menentukan jumlah level rekursi ( 0to 32767, 0adalah "no limit" - dapat menghancurkan server). BTW, jawabannya jauh lebih baik daripada PARSENAME, karena itu universal :-). +1
Michał Powaga
Menambahkan maxrecursionke solusi ini perlu diingat pertanyaan ini dan jawabannya Bagaimana mengatur maxrecursionopsi untuk CTE di dalam Table-Valued-Function .
Michał Powaga
Secara khusus, referensi jawaban oleh Crisfole - metodenya agak memperlambatnya, tetapi lebih sederhana daripada kebanyakan opsi lain.
AHiggins
titik minor tetapi penggunaannya tidak tetap sama karena Anda mengubah nama kolom, jadi stidak lagi ditentukan
Tim Abell
62

Sebagian besar solusi di sini digunakan saat loop atau CTE rekursif. Pendekatan berbasis set akan lebih unggul, saya janji, jika Anda dapat menggunakan pembatas selain spasi:

CREATE FUNCTION [dbo].[SplitString]
    (
        @List NVARCHAR(MAX),
        @Delim VARCHAR(255)
    )
    RETURNS TABLE
    AS
        RETURN ( SELECT [Value], idx = RANK() OVER (ORDER BY n) FROM 
          ( 
            SELECT n = Number, 
              [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
              CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
            FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
              FROM sys.all_objects) AS x
              WHERE Number <= LEN(@List)
              AND SUBSTRING(@Delim + @List, [Number], LEN(@Delim)) = @Delim
          ) AS y
        );

Penggunaan sampel:

SELECT Value FROM dbo.SplitString('foo,bar,blat,foo,splunge',',')
  WHERE idx = 3;

Hasil:

----
blat

Anda juga bisa menambahkan yang idxAnda inginkan sebagai argumen ke fungsi, tetapi saya akan membiarkannya sebagai latihan bagi pembaca.

Anda tidak dapat melakukan ini hanya dengan fungsi asli yangSTRING_SPLIT ditambahkan di SQL Server 2016, karena tidak ada jaminan bahwa output akan diberikan dalam urutan daftar asli. Dengan kata lain, jika Anda lulus dalam 3,6,1hasil kemungkinan akan berada di urutan itu, tetapi bisa jadi 1,3,6. Saya telah meminta bantuan komunitas dalam meningkatkan fungsi bawaan di sini:

Dengan umpan balik kualitatif yang cukup , mereka mungkin benar-benar mempertimbangkan untuk membuat beberapa peningkatan ini:

Lebih lanjut tentang fungsi split, mengapa (dan buktikan itu) sementara loop dan CTE rekursif tidak skala, dan alternatif yang lebih baik, jika memisahkan string yang berasal dari lapisan aplikasi:

Pada SQL Server 2016 atau lebih baru, Anda harus melihat STRING_SPLIT()dan STRING_AGG():

Aaron Bertrand
sumber
1
Jawaban terbaik, IMHO. Dalam beberapa jawaban lain ada masalah batas rekursi SQL 100, tetapi tidak dalam kasus ini. Implementasinya sangat cepat dan sangat sederhana. Di mana tombol +2?
T-moty
5
Saya mencoba fungsi ini kata demi kata dengan penggunaan: select * from DBO.SplitString('Hello John smith', ' ');dan output yang dihasilkan adalah: Nilai Halo ello llo lo o John ohn hn n smith mith ith th h
wwmbes
2
@AaronBertrand Masalah asli yang diposting oleh GateKiller melibatkan pembatas ruang.
wwmbes
1
@ user1255933 Ditujukan.
Aaron Bertrand
1
@Michael Ya, itu benar. Anda juga tidak akan memiliki tabel untuk dipilih jika Anda tidak memiliki izin ALTER SCHEMA, dan tidak akan dapat memilih dari itu jika Anda tidak memiliki izin SELECT Anda selalu dapat meminta seseorang untuk membuat fungsi untuk Anda . Atau buat di suatu tempat Anda dapat membuatnya (bahkan sementara, katakan di tempdb). Dan pada 2016+ Anda harus menggunakan STRING_SPLIT () dan bukan fungsi yang harus Anda buat sendiri.
Aaron Bertrand
38

Anda bisa memanfaatkan tabel angka untuk melakukan penguraian string.

Buat tabel angka fisik:

    create table dbo.Numbers (N int primary key);
    insert into dbo.Numbers
        select top 1000 row_number() over(order by number) from master..spt_values
    go

Buat tabel uji dengan 10.00000 baris

    create table #yak (i int identity(1,1) primary key, array varchar(50))

    insert into #yak(array)
        select 'a,b,c' from dbo.Numbers n cross join dbo.Numbers nn
    go

Buat fungsinya

    create function [dbo].[ufn_ParseArray]
        (   @Input      nvarchar(4000), 
            @Delimiter  char(1) = ',',
            @BaseIdent  int
        )
    returns table as
    return  
        (   select  row_number() over (order by n asc) + (@BaseIdent - 1) [i],
                    substring(@Input, n, charindex(@Delimiter, @Input + @Delimiter, n) - n) s
            from    dbo.Numbers
            where   n <= convert(int, len(@Input)) and
                    substring(@Delimiter + @Input, n, 1) = @Delimiter
        )
    go

Penggunaan (menghasilkan 3mil baris dalam 40-an di laptop saya)

    select * 
    from #yak 
    cross apply dbo.ufn_ParseArray(array, ',', 1)

membersihkan

    drop table dbo.Numbers;
    drop function  [dbo].[ufn_ParseArray]

Performa di sini tidak luar biasa, tetapi memanggil fungsi lebih dari satu juta tabel baris bukanlah ide terbaik. Jika melakukan split string pada banyak baris saya akan menghindari fungsi.

Nathan Skerl
sumber
2
Solusi IMO terbaik, yang lain memiliki beberapa batasan .. ini cepat dan dapat mengurai string panjang dengan banyak elemen.
Pking
Mengapa Anda memesan dan turun? Jika ada di mana tiga item, dan kami mulai penomoran pada 1, maka item pertama akan menjadi nomor 3, dan yang terakhir akan menjadi nomor 1. Tidakkah akan memberikan hasil yang lebih intuitif jika descsudah dihapus?
kapak - dilakukan dengan SOverflow
1
Setuju, akan lebih intuitif dalam arah naik. Saya mengikuti konvensi parsename () yang menggunakan desc
Nathan Skerl
3
beberapa penjelasan tentang bagaimana ini bekerja akan menjadi luar biasa
Tim Abell
Dalam pengujian pada 100 juta baris hingga 3 bidang untuk diuraikan, ufn_ParseArray tidak selesai setelah 25 menit, sedangkan REVERSE(PARSENAME(REPLACE(REVERSE('Hello John Smith'), ' ', '.'), 1)) dari @NothingsImpossible diselesaikan dalam 1,5 menit. @hello_earth Bagaimana solusi Anda membandingkan string yang lebih panjang dengan lebih dari 4 bidang?
wwmbes
32

Pertanyaan ini bukan tentang pendekatan string split , tetapi tentang bagaimana mendapatkan elemen ke-n .

Semua jawaban di sini sedang melakukan semacam pemisahan string menggunakan rekursi, CTEs, banyak CHARINDEX, REVERSEdan PATINDEX, menciptakan fungsi, panggilan untuk metode CLR, tabel angka, CROSS APPLYs ... Sebagian besar jawaban mencakup banyak baris kode.

Tetapi - jika Anda benar - benar menginginkan tidak lebih dari sebuah pendekatan untuk mendapatkan elemen ke-n - ini dapat dilakukan sebagai one-liner nyata , tanpa UDF, bahkan bukan sub-pilih ... Dan sebagai manfaat tambahan: ketik aman

Dapatkan bagian 2 dibatasi oleh spasi:

DECLARE @input NVARCHAR(100)=N'part1 part2 part3';
SELECT CAST(N'<x>' + REPLACE(@input,N' ',N'</x><x>') + N'</x>' AS XML).value('/x[2]','nvarchar(max)')

Tentu saja Anda dapat menggunakan variabel untuk pembatas dan posisi (gunakan sql:columnuntuk mengambil posisi secara langsung dari nilai kueri):

DECLARE @dlmt NVARCHAR(10)=N' ';
DECLARE @pos INT = 2;
SELECT CAST(N'<x>' + REPLACE(@input,@dlmt,N'</x><x>') + N'</x>' AS XML).value('/x[sql:variable("@pos")][1]','nvarchar(max)')

Jika string Anda mungkin menyertakan karakter terlarang (terutama satu di antaranya &><), Anda masih bisa melakukannya dengan cara ini. Cukup gunakan FOR XML PATHpada string Anda terlebih dahulu untuk mengganti semua karakter terlarang dengan urutan pelepasan yang sesuai secara implisit.

Ini adalah kasus yang sangat istimewa jika - tambahan - pembatas Anda adalah titik koma . Dalam hal ini saya mengganti pembatas pertama ke '# DLMT #', dan akhirnya ganti ini dengan tag XML:

SET @input=N'Some <, > and &;Other äöü@€;One more';
SET @dlmt=N';';
SELECT CAST(N'<x>' + REPLACE((SELECT REPLACE(@input,@dlmt,'#DLMT#') AS [*] FOR XML PATH('')),N'#DLMT#',N'</x><x>') + N'</x>' AS XML).value('/x[sql:variable("@pos")][1]','nvarchar(max)');

PEMBARUAN untuk SQL-Server 2016+

Dengan menyesal para pengembang lupa untuk mengembalikan indeks bagian itu STRING_SPLIT. Tapi, menggunakan SQL-Server 2016+, ada JSON_VALUEdan OPENJSON.

Dengan JSON_VALUEkita dapat lewat di posisi sebagai array indeks.

Untuk OPENJSONitu dokumentasi menyatakan dengan jelas:

Ketika OPENJSON mem-parsing array JSON, fungsi mengembalikan indeks elemen-elemen dalam teks JSON sebagai kunci.

Sebuah string seperti 1,2,3kebutuhan tidak lebih dari kurung: [1,2,3].
Sederetan kata-kata seperti this is an exampleperlu ["this","is","an","example"].
Ini adalah operasi string yang sangat mudah. Coba saja:

DECLARE @str VARCHAR(100)='Hello John Smith';
DECLARE @position INT = 2;

--We can build the json-path '$[1]' using CONCAT
SELECT JSON_VALUE('["' + REPLACE(@str,' ','","') + '"]',CONCAT('$[',@position-1,']'));

- Lihat ini untuk pemisah tali yang aman ( berbasis nol ):

SELECT  JsonArray.[key] AS [Position]
       ,JsonArray.[value] AS [Part]
FROM OPENJSON('["' + REPLACE(@str,' ','","') + '"]') JsonArray

Dalam posting ini saya menguji berbagai pendekatan dan menemukan, itu OPENJSONsangat cepat. Bahkan jauh lebih cepat daripada metode "delimitedSplit8k ()" yang terkenal ...

UPDATE 2 - Dapatkan nilai-nilai yang aman

Kita bisa menggunakan array dalam array hanya dengan menggunakan doubled [[]]. Ini memungkinkan untuk sebuah WITHklausa yang diketik :

DECLARE  @SomeDelimitedString VARCHAR(100)='part1|1|20190920';

DECLARE @JsonArray NVARCHAR(MAX)=CONCAT('[["',REPLACE(@SomeDelimitedString,'|','","'),'"]]');

SELECT @SomeDelimitedString          AS TheOriginal
      ,@JsonArray                    AS TransformedToJSON
      ,ValuesFromTheArray.*
FROM OPENJSON(@JsonArray)
WITH(TheFirstFragment  VARCHAR(100) '$[0]'
    ,TheSecondFragment INT          '$[1]'
    ,TheThirdFragment  DATE         '$[2]') ValuesFromTheArray
Shnugo
sumber
Re: jika string Anda mungkin menyertakan karakter terlarang ... Anda bisa membungkus substring seperti itu <x><![CDATA[x<&>x]]></x>.
Salman A
@SalmanA, yeah, CDATA-bagian bisa menangani ini juga ... Tapi setelah para pemain mereka pergi (diubah menjadi melarikan diri text()secara implisit). Saya tidak suka sihir di bawah tenda , jadi saya lebih suka (SELECT 'Text with <&>' AS [*] FOR XML PATH(''))pendekatan -. Ini terlihat lebih bersih bagi saya dan tetap terjadi ... (Lebih banyak tentang CDATA dan XML ).
Shnugo
22

Inilah UDF yang akan melakukannya. Ini akan mengembalikan tabel nilai yang dibatasi, belum mencoba semua skenario di atasnya tetapi contoh Anda berfungsi dengan baik.


CREATE FUNCTION SplitString 
(
    -- Add the parameters for the function here
    @myString varchar(500),
    @deliminator varchar(10)
)
RETURNS 
@ReturnTable TABLE 
(
    -- Add the column definitions for the TABLE variable here
    [id] [int] IDENTITY(1,1) NOT NULL,
    [part] [varchar](50) NULL
)
AS
BEGIN
        Declare @iSpaces int
        Declare @part varchar(50)

        --initialize spaces
        Select @iSpaces = charindex(@deliminator,@myString,0)
        While @iSpaces > 0

        Begin
            Select @part = substring(@myString,0,charindex(@deliminator,@myString,0))

            Insert Into @ReturnTable(part)
            Select @part

    Select @myString = substring(@mystring,charindex(@deliminator,@myString,0)+ len(@deliminator),len(@myString) - charindex(' ',@myString,0))


            Select @iSpaces = charindex(@deliminator,@myString,0)
        end

        If len(@myString) > 0
            Insert Into @ReturnTable
            Select @myString

    RETURN 
END
GO

Anda akan menyebutnya seperti ini:


Select * From SplitString('Hello John Smith',' ')

Sunting: Solusi yang diperbarui untuk menangani pembatas dengan len> 1 seperti pada:


select * From SplitString('Hello**John**Smith','**')
brendan
sumber
Tidak bekerja untuk select * dari dbo.ethos_SplitString_fn ('guy, wicks, here here', ',') id part ----------- ------------ -------------------------------------- 1 guy 2 wick
Guy
2
hati-hati dengan len () karena tidak akan mengembalikan angka yang benar jika argumennya memiliki spasi tambahan, misalnya len ('-') = 2.
Rory
Tidak berfungsi: pilih * dari dbo.SplitString ('foo, foo test ,,,, foo', ',')
cbp
1
Perbaiki untuk cbp .. Pilih @ myString = substring (@ mystring, @ iSpaces + len (@deliminator), len (@myString) - charindex (@ deliminator, @ myString, 0))
Alxwest
16

Di sini saya memposting cara solusi sederhana

CREATE FUNCTION [dbo].[split](
          @delimited NVARCHAR(MAX),
          @delimiter NVARCHAR(100)
        ) RETURNS @t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
        AS
        BEGIN
          DECLARE @xml XML
          SET @xml = N'<t>' + REPLACE(@delimited,@delimiter,'</t><t>') + '</t>'

          INSERT INTO @t(val)
          SELECT  r.value('.','varchar(MAX)') as item
          FROM  @xml.nodes('/t') as records(r)
          RETURN
        END


Jalankan fungsi seperti ini

  select * from dbo.split('Hello John Smith',' ')
Sivaganesh Tamilvendhan
sumber
Saya menyukai solusi ini. Perluas untuk mengembalikan nilai skalar berdasarkan kolom yang ditentukan dalam hasil.
Alan
Saya terbakar dengan tanda '&' di tali untuk dipisah menggunakan ini
KeithL
10

Menurut pendapat saya kalian membuatnya terlalu rumit. Cukup buat CLR UDF dan lakukan saja.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;

public partial class UserDefinedFunctions {
  [SqlFunction]
  public static SqlString SearchString(string Search) {
    List<string> SearchWords = new List<string>();
    foreach (string s in Search.Split(new char[] { ' ' })) {
      if (!s.ToLower().Equals("or") && !s.ToLower().Equals("and")) {
        SearchWords.Add(s);
      }
    }

    return new SqlString(string.Join(" OR ", SearchWords.ToArray()));
  }
};
Damon Drake
sumber
20
Saya kira ini terlalu rumit, karena saya perlu memiliki Visual Studio, kemudian aktifkan CLR di server, kemudian buat dan kompilasi proyek, dan akhirnya tambahkan rakitan ke database, untuk menggunakannya. Namun tetap merupakan jawaban yang menarik.
Guillermo Gutiérrez
3
@ guillegr123, tidak harus rumit. Anda cukup mengunduh dan menginstal (gratis!), SQL #, yang merupakan pustaka fungsi dan procs SQLCLR. Anda bisa mendapatkannya dari SQLsharp.com . Ya, saya penulisnya, tetapi String_Split termasuk dalam versi Gratis.
Solomon Rutzky
10

Bagaimana dengan penggunaan stringdan values()pernyataan?

DECLARE @str varchar(max)
SET @str = 'Hello John Smith'

DECLARE @separator varchar(max)
SET @separator = ' '

DECLARE @Splited TABLE(id int IDENTITY(1,1), item varchar(max))

SET @str = REPLACE(@str, @separator, '''),(''')
SET @str = 'SELECT * FROM (VALUES(''' + @str + ''')) AS V(A)' 

INSERT INTO @Splited
EXEC(@str)

SELECT * FROM @Splited

Hasil-set tercapai.

id  item
1   Hello
2   John
3   Smith
Frederic
sumber
1
saya menggunakan jawaban Anda tetapi tidak berhasil, tetapi saya memodifikasi dan ini bekerja dengan semua, saya menggunakan sql 2005
angel
9

Saya menggunakan jawaban frederic tetapi ini tidak berhasil di SQL Server 2005

Aku diubah dan saya menggunakan selectdengan union alldan bekerja

DECLARE @str varchar(max)
SET @str = 'Hello John Smith how are you'

DECLARE @separator varchar(max)
SET @separator = ' '

DECLARE @Splited table(id int IDENTITY(1,1), item varchar(max))

SET @str = REPLACE(@str, @separator, ''' UNION ALL SELECT ''')
SET @str = ' SELECT  ''' + @str + '''  ' 

INSERT INTO @Splited
EXEC(@str)

SELECT * FROM @Splited

Dan hasil-set adalah:

id  item
1   Hello
2   John
3   Smith
4   how
5   are
6   you
malaikat
sumber
Ini benar-benar hebat yang pernah saya lihat dalam hal sql, ini bekerja untuk pekerjaan saya dan saya menghargai itu, terima kasih!
Abdurrahman I.
Saya sangat bersemangat ketika melihat ini karena terlihat sangat bersih dan mudah dimengerti, tetapi sayangnya Anda tidak dapat memasukkan ini ke dalam UDF karena EXEC. EXECsecara implisit memanggil prosedur tersimpan, dan Anda tidak dapat menggunakan prosedur tersimpan di UDF.
Kristen Hammack
Ini Berhasil !! saya sedang mencari menggunakan fungsi (SplitStrings_Moden) dari sini: sqlperformance.com/2012/07/t-sql-queries/split-strings#comments yang melakukan ini dan butuh satu setengah menit untuk membagi data dan kembali baris ketika hanya menggunakan 4 nomor akun. Saya menguji versi Anda dengan gabungan kiri di atas meja dengan data pada nomor akun dan butuh waktu 2 atau 3 detik! Perbedaan besar dan bekerja dengan sempurna! Saya akan memberikan 20 suara ini jika memungkinkan!
MattE
8

Pola ini berfungsi dengan baik dan Anda dapat menggeneralisasi

Convert(xml,'<n>'+Replace(FIELD,'.','</n><n>')+'</n>').value('(/n[INDEX])','TYPE')
                          ^^^^^                                   ^^^^^     ^^^^

perhatikan BIDANG , INDEKS dan JENIS .

Biarkan beberapa tabel dengan pengenal suka

sys.message.1234.warning.A45
sys.message.1235.error.O98
....

Lalu, Anda bisa menulis

SELECT Source         = q.value('(/n[1])', 'varchar(10)'),
       RecordType     = q.value('(/n[2])', 'varchar(20)'),
       RecordNumber   = q.value('(/n[3])', 'int'),
       Status         = q.value('(/n[4])', 'varchar(5)')
FROM   (
         SELECT   q = Convert(xml,'<n>'+Replace(fieldName,'.','</n><n>')+'</n>')
         FROM     some_TABLE
       ) Q

membelah dan casting semua bagian.

josejuan
sumber
Ini adalah satu-satunya solusi di sini yang memungkinkan Anda untuk menggunakan tipe tertentu, dan cukup efisien (CLR masih paling efisien, tetapi pendekatan ini menangani tabel baris 8gb, 10 token, 10M dalam waktu sekitar 9 menit (aws m3 server, 4k iops drive yang disediakan)
Andrew Hill
7

Jika database Anda memiliki tingkat kompatibilitas 130 atau lebih tinggi maka Anda dapat menggunakan fungsi STRING_SPLIT bersama dengan klausa OFFSET FETCH untuk mendapatkan item tertentu dengan indeks.

Untuk mendapatkan item pada indeks N (berbasis nol), Anda dapat menggunakan kode berikut

SELECT value
FROM STRING_SPLIT('Hello John Smith',' ')
ORDER BY (SELECT NULL)
OFFSET N ROWS
FETCH NEXT 1 ROWS ONLY

Untuk memeriksa tingkat kompatibilitas basis data Anda , jalankan kode ini:

SELECT compatibility_level  
FROM sys.databases WHERE name = 'YourDBName';
Gorgi Rankovski
sumber
Caranya ada di OFFSET 1 ROWS, yang akan melewati item pertama dan akan mengembalikan item kedua. Jika indeks Anda berbasis 0 dan @X adalah variabel yang menahan indeks item yang ingin Anda ambil, Anda pasti dapat melakukan OFFSET @X ROWS
Gorgi Rankovski
Oke, tidak menggunakan ini sebelumnya ... Senang tahu ... Saya masih lebih suka xmlpendekatan berbasis-duduk, karena memungkinkan untuk mengambil tipe nilai-aman dan tidak perlu sub-permintaan, tapi ini adalah bagus +1 dari sisi saya
Shnugo
3
masalahnya di sini adalah STRING_SPLIT tidak menjamin urutan hasil yang dikembalikan. Jadi item Anda 1 mungkin atau mungkin bukan item saya 1.
user1443098
@GorgiRankovski, Menggunakan STRING_SPLITtuntutan untuk v2016 +. Dalam hal ini lebih baik menggunakan OPENJSONatau JSON_VALUE. Anda mungkin ingin memeriksa jawaban saya
Shnugo
6

Saya mencari solusi di internet dan di bawah ini berfungsi untuk saya. Ref .

Dan Anda memanggil fungsi seperti ini:

SELECT * FROM dbo.split('ram shyam hari gopal',' ')

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[Split](@String VARCHAR(8000), @Delimiter CHAR(1))       
RETURNS @temptable TABLE (items VARCHAR(8000))       
AS       
BEGIN       
    DECLARE @idx INT       
    DECLARE @slice VARCHAR(8000)        
    SELECT @idx = 1       
    IF len(@String)<1 OR @String IS NULL  RETURN       
    WHILE @idx!= 0       
    BEGIN       
        SET @idx = charindex(@Delimiter,@String)       
        IF @idx!=0       
            SET @slice = LEFT(@String,@idx - 1)       
        ELSE       
            SET @slice = @String       
        IF(len(@slice)>0)  
            INSERT INTO @temptable(Items) VALUES(@slice)       
        SET @String = RIGHT(@String,len(@String) - @idx)       
        IF len(@String) = 0 break       
    END   
    RETURN       
END
kta
sumber
Anda tidak dapat dengan mudah mengakses item ke-N menggunakan fungsi ini.
Björn Lindqvist
6

Namun bagian lain dari string dengan fungsi delimeter:

create function GetStringPartByDelimeter (
    @value as nvarchar(max),
    @delimeter as nvarchar(max),
    @position as int
) returns NVARCHAR(MAX) 
AS BEGIN
    declare @startPos as int
    declare @endPos as int
    set @endPos = -1
    while (@position > 0 and @endPos != 0) begin
        set @startPos = @endPos + 1
        set @endPos = charindex(@delimeter, @value, @startPos)

        if(@position = 1) begin
            if(@endPos = 0)
                set @endPos = len(@value) + 1

            return substring(@value, @startPos, @endPos - @startPos)
        end

        set @position = @position - 1
    end

    return null
end

dan penggunaannya:

select dbo.GetStringPartByDelimeter ('a;b;c;d;e', ';', 3)

yang mengembalikan:

c
Ramazan Binarbasi
sumber
Saya suka solusi ini sebagai opsi untuk mengembalikan satu substring sebagai ganti untuk mendapatkan tabel parsing yang kemudian harus Anda pilih. Menggunakan hasil tabel memiliki kegunaannya, tetapi untuk apa yang saya butuhkan ini bekerja dengan sempurna.
James H
5

Coba ini:

CREATE function [SplitWordList]
(
 @list varchar(8000)
)
returns @t table 
(
 Word varchar(50) not null,
 Position int identity(1,1) not null
)
as begin
  declare 
    @pos int,
    @lpos int,
    @item varchar(100),
    @ignore varchar(100),
    @dl int,
    @a1 int,
    @a2 int,
    @z1 int,
    @z2 int,
    @n1 int,
    @n2 int,
    @c varchar(1),
    @a smallint
  select 
    @a1 = ascii('a'),
    @a2 = ascii('A'),
    @z1 = ascii('z'),
    @z2 = ascii('Z'),
    @n1 = ascii('0'),
    @n2 = ascii('9')
  set @ignore = '''"'
  set @pos = 1
  set @dl = datalength(@list)
  set @lpos = 1
  set @item = ''
  while (@pos <= @dl) begin
    set @c = substring(@list, @pos, 1)
    if (@ignore not like '%' + @c + '%') begin
      set @a = ascii(@c)
      if ((@a >= @a1) and (@a <= @z1))  
        or ((@a >= @a2) and (@a <= @z2))
        or ((@a >= @n1) and (@a <= @n2))
      begin
        set @item = @item + @c
      end else if (@item > '') begin
        insert into @t values (@item)
        set @item = ''
      end
    end 
    set @pos = @pos + 1
  end
  if (@item > '') begin
    insert into @t values (@item)
  end
  return
end

Uji seperti ini:

select * from SplitWordList('Hello John Smith')
Seibar
sumber
Saya telah mengalaminya & itu sempurna seperti yang saya inginkan! bahkan saya juga dapat menyesuaikannya untuk mengabaikan karakter khusus yang saya pilih!
Vikas
5

Contoh berikut menggunakan CTE rekursif

Perbarui 18.09.2013

CREATE FUNCTION dbo.SplitStrings_CTE(@List nvarchar(max), @Delimiter nvarchar(1))
RETURNS @returns TABLE (val nvarchar(max), [level] int, PRIMARY KEY CLUSTERED([level]))
AS
BEGIN
;WITH cte AS
 (
  SELECT SUBSTRING(@List, 0, CHARINDEX(@Delimiter,  @List + @Delimiter)) AS val,
         CAST(STUFF(@List + @Delimiter, 1, CHARINDEX(@Delimiter, @List + @Delimiter), '') AS nvarchar(max)) AS stval, 
         1 AS [level]
  UNION ALL
  SELECT SUBSTRING(stval, 0, CHARINDEX(@Delimiter, stval)),
         CAST(STUFF(stval, 1, CHARINDEX(@Delimiter, stval), '') AS nvarchar(max)),
         [level] + 1
  FROM cte
  WHERE stval != ''
  )
  INSERT @returns
  SELECT REPLACE(val, ' ','' ) AS val, [level]
  FROM cte
  WHERE val > ''
  RETURN
END

Demo di SQLFiddle

Aleksandr Fedorenko
sumber
2


    Alter Function dbo.fn_Split
    (
    @Expression nvarchar(max),
    @Delimiter  nvarchar(20) = ',',
    @Qualifier  char(1) = Null
    )
    RETURNS @Results TABLE (id int IDENTITY(1,1), value nvarchar(max))
    AS
    BEGIN
       /* USAGE
            Select * From dbo.fn_Split('apple pear grape banana orange honeydew cantalope 3 2 1 4', ' ', Null)
            Select * From dbo.fn_Split('1,abc,"Doe, John",4', ',', '"')
            Select * From dbo.fn_Split('Hello 0,"&""&&&&', ',', '"')
       */

       -- Declare Variables
       DECLARE
          @X     xml,
          @Temp  nvarchar(max),
          @Temp2 nvarchar(max),
          @Start int,
          @End   int

       -- HTML Encode @Expression
       Select @Expression = (Select @Expression For XML Path(''))

       -- Find all occurences of @Delimiter within @Qualifier and replace with |||***|||
       While PATINDEX('%' + @Qualifier + '%', @Expression) > 0 AND Len(IsNull(@Qualifier, '')) > 0
       BEGIN
          Select
             -- Starting character position of @Qualifier
             @Start = PATINDEX('%' + @Qualifier + '%', @Expression),
             -- @Expression starting at the @Start position
             @Temp = SubString(@Expression, @Start + 1, LEN(@Expression)-@Start+1),
             -- Next position of @Qualifier within @Expression
             @End = PATINDEX('%' + @Qualifier + '%', @Temp) - 1,
             -- The part of Expression found between the @Qualifiers
             @Temp2 = Case When @End < 0 Then @Temp Else Left(@Temp, @End) End,
             -- New @Expression
             @Expression = REPLACE(@Expression,
                                   @Qualifier + @Temp2 + Case When @End < 0 Then '' Else @Qualifier End,
                                   Replace(@Temp2, @Delimiter, '|||***|||')
                           )
       END

       -- Replace all occurences of @Delimiter within @Expression with '</fn_Split><fn_Split>'
       -- And convert it to XML so we can select from it
       SET
          @X = Cast('<fn_Split>' +
                    Replace(@Expression, @Delimiter, '</fn_Split><fn_Split>') +
                    '</fn_Split>' as xml)

       -- Insert into our returnable table replacing '|||***|||' back to @Delimiter
       INSERT @Results
       SELECT
          "Value" = LTRIM(RTrim(Replace(C.value('.', 'nvarchar(max)'), '|||***|||', @Delimiter)))
       FROM
          @X.nodes('fn_Split') as X(C)

       -- Return our temp table
       RETURN
    END
T-Rex
sumber
2

Anda dapat membagi string dalam SQL tanpa membutuhkan fungsi:

DECLARE @bla varchar(MAX)
SET @bla = 'BED40DFC-F468-46DD-8017-00EF2FA3E4A4,64B59FC5-3F4D-4B0E-9A48-01F3D4F220B0,A611A108-97CA-42F3-A2E1-057165339719,E72D95EA-578F-45FC-88E5-075F66FD726C'

-- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes
SELECT 
    x.XmlCol.value('.', 'varchar(36)') AS val 
FROM 
(
    SELECT 
    CAST('<e>' + REPLACE(@bla, ',', '</e><e>') + '</e>' AS xml) AS RawXml
) AS b 
CROSS APPLY b.RawXml.nodes('e') x(XmlCol);

Jika Anda perlu mendukung string acak (dengan karakter khusus xml)

DECLARE @bla NVARCHAR(MAX)
SET @bla = '<html>unsafe & safe Utf8CharsDon''tGetEncoded ÄöÜ - "Conex"<html>,Barnes & Noble,abc,def,ghi'

-- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes
SELECT 
    x.XmlCol.value('.', 'nvarchar(MAX)') AS val 
FROM 
(
    SELECT 
    CAST('<e>' + REPLACE((SELECT @bla FOR XML PATH('')), ',', '</e><e>') + '</e>' AS xml) AS RawXml
) AS b 
CROSS APPLY b.RawXml.nodes('e') x(XmlCol); 
Stefan Steiger
sumber
1

Saya tahu ini adalah pertanyaan lama, tetapi saya pikir seseorang dapat mengambil manfaat dari solusi saya.

select 
SUBSTRING(column_name,1,CHARINDEX(' ',column_name,1)-1)
,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name))
    ,1
    ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)-1)
,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name))
    ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)+1
    ,LEN(column_name))
from table_name

SQL FIDDLE

Keuntungan:

  • Ini memisahkan semua 3 pembatas sub-string dengan ''.
  • Seseorang tidak boleh menggunakan while, karena ini akan menurunkan kinerja.
  • Tidak perlu Pivot karena semua sub-string yang dihasilkan akan ditampilkan dalam satu Baris

Keterbatasan:

  • Orang harus tahu total tidak. ruang (sub-string).

Catatan : solusinya dapat memberikan sub-string hingga N.

Untuk mengatasi batasan kita dapat menggunakan referensi berikut .

Tetapi sekali lagi solusi di atas tidak dapat digunakan dalam sebuah tabel (Actaully saya tidak dapat menggunakannya).

Sekali lagi saya berharap solusi ini dapat membantu seseorang.

Pembaruan: Dalam hal Catatan> 50000 tidak disarankan untuk digunakan LOOPSkarena akan menurunkan Kinerja

Luv
sumber
1

Solusi berbasis set murni menggunakan TVFdengan rekursif CTE. Anda bisa JOINdan APPLYfungsi ini untuk dataset apa pun.

create function [dbo].[SplitStringToResultSet] (@value varchar(max), @separator char(1))
returns table
as return
with r as (
    select value, cast(null as varchar(max)) [x], -1 [no] from (select rtrim(cast(@value as varchar(max))) [value]) as j
    union all
    select right(value, len(value)-case charindex(@separator, value) when 0 then len(value) else charindex(@separator, value) end) [value]
    , left(r.[value], case charindex(@separator, r.value) when 0 then len(r.value) else abs(charindex(@separator, r.[value])-1) end ) [x]
    , [no] + 1 [no]
    from r where value > '')

select ltrim(x) [value], [no] [index] from r where x is not null;
go

Pemakaian:

select *
from [dbo].[SplitStringToResultSet]('Hello John Smith', ' ')
where [index] = 1;

Hasil:

value   index
-------------
John    1
Andrey Morozov
sumber
1

Hampir semua jawaban lain menggantikan string yang sedang dibagi yang membuang siklus CPU dan melakukan alokasi memori yang tidak perlu.

Saya membahas cara yang jauh lebih baik untuk melakukan pemisahan string di sini: http://www.digitalruby.com/split-string-sql-server/

Ini kodenya:

SET NOCOUNT ON

-- You will want to change nvarchar(MAX) to nvarchar(50), varchar(50) or whatever matches exactly with the string column you will be searching against
DECLARE @SplitStringTable TABLE (Value nvarchar(MAX) NOT NULL)
DECLARE @StringToSplit nvarchar(MAX) = 'your|string|to|split|here'
DECLARE @SplitEndPos int
DECLARE @SplitValue nvarchar(MAX)
DECLARE @SplitDelim nvarchar(1) = '|'
DECLARE @SplitStartPos int = 1

SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos)

WHILE @SplitEndPos > 0
BEGIN
    SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, (@SplitEndPos - @SplitStartPos))
    INSERT @SplitStringTable (Value) VALUES (@SplitValue)
    SET @SplitStartPos = @SplitEndPos + 1
    SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos)
END

SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, 2147483647)
INSERT @SplitStringTable (Value) VALUES(@SplitValue)

SET NOCOUNT OFF

-- You can select or join with the values in @SplitStringTable at this point.
jjxtra
sumber
0

Solusi CTE rekursif dengan nyeri server, ujilah

Setup Skema MS SQL Server 2008 :

create table Course( Courses varchar(100) );
insert into Course values ('Hello John Smith');

Pertanyaan 1 :

with cte as
   ( select 
        left( Courses, charindex( ' ' , Courses) ) as a_l,
        cast( substring( Courses, 
                         charindex( ' ' , Courses) + 1 , 
                         len(Courses ) ) + ' ' 
              as varchar(100) )  as a_r,
        Courses as a,
        0 as n
     from Course t
    union all
      select 
        left(a_r, charindex( ' ' , a_r) ) as a_l,
        substring( a_r, charindex( ' ' , a_r) + 1 , len(a_R ) ) as a_r,
        cte.a,
        cte.n + 1 as n
    from Course t inner join cte 
         on t.Courses = cte.a and len( a_r ) > 0

   )
select a_l, n from cte
--where N = 1

Hasil :

|    A_L | N |
|--------|---|
| Hello  | 0 |
|  John  | 1 |
| Smith  | 2 |
dani herrera
sumber
0

sementara mirip dengan jawaban berbasis xml oleh josejuan, saya menemukan bahwa memproses jalur xml hanya sekali, kemudian pivoting cukup efisien:

select ID,
    [3] as PathProvidingID,
    [4] as PathProvider,
    [5] as ComponentProvidingID,
    [6] as ComponentProviding,
    [7] as InputRecievingID,
    [8] as InputRecieving,
    [9] as RowsPassed,
    [10] as InputRecieving2
    from
    (
    select id,message,d.* from sysssislog cross apply       ( 
          SELECT Item = y.i.value('(./text())[1]', 'varchar(200)'),
              row_number() over(order by y.i) as rn
          FROM 
          ( 
             SELECT x = CONVERT(XML, '<i>' + REPLACE(Message, ':', '</i><i>') + '</i>').query('.')
          ) AS a CROSS APPLY x.nodes('i') AS y(i)
       ) d
       WHERE event
       = 
       'OnPipelineRowsSent'
    ) as tokens 
    pivot 
    ( max(item) for [rn] in ([3],[4],[5],[6],[7],[8],[9],[10]) 
    ) as data

berlari jam 8:30

select id,
tokens.value('(/n[3])', 'varchar(100)')as PathProvidingID,
tokens.value('(/n[4])', 'varchar(100)') as PathProvider,
tokens.value('(/n[5])', 'varchar(100)') as ComponentProvidingID,
tokens.value('(/n[6])', 'varchar(100)') as ComponentProviding,
tokens.value('(/n[7])', 'varchar(100)') as InputRecievingID,
tokens.value('(/n[8])', 'varchar(100)') as InputRecieving,
tokens.value('(/n[9])', 'varchar(100)') as RowsPassed
 from
(
    select id, Convert(xml,'<n>'+Replace(message,'.','</n><n>')+'</n>') tokens
         from sysssislog 
       WHERE event
       = 
       'OnPipelineRowsSent'
    ) as data

berlari di 9:20

Andrew Hill
sumber
0
CREATE FUNCTION [dbo].[fnSplitString] 
( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE(splitdata NVARCHAR(MAX) 
) 
BEGIN 
    DECLARE @start INT, @end INT 
    SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
    WHILE @start < LEN(@string) + 1 BEGIN 
        IF @end = 0  
            SET @end = LEN(@string) + 1

        INSERT INTO @output (splitdata)  
        VALUES(SUBSTRING(@string, @start, @end - @start)) 
        SET @start = @end + 1 
        SET @end = CHARINDEX(@delimiter, @string, @start)

    END 
    RETURN 
END

DAN GUNAKAN ITU

select *from dbo.fnSplitString('Querying SQL Server','')
Savas Adar
sumber
0

jika ada yang ingin mendapatkan hanya satu bagian dari teks terpisah dapat menggunakan ini

pilih * dari fromSplitStringSep ('Word1 wordr2 word3', '')

CREATE function [dbo].[SplitStringSep] 
(
    @str nvarchar(4000), 
    @separator char(1)
)
returns table
AS
return (
    with tokens(p, a, b) AS (
        select 
        1, 
        1, 
        charindex(@separator, @str)
        union all
        select
            p + 1, 
            b + 1, 
            charindex(@separator, @str, b + 1)
        from tokens
        where b > 0
        )
        select
            p-1 zeroBasedOccurance,
            substring(
                @str, 
                a, 
                case when b > 0 then b-a ELSE 4000 end) 
            AS s
        from tokens
  )
nazim hatipoglu
sumber
0

Saya memindahkan ini,

declare @x nvarchar(Max) = 'ali.veli.deli.';
declare @item nvarchar(Max);
declare @splitter char='.';

while CHARINDEX(@splitter,@x) != 0
begin
    set @item = LEFT(@x,CHARINDEX(@splitter,@x))
    set @x    = RIGHT(@x,len(@x)-len(@item) )
     select @item as item, @x as x;
end

satu-satunya perhatian yang harus Anda perhatikan adalah titik '.' ujung @x harus selalu ada di sana.

Ali CAKIL
sumber
0

membangun pada @NothingsImpossible solusi, atau, lebih tepatnya, mengomentari jawaban yang paling banyak dipilih (tepat di bawah yang diterima), saya menemukan solusi cepat dan kotor berikut memenuhi kebutuhan saya sendiri - itu memiliki manfaat hanya berada dalam domain SQL.

diberi string "pertama; kedua; ketiga; keempat; kelima", katakan, saya ingin mendapatkan token ketiga. ini bekerja hanya jika kita tahu berapa banyak token yang akan dimiliki string - dalam kasus ini 5., jadi cara tindakan saya adalah memotong dua token terakhir (permintaan dalam), dan kemudian memotong dua token pertama pergi ( permintaan luar)

saya tahu bahwa ini jelek dan mencakup kondisi spesifik saya, tetapi saya mempostingnya kalau-kalau ada yang merasa berguna. Bersulang

select 
    REVERSE(
        SUBSTRING(
            reverse_substring, 
            0, 
            CHARINDEX(';', reverse_substring)
        )
    ) 
from 
(
    select 
        msg,
        SUBSTRING(
            REVERSE(msg), 
            CHARINDEX(
                ';', 
                REVERSE(msg), 
                CHARINDEX(
                    ';',
                    REVERSE(msg)
                )+1
            )+1,
            1000
        ) reverse_substring
    from 
    (
        select 'first;second;third;fourth;fifth' msg
    ) a
) b
hello_earth
sumber
ini bekerja hanya jika kita tahu berapa banyak token yang dimiliki string - batasan yang melanggar ...
Shnugo
0
declare @strng varchar(max)='hello john smith'
select (
    substring(
        @strng,
        charindex(' ', @strng) + 1,
        (
          (charindex(' ', @strng, charindex(' ', @strng) + 1))
          - charindex(' ',@strng)
        )
    ))
Smart003
sumber
0

Dimulai dengan SQL Server 2016 kami string_split

DECLARE @string varchar(100) = 'Richard, Mike, Mark'

SELECT value FROM string_split(@string, ',')
Victor Hugo Terceros
sumber
Ini bagus dan bagus, tetapi tidak menjawab pertanyaan untuk mendapatkan hasil ke-n.
Johnie Karr
STRING_SPLITtidak menjamin untuk mengembalikan pesanan yang sama. Tapi OPENJSONapakah (lihat jawaban saya (bagian pembaruan) )
Shnugo