ฉันมีคอลัมน์ SQL Server 2008 R2 ที่มีสตริงซึ่งฉันต้องแยกด้วยเครื่องหมายจุลภาค ฉันเห็นคำตอบมากมายเกี่ยวกับ StackOverflow แต่ไม่มีคำตอบใด ๆ ใน R2 ฉันแน่ใจว่าฉันได้เลือกการอนุญาตในตัวอย่างฟังก์ชันแยกใด ๆ ความช่วยเหลือใด ๆ ที่ชื่นชมอย่างมาก
ฉันมีคอลัมน์ SQL Server 2008 R2 ที่มีสตริงซึ่งฉันต้องแยกด้วยเครื่องหมายจุลภาค ฉันเห็นคำตอบมากมายเกี่ยวกับ StackOverflow แต่ไม่มีคำตอบใด ๆ ใน R2 ฉันแน่ใจว่าฉันได้เลือกการอนุญาตในตัวอย่างฟังก์ชันแยกใด ๆ ความช่วยเหลือใด ๆ ที่ชื่นชมอย่างมาก
คำตอบ:
ฉันใช้ SQL นี้มาก่อนซึ่งอาจเหมาะกับคุณ: -
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
และใช้มัน: -
SELECT * FROM dbo.splitstring('91,12,65,78,56,789')
แทนที่จะเป็น CTE แบบเรียกซ้ำและในขณะที่ลูปมีใครคิดว่าเป็นวิธีการตั้งค่าที่มากขึ้น? โปรดทราบว่าฟังก์ชั่นนี้ถูกเขียนขึ้นสำหรับคำถามที่ซึ่งอยู่บนพื้นฐาน SQL Server 2008 และจุลภาคเป็นตัวคั่น ใน SQL Server 2016 และสูงกว่า (และในระดับความเข้ากัน 130 ขึ้นไป) เป็นตัวเลือกที่ดีกว่าSTRING_SPLIT()
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
หากคุณต้องการหลีกเลี่ยงข้อ จำกัด ของความยาวของสตริงที่เป็น <= จำนวนแถวในsys.all_columns
(9,980 ในmodel
ใน SQL Server 2017; สูงกว่ามากในฐานข้อมูลผู้ใช้ของคุณเอง) คุณสามารถใช้วิธีอื่นในการหาตัวเลขเช่น การสร้างของคุณเองตารางของตัวเลข คุณสามารถใช้ CTE แบบเรียกซ้ำในกรณีที่คุณไม่สามารถใช้ตารางระบบหรือสร้างของคุณเอง:
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
แต่คุณจะต้องผนวกOPTION (MAXRECURSION 0)
(หรือMAXRECURSION <longest possible string length if < 32768>
) ลงในคิวรีด้านนอกเพื่อหลีกเลี่ยงข้อผิดพลาดกับการเรียกซ้ำสตริง> 100 ตัวอักษร หากนั่นไม่ใช่ทางเลือกที่ดีก็ให้ดูคำตอบนี้ตามที่ระบุไว้ในความคิดเห็น
(นอกจากนี้ตัวคั่นจะต้องเป็นNCHAR(<=1228)
ยังคงค้นคว้าสาเหตุ)
เพิ่มเติมเกี่ยวกับฟังก์ชั่นแยกทำไม (และพิสูจน์ว่า) ในขณะที่ลูปและ CTE แบบเรียกซ้ำไม่ปรับขนาดและทางเลือกที่ดีกว่าถ้าแยกสตริงที่มาจากชั้นแอปพลิเคชัน:
ในที่สุดการรออยู่ในSQL Server 2016พวกเขาได้แนะนำฟังก์ชั่นสตริงการแยก:STRING_SPLIT
select * From STRING_SPLIT ('a,b', ',') cs
วิธีอื่น ๆ ทั้งหมดในการแยกสตริงเช่น XML, Tally table, ขณะที่ loop ฯลฯ ได้ถูกพัดพาไปโดยSTRING_SPLIT
ฟังก์ชั่นนี้
นี่คือบทความที่ยอดเยี่ยมพร้อมการเปรียบเทียบประสิทธิภาพ: ประสิทธิภาพที่น่าประหลาดใจและข้อสันนิษฐาน: STRING_SPLIT
วิธีที่ง่ายที่สุดคือการใช้XML
รูปแบบ
1. การแปลงสตริงเป็นแถวโดยไม่มีตาราง
QUERY
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)
ผลลัพธ์
x---------x
| Value |
x---------x
| String1 |
| String2 |
| String3 |
x---------x
2. แปลงเป็นแถวจากตารางที่มี ID สำหรับแต่ละแถว CSV
ตารางที่มา
x-----x--------------------------x
| Id | Value |
x-----x--------------------------x
| 1 | String1,String2,String3 |
| 2 | String4,String5,String6 |
x-----x--------------------------x
QUERY
-- 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)
ผลลัพธ์
x-----x----------x
| Id | Value |
x-----x----------x
| 1 | String1 |
| 1 | String2 |
| 1 | String3 |
| 2 | String4 |
| 2 | String5 |
| 2 | String6 |
x-----x----------x
ผมจำเป็นต้องมีวิธีที่รวดเร็วในการกำจัดของ+4
จากรหัสไปรษณีย์
UPDATE #Emails
SET ZIPCode = SUBSTRING(ZIPCode, 1, (CHARINDEX('-', ZIPCODE)-1))
WHERE ZIPCode LIKE '%-%'
ไม่มี proc ... no UDF ... เพียงหนึ่งคำสั่งแบบอินไลน์เล็กน้อยที่ทำสิ่งที่มันต้องทำ ไม่แฟนซีไม่สง่างาม
เปลี่ยนตัวคั่นตามต้องการ ฯลฯ และมันจะทำงานเพื่ออะไร
ถ้าคุณเปลี่ยน
WHILE CHARINDEX(',', @stringToSplit) > 0
กับ
WHILE LEN(@stringToSplit) > 0
คุณสามารถกำจัดเม็ดมีดสุดท้ายหลังจากวนลูป!
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
+1
จะSELECT @pos = LEN(@stringToSplit)
ปรากฏขึ้นเพื่อแก้ไขปัญหาที่ อย่างไรก็ตามค่าSELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos)
จะส่งกลับInvalid length parameter passed to the LEFT or SUBSTRING function
เว้นแต่คุณ+1
จะเพิ่มพารามิเตอร์ที่สามของ SUBSTRING เช่นกัน หรือคุณสามารถแทนที่การมอบหมายนั้นด้วยSET @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, 4000) --MAX len of nvarchar is 4000
ฟังก์ชันทั้งหมดสำหรับการแยกสตริงที่ใช้ Loop-ing (การวนซ้ำ) บางประเภทมีประสิทธิภาพต่ำ ควรแทนที่ด้วยโซลูชันที่ตั้งค่าไว้
รหัสนี้รันดีมาก
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
วิธีการที่ใช้บ่อยกับองค์ประกอบ XML แบ่งในกรณีของตัวอักษรต้องห้าม นี่เป็นวิธีการที่จะใช้วิธีนี้กับตัวละครทุกชนิดแม้จะมีเครื่องหมายอัฒภาคเป็นตัวคั่น
เคล็ดลับคือก่อนอื่นให้ใช้SELECT SomeString AS [*] FOR XML PATH('')
เพื่อให้ได้อักขระที่ต้องห้ามทั้งหมดได้อย่างปลอดภัย นั่นคือเหตุผลที่ทำไมฉันแทนที่ตัวคั่นเป็นค่าวิเศษเพื่อหลีกเลี่ยงปัญหาด้วย;
ตัวคั่น
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)
ผลลัพธ์
ID Part
1 A&B
1 C
1 D
1 E, F
2 "C" & 'D'
2 <C>
2 D
2 E, F
ฉันต้องเขียนอะไรแบบนี้เมื่อไม่นานมานี้ นี่คือทางออกที่ฉันคิดขึ้นมา มันวางอยู่ทั่วไปสำหรับสตริงตัวคั่นและฉันคิดว่ามันจะทำงานได้ดีขึ้นเล็กน้อย:
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
วิธีการแก้ปัญหาโดยใช้ CTE หากใครก็ตามต้องการมัน (นอกเหนือจากฉันที่เห็นได้ชัดว่านั่นคือเหตุผลที่ฉันเขียนมัน)
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
สิ่งนี้ปรับให้แคบลงกว่าเดิม เมื่อฉันทำสิ่งนี้ฉันมักจะมีรายการรหัสที่คั่นด้วยจุลภาค (INT หรือ BIGINT) ซึ่งฉันต้องการใช้เป็นตารางเพื่อใช้เป็นการรวมภายในกับตารางอื่นที่มีคีย์หลักของ INT หรือ BIGINT ฉันต้องการฟังก์ชั่นตารางมูลค่ากลับมาเพื่อให้ฉันได้เข้าร่วมที่มีประสิทธิภาพมากที่สุด
การใช้งานตัวอย่างจะเป็น:
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
ฉันขโมยความคิดจากhttp://sqlrecords.blogspot.com/2012/11/converting-delimited-list-to-table.htmlเปลี่ยนให้เป็นแบบตารางที่มีมูลค่าและใช้ 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
มีรุ่นที่ถูกต้องอยู่ที่นี่ แต่ฉันคิดว่ามันจะดีที่จะเพิ่มความผิดพลาดเล็กน้อยในกรณีที่พวกเขามีเครื่องหมายจุลภาคต่อท้ายรวมทั้งทำให้คุณสามารถใช้มันไม่ได้เป็นฟังก์ชั่น แต่เป็นส่วนหนึ่งของรหัสขนาดใหญ่ . ในกรณีที่คุณใช้เพียงครั้งเดียวและไม่ต้องการฟังก์ชั่น นี่เป็นจำนวนเต็มด้วย (ซึ่งเป็นสิ่งที่ฉันต้องการ) ดังนั้นคุณอาจต้องเปลี่ยนชนิดข้อมูลของคุณ
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
SET @StringToSeperate = @StringToSeperate+','
ก่อนWHILE
ลูปฉันคิดว่าคุณสามารถกำจัดบล็อก "เพิ่มค่าล่าสุด" ดู sol'n ของฉันบน github ด้วย
ฉันแก้ไข + ฟังก์ชั่นของ Andy Robinson นิดหน่อย ตอนนี้คุณสามารถเลือกได้เฉพาะส่วนที่ต้องการจากตารางส่งคืน:
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
หากคุณต้องการวิธีแก้ปัญหาแบบเร่งด่วนสำหรับกรณีทั่วไปที่มีรหัสขั้นต่ำ CTE แบบสองบรรทัดที่เรียกซ้ำนี้จะทำดังนี้
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
ใช้สิ่งนี้เป็นคำสั่งแบบสแตนด์อโลนหรือเพียงแค่เพิ่ม CTE ข้างต้นลงในแบบสอบถามของคุณและคุณจะสามารถเข้าร่วมตารางผลลัพธ์ b
กับผู้อื่นเพื่อใช้ในการแสดงออกเพิ่มเติม
หากคุณเพิ่มตัวนับคุณจะได้รับดัชนีตำแหน่งพร้อมกับรายการ:
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;
ผลลัพธ์:
n s
1 1
2 2333
3 344
4 4
ฉันใช้เส้นทาง xml โดยห่อค่าเป็นองค์ประกอบ (M แต่อะไรก็ได้):
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)
นี่คือรุ่นที่สามารถแยกออกเป็นแพทเทิร์นโดยใช้ patindex ซึ่งเป็นการดัดแปลงโพสต์ด้านบน ฉันมีกรณีที่ฉันต้องการแยกสตริงที่มีตัวคั่นหลายตัว
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','%[/,]%');
ผลลัพธ์จะมีลักษณะเช่นนี้
stringa stringb x y z
ส่วนตัวฉันใช้ฟังก์ชั่นนี้:
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
)
ผมได้พัฒนา Splitter คู่ (ใช้ตัวละครทั้งสองแยก) ตามที่ร้องขอต่อไป อาจเป็นค่าบางอย่างในชุดข้อความนี้ซึ่งจะมีการอ้างอิงมากที่สุดสำหรับแบบสอบถามที่เกี่ยวข้องกับการแยกสตริง
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
การใช้งาน:
--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===','&','=')
การใช้งานที่เป็นไปได้ (รับค่าที่สองของแต่ละการแยก):
SELECT fn.SValue
FROM uft_DoubleSplitter('ValueA=ValueB=ValueC=ValueD==ValueE&ValueA=ValueB=ValueC===ValueE&ValueA=ValueB==ValueD===', '&', '=')AS fn
WHERE fn.mid = 2
นี่คือตัวอย่างที่คุณสามารถใช้เป็นฟังก์ชั่นหรือคุณสามารถใส่ตรรกะเดียวกันในกระบวนการ - เลือก * จาก [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;
โซลูชันที่เรียกใช้ cte แบบเรียกซ้ำ
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;
นี้ขึ้นอยู่กับคำตอบของ Andy Robertson ฉันต้องการตัวคั่นอื่นที่ไม่ใช่จุลภาค
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
และใช้มัน:
SELECT * FROM dbo.splitstring('test1 test2 test3', ' ');
(ทดสอบบน SQL Server 2008 R2)
แก้ไข: รหัสทดสอบที่ถูกต้อง
/ *
คำตอบของT-SQL split string
อ้างอิงจากคำตอบจากAndy RobinsonและAviG
Enhanced function อ้างอิง: ฟังก์ชั่น LEN ไม่รวมช่องว่างต่อท้ายใน SQL Server
'file' นี้ควรจะใช้ได้ทั้งไฟล์ markdown และไฟล์ SQL
*/
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
/*
กรณีทดสอบ: ดู URL ที่อ้างถึงว่าเป็น "ฟังก์ชันการทำงานขั้นสูง" ด้านบน
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
* /
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
คุณสามารถใช้ฟังก์ชั่นนี้:
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
ด้วยความเคารพจาก @AviG ทั้งหมดนี้เป็นรุ่นที่ไม่มีข้อบกพร่องของฟังก์ชั่นที่พัฒนาโดยเขาเพื่อส่งสัญญาณโทเค็นทั้งหมดให้เต็ม
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
วิธีที่ง่ายที่สุด:
มันทำงานได้แม้ใน Express Edition :)