Cách chia giá trị được phân tách bằng dấu phẩy thành các cột


138

Tôi có một cái bàn như thế này

Value   String
-------------------
1       Cleo, Smith

Tôi muốn tách chuỗi được phân cách bằng dấu phẩy thành hai cột

Value  Name Surname
-------------------
1      Cleo   Smith

Tôi chỉ cần hai cột phụ cố định


Câu trả lời:


12
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

3
Đây không phải là câu trả lời được chấp nhận ... Một TVF đa câu lệnh (rất tệ!) Và một WHILEvòng lặp (thậm chí tệ hơn) cùng nhau sẽ hoạt động khủng khiếp. Bên cạnh đó, đây là câu trả lời chỉ có mã và thậm chí không giải quyết được vấn đề Có nhiều cách tiếp cận tốt hơn xung quanh! Đối với SQL-Server 2016+, hãy tìm kiếm STRING_SPLIT()(không mang vị trí của mảnh vỡ, một thất bại lớn!) Hoặc rất nhanh JSON. Đối với phiên bản cũ hơn, hãy tìm XML-hack nổi tiếng (chi tiết json và xml tại đây ). Hoặc tìm kiếm một trong những iTVF có thể dựa trên CTE đệ quy.
Shnugo

SQL 2016 trở lên:SELECT * FROM STRING_SPLIT('John,Jeremy,Jack',',')
Alaa

Đồng ý với giải pháp đã cho. tuy nhiên, nếu bạn là SQL Server 2016, bạn có thể sử dụng hàm string_split .. Bạn cũng có thể tìm thấy cách sử dụng hàm dựng sẵn này tại đây tecloger.com/opes-split-feft-in-sql-server
Khatri

2
Mọi người đề xuất STRINGinksLIT, làm thế nào chức năng này có thể chia chuỗi thành các cột (không phải hàng như dự định)?
geominded

128

Mục đích của bạn có thể được giải quyết bằng truy vấn sau -

Select Value  , Substring(FullName, 1,Charindex(',', FullName)-1) as Name,
Substring(FullName, Charindex(',', FullName)+1, LEN(FullName)) as  Surname
from Table1

Không có chức năng Split readymade trong máy chủ sql, vì vậy chúng ta cần tạo chức năng do người dùng xác định.

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

1
Nhìn vào giải pháp bảng số DeliatedSplit8K của Jeff Moden trong câu trả lời @ughai bên dưới.
Ruskin

2
SQL 2016 hiện có chức năng phân tách
tvanharp

SQL 2016 trở lên:SELECT * FROM STRING_SPLIT('John,Jeremy,Jack',',')
Alaa

51
;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

và cũng kiểm tra liên kết dưới đây để tham khảo

http://jahaines.blogspot.in/2009/06/converting-delrict-opes-of-values.html


4
Điều này tốt hơn .. nó đơn giản và ngắn gọn.
Kimchi Man

4
Tôi thực sự thích cách này. CHARINDEX và SUBSTRING là một mớ hỗn độn khi bạn có nhiều hơn 2 giá trị để phân chia (Ví dụ: 1,2,3). Cảm ơn rất nhiều
jotapdiez

2
Ý tưởng tuyệt vời. Mặc dù chậm hơn ba lần so CHARINDEXvới SUBSTRINGmớ hỗn độn cộng , ít nhất là đối với tôi. :-(
Michel de Ruiter

1
Đây là GENIUS để phân chia số lượng lớn các trường khi mỗi trường cần một tên cụ thể! Toàn bộ truy vấn có thể dễ dàng được xây dựng bằng SQL động!
devinbost

4
Tuy nhiên, giải pháp tuyệt vời là một số ký tự không hợp lệ trong XML (ví dụ '&') vì vậy tôi phải bọc từng trường trong thẻ CDATA ...CONVERT(XML,'<Names><name><![CDATA[' + REPLACE(Name,',', ']]></name><name><![CDATA[') + ']]></name></name>') AS xmlname
Tony

44

xml câu trả lời đơn giản và sạch sẽ

giới thiệu này

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)

1
Điều này thực sự mát mẻ. Các tính năng như mảng là rất hữu ích và tôi không có ý tưởng về. Cảm ơn!
Trả thù

34

Tôi nghĩ điều này thật tuyệt

SELECT value,
    PARSENAME(REPLACE(String,',','.'),2) 'Name' ,
    PARSENAME(REPLACE(String,',','.'),1) 'Sur Name'
FROM table WITH (NOLOCK)

3
Yêu cầu của bạn chỉ dành cho tên và họ chỉ na
Azar

1
Bạn cũng cần lưu ý rằng PARSENAME sẽ trả về NULL cho các mục dài hơn 128 ký tự.
Luis Cazares

Đẹp. Hoạt động tốt cho dữ liệu của tôi quá!
glass_kites 17/07/19

27

Với ứng dụng CROSS

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

5
Tôi không thể quấn đầu xung quanh lý do tại sao bạn cần thêm 2 dấu phẩy ở cuối chuỗi gốc để làm việc này. Tại sao nó không hoạt động mà không có "+ ',,'"?
cà chua

@ developer.ejay có phải vì các hàm Left / SubString không thể lấy giá trị 0?
Waller

Tuyệt quá! Bạn có thể dễ dàng sao chép / dán 2 dòng cho mỗi cột thêm mà bạn muốn - sau đó chỉ cần tăng các số, ví dụ: chọn ParsedData. * Từ áp dụng chéo MyTable mt (chọn str = mt.String + ',,') áp dụng chéo F1 (chọn p1 = charindex (',', str)) ap1 cross áp dụng (chọn p2 = charindex (',', str, p1 + 1)) áp dụng chéo ap2 (chọn p3 = charindex (',', str, p2 + 1)) áp dụng chéo ap3 (chọn FName = chuỗi con (str, 1, p1-1), LName = chuỗi con (str, p1 + 1, p2-p1-1), Age = chuỗi con (str, p2 + 1, p3-p2-1 )) ParsedData
Mike

23

Có nhiều cách để giải quyết điều này và nhiều cách khác nhau đã được đề xuất. Đơn giản nhất sẽ là sử dụng LEFT/ SUBSTRINGvà các hàm chuỗi khác để đạt được kết quả mong muốn.

Dữ liệu mẫu

DECLARE @tbl1 TABLE (Value INT,String VARCHAR(MAX))

INSERT INTO @tbl1 VALUES(1,'Cleo, Smith');
INSERT INTO @tbl1 VALUES(2,'John, Mathew');

Sử dụng các hàm chuỗi như LEFT

SELECT
    Value,
    LEFT(String,CHARINDEX(',',String)-1) as Fname,
    LTRIM(RIGHT(String,LEN(String) - CHARINDEX(',',String) )) AS Lname
FROM @tbl1

Cách tiếp cận này thất bại nếu có thêm 2 mục trong Chuỗi. Trong kịch bản như vậy, chúng ta có thể sử dụng bộ chia và sau đó sử dụng PIVOThoặc chuyển đổi chuỗi thành một XMLvà sử dụng .nodesđể lấy các mục chuỗi. XMLgiải pháp dựa trên đã được chi tiết bởi aads và bvr trong giải pháp của họ.

Các câu trả lời cho câu hỏi này sử dụng bộ chia, tất cả đều sử dụng WHILEkhông hiệu quả để chia. Kiểm tra so sánh hiệu suất này . Một trong những bộ chia tốt nhất xung quanh là DelimitedSplit8K, được tạo bởi Jeff Moden. Bạn có thể đọc thêm về nó ở đây

Bộ chia với 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

Đầu ra

Value   Fname   Lname
1   Cleo    Smith
2   John    Mathew

DelimitedSplit8K bởi Jeff Moden

CREATE 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

15

Hãy thử điều này (thay đổi phiên bản của '' thành ',' hoặc bất kỳ dấu phân cách nào bạn muốn sử dụng)

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

Ví dụ sử dụng:

SELECT dbo.Wordparser(COLUMN, 1),
       dbo.Wordparser(COLUMN, 2),
       dbo.Wordparser(COLUMN, 3)
FROM   TABLE

Thất bại cho tôi nếu các giá trị giống hệt nhau trong cùng một hàng.
Pete Alvin

14

Với SQL Server 2016, chúng tôi có thể sử dụng chuỗi_split để thực hiện việc này:

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,',')

Tôi đang sử dụng SQL Server 2016 nhưng nó bị lỗiInvalid object name 'string_split'
ibiza

2
Bạn có thể kiểm tra mức độ tương thích của cơ sở dữ liệu của bạn? Nó phải là 130 là máy chủ sql 2016. Bạn có thể sử dụng truy vấn này chọn * từ sys.database
Kannan Kandasamy

đúng, tôi thấy 120 vì vậy nó phải chỉ là máy khách (Microsoft SQL Server Management Studio) là năm 2016 chứ không phải máy chủ cơ sở dữ liệu vì nếu tôi đi tới Trợ giúp -> Giới thiệu, tôi thấy SQL Server 2016 Management Studio v13.0.15000. 23. Cảm ơn
ibiza

Có thể xảy ra rằng mức được đặt thành bất kỳ giá trị thấp hơn bởi các nhà phát triển db để giữ cho db tương thích, ngay cả khi phiên bản cài đặt thực tế cao hơn. Sử dụng công cụ này để đặt mức cao theo yêu cầu miễn là db hỗ trợ điều này: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;
Joerg Krause

điều này là vô ích trừ khi bạn xoay nó từ hàng sang cột.
Guy Manova

12

Tôi nghĩ PARSENAME là chức năng gọn gàng để sử dụng cho ví dụ này, như được mô tả trong bài viết này: http://www.sqlshack.com/parsing-and-rotating-deliated-data-in-sql-server-2012/

Hàm PARSENAME được thiết kế hợp lý để phân tích tên đối tượng bốn phần. Điều thú vị về PARSENAME là nó không bị giới hạn khi chỉ phân tích cú pháp tên đối tượng bốn phần của SQL Server - nó sẽ phân tích bất kỳ hàm hoặc chuỗi dữ liệu nào được phân cách bằng dấu chấm.

Tham số đầu tiên là đối tượng để phân tích và thứ hai là giá trị nguyên của phần đối tượng cần trả về. Bài viết này đang thảo luận về phân tích cú pháp và xoay dữ liệu được phân tách - số điện thoại của công ty, nhưng nó cũng có thể được sử dụng để phân tích dữ liệu tên / họ.

Thí dụ:

USE COMPANY;
SELECT PARSENAME('Whatever.you.want.parsed',3) AS 'ReturnValue';

Bài viết cũng mô tả bằng cách sử dụng Biểu thức bảng chung (CTE) được gọi là 'thay thế', để chạy PARSENAME đối với các giá trị được thay thế bằng dấu phân cách. CTE rất hữu ích để trả về chế độ xem tạm thời hoặc tập kết quả.

Sau đó, hàm UNPIVOT đã được sử dụng để chuyển đổi một số cột thành hàng; Các hàm SUBSTRING và CHARINDEX đã được sử dụng để dọn dẹp sự không nhất quán trong dữ liệu và hàm LAG (mới cho SQL Server 2012) đã được sử dụng cuối cùng, vì nó cho phép tham chiếu các bản ghi trước đó.


11
SELECT id,
       Substring(NAME, 0, Charindex(',', NAME))             AS firstname,
       Substring(NAME, Charindex(',', NAME), Len(NAME) + 1) AS lastname
FROM   spilt  

6
Sẽ rất hữu ích nếu bạn có thể mở rộng câu trả lời của mình và cũng sử dụng các công cụ định dạng mã.
Politank-Z

Đóng, điều này sẽ bao gồm dấu phẩy trong họ. Có +1 ở sai vị trí. Phải là Chuỗi con (NAME, Charindex (',', NAME) +1, Len (NAME)) NHƯ tên cuối cùng
LarryBud

10

Chúng ta có thể tạo một hàm như thế này

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

Sau đó, chúng tôi có thể tách các giá trị CSV thành các cột tương ứng bằng cách sử dụng câu lệnh CHỌN


8

Tôi nghĩ rằng chức năng sau đây sẽ làm việc cho bạn:

Bạn phải tạo một hàm trong SQL trước. Như thế này

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

Bạn có thể gọi hàm này, như thế này:

select * from fn_split('1,24,5',',')

Thực hiện:

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

Kết quả sẽ như thế này:

nhập mô tả hình ảnh ở đây


Sử dụng các vòng lặp để phân tách chuỗi là không hiệu quả khủng khiếp. Dưới đây là một số tùy chọn tốt hơn cho chức năng phân chia đó. sqlperformance.com/2012/07/t-sql-queries/split-strings
Sean Lange

8

Bạn có thể sử dụng hàm có giá trị bảngSTRING_SPLIT , chỉ khả dụng ở mức tương thích 130. Nếu mức độ tương thích cơ sở dữ liệu của bạn thấp hơn 130, SQL Server sẽ không thể tìm và thực thi STRING_SPLITchức năng. Bạn có thể thay đổi mức độ tương thích của cơ sở dữ liệu bằng lệnh sau:

ALTER DATABASE DatabaseName SET COMPATIBILITY_LEVEL = 130

Cú pháp

SELECT * FROM STRING_SPLIT ( string, separator )

xem tài liệu ở đây


Đẹp. Nhưng nó không áp dụng cho SQL Server ít hơn 2016
Lokesh

Đúng, trong câu trả lời của tôi, tôi đã chỉ ra rằng nó sẽ chỉ khả dụng ở mức độ tương thích 130 trở lên.
bwanamaina

1
Nhưng STRINGinksLIT chia thành nhiều hàng, không chia thành nhiều cột cho mỗi lần phân tách. OP đã hỏi về việc chia thành nhiều cột phải không?
geominded

7

Sử dụng hàm 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

Kết quả

Name    Family
-----   ------
Aria    Karimi
Bab     Karimi
Joe     Karimi

6

Thử cái này:

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

Làm việc như một cơ duyên!
yu yang Jian

5

Tôi đã gặp một vấn đề tương tự nhưng một vấn đề phức tạp và vì đây là chủ đề đầu tiên tôi tìm thấy liên quan đến vấn đề đó nên tôi quyết định đăng bài tìm kiếm của mình. Tôi biết đó là giải pháp phức tạp cho một vấn đề đơn giản nhưng tôi hy vọng rằng tôi có thể giúp những người khác tìm đến chủ đề này để tìm kiếm một giải pháp phức tạp hơn. tôi đã phải chia một chuỗi chứa 5 số (tên cột: levelFeed) và để hiển thị mỗi số trong một cột riêng biệt. ví dụ: 8,1,2,2,2 nên được hiển thị là:

1  2  3  4  5
-------------
8  1  2  2  2

Giải pháp 1: sử dụng các hàm XML: giải pháp này cho giải pháp chậm nhất cho đến nay

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)

Giải pháp 2: sử dụng chức năng Split và trục. (hàm split chia một chuỗi thành các hàng với dữ liệu tên cột)

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

Giải pháp 3: sử dụng các hàm thao tác chuỗi - nhanh nhất bằng lề nhỏ so với giải pháp 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

vì levelFeed chứa 5 giá trị chuỗi tôi cần để sử dụng hàm chuỗi con cho chuỗi đầu tiên.

tôi hy vọng rằng giải pháp của tôi sẽ giúp những người khác có được chủ đề này đang tìm kiếm một phương pháp phân tách phức tạp hơn cho các cột


5

Sử dụng chức năng Barsing :)

select Value, 
       substring(String,1,instr(String," ") -1) Fname,  
       substring(String,instr(String,",") +1) Sname 
from tablename;

Đã sử dụng hai hàm,
1. substring(string, position, length) ==> trả về chuỗi từ positon đến chiều dài
2.instr(string,pattern) ==> trả về vị trí của mẫu.

Nếu chúng tôi không cung cấp đối số độ dài trong chuỗi con, nó sẽ trả về cho đến hết chuỗi


1
Không chắc chắn phương ngữ SQL nào bạn đang sử dụng, nhưng trong SQL Server, chúng tôi sẽ phải sử dụng một cái gì đó như substring(@str, 1, charindex(@sep, @str) - 1)sau substring(@str, charindex(@sep, @str) + 1, len(@str)).
Peter B

5

Chức năng này nhanh nhất:

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

Ví dụ sử dụng:

SELECT dbo.F_ExtractSubString(COLUMN, 1, ', '),
       dbo.F_ExtractSubString(COLUMN, 2, ', '),
       dbo.F_ExtractSubString(COLUMN, 3, ', ')
FROM   TABLE

3
Cảm ơn bạn vì đoạn mã này, có thể cung cấp một số trợ giúp hạn chế, ngay lập tức. Một lời giải thích phù hợp sẽ cải thiện đáng kể giá trị lâu dài của nó bằng cách chỉ ra lý do tại sao đây là một giải pháp tốt cho vấn đề và sẽ giúp nó hữu ích hơn cho những người đọc tương lai với những câu hỏi tương tự khác. Vui lòng chỉnh sửa câu trả lời của bạn để thêm một số giải thích, bao gồm các giả định bạn đã thực hiện.
Toby Speight

4

Điều này làm việc cho tôi

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

Bạn có biết làm thế nào để xử lý xml đặc biệt?
Zach Smith

3

khả năng của tôi

Value  ColOne
--------------------
1      Cleo, Smith

Các mục sau sẽ hoạt động nếu không có quá nhiều cột

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', '') 

Kết quả:

Value  ColOne ColTwo
--------------------
1      Cleo   Smith

3

nó rất dễ dàng, bạn có thể lấy nó bằng cách truy vấn bên dưới:

DECLARE @str NVARCHAR(MAX)='ControlID_05436b78-04ba-9667-fa01-9ff8c1b7c235,3'
SELECT LEFT(@str, CHARINDEX(',',@str)-1),RIGHT(@str,LEN(@str)-(CHARINDEX(',',@str)))

3
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

Tôi đã sử dụng mã của bạn, nó đơn giản nhưng có lỗi chính tả trong ELIMINATE_CHAT, nó phải là ELIMINATE_CHAR và BẮT ĐẦU Ở cuối tập lệnh nên là L_START. cảm ơn bạn.
Wessam El Mahdy

3

Bạn có thể tìm thấy giải pháp trong Hàm do người dùng xác định SQL để phân tích chuỗi được phân tách hữu ích (từ Dự án mã ).

Đây là phần mã từ trang này:

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:Clayton_Groom@hotmail.com">Clayton_Groom@hotmail.com</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

11
Bất kỳ cơ hội nào bạn có thể tóm tắt giải pháp ở đây để đảm bảo câu trả lời không bị lỗi thời nếu liên kết bị chết?
Adam Lear

3
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 

2

Tôi thấy rằng việc sử dụng PARSENAME như trên đã khiến bất kỳ tên nào có thời gian bị vô hiệu hóa.

Vì vậy, nếu có một chữ cái đầu hoặc một tiêu đề trong tên được theo sau bởi một dấu chấm, chúng sẽ trả về NULL.

Tôi thấy điều này làm việc cho tôi:

SELECT 
REPLACE(SUBSTRING(FullName, 1,CHARINDEX(',', FullName)), ',','') as Name,
REPLACE(SUBSTRING(FullName, CHARINDEX(',', FullName), LEN(FullName)), ',', '') as Surname
FROM Table1

2
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

2
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

4
Mặc dù mã này có thể trả lời câu hỏi, cung cấp ngữ cảnh bổ sung về lý do và / hoặc cách mã này trả lời câu hỏi cải thiện giá trị lâu dài của nó.
Kéo và thả

2

Tôi đã viết lại một câu trả lời ở trên và làm cho nó tốt hơn:

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

Ví dụ sử dụng:

SELECT dbo.CSVParser(COLUMN, 1),
       dbo.CSVParser(COLUMN, 2),
       dbo.CSVParser(COLUMN, 3)
FROM   TABLE

0
CREATE FUNCTION [dbo].[fnSplit](@sInputList VARCHAR(8000), @sDelimiter VARCHAR(8000) = ',')
RETURNS @List TABLE (item VARCHAR(8000))
BEGIN

    DECLARE @sItem VARCHAR(8000)
    WHILE CHARINDEX(@sDelimiter, @sInputList, 0) <> 0
    BEGIN

        SELECT @sItem = RTRIM(LTRIM(SUBSTRING(@sInputList, 1, CHARINDEX(@sDelimiter, @sInputList,0) - 1))),
               @sInputList = RTRIM(LTRIM(SUBSTRING(@sInputList, CHARINDEX(@sDelimiter, @sInputList, 0) + LEN(@sDelimiter),LEN(@sInputList))))

        -- Indexes to keep the position of searching
        IF LEN(@sItem) > 0

        INSERT INTO @List SELECT @sItem

    END

    IF LEN(@sInputList) > 0
    BEGIN

        INSERT INTO @List SELECT @sInputList -- Put the last item in

    END

    RETURN

END
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.