การเปลี่ยนสตริงที่คั่นด้วยเครื่องหมายจุลภาคเป็นแต่ละแถว


234

ฉันมีตาราง SQL ดังนี้:

| SomeID         | OtherID     | Data
+----------------+-------------+-------------------
| abcdef-.....   | cdef123-... | 18,20,22
| abcdef-.....   | 4554a24-... | 17,19
| 987654-.....   | 12324a2-... | 13,19,20

มีแบบสอบถามที่ฉันสามารถดำเนินการแบบสอบถามเช่นSELECT OtherID, SplitData WHERE SomeID = 'abcdef-.......'ที่ส่งกลับแต่ละแถวเช่นนี้

| OtherID     | SplitData
+-------------+-------------------
| cdef123-... | 18
| cdef123-... | 20
| cdef123-... | 22
| 4554a24-... | 17
| 4554a24-... | 19

โดยทั่วไปแบ่งข้อมูลของฉันที่เครื่องหมายจุลภาคเป็นแต่ละแถวหรือไม่

ฉันทราบว่าการจัดเก็บcomma-separatedสตริงลงในฐานข้อมูลเชิงสัมพันธ์นั้นเป็นใบ้ แต่กรณีการใช้งานปกติในแอพพลิเคชั่นสำหรับผู้ใช้ทั่วไปนั้นมีประโยชน์จริง ๆ

ฉันไม่ต้องการแยกในแอปพลิเคชันตามที่ฉันต้องการเพจดังนั้นฉันต้องการสำรวจตัวเลือกก่อนทำการ refactoring แอพทั้งหมด

มันคือSQL Server 2008(ไม่ใช่ -R2)


ดูเพิ่มเติมที่: periscopedata.com/blog/…
Rick James

คำตอบ:


265

คุณสามารถใช้ฟังก์ชั่นการเรียกซ้ำที่ยอดเยี่ยมจาก SQL Server:


ตารางตัวอย่าง:

CREATE TABLE Testdata
(
    SomeID INT,
    OtherID INT,
    String VARCHAR(MAX)
)

INSERT Testdata SELECT 1,  9, '18,20,22'
INSERT Testdata SELECT 2,  8, '17,19'
INSERT Testdata SELECT 3,  7, '13,19,20'
INSERT Testdata SELECT 4,  6, ''
INSERT Testdata SELECT 9, 11, '1,2,3,4'

แบบสอบถาม

;WITH tmp(SomeID, OtherID, DataItem, String) AS
(
    SELECT
        SomeID,
        OtherID,
        LEFT(String, CHARINDEX(',', String + ',') - 1),
        STUFF(String, 1, CHARINDEX(',', String + ','), '')
    FROM Testdata
    UNION all

    SELECT
        SomeID,
        OtherID,
        LEFT(String, CHARINDEX(',', String + ',') - 1),
        STUFF(String, 1, CHARINDEX(',', String + ','), '')
    FROM tmp
    WHERE
        String > ''
)

SELECT
    SomeID,
    OtherID,
    DataItem
FROM tmp
ORDER BY SomeID
-- OPTION (maxrecursion 0)
-- normally recursion is limited to 100. If you know you have very long
-- strings, uncomment the option

เอาท์พุต

 SomeID | OtherID | DataItem 
--------+---------+----------
 1      | 9       | 18       
 1      | 9       | 20       
 1      | 9       | 22       
 2      | 8       | 17       
 2      | 8       | 19       
 3      | 7       | 13       
 3      | 7       | 19       
 3      | 7       | 20       
 4      | 6       |          
 9      | 11      | 1        
 9      | 11      | 2        
 9      | 11      | 3        
 9      | 11      | 4        

1
รหัสไม่ทำงานหากเปลี่ยนประเภทข้อมูลของคอลัมน์Dataจากvarchar(max)เป็นvarchar(4000)เช่นcreate table Testdata(SomeID int, OtherID int, Data varchar(4000))?
ca9163d9

4
@NickW อาจเป็นเพราะส่วนก่อนและหลังยูเนี่ยนทั้งหมดกลับมาประเภทที่แตกต่างจากฟังก์ชั่นซ้าย โดยส่วนตัวฉันไม่เห็นสาเหตุที่คุณจะไม่ข้ามไปที่ MAX เมื่อคุณไปถึง 4000 ...
RichardTheKiwi

สำหรับชุดค่าขนาดใหญ่สิ่งนี้สามารถใช้เกินขีด จำกัด การเรียกซ้ำสำหรับ CTE
dsz

3
@dsz นั่นคือเมื่อคุณใช้OPTION (maxrecursion 0)
RichardTheKiwi

14
ฟังก์ชัน LEFT อาจต้องใช้ CAST เพื่อทำงาน .... เช่น LEFT (CAST (Data AS VARCHAR (MAX)) ....
smoore4

141

ในที่สุดการรอคอยเป็นไปด้วยSQL Server 2016 พวกเขาได้แนะนำฟังก์ชั่นสตริงการแยกSTRING_SPLIT:

select OtherID, cs.Value --SplitData
from yourtable
cross apply STRING_SPLIT (Data, ',') cs

วิธีอื่น ๆ ทั้งหมดในการแยกสตริงเช่น XML, Tally table, ในขณะที่ loop ฯลฯ ได้ถูกพัดพาไปโดยSTRING_SPLITฟังก์ชั่นนี้

นี่เป็นบทความที่ดีกับการเปรียบเทียบประสิทธิภาพ: ประหลาดใจประสิทธิภาพและสมมติฐาน: STRING_SPLIT

สำหรับรุ่นที่เก่ากว่าการใช้ตารางนับที่นี่คือฟังก์ชั่นสตริงแยกหนึ่งอัน (แนวทางที่ดีที่สุด)

CREATE FUNCTION [dbo].[DelimitedSplit8K]
        (@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
;

อ้างอิงจากTally OH! ฟังก์ชั่น“ CSV Splitter” SQL 8K ที่ได้รับการปรับปรุง


9
คำตอบที่สำคัญมาก
Syed Md Kamruzzaman

ฉันจะใช้ STRING_SPLIT หากเซิร์ฟเวอร์อยู่บน SQL Server 2016 เท่านั้น! BTW ตามหน้าเว็บที่คุณได้เชื่อมโยงกับชื่อสนามมันจะออกผลลัพธ์คือไม่value SplitData
สจ๊วต

89

ตรวจสอบสิ่งนี้

 SELECT A.OtherID,  
     Split.a.value('.', 'VARCHAR(100)') AS Data  
 FROM  
 (
     SELECT OtherID,  
         CAST ('<M>' + REPLACE(Data, ',', '</M><M>') + '</M>' AS XML) AS Data  
     FROM  Table1
 ) AS A CROSS APPLY Data.nodes ('/M') AS Split(a); 

8
เมื่อใช้วิธีนี้คุณต้องตรวจสอบให้แน่ใจว่าไม่มีค่าใดที่มีสิ่งที่จะเป็น XML ที่ผิดกฎหมาย
user1151923

มันเยี่ยมมาก ฉันจะถามคุณได้อย่างไรฉันจะเขียนใหม่ได้อย่างไรถ้าฉันต้องการให้คอลัมน์ใหม่แสดงเฉพาะอักขระแรกจากสตริงแยกของฉัน
ควบคุม

มันทำงานได้อย่างสมบูรณ์ขอบคุณ! ฉันต้องอัปเดตขีด จำกัด VARCHAR แต่ใช้งานได้ดีหลังจากนั้น
chazbot7

ฉันต้องบอกคุณว่าวิธีการคือ "lovingl" (รู้สึกถึงความรัก?) ที่เรียกว่า "XML Splitter Method" และเกือบจะช้าเท่ากับในขณะ Loop หรือ Recursive CTE ฉันขอแนะนำให้คุณหลีกเลี่ยงมันตลอดเวลา ใช้ DelimitedSplit8K แทน มันพัดประตูออกจากทุกอย่างยกเว้นฟังก์ชั่น Split_String () ในปี 2559 หรือ CLR ที่เขียนขึ้นอย่างดี
Jeff Moden

20
select t.OtherID,x.Kod
    from testData t
    cross apply (select Code from dbo.Split(t.Data,',') ) x

3
ทำสิ่งที่ฉันเป็นมาอย่างแน่นอนและอ่านได้ง่ายกว่าตัวอย่างอื่น ๆ อีกมากมาย (หากมีฟังก์ชั่นใน DB สำหรับการแยกสตริงที่มีตัวคั่นอยู่แล้ว) ในฐานะที่เป็นคนที่ไม่คุ้นเคยมาก่อนหน้านี้มีCROSS APPLYประโยชน์มาก
tobriand

ฉันไม่เข้าใจส่วนนี้ (เลือกรหัสจาก dbo.Split (t.Data, ','))? dbo.Splitเป็นตารางที่มีอยู่และรหัสคือคอลัมน์ในตาราง Split? ฉันไม่พบรายการของตารางหรือค่าเหล่านั้นในที่ใดก็ได้ในหน้านี้?
Jayendran

1
รหัสการทำงานของฉันคือ:select t.OtherID, x.* from testData t cross apply (select item as Data from dbo.Split(t.Data,',') ) x
Akbar Kautsar

12

ข้อมูล ณ เดือนกุมภาพันธ์ 2559 - ดูตัวอย่างตาราง TALLY - มีแนวโน้มสูงกว่า TVF ของฉันด้านล่างตั้งแต่เดือนกุมภาพันธ์ 2014 การโพสต์ต้นฉบับด้านล่างเพื่อลูกหลาน:


รหัสซ้ำมากเกินไปสำหรับความชอบของฉันในตัวอย่างข้างต้น และฉันไม่ชอบประสิทธิภาพของ CTEs และ XML นอกจากนี้ยังมีความชัดเจนIdเพื่อให้ผู้บริโภคที่มีคำสั่งซื้อที่เฉพาะเจาะจงสามารถระบุORDER BYข้อ

CREATE FUNCTION dbo.Split
(
    @Line nvarchar(MAX),
    @SplitOn nvarchar(5) = ','
)
RETURNS @RtnValue table
(
    Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    Data nvarchar(100) NOT NULL
)
AS
BEGIN
    IF @Line IS NULL RETURN

    DECLARE @split_on_len INT = LEN(@SplitOn)
    DECLARE @start_at INT = 1
    DECLARE @end_at INT
    DECLARE @data_len INT

    WHILE 1=1
    BEGIN
        SET @end_at = CHARINDEX(@SplitOn,@Line,@start_at)
        SET @data_len = CASE @end_at WHEN 0 THEN LEN(@Line) ELSE @end_at-@start_at END
        INSERT INTO @RtnValue (data) VALUES( SUBSTRING(@Line,@start_at,@data_len) );
        IF @end_at = 0 BREAK;
        SET @start_at = @end_at + @split_on_len
    END

    RETURN
END

6

ยินดีที่ได้เห็นว่ามันได้รับการแก้ไขในเวอร์ชั่น 2016 แต่สำหรับทั้งหมดที่ไม่ได้อยู่ในนั้นนี่เป็นวิธีการทั่วไปสองแบบที่ง่ายขึ้น

XML- เมธอดสั้นลง แต่แน่นอนต้องใช้สตริงเพื่ออนุญาตให้ใช้เคล็ดลับ xml (ไม่มีตัวอักษร 'ไม่ดี')

XML-วิธีการ:

create function dbo.splitString(@input Varchar(max), @Splitter VarChar(99)) returns table as
Return
    SELECT Split.a.value('.', 'VARCHAR(max)') AS Data FROM
    ( SELECT CAST ('<M>' + REPLACE(@input, @Splitter, '</M><M>') + '</M>' AS XML) AS Data 
    ) AS A CROSS APPLY Data.nodes ('/M') AS Split(a); 

วิธีการเรียกซ้ำ:

create function dbo.splitString(@input Varchar(max), @Splitter Varchar(99)) returns table as
Return
  with tmp (DataItem, ix) as
   ( select @input  , CHARINDEX('',@Input)  --Recu. start, ignored val to get the types right
     union all
     select Substring(@input, ix+1,ix2-ix-1), ix2
     from (Select *, CHARINDEX(@Splitter,@Input+@Splitter,ix+1) ix2 from tmp) x where ix2<>0
   ) select DataItem from tmp where ix<>0

ฟังก์ชั่นการทำงาน

Create table TEST_X (A int, CSV Varchar(100));
Insert into test_x select 1, 'A,B';
Insert into test_x select 2, 'C,D';

Select A,data from TEST_X x cross apply dbo.splitString(x.CSV,',') Y;

Drop table TEST_X

XML-METHOD 2: Unicode Friendly 😀 (ได้รับความอนุเคราะห์จาก Max Hodges) create function dbo.splitString(@input nVarchar(max), @Splitter nVarchar(99)) returns table as Return SELECT Split.a.value('.', 'NVARCHAR(max)') AS Data FROM ( SELECT CAST ('<M>' + REPLACE(@input, @Splitter, '</M><M>') + '</M>' AS XML) AS Data ) AS A CROSS APPLY Data.nodes ('/M') AS Split(a);


1
สิ่งนี้อาจดูเหมือนชัดเจน แต่คุณจะใช้ฟังก์ชันทั้งสองนี้ได้อย่างไร คุณสามารถแสดงวิธีใช้ในกรณีการใช้ของ OP ได้หรือไม่?
jpaugh

1
นี่คือตัวอย่างด่วน: สร้างตาราง TEST_X (A, CSV Varchar (100)); แทรกลงใน test_x เลือก 1, 'A, B'; แทรกลงใน test_x เลือก 2, 'C, D'; เลือก A ข้อมูลจาก TEST_X x cross ใช้ dbo.splitString (x.CSV, ',') Y; วางตาราง TEST_X
Eske Rahn

นี่คือสิ่งที่ฉันต้องการ! ขอบคุณ.
Nitin Badole

5

โปรดอ้างอิง TSQL ด้านล่าง ฟังก์ชัน STRING_SPLIT ใช้ได้เฉพาะในระดับความเข้ากันได้ระดับ 130 ขึ้นไป

TSQL:

DECLARE @stringValue NVARCHAR(400) = 'red,blue,green,yellow,black'  
DECLARE @separator CHAR = ','

SELECT [value]  As Colour
FROM STRING_SPLIT(@stringValue, @separator); 

ผลลัพธ์:

สี

สีแดงสีน้ำเงินสีเขียวสีเหลืองสีดำ


5

ดึกมาก แต่ลองทำดู:

SELECT ColumnID, Column1, value  --Do not change 'value' name. Leave it as it is.
FROM tbl_Sample  
CROSS APPLY STRING_SPLIT(Tags, ','); --'Tags' is the name of column containing comma separated values

ดังนั้นเราจึงมีสิ่งนี้: tbl_Sample:

ColumnID|   Column1 |   Tags
--------|-----------|-------------
1       |   ABC     |   10,11,12    
2       |   PQR     |   20,21,22

หลังจากเรียกใช้แบบสอบถามนี้:

ColumnID|   Column1 |   value
--------|-----------|-----------
1       |   ABC     |   10
1       |   ABC     |   11
1       |   ABC     |   12
2       |   PQR     |   20
2       |   PQR     |   21
2       |   PQR     |   22

ขอบคุณ!


STRING_SPLITดี แต่ต้องใช้ SQL Server 2016 docs.microsoft.com/en-us/sql/t-sql/functions/…
Craig Silver

ทางออกที่สง่างาม
Sangram Nandkhile

3
DECLARE @id_list VARCHAR(MAX) = '1234,23,56,576,1231,567,122,87876,57553,1216'
DECLARE @table TABLE ( id VARCHAR(50) )
DECLARE @x INT = 0
DECLARE @firstcomma INT = 0
DECLARE @nextcomma INT = 0

SET @x = LEN(@id_list) - LEN(REPLACE(@id_list, ',', '')) + 1 -- number of ids in id_list

WHILE @x > 0
    BEGIN
        SET @nextcomma = CASE WHEN CHARINDEX(',', @id_list, @firstcomma + 1) = 0
                              THEN LEN(@id_list) + 1
                              ELSE CHARINDEX(',', @id_list, @firstcomma + 1)
                         END
        INSERT  INTO @table
        VALUES  ( SUBSTRING(@id_list, @firstcomma + 1, (@nextcomma - @firstcomma) - 1) )
        SET @firstcomma = CHARINDEX(',', @id_list, @firstcomma + 1)
        SET @x = @x - 1
    END

SELECT  *
FROM    @table

นี่เป็นหนึ่งในวิธีการสองสามอย่างที่ทำงานกับการสนับสนุน SQL ที่ จำกัด ในคลังข้อมูล Azure SQL
Aaron Schultz

1
;WITH tmp(SomeID, OtherID, DataItem, Data) as (
    SELECT SomeID, OtherID, LEFT(Data, CHARINDEX(',',Data+',')-1),
        STUFF(Data, 1, CHARINDEX(',',Data+','), '')
FROM Testdata
WHERE Data > ''
)
SELECT SomeID, OtherID, Data
FROM tmp
ORDER BY SomeID

ด้วยการปรับเปลี่ยนเล็กน้อยเพียงเล็กน้อยเพื่อค้นหาด้านบน ...


6
คุณสามารถอธิบายสั้น ๆ ว่านี่เป็นการปรับปรุงเวอร์ชั่นในคำตอบที่ยอมรับได้อย่างไร
Leigh

ไม่มีการรวมรหัสน้อย ... ทั้งหมดเข้าด้วยกัน เนื่องจากใช้สหภาพทั้งหมดแทนที่จะเป็นสหภาพจึงไม่ควรมีความแตกต่างด้านประสิทธิภาพ
TamusJRoyce

1
นี่ไม่ได้ส่งคืนแถวทั้งหมดที่ควรมี ฉันไม่แน่ใจว่าข้อมูลเกี่ยวข้องกับการรวมทั้งหมด แต่โซลูชันของคุณส่งคืนจำนวนแถวเดียวกันกับตารางดั้งเดิม
Oedhel Setren

1
(ปัญหานี่คือส่วนที่เรียกซ้ำเป็นส่วนที่ละเว้น ... )
Eske Rahn

ไม่ให้ฉันเอาท์พุทคาดหวังเพียงให้บันทึกแรกในแถวแยก
Ankit Misra

1

เมื่อใช้วิธีการนี้คุณต้องตรวจสอบให้แน่ใจว่าไม่มีค่าใดที่มีสิ่งที่จะผิดกฎหมาย XML - user1151923

ฉันมักจะใช้วิธี XML ตรวจสอบให้แน่ใจว่าคุณใช้ VALID XML ฉันมีสองหน้าที่ในการแปลงระหว่าง XML ที่ถูกต้องและข้อความ (ฉันมักจะถอดสายการบินคืนเพราะฉันไม่ต้องการพวกเขา

CREATE FUNCTION dbo.udf_ConvertTextToXML (@Text varchar(MAX)) 
    RETURNS varchar(MAX)
AS
    BEGIN
        SET @Text = REPLACE(@Text,CHAR(10),'')
        SET @Text = REPLACE(@Text,CHAR(13),'')
        SET @Text = REPLACE(@Text,'<','&lt;')
        SET @Text = REPLACE(@Text,'&','&amp;')
        SET @Text = REPLACE(@Text,'>','&gt;')
        SET @Text = REPLACE(@Text,'''','&apos;')
        SET @Text = REPLACE(@Text,'"','&quot;')
    RETURN @Text
END


CREATE FUNCTION dbo.udf_ConvertTextFromXML (@Text VARCHAR(MAX)) 
    RETURNS VARCHAR(max)
AS
    BEGIN
        SET @Text = REPLACE(@Text,'&lt;','<')
        SET @Text = REPLACE(@Text,'&amp;','&')
        SET @Text = REPLACE(@Text,'&gt;','>')
        SET @Text = REPLACE(@Text,'&apos;','''')
        SET @Text = REPLACE(@Text,'&quot;','"')
    RETURN @Text
END

1
มีปัญหาเล็กน้อยเกี่ยวกับรหัสที่คุณได้รับ มันจะเปลี่ยน '<' เป็น '& amp; lt;' แทน '& lt;' อย่างที่ควรจะเป็น ดังนั้นคุณต้องเข้ารหัส '&' ก่อน
Stewart

ไม่จำเป็นต้องใช้ฟังก์ชั่นดังกล่าว ... เพียงแค่ใช้ความสามารถโดยนัย ลองใช้ดู:SELECT (SELECT '<&> blah' + CHAR(13)+CHAR(10) + 'next line' FOR XML PATH(''))
Shnugo

1

ฟังก์ชัน

CREATE FUNCTION dbo.SplitToRows (@column varchar(100), @separator varchar(10))
RETURNS @rtnTable TABLE
  (
  ID int identity(1,1),
  ColumnA varchar(max)
  )
 AS
BEGIN
    DECLARE @position int = 0
    DECLARE @endAt int = 0
    DECLARE @tempString varchar(100)

    set @column = ltrim(rtrim(@column))

    WHILE @position<=len(@column)
    BEGIN       
        set @endAt = CHARINDEX(@separator,@column,@position)
            if(@endAt=0)
            begin
            Insert into @rtnTable(ColumnA) Select substring(@column,@position,len(@column)-@position)
            break;
            end
        set @tempString = substring(ltrim(rtrim(@column)),@position,@endAt-@position)

        Insert into @rtnTable(ColumnA) select @tempString
        set @position=@endAt+1;
    END
    return
END

ใช้กรณี

select * from dbo.SplitToRows('T14; p226.0001; eee; 3554;', ';')

หรือเพียงแค่เลือกพร้อมชุดผลลัพธ์หลายชุด

DECLARE @column varchar(max)= '1234; 4748;abcde; 324432'
DECLARE @separator varchar(10) = ';'
DECLARE @position int = 0
DECLARE @endAt int = 0
DECLARE @tempString varchar(100)

set @column = ltrim(rtrim(@column))

WHILE @position<=len(@column)
BEGIN       
    set @endAt = CHARINDEX(@separator,@column,@position)
        if(@endAt=0)
        begin
        Select substring(@column,@position,len(@column)-@position)
        break;
        end
    set @tempString = substring(ltrim(rtrim(@column)),@position,@endAt-@position)

    select @tempString
    set @position=@endAt+1;
END

การใช้ขณะที่ลูปภายในฟังก์ชันหลายค่าที่มีค่าเป็นตารางเป็นวิธีที่แย่ที่สุดในการแยกสตริง มีคำถามมากมายเกี่ยวกับคำถามนี้อยู่แล้ว
Sean Lange

0

ด้านล่างใช้งานได้บน sql server 2008

select *, ROW_NUMBER() OVER(order by items) as row# 
from 
( select 134 myColumn1, 34 myColumn2, 'd,c,k,e,f,g,h,a' comaSeperatedColumn) myTable
    cross apply 
SPLIT (rtrim(comaSeperatedColumn), ',') splitedTable -- gives 'items'  column 

จะได้รับผลิตภัณฑ์คาร์ทีเซียนทั้งหมดที่มีคอลัมน์ตารางกำเนิดพร้อมกับ "รายการ" ของตารางแยก


0

คุณสามารถใช้ฟังก์ชั่นต่อไปนี้เพื่อดึงข้อมูล

CREATE FUNCTION [dbo].[SplitString]
(    
    @RowData NVARCHAR(MAX),
    @Delimeter NVARCHAR(MAX)
)
RETURNS @RtnValue TABLE 
(
    ID INT IDENTITY(1,1),
    Data NVARCHAR(MAX)
) 
AS
BEGIN 
    DECLARE @Iterator INT
    SET @Iterator = 1

    DECLARE @FoundIndex INT
    SET @FoundIndex = CHARINDEX(@Delimeter,@RowData)

    WHILE (@FoundIndex>0)
    BEGIN
        INSERT INTO @RtnValue (data)
        SELECT 
            Data = LTRIM(RTRIM(SUBSTRING(@RowData, 1, @FoundIndex - 1)))

        SET @RowData = SUBSTRING(@RowData,
                @FoundIndex + DATALENGTH(@Delimeter) / 2,
                LEN(@RowData))

        SET @Iterator = @Iterator + 1
        SET @FoundIndex = CHARINDEX(@Delimeter, @RowData)
    END

    INSERT INTO @RtnValue (Data)
    SELECT Data = LTRIM(RTRIM(@RowData))

    RETURN
END

การใช้ขณะที่ลูปภายในฟังก์ชันหลายค่าที่มีค่าเป็นตารางเป็นวิธีที่แย่ที่สุดในการแยกสตริง มีคำถามมากมายเกี่ยวกับคำถามนี้อยู่แล้ว
Sean Lange
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.