Saya memiliki kolom SQL Server 2008 R2 yang berisi string yang perlu saya pisahkan dengan koma. Saya telah melihat banyak jawaban di StackOverflow tetapi tidak ada yang berfungsi di R2. Saya telah memastikan bahwa saya memiliki izin pilih pada setiap contoh fungsi pemisahan. Setiap bantuan sangat dihargai.
sql
sql-server
tsql
sql-server-2008
split
Lee Grindon
sumber
sumber
mdq.RegexSplit
fungsi di add-on "Master Data Services", yang dapat membantu. Tentu patut diselidiki .Jawaban:
Saya telah menggunakan SQL ini sebelumnya yang mungkin berhasil untuk Anda: -
CREATE FUNCTION dbo.splitstring ( @stringToSplit VARCHAR(MAX) ) RETURNS @returnList TABLE ([Name] [nvarchar] (500)) AS BEGIN DECLARE @name NVARCHAR(255) DECLARE @pos INT WHILE CHARINDEX(',', @stringToSplit) > 0 BEGIN SELECT @pos = CHARINDEX(',', @stringToSplit) SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1) INSERT INTO @returnList SELECT @name SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos) END INSERT INTO @returnList SELECT @stringToSplit RETURN END
dan untuk menggunakannya: -
SELECT * FROM dbo.splitstring('91,12,65,78,56,789')
sumber
select * from dbo.splitstring('')
Alih-alih CTE rekursif dan while loop, adakah yang mempertimbangkan pendekatan yang lebih berbasis set? Perhatikan bahwa fungsi ini ditulis untuk pertanyaan, yang didasarkan pada SQL Server 2008 dan koma sebagai pemisah . Di SQL Server 2016 dan di atasnya (dan di tingkat kompatibilitas 130 ke atas),
STRING_SPLIT()
adalah opsi yang lebih baik .CREATE FUNCTION dbo.SplitString ( @List nvarchar(max), @Delim nvarchar(255) ) RETURNS TABLE AS RETURN ( SELECT [Value] FROM ( SELECT [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number], CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number]))) FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name) FROM sys.all_columns) AS x WHERE Number <= LEN(@List) AND SUBSTRING(@Delim + @List, [Number], DATALENGTH(@Delim)/2) = @Delim ) AS y ); GO
Jika Anda ingin menghindari batasan panjang string menjadi <= jumlah baris dalam
sys.all_columns
(9.980 dimodel
dalam SQL Server 2017; jauh lebih tinggi di database pengguna Anda sendiri), Anda dapat menggunakan pendekatan lain untuk mendapatkan angka, seperti membuat tabel angka Anda sendiri . Anda juga dapat menggunakan CTE rekursif jika Anda tidak dapat menggunakan tabel sistem atau membuatnya sendiri:CREATE FUNCTION dbo.SplitString ( @List nvarchar(max), @Delim nvarchar(255) ) RETURNS TABLE WITH SCHEMABINDING AS RETURN ( WITH n(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n <= LEN(@List)) SELECT [Value] = SUBSTRING(@List, n, CHARINDEX(@Delim, @List + @Delim, n) - n) FROM n WHERE n <= LEN(@List) AND SUBSTRING(@Delim + @List, n, DATALENGTH(@Delim)/2) = @Delim ); GO
Tetapi Anda harus menambahkan
OPTION (MAXRECURSION 0)
(atauMAXRECURSION <longest possible string length if < 32768>
) ke kueri luar untuk menghindari kesalahan dengan rekursi untuk string> 100 karakter. Jika itu juga bukan alternatif yang baik, lihat jawaban ini seperti yang ditunjukkan di komentar.(Selain itu, pembatas haruslah
NCHAR(<=1228)
. Masih meneliti alasannya.)Lebih lanjut tentang fungsi split, mengapa (dan membuktikan bahwa) sementara loop dan CTE rekursif tidak diskalakan, dan alternatif yang lebih baik, jika string pemisahan berasal dari lapisan aplikasi:
sumber
sys.all_objects
kurang dari jumlah karakter dalam string input maka itu akan memotong string dan nilainya akan hilang. Karenasys.all_objects
hanya digunakan sebagai sedikit retasan untuk menghasilkan baris, maka ada cara yang lebih baik untuk melakukan ini, misalnya jawaban ini .Akhirnya penantian selesai di SQL Server 2016 mereka telah memperkenalkan fungsi string terpisah:
STRING_SPLIT
select * From STRING_SPLIT ('a,b', ',') cs
Semua metode lain untuk membagi string seperti XML, tabel Tally, while loop, dll .. telah terpesona oleh
STRING_SPLIT
fungsi ini .Berikut adalah artikel luar biasa dengan perbandingan kinerja: Kejutan dan Asumsi Kinerja: STRING_SPLIT
sumber
Cara termudah untuk melakukannya adalah dengan menggunakan
XML
format.1. Mengubah string menjadi baris tanpa tabel
PERTANYAAN
DECLARE @String varchar(100) = 'String1,String2,String3' -- To change ',' to any other delimeter, just change ',' to your desired one DECLARE @Delimiter CHAR = ',' SELECT LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) 'Value' FROM ( SELECT CAST ('<M>' + REPLACE(@String, @Delimiter, '</M><M>') + '</M>' AS XML) AS Data ) AS A CROSS APPLY Data.nodes ('/M') AS Split(a)
HASIL
x---------x | Value | x---------x | String1 | | String2 | | String3 | x---------x
2. Mengonversi ke baris dari tabel yang memiliki ID untuk setiap baris CSV
TABEL SUMBER
x-----x--------------------------x | Id | Value | x-----x--------------------------x | 1 | String1,String2,String3 | | 2 | String4,String5,String6 | x-----x--------------------------x
PERTANYAAN
-- To change ',' to any other delimeter, just change ',' before '</M><M>' to your desired one DECLARE @Delimiter CHAR = ',' SELECT ID,LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) 'Value' FROM ( SELECT ID,CAST ('<M>' + REPLACE(VALUE, @Delimiter, '</M><M>') + '</M>' AS XML) AS Data FROM TABLENAME ) AS A CROSS APPLY Data.nodes ('/M') AS Split(a)
HASIL
x-----x----------x | Id | Value | x-----x----------x | 1 | String1 | | 1 | String2 | | 1 | String3 | | 2 | String4 | | 2 | String5 | | 2 | String6 | x-----x----------x
sumber
@String
mengandung karakter terlarang ... Saya baru saja memposting jawaban untuk mengatasi masalah ini.Saya membutuhkan cara cepat untuk menyingkirkan
+4
dari kode pos .UPDATE #Emails SET ZIPCode = SUBSTRING(ZIPCode, 1, (CHARINDEX('-', ZIPCODE)-1)) WHERE ZIPCode LIKE '%-%'
Tidak ada proses ... tidak ada UDF ... hanya satu perintah inline kecil yang ketat yang melakukan apa yang harus dilakukan. Tidak mewah, tidak elegan.
Ubah pembatas sesuai kebutuhan, dll, dan itu akan berfungsi untuk apa pun.
sumber
jika Anda mengganti
dengan
Anda dapat menghilangkan sisipan terakhir setelah loop sementara!
CREATE FUNCTION dbo.splitstring ( @stringToSplit VARCHAR(MAX) ) RETURNS @returnList TABLE ([Name] [nvarchar] (500)) AS BEGIN DECLARE @name NVARCHAR(255) DECLARE @pos INT WHILE LEN(@stringToSplit) > 0 BEGIN SELECT @pos = CHARINDEX(',', @stringToSplit) if @pos = 0 SELECT @pos = LEN(@stringToSplit) SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1) INSERT INTO @returnList SELECT @name SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos) END RETURN END
sumber
+1
keSELECT @pos = LEN(@stringToSplit)
tampaknya mengatasi masalah itu. Namun,SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos)
akan kembaliInvalid length parameter passed to the LEFT or SUBSTRING function
kecuali Anda menambahkan+1
parameter ketiga dari SUBSTRING juga. atau Anda dapat mengganti tugas itu denganSET @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, 4000) --MAX len of nvarchar is 4000
Semua fungsi untuk pemisahan string yang menggunakan beberapa jenis Perulangan (iterasi) memiliki kinerja yang buruk. Mereka harus diganti dengan solusi berbasis set.
Kode ini dijalankan dengan sangat baik.
CREATE FUNCTION dbo.SplitStrings ( @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
sumber
@List
mengandung karakter terlarang ... Saya baru saja memposting jawaban untuk mengatasi masalah ini.Pendekatan yang sering digunakan dengan elemen XML putus jika ada karakter terlarang. Ini adalah pendekatan untuk menggunakan metode ini dengan semua jenis karakter, bahkan dengan titik koma sebagai pembatas.
Triknya adalah, pertama gunakan
SELECT SomeString AS [*] FOR XML PATH('')
untuk membuat semua karakter terlarang lolos dengan benar. Itulah alasan mengapa saya mengganti pembatas menjadi nilai ajaib untuk menghindari masalah dengan;
pembatas.DECLARE @Dummy TABLE (ID INT, SomeTextToSplit NVARCHAR(MAX)) INSERT INTO @Dummy VALUES (1,N'A&B;C;D;E, F') ,(2,N'"C" & ''D'';<C>;D;E, F'); DECLARE @Delimiter NVARCHAR(10)=';'; --special effort needed (due to entities coding with "&code;")! WITH Casted AS ( SELECT * ,CAST(N'<x>' + REPLACE((SELECT REPLACE(SomeTextToSplit,@Delimiter,N'§§Split$me$here§§') AS [*] FOR XML PATH('')),N'§§Split$me$here§§',N'</x><x>') + N'</x>' AS XML) AS SplitMe FROM @Dummy ) SELECT Casted.ID ,x.value(N'.',N'nvarchar(max)') AS Part FROM Casted CROSS APPLY SplitMe.nodes(N'/x') AS A(x)
Hasil
sumber
Saya harus menulis sesuatu seperti ini baru-baru ini. Inilah solusi yang saya dapatkan. Ini digeneralisasi untuk string pembatas apa pun dan saya pikir itu akan bekerja sedikit lebih baik:
CREATE FUNCTION [dbo].[SplitString] ( @string nvarchar(4000) , @delim nvarchar(100) ) RETURNS @result TABLE ( [Value] nvarchar(4000) NOT NULL , [Index] int NOT NULL ) AS BEGIN DECLARE @str nvarchar(4000) , @pos int , @prv int = 1 SELECT @pos = CHARINDEX(@delim, @string) WHILE @pos > 0 BEGIN SELECT @str = SUBSTRING(@string, @prv, @pos - @prv) INSERT INTO @result SELECT @str, @prv SELECT @prv = @pos + LEN(@delim) , @pos = CHARINDEX(@delim, @string, @pos + 1) END INSERT INTO @result SELECT SUBSTRING(@string, @prv, 4000), @prv RETURN END
sumber
Solusi dengan menggunakan CTE, jika ada yang membutuhkannya (selain saya, yang jelas melakukannya, itulah mengapa saya menulisnya).
declare @StringToSplit varchar(100) = 'Test1,Test2,Test3'; declare @SplitChar varchar(10) = ','; with StringToSplit as ( select ltrim( rtrim( substring( @StringToSplit, 1, charindex( @SplitChar, @StringToSplit ) - 1 ) ) ) Head , substring( @StringToSplit, charindex( @SplitChar, @StringToSplit ) + 1, len( @StringToSplit ) ) Tail union all select ltrim( rtrim( substring( Tail, 1, charindex( @SplitChar, Tail ) - 1 ) ) ) Head , substring( Tail, charindex( @SplitChar, Tail ) + 1, len( Tail ) ) Tail from StringToSplit where charindex( @SplitChar, Tail ) > 0 union all select ltrim( rtrim( Tail ) ) Head , '' Tail from StringToSplit where charindex( @SplitChar, Tail ) = 0 and len( Tail ) > 0 ) select Head from StringToSplit
sumber
Ini lebih sempit. Ketika saya melakukan ini, saya biasanya memiliki daftar ID unik yang dipisahkan koma (INT atau BIGINT), yang ingin saya gunakan sebagai tabel untuk digunakan sebagai gabungan dalam ke tabel lain yang memiliki kunci utama INT atau BIGINT. Saya ingin fungsi nilai tabel sebaris dikembalikan sehingga saya memiliki gabungan yang paling efisien.
Penggunaan sampel adalah:
DECLARE @IDs VARCHAR(1000); SET @IDs = ',99,206,124,8967,1,7,3,45234,2,889,987979,'; SELECT me.Value FROM dbo.MyEnum me INNER JOIN dbo.GetIntIdsTableFromDelimitedString(@IDs) ids ON me.PrimaryKey = ids.ID
Saya mencuri ide dari http://sqlrecords.blogspot.com/2012/11/converting-delimited-list-to-table.html , mengubahnya menjadi nilai tabel in-line dan berperan sebagai INT.
create function dbo.GetIntIDTableFromDelimitedString ( @IDs VARCHAR(1000) --this parameter must start and end with a comma, eg ',123,456,' --all items in list must be perfectly formatted or function will error ) RETURNS TABLE AS RETURN SELECT CAST(SUBSTRING(@IDs,Nums.number + 1,CHARINDEX(',',@IDs,(Nums.number+2)) - Nums.number - 1) AS INT) AS ID FROM [master].[dbo].[spt_values] Nums WHERE Nums.Type = 'P' AND Nums.number BETWEEN 1 AND DATALENGTH(@IDs) AND SUBSTRING(@IDs,Nums.number,1) = ',' AND CHARINDEX(',',@IDs,(Nums.number+1)) > Nums.number; GO
sumber
Ada versi yang benar di sini tetapi saya pikir akan lebih baik untuk menambahkan sedikit toleransi kesalahan jika mereka memiliki tanda koma serta membuatnya sehingga Anda dapat menggunakannya bukan sebagai fungsi tetapi sebagai bagian dari kode yang lebih besar . Untuk berjaga-jaga jika Anda hanya menggunakannya sekali dan tidak memerlukan fungsi. Ini juga untuk integer (yang saya butuhkan untuk itu) jadi Anda mungkin harus mengubah tipe data Anda.
DECLARE @StringToSeperate VARCHAR(10) SET @StringToSeperate = '1,2,5' --SELECT @StringToSeperate IDs INTO #Test DROP TABLE #IDs CREATE TABLE #IDs (ID int) DECLARE @CommaSeperatedValue NVARCHAR(255) = '' DECLARE @Position INT = LEN(@StringToSeperate) --Add Each Value WHILE CHARINDEX(',', @StringToSeperate) > 0 BEGIN SELECT @Position = CHARINDEX(',', @StringToSeperate) SELECT @CommaSeperatedValue = SUBSTRING(@StringToSeperate, 1, @Position-1) INSERT INTO #IDs SELECT @CommaSeperatedValue SELECT @StringToSeperate = SUBSTRING(@StringToSeperate, @Position+1, LEN(@StringToSeperate)-@Position) END --Add Last Value IF (LEN(LTRIM(RTRIM(@StringToSeperate)))>0) BEGIN INSERT INTO #IDs SELECT SUBSTRING(@StringToSeperate, 1, @Position) END SELECT * FROM #IDs
sumber
SET @StringToSeperate = @StringToSeperate+','
langsung sebelumWHILE
pengulangan, saya pikir Anda mungkin dapat menghilangkan blok "tambahkan nilai terakhir". Lihat juga sol'n saya di githubSaya sedikit memodifikasi + fungsi Andy Robinson. Sekarang Anda hanya dapat memilih bagian yang diperlukan dari tabel pengembalian:
CREATE FUNCTION dbo.splitstring ( @stringToSplit VARCHAR(MAX) ) RETURNS @returnList TABLE ([numOrder] [tinyint] , [Name] [nvarchar] (500)) AS BEGIN DECLARE @name NVARCHAR(255) DECLARE @pos INT DECLARE @orderNum INT SET @orderNum=0 WHILE CHARINDEX('.', @stringToSplit) > 0 BEGIN SELECT @orderNum=@orderNum+1; SELECT @pos = CHARINDEX('.', @stringToSplit) SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1) INSERT INTO @returnList SELECT @orderNum,@name SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos) END SELECT @orderNum=@orderNum+1; INSERT INTO @returnList SELECT @orderNum, @stringToSplit RETURN END Usage:
SELECT Name FROM dbo.splitstring('ELIS.YD.CRP1.1.CBA.MDSP.T389.BT') WHERE numOrder=5
sumber
Jika Anda memerlukan solusi ad-hoc cepat untuk kasus umum dengan kode minimum, CTE dua baris rekursif ini akan melakukannya:
DECLARE @s VARCHAR(200) = ',1,2,,3,,,4,,,,5,' ;WITH a AS (SELECT i=-1, j=0 UNION ALL SELECT j, CHARINDEX(',', @s, j + 1) FROM a WHERE j > i), b AS (SELECT SUBSTRING(@s, i+1, IIF(j>0, j, LEN(@s)+1)-i-1) s FROM a WHERE i >= 0) SELECT * FROM b
Gunakan ini sebagai pernyataan yang berdiri sendiri atau cukup tambahkan CTE di atas ke salah satu kueri Anda dan Anda akan dapat menggabungkan tabel yang dihasilkan
b
dengan yang lain untuk digunakan dalam ekspresi lebih lanjut.edit (oleh Shnugo)
Jika Anda menambahkan penghitung, Anda akan mendapatkan indeks posisi bersama dengan Daftar:
DECLARE @s VARCHAR(200) = '1,2333,344,4' ;WITH a AS (SELECT n=0, i=-1, j=0 UNION ALL SELECT n+1, j, CHARINDEX(',', @s, j+1) FROM a WHERE j > i), b AS (SELECT n, SUBSTRING(@s, i+1, IIF(j>0, j, LEN(@s)+1)-i-1) s FROM a WHERE i >= 0) SELECT * FROM b;
Hasil:
sumber
Saya mengambil rute xml dengan membungkus nilai-nilai menjadi elemen (M tetapi ada yang berfungsi):
declare @v nvarchar(max) = '100,201,abcde' select a.value('.', 'varchar(max)') from (select cast('<M>' + REPLACE(@v, ',', '</M><M>') + '</M>' AS XML) as col) as A CROSS APPLY A.col.nodes ('/M') AS Split(a)
sumber
berikut adalah versi yang dapat dibagi berdasarkan pola menggunakan patindex, adaptasi sederhana dari posting di atas. Saya punya kasus di mana saya perlu membagi string yang berisi beberapa karakter pemisah.
alter FUNCTION dbo.splitstring ( @stringToSplit VARCHAR(1000), @splitPattern varchar(10) ) RETURNS @returnList TABLE ([Name] [nvarchar] (500)) AS BEGIN DECLARE @name NVARCHAR(255) DECLARE @pos INT WHILE PATINDEX(@splitPattern, @stringToSplit) > 0 BEGIN SELECT @pos = PATINDEX(@splitPattern, @stringToSplit) SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1) INSERT INTO @returnList SELECT @name SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos) END INSERT INTO @returnList SELECT @stringToSplit RETURN END select * from dbo.splitstring('stringa/stringb/x,y,z','%[/,]%');
hasilnya terlihat seperti ini
stringa stringb x y z
sumber
Secara pribadi saya menggunakan fungsi ini:
ALTER FUNCTION [dbo].[CUST_SplitString] ( @String NVARCHAR(4000), @Delimiter NCHAR(1) ) RETURNS TABLE AS RETURN ( WITH Split(stpos,endpos) AS( SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos UNION ALL SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1) FROM Split WHERE endpos > 0 ) SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)), 'Data' = SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)-stpos) FROM Split )
sumber
Saya telah mengembangkan Splitter ganda (Membawa dua karakter terpisah) seperti yang diminta di sini . Bisa menjadi beberapa nilai di utas ini karena paling banyak dirujuk untuk kueri yang berkaitan dengan pemisahan string.
CREATE FUNCTION uft_DoubleSplitter ( -- Add the parameters for the function here @String VARCHAR(4000), @Splitter1 CHAR, @Splitter2 CHAR ) RETURNS @Result TABLE (Id INT,MId INT,SValue VARCHAR(4000)) AS BEGIN DECLARE @FResult TABLE(Id INT IDENTITY(1, 1), SValue VARCHAR(4000)) DECLARE @SResult TABLE(Id INT IDENTITY(1, 1), MId INT, SValue VARCHAR(4000)) SET @String = @String+@Splitter1 WHILE CHARINDEX(@Splitter1, @String) > 0 BEGIN DECLARE @WorkingString VARCHAR(4000) = NULL SET @WorkingString = SUBSTRING(@String, 1, CHARINDEX(@Splitter1, @String) - 1) --Print @workingString INSERT INTO @FResult SELECT CASE WHEN @WorkingString = '' THEN NULL ELSE @WorkingString END SET @String = SUBSTRING(@String, LEN(@WorkingString) + 2, LEN(@String)) END IF ISNULL(@Splitter2, '') != '' BEGIN DECLARE @OStartLoop INT DECLARE @OEndLoop INT SELECT @OStartLoop = MIN(Id), @OEndLoop = MAX(Id) FROM @FResult WHILE @OStartLoop <= @OEndLoop BEGIN DECLARE @iString VARCHAR(4000) DECLARE @iMId INT SELECT @iString = SValue+@Splitter2, @iMId = Id FROM @FResult WHERE Id = @OStartLoop WHILE CHARINDEX(@Splitter2, @iString) > 0 BEGIN DECLARE @iWorkingString VARCHAR(4000) = NULL SET @IWorkingString = SUBSTRING(@iString, 1, CHARINDEX(@Splitter2, @iString) - 1) INSERT INTO @SResult SELECT @iMId, CASE WHEN @iWorkingString = '' THEN NULL ELSE @iWorkingString END SET @iString = SUBSTRING(@iString, LEN(@iWorkingString) + 2, LEN(@iString)) END SET @OStartLoop = @OStartLoop + 1 END INSERT INTO @Result SELECT MId AS PrimarySplitID, ROW_NUMBER() OVER (PARTITION BY MId ORDER BY Mid, Id) AS SecondarySplitID , SValue FROM @SResult END ELSE BEGIN INSERT INTO @Result SELECT Id AS PrimarySplitID, NULL AS SecondarySplitID, SValue FROM @FResult END RETURN
Pemakaian:
--FirstSplit SELECT * FROM uft_DoubleSplitter('ValueA=ValueB=ValueC=ValueD==ValueE&ValueA=ValueB=ValueC===ValueE&ValueA=ValueB==ValueD===','&',NULL) --Second Split SELECT * FROM uft_DoubleSplitter('ValueA=ValueB=ValueC=ValueD==ValueE&ValueA=ValueB=ValueC===ValueE&ValueA=ValueB==ValueD===','&','=')
Kemungkinan Penggunaan (Dapatkan nilai kedua dari setiap pemisahan):
SELECT fn.SValue FROM uft_DoubleSplitter('ValueA=ValueB=ValueC=ValueD==ValueE&ValueA=ValueB=ValueC===ValueE&ValueA=ValueB==ValueD===', '&', '=')AS fn WHERE fn.mid = 2
sumber
Solusi berbasis cte rekursif
declare @T table (iden int identity, col1 varchar(100)); insert into @T(col1) values ('ROOT/South America/Lima/Test/Test2') , ('ROOT/South America/Peru/Test/Test2') , ('ROOT//South America/Venuzuala ') , ('RtT/South America / ') , ('ROOT/South Americas// '); declare @split char(1) = '/'; select @split as split; with cte as ( select t.iden, case when SUBSTRING(REVERSE(rtrim(t.col1)), 1, 1) = @split then LTRIM(RTRIM(t.col1)) else LTRIM(RTRIM(t.col1)) + @split end as col1, 0 as pos , 1 as cnt from @T t union all select t.iden, t.col1 , charindex(@split, t.col1, t.pos + 1), cnt + 1 from cte t where charindex(@split, t.col1, t.pos + 1) > 0 ) select t1.*, t2.pos, t2.cnt , ltrim(rtrim(SUBSTRING(t1.col1, t1.pos+1, t2.pos-t1.pos-1))) as bingo from cte t1 join cte t2 on t2.iden = t1.iden and t2.cnt = t1.cnt+1 and t2.pos > t1.pos order by t1.iden, t1.cnt;
sumber
Dengan segala hormat kepada @AviG, ini adalah versi bug gratis dari fungsi yang ia sediakan untuk mengembalikan semua token secara penuh.
IF EXISTS (SELECT * FROM sys.objects WHERE type = 'TF' AND name = 'TF_SplitString') DROP FUNCTION [dbo].[TF_SplitString] GO -- ============================================= -- Author: AviG -- Amendments: Parameterize the delimeter and included the missing chars in last token - Gemunu Wickremasinghe -- Description: Tabel valued function that Breaks the delimeted string by given delimeter and returns a tabel having split results -- Usage -- select * from [dbo].[TF_SplitString]('token1,token2,,,,,,,,token969',',') -- 969 items should be returned -- select * from [dbo].[TF_SplitString]('4672978261,4672978255',',') -- 2 items should be returned -- ============================================= CREATE FUNCTION dbo.TF_SplitString ( @stringToSplit VARCHAR(MAX) , @delimeter char = ',' ) RETURNS @returnList TABLE ([Name] [nvarchar] (500)) AS BEGIN DECLARE @name NVARCHAR(255) DECLARE @pos INT WHILE LEN(@stringToSplit) > 0 BEGIN SELECT @pos = CHARINDEX(@delimeter, @stringToSplit) if @pos = 0 BEGIN SELECT @pos = LEN(@stringToSplit) SELECT @name = SUBSTRING(@stringToSplit, 1, @pos) END else BEGIN SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1) END INSERT INTO @returnList SELECT @name SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos) END RETURN END
sumber
Ini berdasarkan jawaban Andy Robertson, saya membutuhkan pembatas selain koma.
CREATE FUNCTION dbo.splitstring ( @stringToSplit nvarchar(MAX), @delim nvarchar(max)) RETURNS @returnList TABLE ([value] [nvarchar] (MAX)) AS BEGIN DECLARE @value NVARCHAR(max) DECLARE @pos INT WHILE CHARINDEX(@delim, @stringToSplit) > 0 BEGIN SELECT @pos = CHARINDEX(@delim, @stringToSplit) SELECT @value = SUBSTRING(@stringToSplit, 1, @pos - 1) INSERT INTO @returnList SELECT @value SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos + LEN(@delim), LEN(@stringToSplit) - @pos) END INSERT INTO @returnList SELECT @stringToSplit RETURN END GO
Dan untuk menggunakannya:
SELECT * FROM dbo.splitstring('test1 test2 test3', ' ');
(Diuji di SQL Server 2008 R2)
EDIT: kode tes yang benar
sumber
ALTER FUNCTION [dbo].func_split_string ( @input as varchar(max), @delimiter as varchar(10) = ";" ) RETURNS @result TABLE ( id smallint identity(1,1), csv_value varchar(max) not null ) AS BEGIN DECLARE @pos AS INT; DECLARE @string AS VARCHAR(MAX) = ''; WHILE LEN(@input) > 0 BEGIN SELECT @pos = CHARINDEX(@delimiter,@input); IF(@pos<=0) select @pos = len(@input) IF(@pos <> LEN(@input)) SELECT @string = SUBSTRING(@input, 1, @pos-1); ELSE SELECT @string = SUBSTRING(@input, 1, @pos); INSERT INTO @result SELECT @string SELECT @input = SUBSTRING(@input, @pos+len(@delimiter), LEN(@input)-@pos) END RETURN END
sumber
Anda dapat menggunakan fungsi ini:
CREATE FUNCTION SplitString ( @Input NVARCHAR(MAX), @Character CHAR(1) ) RETURNS @Output TABLE ( Item NVARCHAR(1000) ) AS BEGIN DECLARE @StartIndex INT, @EndIndex INT SET @StartIndex = 1 IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character BEGIN SET @Input = @Input + @Character END WHILE CHARINDEX(@Character, @Input) > 0 BEGIN SET @EndIndex = CHARINDEX(@Character, @Input) INSERT INTO @Output(Item) SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1) SET @Input = SUBSTRING(@Input, @EndIndex + 1, LEN(@Input)) END RETURN END GO
sumber
Berikut adalah contoh yang bisa Anda gunakan sebagai fungsi atau Anda juga bisa meletakkan logika yang sama di dalam prosedur. --PILIH * dari [dbo] .fn_SplitString;
CREATE FUNCTION [dbo].[fn_SplitString] (@CSV VARCHAR(MAX), @Delimeter VARCHAR(100) = ',') RETURNS @retTable TABLE ( [value] VARCHAR(MAX) NULL )AS BEGIN DECLARE @vCSV VARCHAR (MAX) = @CSV, @vDelimeter VARCHAR (100) = @Delimeter; IF @vDelimeter = ';' BEGIN SET @vCSV = REPLACE(@vCSV, ';', '~!~#~'); SET @vDelimeter = REPLACE(@vDelimeter, ';', '~!~#~'); END; SET @vCSV = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@vCSV, '&', '&'), '<', '<'), '>', '>'), '''', '''), '"', '"'); DECLARE @xml XML; SET @xml = '<i>' + REPLACE(@vCSV, @vDelimeter, '</i><i>') + '</i>'; INSERT INTO @retTable SELECT x.i.value('.', 'varchar(max)') AS COLUMNNAME FROM @xml.nodes('//i')AS x(i); RETURN; END;
sumber
@vCSV
mengandung karakter terlarang ... Saya baru saja memposting jawaban untuk mengatasi masalah ini./ *
*/ CREATE FUNCTION dbo.splitstring ( --CREATE OR ALTER @stringToSplit NVARCHAR(MAX) ) RETURNS @returnList TABLE ([Item] NVARCHAR (MAX)) AS BEGIN DECLARE @name NVARCHAR(MAX) DECLARE @pos BIGINT SET @stringToSplit = @stringToSplit + ',' -- this should allow entries that end with a `,` to have a blank value in that "column" WHILE ((LEN(@stringToSplit+'_') > 1)) BEGIN -- `+'_'` gets around LEN trimming terminal spaces. See URL referenced above SET @pos = COALESCE(NULLIF(CHARINDEX(',', @stringToSplit),0),LEN(@stringToSplit+'_')) -- COALESCE grabs first non-null value SET @name = SUBSTRING(@stringToSplit, 1, @pos-1) --MAX size of string of type nvarchar is 4000 SET @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, 4000) -- With SUBSTRING fn (MS web): "If start is greater than the number of characters in the value expression, a zero-length expression is returned." INSERT INTO @returnList SELECT @name --additional debugging parameters below can be added -- + ' pos:' + CAST(@pos as nvarchar) + ' remain:''' + @stringToSplit + '''(' + CAST(LEN(@stringToSplit+'_')-1 as nvarchar) + ')' END RETURN END GO /*
Contoh uji: lihat URL yang direferensikan sebagai "fungsi yang ditingkatkan" di atas
SELECT *,LEN(Item+'_')-1 'L' from splitstring('a,,b')
Item | L --- | --- a | 1 | 0 b | 1
SELECT *,LEN(Item+'_')-1 'L' from splitstring('a,,')
Item | L --- | --- a | 1 | 0 | 0
SELECT *,LEN(Item+'_')-1 'L' from splitstring('a,, ')
Item | L --- | --- a | 1 | 0 | 1
SELECT *,LEN(Item+'_')-1 'L' from splitstring('a,, c ')
Item | L --- | --- a | 1 | 0 c | 3
* /
sumber
Cara termudah:
Ia bekerja bahkan dalam edisi ekspres :).
sumber