Saya punya meja seperti ini
Value String
-------------------
1 Cleo, Smith
Saya ingin memisahkan string yang dipisahkan koma menjadi dua kolom
Value Name Surname
-------------------
1 Cleo Smith
Saya hanya perlu dua kolom tambahan tetap
sql-server
sql-server-2008
csv
Gurru
sumber
sumber
String_Split
: "Baris keluaran mungkin dalam urutan apa pun. Urutan tidak dijamin sesuai dengan urutan substring dalam string masukan." Itu ditambahkan di SQL Server 2016.Jawaban:
CREATE FUNCTION [dbo].[fn_split_string_to_column] ( @string NVARCHAR(MAX), @delimiter CHAR(1) ) RETURNS @out_put TABLE ( [column_id] INT IDENTITY(1, 1) NOT NULL, [value] NVARCHAR(MAX) ) AS BEGIN DECLARE @value NVARCHAR(MAX), @pos INT = 0, @len INT = 0 SET @string = CASE WHEN RIGHT(@string, 1) != @delimiter THEN @string + @delimiter ELSE @string END WHILE CHARINDEX(@delimiter, @string, @pos + 1) > 0 BEGIN SET @len = CHARINDEX(@delimiter, @string, @pos + 1) - @pos SET @value = SUBSTRING(@string, @pos, @len) INSERT INTO @out_put ([value]) SELECT LTRIM(RTRIM(@value)) AS [column] SET @pos = CHARINDEX(@delimiter, @string, @pos + @len) + 1 END RETURN END
sumber
WHILE
loop (bahkan lebih buruk) bersama-sama akan berkinerja buruk. Selain itu, ini adalah jawaban hanya kode dan bahkan tidak menyelesaikan masalah Ada pendekatan yang jauh lebih baik di sekitar! Untuk pencarian SQL-Server 2016+STRING_SPLIT()
(yang tidak membawa posisi fragmen, gagal besar!) AtauJSON
-hack yang sangat cepat . Untuk versi yang lebih lama, cari XML-hack yang terkenal (detail json dan xml di sini ). Atau cari salah satu dari iTVFs berdasarkan CTE rekursif.SELECT * FROM STRING_SPLIT('John,Jeremy,Jack',',')
Tujuan Anda dapat diselesaikan menggunakan kueri berikut -
Select Value , Substring(FullName, 1,Charindex(',', FullName)-1) as Name, Substring(FullName, Charindex(',', FullName)+1, LEN(FullName)) as Surname from Table1
Tidak ada fungsi Split readymade di sql server, jadi kita perlu membuat fungsi yang ditentukan pengguna.
CREATE FUNCTION Split ( @InputString VARCHAR(8000), @Delimiter VARCHAR(50) ) RETURNS @Items TABLE ( Item VARCHAR(8000) ) AS BEGIN IF @Delimiter = ' ' BEGIN SET @Delimiter = ',' SET @InputString = REPLACE(@InputString, ' ', @Delimiter) END IF (@Delimiter IS NULL OR @Delimiter = '') SET @Delimiter = ',' --INSERT INTO @Items VALUES (@Delimiter) -- Diagnostic --INSERT INTO @Items VALUES (@InputString) -- Diagnostic DECLARE @Item VARCHAR(8000) DECLARE @ItemList VARCHAR(8000) DECLARE @DelimIndex INT SET @ItemList = @InputString SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0) WHILE (@DelimIndex != 0) BEGIN SET @Item = SUBSTRING(@ItemList, 0, @DelimIndex) INSERT INTO @Items VALUES (@Item) -- Set @ItemList = @ItemList minus one less item SET @ItemList = SUBSTRING(@ItemList, @DelimIndex+1, LEN(@ItemList)-@DelimIndex) SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0) END -- End WHILE IF @Item IS NOT NULL -- At least one delimiter was encountered in @InputString BEGIN SET @Item = @ItemList INSERT INTO @Items VALUES (@Item) END -- No delimiters were encountered in @InputString, so just return @InputString ELSE INSERT INTO @Items VALUES (@InputString) RETURN END -- End Function GO ---- Set Permissions --GRANT SELECT ON Split TO UserRole1 --GRANT SELECT ON Split TO UserRole2 --GO
sumber
SELECT * FROM STRING_SPLIT('John,Jeremy,Jack',',')
;WITH Split_Names (Value,Name, xmlname) AS ( SELECT Value, Name, CONVERT(XML,'<Names><name>' + REPLACE(Name,',', '</name><name>') + '</name></Names>') AS xmlname FROM tblnames ) SELECT Value, xmlname.value('/Names[1]/name[1]','varchar(100)') AS Name, xmlname.value('/Names[1]/name[2]','varchar(100)') AS Surname FROM Split_Names
dan juga periksa tautan di bawah untuk referensi
http://jahaines.blogspot.in/2009/06/converting-delimited-string-of-values.html
sumber
CHARINDEX
plusSUBSTRING
, setidaknya untuk saya. :-(CONVERT(XML,'<Names><name><![CDATA[' + REPLACE(Name,',', ']]></name><name><![CDATA[') + ']]></name></name>') AS xmlname
CONVERT(XML,'<Names><name><![CDATA[' + REPLACE(address1,',', ']]></name><name><![CDATA[') + ']]></name></Names>') AS xmlname
(Missing the final s on </Names>)jawaban dasar xml sederhana dan bersih
lihat ini
DECLARE @S varchar(max), @Split char(1), @X xml SELECT @S = 'ab,cd,ef,gh,ij', @Split = ',' SELECT @X = CONVERT(xml,' <root> <myvalue>' + REPLACE(@S,@Split,'</myvalue> <myvalue>') + '</myvalue> </root> ') SELECT T.c.value('.','varchar(20)'), --retrieve ALL values at once T.c.value('(/root/myvalue)[1]','VARCHAR(20)') , --retrieve index 1 only, which is the 'ab' T.c.value('(/root/myvalue)[2]','VARCHAR(20)') FROM @X.nodes('/root/myvalue') T(c)
sumber
Saya pikir ini keren
SELECT value, PARSENAME(REPLACE(String,',','.'),2) 'Name' , PARSENAME(REPLACE(String,',','.'),1) 'Sur Name' FROM table WITH (NOLOCK)
sumber
Dengan CROSS APPLY
select ParsedData.* from MyTable mt cross apply ( select str = mt.String + ',,' ) f1 cross apply ( select p1 = charindex( ',', str ) ) ap1 cross apply ( select p2 = charindex( ',', str, p1 + 1 ) ) ap2 cross apply ( select Nmame = substring( str, 1, p1-1 ) , Surname = substring( str, p1+1, p2-p1-1 ) ) ParsedData
sumber
Ada banyak cara untuk menyelesaikan ini dan banyak cara berbeda telah diusulkan. Paling sederhana adalah dengan menggunakan
LEFT
/SUBSTRING
dan fungsi string lainnya untuk mencapai hasil yang diinginkan.Contoh data
DECLARE @tbl1 TABLE (Value INT,String VARCHAR(MAX)) INSERT INTO @tbl1 VALUES(1,'Cleo, Smith'); INSERT INTO @tbl1 VALUES(2,'John, Mathew');
Menggunakan Fungsi String seperti
LEFT
SELECT Value, LEFT(String,CHARINDEX(',',String)-1) as Fname, LTRIM(RIGHT(String,LEN(String) - CHARINDEX(',',String) )) AS Lname FROM @tbl1
Pendekatan ini gagal jika ada lebih dari 2 item dalam sebuah String. Dalam skenario seperti itu, kita dapat menggunakan pemisah dan kemudian menggunakan
PIVOT
atau mengubah string menjadiXML
dan digunakan.nodes
untuk mendapatkan item string.XML
solusi berbasis telah dirinci oleh aads dan bvr dalam solusi mereka.Jawaban untuk pertanyaan ini yang menggunakan splitter, semua penggunaan
WHILE
yang tidak efisien untuk pemisahan. Periksa perbandingan kinerja ini . Salah satu splitter terbaik adalahDelimitedSplit8K
, dibuat oleh Jeff Moden. Anda dapat membaca lebih lanjut di siniSplitter dengan
PIVOT
DECLARE @tbl1 TABLE (Value INT,String VARCHAR(MAX)) INSERT INTO @tbl1 VALUES(1,'Cleo, Smith'); INSERT INTO @tbl1 VALUES(2,'John, Mathew'); SELECT t3.Value,[1] as Fname,[2] as Lname FROM @tbl1 as t1 CROSS APPLY [dbo].[DelimitedSplit8K](String,',') as t2 PIVOT(MAX(Item) FOR ItemNumber IN ([1],[2])) as t3
Keluaran
DelimitedSplit8K
oleh Jeff ModenCREATE FUNCTION [dbo].[DelimitedSplit8K] /********************************************************************************************************************** Purpose: Split a given string at a given delimiter and return a list of the split elements (items). Notes: 1. Leading a trailing delimiters are treated as if an empty string element were present. 2. Consecutive delimiters are treated as if an empty string element were present between them. 3. Except when spaces are used as a delimiter, all spaces present in each element are preserved. Returns: iTVF containing the following: ItemNumber = Element position of Item as a BIGINT (not converted to INT to eliminate a CAST) Item = Element value as a VARCHAR(8000) Statistics on this function may be found at the following URL: http://www.sqlservercentral.com/Forums/Topic1101315-203-4.aspx CROSS APPLY Usage Examples and Tests: --===================================================================================================================== -- TEST 1: -- This tests for various possible conditions in a string using a comma as the delimiter. The expected results are -- laid out in the comments --===================================================================================================================== --===== Conditionally drop the test tables to make reruns easier for testing. -- (this is NOT a part of the solution) IF OBJECT_ID('tempdb..#JBMTest') IS NOT NULL DROP TABLE #JBMTest ; --===== Create and populate a test table on the fly (this is NOT a part of the solution). -- In the following comments, "b" is a blank and "E" is an element in the left to right order. -- Double Quotes are used to encapsulate the output of "Item" so that you can see that all blanks -- are preserved no matter where they may appear. SELECT * INTO #JBMTest FROM ( --# & type of Return Row(s) SELECT 0, NULL UNION ALL --1 NULL SELECT 1, SPACE(0) UNION ALL --1 b (Empty String) SELECT 2, SPACE(1) UNION ALL --1 b (1 space) SELECT 3, SPACE(5) UNION ALL --1 b (5 spaces) SELECT 4, ',' UNION ALL --2 b b (both are empty strings) SELECT 5, '55555' UNION ALL --1 E SELECT 6, ',55555' UNION ALL --2 b E SELECT 7, ',55555,' UNION ALL --3 b E b SELECT 8, '55555,' UNION ALL --2 b B SELECT 9, '55555,1' UNION ALL --2 E E SELECT 10, '1,55555' UNION ALL --2 E E SELECT 11, '55555,4444,333,22,1' UNION ALL --5 E E E E E SELECT 12, '55555,4444,,333,22,1' UNION ALL --6 E E b E E E SELECT 13, ',55555,4444,,333,22,1,' UNION ALL --8 b E E b E E E b SELECT 14, ',55555,4444,,,333,22,1,' UNION ALL --9 b E E b b E E E b SELECT 15, ' 4444,55555 ' UNION ALL --2 E (w/Leading Space) E (w/Trailing Space) SELECT 16, 'This,is,a,test.' --E E E E ) d (SomeID, SomeValue) ; --===== Split the CSV column for the whole table using CROSS APPLY (this is the solution) SELECT test.SomeID, test.SomeValue, split.ItemNumber, Item = QUOTENAME(split.Item,'"') FROM #JBMTest test CROSS APPLY dbo.DelimitedSplit8K(test.SomeValue,',') split ; --===================================================================================================================== -- TEST 2: -- This tests for various "alpha" splits and COLLATION using all ASCII characters from 0 to 255 as a delimiter against -- a given string. Note that not all of the delimiters will be visible and some will show up as tiny squares because -- they are "control" characters. More specifically, this test will show you what happens to various non-accented -- letters for your given collation depending on the delimiter you chose. --===================================================================================================================== WITH cteBuildAllCharacters (String,Delimiter) AS ( SELECT TOP 256 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', CHAR(ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1) FROM master.sys.all_columns ) SELECT ASCII_Value = ASCII(c.Delimiter), c.Delimiter, split.ItemNumber, Item = QUOTENAME(split.Item,'"') FROM cteBuildAllCharacters c CROSS APPLY dbo.DelimitedSplit8K(c.String,c.Delimiter) split ORDER BY ASCII_Value, split.ItemNumber ; ----------------------------------------------------------------------------------------------------------------------- Other Notes: 1. Optimized for VARCHAR(8000) or less. No testing or error reporting for truncation at 8000 characters is done. 2. Optimized for single character delimiter. Multi-character delimiters should be resolvedexternally from this function. 3. Optimized for use with CROSS APPLY. 4. Does not "trim" elements just in case leading or trailing blanks are intended. 5. If you don't know how a Tally table can be used to replace loops, please see the following... http://www.sqlservercentral.com/articles/T-SQL/62867/ 6. Changing this function to use NVARCHAR(MAX) will cause it to run twice as slow. It's just the nature of VARCHAR(MAX) whether it fits in-row or not. 7. Multi-machine testing for the method of using UNPIVOT instead of 10 SELECT/UNION ALLs shows that the UNPIVOT method is quite machine dependent and can slow things down quite a bit. ----------------------------------------------------------------------------------------------------------------------- Credits: This code is the product of many people's efforts including but not limited to the following: cteTally concept originally by Iztek Ben Gan and "decimalized" by Lynn Pettis (and others) for a bit of extra speed and finally redacted by Jeff Moden for a different slant on readability and compactness. Hat's off to Paul White for his simple explanations of CROSS APPLY and for his detailed testing efforts. Last but not least, thanks to Ron "BitBucket" McCullough and Wayne Sheffield for their extreme performance testing across multiple machines and versions of SQL Server. The latest improvement brought an additional 15-20% improvement over Rev 05. Special thanks to "Nadrek" and "peter-757102" (aka Peter de Heer) for bringing such improvements to light. Nadrek's original improvement brought about a 10% performance gain and Peter followed that up with the content of Rev 07. I also thank whoever wrote the first article I ever saw on "numbers tables" which is located at the following URL and to Adam Machanic for leading me to it many years ago. http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-numbers-table.html ----------------------------------------------------------------------------------------------------------------------- Revision History: Rev 00 - 20 Jan 2010 - Concept for inline cteTally: Lynn Pettis and others. Redaction/Implementation: Jeff Moden - Base 10 redaction and reduction for CTE. (Total rewrite) Rev 01 - 13 Mar 2010 - Jeff Moden - Removed one additional concatenation and one subtraction from the SUBSTRING in the SELECT List for that tiny bit of extra speed. Rev 02 - 14 Apr 2010 - Jeff Moden - No code changes. Added CROSS APPLY usage example to the header, some additional credits, and extra documentation. Rev 03 - 18 Apr 2010 - Jeff Moden - No code changes. Added notes 7, 8, and 9 about certain "optimizations" that don't actually work for this type of function. Rev 04 - 29 Jun 2010 - Jeff Moden - Added WITH SCHEMABINDING thanks to a note by Paul White. This prevents an unnecessary "Table Spool" when the function is used in an UPDATE statement even though the function makes no external references. Rev 05 - 02 Apr 2011 - Jeff Moden - Rewritten for extreme performance improvement especially for larger strings approaching the 8K boundary and for strings that have wider elements. The redaction of this code involved removing ALL concatenation of delimiters, optimization of the maximum "N" value by using TOP instead of including it in the WHERE clause, and the reduction of all previous calculations (thanks to the switch to a "zero based" cteTally) to just one instance of one add and one instance of a subtract. The length calculation for the final element (not followed by a delimiter) in the string to be split has been greatly simplified by using the ISNULL/NULLIF combination to determine when the CHARINDEX returned a 0 which indicates there are no more delimiters to be had or to start with. Depending on the width of the elements, this code is between 4 and 8 times faster on a single CPU box than the original code especially near the 8K boundary. - Modified comments to include more sanity checks on the usage example, etc. - Removed "other" notes 8 and 9 as they were no longer applicable. Rev 06 - 12 Apr 2011 - Jeff Moden - Based on a suggestion by Ron "Bitbucket" McCullough, additional test rows were added to the sample code and the code was changed to encapsulate the output in pipes so that spaces and empty strings could be perceived in the output. The first "Notes" section was added. Finally, an extra test was added to the comments above. Rev 07 - 06 May 2011 - Peter de Heer, a further 15-20% performance enhancement has been discovered and incorporated into this code which also eliminated the need for a "zero" position in the cteTally table. **********************************************************************************************************************/ --===== Define I/O parameters (@pString VARCHAR(8000), @pDelimiter CHAR(1)) RETURNS TABLE WITH SCHEMABINDING AS RETURN --===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000... -- enough to cover NVARCHAR(4000) WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 ), --10E+1 or 10 rows E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front -- for both a performance gain and prevention of accidental "overruns" SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4 ), cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter) SELECT 1 UNION ALL SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter ), cteLen(N1,L1) AS(--==== Return start and length (for use in substring) SELECT s.N1, ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000) FROM cteStart s ) --===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found. SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1), Item = SUBSTRING(@pString, l.N1, l.L1) FROM cteLen l ; GO
sumber
Dengan SQL Server 2016 kita dapat menggunakan string_split untuk mencapai ini:
create table commasep ( id int identity(1,1) ,string nvarchar(100) ) insert into commasep (string) values ('John, Adam'), ('test1,test2,test3') select id, [value] as String from commasep cross apply string_split(string,',')
sumber
Invalid object name 'string_split'
DECLARE @cl TINYINT; SELECT @cl = compatibility_level FROM [sys].[databases] WHERE name = 'mydb'; IF @cl < 130 BEGIN ALTER DATABASE myDb SET COMPATIBILITY_LEVEL = 130 END;
Coba ini (ubah contoh '' menjadi ',' atau pembatas apa pun yang ingin Anda gunakan)
CREATE FUNCTION dbo.Wordparser ( @multiwordstring VARCHAR(255), @wordnumber NUMERIC ) returns VARCHAR(255) AS BEGIN DECLARE @remainingstring VARCHAR(255) SET @remainingstring=@multiwordstring DECLARE @numberofwords NUMERIC SET @numberofwords=(LEN(@remainingstring) - LEN(REPLACE(@remainingstring, ' ', '')) + 1) DECLARE @word VARCHAR(50) DECLARE @parsedwords TABLE ( line NUMERIC IDENTITY(1, 1), word VARCHAR(255) ) WHILE @numberofwords > 1 BEGIN SET @word=LEFT(@remainingstring, CHARINDEX(' ', @remainingstring) - 1) INSERT INTO @parsedwords(word) SELECT @word SET @remainingstring= REPLACE(@remainingstring, Concat(@word, ' '), '') SET @numberofwords=(LEN(@remainingstring) - LEN(REPLACE(@remainingstring, ' ', '')) + 1) IF @numberofwords = 1 BREAK ELSE CONTINUE END IF @numberofwords = 1 SELECT @word = @remainingstring INSERT INTO @parsedwords(word) SELECT @word RETURN (SELECT word FROM @parsedwords WHERE line = @wordnumber) END
Contoh penggunaan:
SELECT dbo.Wordparser(COLUMN, 1), dbo.Wordparser(COLUMN, 2), dbo.Wordparser(COLUMN, 3) FROM TABLE
sumber
Saya pikir PARSENAME adalah fungsi rapi untuk digunakan untuk contoh ini, seperti yang dijelaskan dalam artikel ini: http://www.sqlshack.com/parsing-and-rotating-delimited-data-in-sql-server-2012/
Fungsi PARSENAME dirancang secara logis untuk mengurai nama objek empat bagian. Hal yang menyenangkan tentang PARSENAME adalah bahwa ini tidak terbatas pada parsing hanya nama objek empat bagian SQL Server - ini akan mengurai semua fungsi atau data string yang dibatasi oleh titik.
Parameter pertama adalah objek yang akan diurai, dan yang kedua adalah nilai integer dari potongan objek yang akan dikembalikan. Artikel ini membahas penguraian dan perputaran data yang dibatasi - nomor telepon perusahaan, tetapi juga dapat digunakan untuk mengurai data nama / nama keluarga.
Contoh:
USE COMPANY; SELECT PARSENAME('Whatever.you.want.parsed',3) AS 'ReturnValue';
Artikel ini juga menjelaskan penggunaan Common Table Expression (CTE) yang disebut 'replaceChars', untuk menjalankan PARSENAME terhadap nilai yang diganti pembatas. CTE berguna untuk mengembalikan tampilan sementara atau kumpulan hasil.
Setelah itu, fungsi UNPIVOT telah digunakan untuk mengubah beberapa kolom menjadi baris; Fungsi SUBSTRING dan CHARINDEX telah digunakan untuk membersihkan ketidakkonsistenan dalam data, dan fungsi LAG (baru untuk SQL Server 2012) telah digunakan pada akhirnya, karena memungkinkan referensi catatan sebelumnya.
sumber
SELECT id, Substring(NAME, 0, Charindex(',', NAME)) AS firstname, Substring(NAME, Charindex(',', NAME), Len(NAME) + 1) AS lastname FROM spilt
sumber
Kita bisa membuat fungsi seperti ini
CREATE Function [dbo].[fn_CSVToTable] ( @CSVList Varchar(max) ) RETURNS @Table TABLE (ColumnData VARCHAR(100)) AS BEGIN IF RIGHT(@CSVList, 1) <> ',' SELECT @CSVList = @CSVList + ',' DECLARE @Pos BIGINT, @OldPos BIGINT SELECT @Pos = 1, @OldPos = 1 WHILE @Pos < LEN(@CSVList) BEGIN SELECT @Pos = CHARINDEX(',', @CSVList, @OldPos) INSERT INTO @Table SELECT LTRIM(RTRIM(SUBSTRING(@CSVList, @OldPos, @Pos - @OldPos))) Col001 SELECT @OldPos = @Pos + 1 END RETURN END
Kami kemudian dapat memisahkan nilai CSV ke dalam kolom masing-masing menggunakan pernyataan SELECT
sumber
Saya pikir fungsi berikut akan bekerja untuk Anda:
Anda harus membuat fungsi di SQL terlebih dahulu. Seperti ini
CREATE FUNCTION [dbo].[fn_split]( @str VARCHAR(MAX), @delimiter CHAR(1) ) RETURNS @returnTable TABLE (idx INT PRIMARY KEY IDENTITY, item VARCHAR(8000)) AS BEGIN DECLARE @pos INT SELECT @str = @str + @delimiter WHILE LEN(@str) > 0 BEGIN SELECT @pos = CHARINDEX(@delimiter,@str) IF @pos = 1 INSERT @returnTable (item) VALUES (NULL) ELSE INSERT @returnTable (item) VALUES (SUBSTRING(@str, 1, @pos-1)) SELECT @str = SUBSTRING(@str, @pos+1, LEN(@str)-@pos) END RETURN END
Anda dapat memanggil fungsi ini, seperti ini:
select * from fn_split('1,24,5',',')
Penerapan:
Declare @test TABLE ( ID VARCHAR(200), Data VARCHAR(200) ) insert into @test (ID, Data) Values ('1','Cleo,Smith') insert into @test (ID, Data) Values ('2','Paul,Grim') select ID, (select item from fn_split(Data,',') where idx in (1)) as Name , (select item from fn_split(Data,',') where idx in (2)) as Surname from @test
Hasilnya akan seperti ini:
sumber
Gunakan fungsi Parsename ()
with cte as( select 'Aria,Karimi' as FullName Union select 'Joe,Karimi' as FullName Union select 'Bab,Karimi' as FullName ) SELECT PARSENAME(REPLACE(FullName,',','.'),2) as Name, PARSENAME(REPLACE(FullName,',','.'),1) as Family FROM cte
Hasil
Name Family ----- ------ Aria Karimi Bab Karimi Joe Karimi
sumber
Anda dapat menggunakan file fungsi nilai tabel
STRING_SPLIT
, yang hanya tersedia di bawah tingkat kompatibilitas 130. Jika tingkat kompatibilitas database Anda lebih rendah dari 130, SQL Server tidak akan dapat menemukan dan menjalankanSTRING_SPLIT
fungsi tersebut. Anda dapat mengubah tingkat kompatibilitas database menggunakan perintah berikut:ALTER DATABASE DatabaseName SET COMPATIBILITY_LEVEL = 130
Sintaksis
SELECT * FROM STRING_SPLIT ( string, separator )
lihat dokumentasi di sini
sumber
Coba ini:
declare @csv varchar(100) ='aaa,bb,csda,daass'; set @csv = @csv+','; with cte as ( select SUBSTRING(@csv,1,charindex(',',@csv,1)-1) as val, SUBSTRING(@csv,charindex(',',@csv,1)+1,len(@csv)) as rem UNION ALL select SUBSTRING(a.rem,1,charindex(',',a.rem,1)-1)as val, SUBSTRING(a.rem,charindex(',',a.rem,1)+1,len(A.rem)) from cte a where LEN(a.rem)>=1 ) select val from cte
sumber
Fungsi ini paling cepat:
CREATE FUNCTION dbo.F_ExtractSubString ( @String VARCHAR(MAX), @NroSubString INT, @Separator VARCHAR(5) ) RETURNS VARCHAR(MAX) AS BEGIN DECLARE @St INT = 0, @End INT = 0, @Ret VARCHAR(MAX) SET @String = @String + @Separator WHILE CHARINDEX(@Separator, @String, @End + 1) > 0 AND @NroSubString > 0 BEGIN SET @St = @End + 1 SET @End = CHARINDEX(@Separator, @String, @End + 1) SET @NroSubString = @NroSubString - 1 END IF @NroSubString > 0 SET @Ret = '' ELSE SET @Ret = SUBSTRING(@String, @St, @End - @St) RETURN @Ret END GO
Contoh penggunaan:
SELECT dbo.F_ExtractSubString(COLUMN, 1, ', '), dbo.F_ExtractSubString(COLUMN, 2, ', '), dbo.F_ExtractSubString(COLUMN, 3, ', ') FROM TABLE
sumber
Saya mengalami masalah yang serupa tetapi yang rumit dan karena ini adalah utas pertama yang saya temukan mengenai masalah itu, saya memutuskan untuk memposting temuan saya. Saya tahu ini adalah solusi kompleks untuk masalah sederhana tetapi saya berharap dapat membantu orang lain yang membuka utas ini untuk mencari solusi yang lebih kompleks. saya harus membagi string yang berisi 5 nomor (nama kolom: levelFeed) dan untuk menampilkan setiap nomor di kolom terpisah. Misalnya: 8,1,2,2,2 harus ditampilkan sebagai:
1 2 3 4 5 ------------- 8 1 2 2 2
Solusi 1: Menggunakan Fungsi XML: Solusi ini untuk solusi paling lambat sejauh ini
SELECT Distinct FeedbackID, , S.a.value('(/H/r)[1]', 'INT') AS level1 , S.a.value('(/H/r)[2]', 'INT') AS level2 , S.a.value('(/H/r)[3]', 'INT') AS level3 , S.a.value('(/H/r)[4]', 'INT') AS level4 , S.a.value('(/H/r)[5]', 'INT') AS level5 FROM ( SELECT *,CAST (N'<H><r>' + REPLACE(levelsFeed, ',', '</r><r>') + '</r> </H>' AS XML) AS [vals] FROM Feedbacks ) as d CROSS APPLY d.[vals].nodes('/H/r') S(a)
Solusi 2: Menggunakan fungsi Split dan poros. (fungsi pemisahan memisahkan string menjadi baris dengan data nama kolom)
SELECT FeedbackID, [1],[2],[3],[4],[5] FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY feedbackID ORDER BY (SELECT null)) as rn FROM ( SELECT FeedbackID, levelsFeed FROM Feedbacks ) as a CROSS APPLY dbo.Split(levelsFeed, ',') ) as SourceTable PIVOT ( MAX(data) FOR rn IN ([1],[2],[3],[4],[5]) )as pivotTable
Solusi 3: Menggunakan fungsi manipulasi string - tercepat dengan margin kecil di atas solusi 2
SELECT FeedbackID, SUBSTRING(levelsFeed,0,CHARINDEX(',',levelsFeed)) AS level1, PARSENAME(REPLACE(SUBSTRING(levelsFeed,CHARINDEX(',',levelsFeed)+1,LEN(levelsFeed)),',','.'),4) AS level2, PARSENAME(REPLACE(SUBSTRING(levelsFeed,CHARINDEX(',',levelsFeed)+1,LEN(levelsFeed)),',','.'),3) AS level3, PARSENAME(REPLACE(SUBSTRING(levelsFeed,CHARINDEX(',',levelsFeed)+1,LEN(levelsFeed)),',','.'),2) AS level4, PARSENAME(REPLACE(SUBSTRING(levelsFeed,CHARINDEX(',',levelsFeed)+1,LEN(levelsFeed)),',','.'),1) AS level5 FROM Feedbacks
karena levelFeed berisi 5 nilai string yang saya butuhkan untuk menggunakan fungsi substring untuk string pertama.
Saya berharap solusi saya akan membantu orang lain yang sampai ke utas ini mencari metode perpecahan ke kolom yang lebih kompleks
sumber
Menggunakan fungsi instring :)
select Value, substring(String,1,instr(String," ") -1) Fname, substring(String,instr(String,",") +1) Sname from tablename;
Menggunakan dua fungsi,
1.
substring(string, position, length)
==> mengembalikan string dari posisi ke panjang2.
instr(string,pattern)
==> mengembalikan posisi pola.Jika kami tidak memberikan argumen panjang dalam substring, ia mengembalikan hingga akhir string
sumber
substring(@str, 1, charindex(@sep, @str) - 1)
diikuti olehsubstring(@str, charindex(@sep, @str) + 1, len(@str))
.Ini berhasil untuk saya
CREATE FUNCTION [dbo].[SplitString]( @delimited NVARCHAR(MAX), @delimiter NVARCHAR(100) ) RETURNS @t TABLE ( 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
sumber
mytable:
Value ColOne -------------------- 1 Cleo, Smith
Berikut ini akan bekerja jika tidak terlalu banyak kolom
ALTER TABLE mytable ADD ColTwo nvarchar(256); UPDATE mytable SET ColTwo = LEFT(ColOne, Charindex(',', ColOne) - 1); --'Cleo' = LEFT('Cleo, Smith', Charindex(',', 'Cleo, Smith') - 1) UPDATE mytable SET ColTwo = REPLACE(ColOne, ColTwo + ',', ''); --' Smith' = REPLACE('Cleo, Smith', 'Cleo' + ',') UPDATE mytable SET ColOne = REPLACE(ColOne, ',' + ColTwo, ''), ColTwo = LTRIM(ColTwo); --'Cleo' = REPLACE('Cleo, Smith', ',' + ' Smith', '')
Hasil:
Value ColOne ColTwo -------------------- 1 Cleo Smith
sumber
sangat mudah, Anda bisa menerimanya dengan query di bawah ini:
DECLARE @str NVARCHAR(MAX)='ControlID_05436b78-04ba-9667-fa01-9ff8c1b7c235,3' SELECT LEFT(@str, CHARINDEX(',',@str)-1),RIGHT(@str,LEN(@str)-(CHARINDEX(',',@str)))
sumber
DECLARE @INPUT VARCHAR (MAX)='N,A,R,E,N,D,R,A' DECLARE @ELIMINATE_CHAR CHAR (1)=',' DECLARE @L_START INT=1 DECLARE @L_END INT=(SELECT LEN (@INPUT)) DECLARE @OUTPUT CHAR (1) WHILE @L_START <=@L_END BEGIN SET @OUTPUT=(SUBSTRING (@INPUT,@L_START,1)) IF @OUTPUT!=@ELIMINATE_CHAR BEGIN PRINT @OUTPUT END SET @L_START=@L_START+1 END
sumber
Anda mungkin menemukan solusi dalam SQL User Defined Function untuk Parse Delimited String bermanfaat (dari The Code Project ).
Ini adalah bagian kode dari halaman ini:
CREATE FUNCTION [fn_ParseText2Table] (@p_SourceText VARCHAR(MAX) ,@p_Delimeter VARCHAR(100)=',' --default to comma delimited. ) RETURNS @retTable TABLE([Position] INT IDENTITY(1,1) ,[Int_Value] INT ,[Num_Value] NUMERIC(18,3) ,[Txt_Value] VARCHAR(MAX) ,[Date_value] DATETIME ) AS /* ******************************************************************************** Purpose: Parse values from a delimited string & return the result as an indexed table Copyright 1996, 1997, 2000, 2003 Clayton Groom (<A href="mailto:[email protected]">[email protected]</A>) Posted to the public domain Aug, 2004 2003-06-17 Rewritten as SQL 2000 function. Reworked to allow for delimiters > 1 character in length and to convert Text values to numbers 2016-04-05 Added logic for date values based on "new" ISDATE() function, Updated to use XML approach, which is more efficient. ******************************************************************************** */ BEGIN DECLARE @w_xml xml; SET @w_xml = N'<root><i>' + replace(@p_SourceText, @p_Delimeter,'</i><i>') + '</i></root>'; INSERT INTO @retTable ([Int_Value] , [Num_Value] , [Txt_Value] , [Date_value] ) SELECT CASE WHEN ISNUMERIC([i].value('.', 'VARCHAR(MAX)')) = 1 THEN CAST(CAST([i].value('.', 'VARCHAR(MAX)') AS NUMERIC) AS INT) END AS [Int_Value] , CASE WHEN ISNUMERIC([i].value('.', 'VARCHAR(MAX)')) = 1 THEN CAST([i].value('.', 'VARCHAR(MAX)') AS NUMERIC(18, 3)) END AS [Num_Value] , [i].value('.', 'VARCHAR(MAX)') AS [txt_Value] , CASE WHEN ISDATE([i].value('.', 'VARCHAR(MAX)')) = 1 THEN CAST([i].value('.', 'VARCHAR(MAX)') AS DATETIME) END AS [Num_Value] FROM @w_xml.nodes('//root/i') AS [Items]([i]); RETURN; END; GO
sumber
ALTER function get_occurance_index(@delimiter varchar(1),@occurence int,@String varchar(100)) returns int AS Begin --Declare @delimiter varchar(1)=',',@occurence int=2,@String varchar(100)='a,b,c' Declare @result int ;with T as ( select 1 Rno,0 as row, charindex(@delimiter, @String) pos,@String st union all select Rno+1,pos + 1, charindex(@delimiter, @String, pos + 1), @String from T where pos > 0 ) select @result=pos from T where pos > 0 and rno = @occurence return isnull(@result,0) ENd declare @data as table (data varchar(100)) insert into @data values('1,2,3') insert into @data values('aaa,bbbbb,cccc') select top 3 Substring (data,0,dbo.get_occurance_index( ',',1,data)) ,--First Record always starts with 0 Substring (data,dbo.get_occurance_index( ',',1,data)+1,dbo.get_occurance_index( ',',2,data)-dbo.get_occurance_index( ',',1,data)-1) , Substring (data,dbo.get_occurance_index( ',',2,data)+1,len(data)) , -- Last record cant be more than len of actual data data From @data
sumber
Saya menemukan bahwa menggunakan PARSENAME seperti di atas menyebabkan nama apapun dengan titik menjadi nulled.
Jadi jika ada inisial atau judul di nama diikuti dengan titik mereka mengembalikan NULL.
Saya menemukan ini berhasil untuk saya:
SELECT REPLACE(SUBSTRING(FullName, 1,CHARINDEX(',', FullName)), ',','') as Name, REPLACE(SUBSTRING(FullName, CHARINDEX(',', FullName), LEN(FullName)), ',', '') as Surname FROM Table1
sumber
select distinct modelFileId,F4.* from contract cross apply (select XmlList=convert(xml, '<x>'+replace(modelFileId,';','</x><x>')+'</x>').query('.')) F2 cross apply (select mfid1=XmlNode.value('/x[1]','varchar(512)') ,mfid2=XmlNode.value('/x[2]','varchar(512)') ,mfid3=XmlNode.value('/x[3]','varchar(512)') ,mfid4=XmlNode.value('/x[4]','varchar(512)') from XmlList.nodes('x') F3(XmlNode)) F4 where modelFileId like '%;%' order by modelFileId
sumber
Select distinct PROJ_UID,PROJ_NAME,RES_UID from E2E_ProjectWiseTimesheetActuals where CHARINDEX(','+cast(PROJ_UID as varchar(8000))+',', @params) > 0 and CHARINDEX(','+cast(RES_UID as varchar(8000))+',', @res) > 0
sumber
Saya menulis ulang jawaban di atas dan membuatnya lebih baik:
CREATE FUNCTION [dbo].[CSVParser] ( @s VARCHAR(255), @idx NUMERIC ) RETURNS VARCHAR(12) BEGIN DECLARE @comma int SET @comma = CHARINDEX(',', @s) WHILE 1=1 BEGIN IF @comma=0 IF @idx=1 RETURN @s ELSE RETURN '' IF @idx=1 BEGIN DECLARE @word VARCHAR(12) SET @word=LEFT(@s, @comma - 1) RETURN @word END SET @s = RIGHT(@s,LEN(@s)-@comma) SET @comma = CHARINDEX(',', @s) SET @idx = @idx - 1 END RETURN 'not used' END
Contoh penggunaan:
SELECT dbo.CSVParser(COLUMN, 1), dbo.CSVParser(COLUMN, 2), dbo.CSVParser(COLUMN, 3) FROM TABLE
sumber
pertanyaannya sederhana, tapi masalahnya panas :)
Jadi saya membuat beberapa pembungkus untuk string_split () yang menghasilkan pivot dengan cara yang lebih umum. Ini fungsi tabel yang mengembalikan nilai (nn, nilai1, nilai2, ..., nilai50) - cukup untuk sebagian besar baris CSV. Jika ada lebih banyak nilai, nilai tersebut akan digabungkan ke baris berikutnya - nn menunjukkan nomor baris. Setel parameter ketiga @columnCnt = [yourNumber] untuk dibungkus pada posisi tertentu:
alter FUNCTION fn_Split50 ( @str varchar(max), @delim char(1), @columnCnt int = 50 ) RETURNS TABLE AS RETURN ( SELECT * FROM (SELECT nn = (nn - 1) / @columnCnt + 1, nnn = 'value' + cast(((nn - 1) % @columnCnt) + 1 as varchar(10)), value FROM (SELECT nn = ROW_NUMBER() over (order by (select null)), value FROM string_split(@str, @delim) aa ) aa where nn > 0 ) bb PIVOT ( max(value) FOR nnn IN ( value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11, value12, value13, value14, value15, value16, value17, value18, value19, value20, value21, value22, value23, value24, value25, value26, value27, value28, value29, value30, value31, value32, value33, value34, value35, value36, value37, value38, value39, value40, value41, value42, value43, value44, value45, value46, value47, value48, value49, value50 ) ) AS PivotTable )
Contoh penggunaan:
select * from dbo.fn_split50('zz1,aa2,ss3,dd4,ff5', ',', DEFAULT)
select * from dbo.fn_split50('zz1,aa2,ss3,dd4,ff5,gg6,hh7,jj8,ww9,qq10', ',', 3)
select * from dbo.fn_split50('zz1,11,aa2,22,ss3,33,dd4,44,ff5,55,gg6,66,hh7,77,jj8,88,ww9,99,qq10,1010', ',',2)
Semoga bisa membantu :)
sumber