Làm cách nào để chia chuỗi để tôi có thể truy cập mục x?


493

Sử dụng SQL Server, làm cách nào để tách một chuỗi để tôi có thể truy cập mục x?

Lấy một chuỗi "Xin chào John Smith". Làm cách nào tôi có thể chia chuỗi theo khoảng trắng và truy cập vào mục tại chỉ mục 1 sẽ trả về "John"?



5
tích hợp sẵn như máy chủ sql 2016 msdn.microsoft.com/en-us/l Library / mt684588.aspx
Tim Abell

4
Các câu trả lời cao nhất ở đây là - ít nhất là đối với tôi - khá lỗi thời và khá lỗi thời. Định vị thủ tục, vòng lặp, thu hồi, CLR, hàm, nhiều dòng mã ... Thật thú vị khi đọc các câu trả lời "hoạt động" để tìm ra các phương pháp cập nhật hơn.
Shnugo

Tôi đã thêm một câu trả lời mới với cách tiếp cận cập nhật hơn: stackoverflow.com/a/49669994/632604
Gorgi Rankovski

Hãy thử Lấy phần tử thứ n của danh sách -> portosql.wordpress.com/2019/05/27/enesimo-elemento-lista
José Diz

Câu trả lời:


191

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ã ).

Bạn có thể sử dụng logic đơn giản này:

Declare @products varchar(200) = '1|20|3|343|44|6|8765'
Declare @individual varchar(20) = null

WHILE LEN(@products) > 0
BEGIN
    IF PATINDEX('%|%', @products) > 0
    BEGIN
        SET @individual = SUBSTRING(@products,
                                    0,
                                    PATINDEX('%|%', @products))
        SELECT @individual

        SET @products = SUBSTRING(@products,
                                  LEN(@individual + '|') + 1,
                                  LEN(@products))
    END
    ELSE
    BEGIN
        SET @individual = @products
        SET @products = NULL
        SELECT @individual
    END
END

1
Tại sao SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( RTRIM( LTRIM( @p_SourceText)))và không SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( @p_SourceText)?
Beth

12
@GateKiller Giải pháp này không hỗ trợ Unicode và nó sử dụng số được mã hóa cứng (18,3), điều này không làm cho nó trở thành một chức năng "có thể tái sử dụng" khả thi.
Filip De Vos

4
Điều này hoạt động nhưng phân bổ rất nhiều bộ nhớ và lãng phí CPU.
jjxtra

2
Kể từ SQL Server 2016, giờ đây đã có một hàm dựng sẵn STRING_SPLITsẽ phân tách một chuỗi và trả về kết quả bảng một cột mà bạn có thể sử dụng trong SELECTcâu lệnh hoặc ở nơi khác.
qJake

Thật tệ khi những người tôi làm việc không phải vào năm 2016. Nhưng, tôi sẽ ghi nhớ điều đó trong trường hợp họ bị mất giày. Giải pháp tuyệt vời trong thời gian tạm thời. Tôi đã triển khai nó như là một hàm và thêm dấu phân cách làm đối số.
Brandon Griffin

355

Tôi không tin SQL Server có chức năng phân tách tích hợp, vì vậy ngoài UDF, câu trả lời duy nhất khác mà tôi biết là chiếm quyền điều khiển PARSENAME:

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 2) 

PARSENAME lấy một chuỗi và chia nó trên ký tự dấu chấm. Nó lấy một số làm đối số thứ hai của nó và số đó chỉ định đoạn nào của chuỗi sẽ trả về (làm việc từ sau ra trước).

SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 3)  --return Hello

Vấn đề rõ ràng là khi chuỗi đã chứa một khoảng thời gian. Tôi vẫn nghĩ sử dụng UDF là cách tốt nhất ... có đề xuất nào khác không?


102
Cảm ơn Saul ... Tôi nên chỉ ra rằng giải pháp này thực sự là một giải pháp tồi cho sự phát triển thực sự. PARSENAME chỉ mong đợi bốn phần, do đó, việc sử dụng một chuỗi có hơn bốn phần sẽ khiến nó trả về NULL. Các giải pháp UDF rõ ràng là tốt hơn.
Nathan Bedford

33
Đây là một hack tuyệt vời, và cũng khiến tôi khóc rằng một cái gì đó như thế này là cần thiết cho một cái gì đó rất đơn giản trong ngôn ngữ thực.
Nhân tố huyền bí

36
Để làm cho các chỉ mục hoạt động theo cách "đúng", nghĩa là bắt đầu từ 1, tôi đã chiếm quyền điều khiển của bạn với REVERSE: REVERSE (PARSENAME (REPLACE (REVERSE ('Xin chào John Smith'), '', '.') , 1)) - Trả về Xin chào
NothingsImpossible

3
@FactorMystic Biểu mẫu thường đầu tiên yêu cầu bạn không đặt nhiều giá trị trong một trường. Đó thực sự là quy tắc đầu tiên của RDBMS. Một SPLIT()chức năng không được cung cấp vì nó khuyến khích thiết kế cơ sở dữ liệu kém và cơ sở dữ liệu sẽ không bao giờ được tối ưu hóa để sử dụng dữ liệu được lưu trữ ở định dạng này. RDBMS không bắt buộc phải giúp các nhà phát triển làm những điều ngu ngốc mà nó được thiết kế để không xử lý. Câu trả lời đúng sẽ luôn là "Bình thường hóa cơ sở dữ liệu của bạn như chúng tôi đã nói với bạn 40 năm trước." Cả SQL và RDBMS đều không đổ lỗi cho thiết kế kém.
Bacon Bits

8
@BaconBits trong khi tôi đồng ý về mặt lý thuyết, trong các công cụ thực hành như thế này rất hữu ích khi bình thường hóa một thiết kế kém được sản xuất bởi một người đi trước bạn.
Tim Abell

110

Đầu tiên, tạo một hàm (sử dụng CTE, biểu thức bảng chung không cần dùng bảng tạm thời)

 create function dbo.SplitString 
    (
        @str nvarchar(4000), 
        @separator char(1)
    )
    returns table
    AS
    return (
        with tokens(p, a, b) AS (
            select 
                1, 
                1, 
                charindex(@separator, @str)
            union all
            select
                p + 1, 
                b + 1, 
                charindex(@separator, @str, b + 1)
            from tokens
            where b > 0
        )
        select
            p-1 zeroBasedOccurance,
            substring(
                @str, 
                a, 
                case when b > 0 then b-a ELSE 4000 end) 
            AS s
        from tokens
      )
    GO

Sau đó, sử dụng nó như bất kỳ bảng nào (hoặc sửa đổi nó để phù hợp với Proc được lưu trữ hiện tại của bạn) như thế này.

select s 
from dbo.SplitString('Hello John Smith', ' ')
where zeroBasedOccurance=1

Cập nhật

Phiên bản trước sẽ thất bại cho chuỗi đầu vào dài hơn 4000 ký tự. Phiên bản này đảm nhận giới hạn:

create function dbo.SplitString 
(
    @str nvarchar(max), 
    @separator char(1)
)
returns table
AS
return (
with tokens(p, a, b) AS (
    select 
        cast(1 as bigint), 
        cast(1 as bigint), 
        charindex(@separator, @str)
    union all
    select
        p + 1, 
        b + 1, 
        charindex(@separator, @str, b + 1)
    from tokens
    where b > 0
)
select
    p-1 ItemIndex,
    substring(
        @str, 
        a, 
        case when b > 0 then b-a ELSE LEN(@str) end) 
    AS s
from tokens
);

GO

Cách sử dụng vẫn như cũ.


14
Nó thanh lịch nhưng chỉ hoạt động cho 100 yếu tố vì giới hạn độ sâu đệ quy.
Pking

4
@Pking, không, mặc định là 100(để ngăn chặn vòng lặp vô hạn). Sử dụng gợi ý MAXRECURSION để xác định số mức đệ quy ( 0đến 32767, 0là "không giới hạn" - có thể đè bẹp máy chủ). BTW, câu trả lời tốt hơn nhiều PARSENAME, bởi vì nó phổ quát :-). +1
Michał Powaga

Thêm maxrecursionvào giải pháp này hãy ghi nhớ câu hỏi này và câu trả lời của nó Cách thiết lập maxrecursiontùy chọn cho CTE bên trong Hàm giá trị bảng .
Michał Powaga

Cụ thể, tham khảo câu trả lời của Crisfole - phương pháp của anh ta làm chậm nó một chút, nhưng đơn giản hơn hầu hết các tùy chọn khác.
AHiggins

điểm nhỏ nhưng cách sử dụng không giữ nguyên vì bạn đã thay đổi tên cột, do đó skhông còn được xác định
Tim Abell

62

Hầu hết các giải pháp ở đây sử dụng trong khi các vòng lặp hoặc CTE đệ quy. Một cách tiếp cận dựa trên tập hợp sẽ tốt hơn, tôi hứa, nếu bạn có thể sử dụng một dấu phân cách khác với khoảng trắng:

CREATE FUNCTION [dbo].[SplitString]
    (
        @List NVARCHAR(MAX),
        @Delim VARCHAR(255)
    )
    RETURNS TABLE
    AS
        RETURN ( SELECT [Value], idx = RANK() OVER (ORDER BY n) FROM 
          ( 
            SELECT n = Number, 
              [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
              CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
            FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
              FROM sys.all_objects) AS x
              WHERE Number <= LEN(@List)
              AND SUBSTRING(@Delim + @List, [Number], LEN(@Delim)) = @Delim
          ) AS y
        );

Sử dụng mẫu:

SELECT Value FROM dbo.SplitString('foo,bar,blat,foo,splunge',',')
  WHERE idx = 3;

Các kết quả:

----
blat

Bạn cũng có thể thêm idx bạn muốn làm đối số cho hàm, nhưng tôi sẽ để nó như một bài tập cho người đọc.

Bạn không thể làm điều này chỉ với chức năng gốcSTRING_SPLIT được thêm vào trong SQL Server 2016, vì không có gì đảm bảo rằng đầu ra sẽ được hiển thị theo thứ tự của danh sách gốc. Nói cách khác, nếu bạn vượt qua trong 3,6,1kết quả có thể sẽ theo thứ tự đó, nhưng nó thể 1,3,6. Tôi đã yêu cầu sự giúp đỡ của cộng đồng trong việc cải thiện chức năng tích hợp ở đây:

Với đủ phản hồi định tính , họ thực sự có thể xem xét thực hiện một số cải tiến sau:

Thông tin thêm về các hàm phân tách, tại sao (và chứng minh điều đó) trong khi các vòng lặp và CTE đệ quy không mở rộng và các lựa chọn thay thế tốt hơn, nếu tách chuỗi đến từ lớp ứng dụng:

Tuy nhiên, trên SQL Server 2016 trở lên, bạn nên xem STRING_SPLIT()STRING_AGG():


1
Câu trả lời hay nhất, IMHO. Trong một số câu trả lời khác, có vấn đề về giới hạn đệ quy SQL là 100, nhưng không phải trong trường hợp này. Thực hiện rất nhanh và rất đơn giản. Nút +2 ở đâu?
T-moty

5
Tôi đã thử chức năng này nguyên văn với cách sử dụng: select * from DBO.SplitString('Hello John smith', ' ');và đầu ra được tạo ra là: Giá trị Xin chào ello llo lo o John ohn hn n smith mith ith th h
wwmbes

2
@AaronBertrand Vấn đề ban đầu được đăng bởi GateKiller liên quan đến một dấu phân cách không gian.
wwmbes

1
@ user1255933 Địa chỉ.
Aaron Bertrand

1
@Michael Vâng, đó là sự thật. Bạn cũng sẽ không có bảng để chọn nếu bạn không có quyền ALTER SCHema và sẽ không thể chọn từ đó nếu bạn không có quyền CHỌN Bạn luôn có thể yêu cầu ai đó tạo chức năng cho bạn . Hoặc tạo nó ở đâu đó bạn có thể tạo nó (thậm chí tạm thời, nói trong tempdb). Và vào năm 2016+, bạn nên sử dụng STRINGinksLIT () và không phải là chức năng bạn phải tự tạo.
Aaron Bertrand

38

Bạn có thể tận dụng bảng Số để thực hiện phân tích chuỗi.

Tạo bảng số vật lý:

    create table dbo.Numbers (N int primary key);
    insert into dbo.Numbers
        select top 1000 row_number() over(order by number) from master..spt_values
    go

Tạo bảng thử nghiệm với 1000000 hàng

    create table #yak (i int identity(1,1) primary key, array varchar(50))

    insert into #yak(array)
        select 'a,b,c' from dbo.Numbers n cross join dbo.Numbers nn
    go

Tạo chức năng

    create function [dbo].[ufn_ParseArray]
        (   @Input      nvarchar(4000), 
            @Delimiter  char(1) = ',',
            @BaseIdent  int
        )
    returns table as
    return  
        (   select  row_number() over (order by n asc) + (@BaseIdent - 1) [i],
                    substring(@Input, n, charindex(@Delimiter, @Input + @Delimiter, n) - n) s
            from    dbo.Numbers
            where   n <= convert(int, len(@Input)) and
                    substring(@Delimiter + @Input, n, 1) = @Delimiter
        )
    go

Cách sử dụng (xuất ra hàng 3 triệu trong 40 giây trên máy tính xách tay của tôi)

    select * 
    from #yak 
    cross apply dbo.ufn_ParseArray(array, ',', 1)

dọn dẹp

    drop table dbo.Numbers;
    drop function  [dbo].[ufn_ParseArray]

Hiệu suất ở đây không đáng kinh ngạc, nhưng gọi một hàm trên một triệu bảng hàng không phải là ý tưởng tốt nhất. Nếu thực hiện một chuỗi phân chia trên nhiều hàng tôi sẽ tránh chức năng.


2
Giải pháp tốt nhất IMO, các giải pháp khác có một số hạn chế .. điều này nhanh và có thể phân tích các chuỗi dài với nhiều yếu tố.
Pking

Tại sao bạn đặt hàng n giảm dần? Nếu có ba mục và chúng tôi bắt đầu đánh số 1, thì mục đầu tiên sẽ là số 3 và mục cuối sẽ là số 1. Nó sẽ không cho kết quả trực quan hơn nếu descloại bỏ?
hatchet - được thực hiện với SOverflow

1
Đồng ý, sẽ trực quan hơn theo hướng asc. Tôi đã theo dõi quy ước Parsename () sử dụng desc
Nathan Skerl

3
Một số lời giải thích về cách thức hoạt động của nó sẽ tuyệt vời
Tim Abell

Trong thử nghiệm trên 100 triệu hàng có tối đa 3 trường để phân tích cú pháp, ufn_PudeArray đã không hoàn thành sau 25 phút, trong khi REVERSE(PARSENAME(REPLACE(REVERSE('Hello John Smith'), ' ', '.'), 1)) từ @NothingsImpossible hoàn thành sau 1,5 phút. @hello_earth Giải pháp của bạn sẽ so sánh như thế nào trên các chuỗi dài hơn với hơn 4 trường?
wwmbes

32

Câu hỏi này không phảivề cách tiếp cận phân tách chuỗi , mà là về cách lấy phần tử thứ n .

Tất cả các câu trả lời ở đây đang làm một số loại tách chuỗi sử dụng đệ quy, CTEs, nhiều CHARINDEX, REVERSEPATINDEX, chức năng phát minh, kêu gọi các phương pháp CLR, bảng số, CROSS APPLYs ... Hầu hết các câu trả lời bao gồm nhiều dòng mã.

Nhưng - nếu bạn thực sự không muốn gì hơn là một cách tiếp cận để có được phần tử thứ n - thì điều này có thể được thực hiện như một lớp lót thực sự , không UDF, thậm chí không phải là một lựa chọn phụ ... Và như một lợi ích bổ sung: gõ an toàn

Nhận phần 2 giới hạn bởi một khoảng trắng:

DECLARE @input NVARCHAR(100)=N'part1 part2 part3';
SELECT CAST(N'<x>' + REPLACE(@input,N' ',N'</x><x>') + N'</x>' AS XML).value('/x[2]','nvarchar(max)')

Tất nhiên, bạn có thể sử dụng các biến cho dấu phân cách và vị trí (sử dụng sql:columnđể truy xuất vị trí trực tiếp từ giá trị của truy vấn):

DECLARE @dlmt NVARCHAR(10)=N' ';
DECLARE @pos INT = 2;
SELECT CAST(N'<x>' + REPLACE(@input,@dlmt,N'</x><x>') + N'</x>' AS XML).value('/x[sql:variable("@pos")][1]','nvarchar(max)')

Nếu chuỗi của bạn có thể bao gồm các ký tự bị cấm (đặc biệt là một trong số &><), bạn vẫn có thể làm theo cách này. Trước tiên, chỉ cần sử dụng FOR XML PATHchuỗi của bạn để thay thế tất cả các ký tự bị cấm bằng chuỗi thoát phù hợp.

Đó là một trường hợp rất đặc biệt nếu - ngoài ra - dấu phân cách của bạn là dấu chấm phẩy . Trong trường hợp này, trước tiên tôi thay thế dấu phân cách thành '# DLMT #' và cuối cùng thay thế nó thành các thẻ XML:

SET @input=N'Some <, > and &;Other äöü@€;One more';
SET @dlmt=N';';
SELECT CAST(N'<x>' + REPLACE((SELECT REPLACE(@input,@dlmt,'#DLMT#') AS [*] FOR XML PATH('')),N'#DLMT#',N'</x><x>') + N'</x>' AS XML).value('/x[sql:variable("@pos")][1]','nvarchar(max)');

CẬP NHẬT cho SQL-Server 2016+

Rất tiếc, các nhà phát triển đã quên trả lại chỉ mục của phần này STRING_SPLIT. Nhưng, sử dụng SQL-Server 2016+, có JSON_VALUEOPENJSON.

Với JSON_VALUEchúng ta có thể vượt qua ở vị trí như mảng của chỉ mục.

Đối với OPENJSONcác tài liệu nêu rõ:

Khi OPENJSON phân tích một mảng JSON, hàm sẽ trả về các chỉ mục của các thành phần trong văn bản JSON dưới dạng các khóa.

Một chuỗi như 1,2,3không cần gì nhiều hơn ngoặc : [1,2,3].
Một chuỗi các từ như this is an examplecần phải được ["this","is","an","example"].
Đây là những hoạt động chuỗi rất dễ dàng. Hãy thử nó:

DECLARE @str VARCHAR(100)='Hello John Smith';
DECLARE @position INT = 2;

--We can build the json-path '$[1]' using CONCAT
SELECT JSON_VALUE('["' + REPLACE(@str,' ','","') + '"]',CONCAT('$[',@position-1,']'));

- Xem cái này cho bộ tách chuỗi an toàn vị trí ( dựa trên zero ):

SELECT  JsonArray.[key] AS [Position]
       ,JsonArray.[value] AS [Part]
FROM OPENJSON('["' + REPLACE(@str,' ','","') + '"]') JsonArray

Trong bài viết này tôi đã thử nghiệm các cách tiếp cận khác nhau và tìm thấy, điều đó OPENJSONthực sự nhanh chóng. Thậm chí nhanh hơn nhiều so với phương thức "deliatedSplit8k ()" nổi tiếng ...

CẬP NHẬT 2 - Nhận các giá trị loại an toàn

Chúng ta có thể sử dụng một mảng trong một mảng chỉ bằng cách sử dụng gấp đôi [[]]. Điều này cho phép gõ WITH-clided:

DECLARE  @SomeDelimitedString VARCHAR(100)='part1|1|20190920';

DECLARE @JsonArray NVARCHAR(MAX)=CONCAT('[["',REPLACE(@SomeDelimitedString,'|','","'),'"]]');

SELECT @SomeDelimitedString          AS TheOriginal
      ,@JsonArray                    AS TransformedToJSON
      ,ValuesFromTheArray.*
FROM OPENJSON(@JsonArray)
WITH(TheFirstFragment  VARCHAR(100) '$[0]'
    ,TheSecondFragment INT          '$[1]'
    ,TheThirdFragment  DATE         '$[2]') ValuesFromTheArray

Re: nếu chuỗi của bạn có thể bao gồm các ký tự bị cấm ... bạn chỉ có thể bọc các chuỗi con như vậy <x><![CDATA[x<&>x]]></x>.
Salman A

@SalmanA, yeah, CDATAphần cũng có thể đối phó với điều này ... Nhưng sau khi các diễn viên họ biến mất (thay đổi thành thoát text()hoàn toàn). Tôi không thích phép thuật dưới mui xe , vì vậy tôi thích (SELECT 'Text with <&>' AS [*] FOR XML PATH(''))cách tiếp cận -. Điều này có vẻ sạch hơn đối với tôi và dù sao cũng xảy ra ... (Một số thông tin khác về CDATA và XML ).
Shnugo

22

Đây là một UDF sẽ làm điều đó. Nó sẽ trả về một bảng các giá trị được phân tách, chưa thử tất cả các kịch bản trên đó nhưng ví dụ của bạn hoạt động tốt.


CREATE FUNCTION SplitString 
(
    -- Add the parameters for the function here
    @myString varchar(500),
    @deliminator varchar(10)
)
RETURNS 
@ReturnTable TABLE 
(
    -- Add the column definitions for the TABLE variable here
    [id] [int] IDENTITY(1,1) NOT NULL,
    [part] [varchar](50) NULL
)
AS
BEGIN
        Declare @iSpaces int
        Declare @part varchar(50)

        --initialize spaces
        Select @iSpaces = charindex(@deliminator,@myString,0)
        While @iSpaces > 0

        Begin
            Select @part = substring(@myString,0,charindex(@deliminator,@myString,0))

            Insert Into @ReturnTable(part)
            Select @part

    Select @myString = substring(@mystring,charindex(@deliminator,@myString,0)+ len(@deliminator),len(@myString) - charindex(' ',@myString,0))


            Select @iSpaces = charindex(@deliminator,@myString,0)
        end

        If len(@myString) > 0
            Insert Into @ReturnTable
            Select @myString

    RETURN 
END
GO

Bạn sẽ gọi nó như thế này:


Select * From SplitString('Hello John Smith',' ')

Chỉnh sửa: Giải pháp cập nhật để xử lý các dấu phân cách với len> 1 như trong:


select * From SplitString('Hello**John**Smith','**')

Không hoạt động để chọn * từ dbo.ethos_SplitString_fn ('chàng, bấc, đã ở đây', ',') phần id ----------- ------------ -------------------------------------- 1 chàng trai 2 bấc
Chàng trai

2
coi chừng với len () vì nó sẽ không trả về số chính xác nếu đối số của nó có dấu cách., ví dụ len ('-') = 2.
Rory

Không hoạt động trên: select * từ dbo.SplitString ('foo, foo test ,,,, foo', ',')
cbp

1
Khắc phục sự cố cho cbp .. Chọn @myString = chuỗi con (@ mystring, @ iSpaces + len (@d006inator), len (@myString) - charindex (@ deliminator, @ myString, 0))
Alxwest

16

Ở đây tôi đăng một cách đơn giản của giải pháp

CREATE FUNCTION [dbo].[split](
          @delimited NVARCHAR(MAX),
          @delimiter NVARCHAR(100)
        ) RETURNS @t TABLE (id INT IDENTITY(1,1), 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


Thực hiện chức năng như thế này

  select * from dbo.split('Hello John Smith',' ')

Tôi thích giải pháp này. Mở rộng nó để trả về giá trị vô hướng dựa trên cột được chỉ định trong kết quả.
Alan

Tôi đã bị đốt cháy với một '&' trong chuỗi được phân tách bằng cách này
KeithL

10

Theo ý kiến ​​của tôi, các bạn đang làm cho nó quá phức tạp. Chỉ cần tạo một UDF CLR và được thực hiện với nó.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;

public partial class UserDefinedFunctions {
  [SqlFunction]
  public static SqlString SearchString(string Search) {
    List<string> SearchWords = new List<string>();
    foreach (string s in Search.Split(new char[] { ' ' })) {
      if (!s.ToLower().Equals("or") && !s.ToLower().Equals("and")) {
        SearchWords.Add(s);
      }
    }

    return new SqlString(string.Join(" OR ", SearchWords.ToArray()));
  }
};

20
Tôi đoán điều này quá phức tạp, vì tôi cần phải có Visual Studio, sau đó kích hoạt CLR trên máy chủ, sau đó tạo và biên dịch dự án, và cuối cùng thêm các tập hợp vào cơ sở dữ liệu, để sử dụng nó. Nhưng vẫn là một câu trả lời thú vị.
Guillermo Gutiérrez

3
@ guillegr123, nó không phải phức tạp. Bạn chỉ có thể tải xuống và cài đặt (miễn phí!), SQL #, là thư viện các hàm và procs của SQLCLR. Bạn có thể lấy nó từ SQLsharp.com . Có, tôi là tác giả nhưng String_Split được bao gồm trong phiên bản Miễn phí.
Solomon Rutzky

10

Điều gì về việc sử dụng stringvalues()tuyên bố?

DECLARE @str varchar(max)
SET @str = 'Hello John Smith'

DECLARE @separator varchar(max)
SET @separator = ' '

DECLARE @Splited TABLE(id int IDENTITY(1,1), item varchar(max))

SET @str = REPLACE(@str, @separator, '''),(''')
SET @str = 'SELECT * FROM (VALUES(''' + @str + ''')) AS V(A)' 

INSERT INTO @Splited
EXEC(@str)

SELECT * FROM @Splited

Kết quả đạt được.

id  item
1   Hello
2   John
3   Smith

1
Tôi đã sử dụng câu trả lời của bạn nhưng không hiệu quả, nhưng tôi đã sửa đổi và điều này đã làm việc với tất cả công đoàn, tôi đang sử dụng sql 2005
thiên thần

9

Tôi sử dụng câu trả lời của frederic nhưng điều này không hoạt động trong SQL Server 2005

Tôi sửa đổi nó và tôi đang sử dụng selectvới union allvà nó hoạt động

DECLARE @str varchar(max)
SET @str = 'Hello John Smith how are you'

DECLARE @separator varchar(max)
SET @separator = ' '

DECLARE @Splited table(id int IDENTITY(1,1), item varchar(max))

SET @str = REPLACE(@str, @separator, ''' UNION ALL SELECT ''')
SET @str = ' SELECT  ''' + @str + '''  ' 

INSERT INTO @Splited
EXEC(@str)

SELECT * FROM @Splited

Và tập kết quả là:

id  item
1   Hello
2   John
3   Smith
4   how
5   are
6   you

Điều này thực sự tuyệt vời tôi từng thấy trong công cụ sql, nó hoạt động cho công việc của tôi và tôi đánh giá cao điều đó, cảm ơn!
Abdurrahman I.

Tôi đã thực sự phấn khích khi thấy điều này bởi vì nó trông rất sạch sẽ và dễ hiểu, nhưng tiếc là bạn không thể đặt nó trong UDF vì EXEC. EXECngầm gọi một thủ tục được lưu trữ và bạn không thể sử dụng các thủ tục được lưu trữ trong UDF.
Võng Kristen

Điều này hoạt động hoàn hảo !! tôi đã xem xét việc sử dụng một hàm (SplitStrings_Moden) từ đây: sqlperformance.com/2012/07/t-sql-queries/split-strings#comments làm điều này và phải mất một phút rưỡi để phân tách dữ liệu và trả về các hàng khi chỉ sử dụng 4 số tài khoản. Tôi đã thử nghiệm phiên bản của bạn với một liên kết bên trái trên bàn với dữ liệu về số tài khoản và mất khoảng 2 hoặc 3 giây! Sự khác biệt lớn và hoạt động hoàn hảo! Tôi sẽ cho 20 phiếu này nếu có thể!
MattE

8

Mẫu này hoạt động tốt và bạn có thể khái quát

Convert(xml,'<n>'+Replace(FIELD,'.','</n><n>')+'</n>').value('(/n[INDEX])','TYPE')
                          ^^^^^                                   ^^^^^     ^^^^

lưu ý FIELD , INDEXTYPE .

Hãy để một số bảng với định danh như

sys.message.1234.warning.A45
sys.message.1235.error.O98
....

Sau đó, bạn có thể viết

SELECT Source         = q.value('(/n[1])', 'varchar(10)'),
       RecordType     = q.value('(/n[2])', 'varchar(20)'),
       RecordNumber   = q.value('(/n[3])', 'int'),
       Status         = q.value('(/n[4])', 'varchar(5)')
FROM   (
         SELECT   q = Convert(xml,'<n>'+Replace(fieldName,'.','</n><n>')+'</n>')
         FROM     some_TABLE
       ) Q

tách và đúc tất cả các bộ phận.


Đây là giải pháp duy nhất ở đây cho phép bạn truyền tới các loại cụ thể và có hiệu quả vừa phải (CLR vẫn hiệu quả nhất, nhưng cách tiếp cận này xử lý một bảng hàng 8gb, 10 token, 10M trong khoảng 9 phút (máy chủ aws m3, 4k iops ổ đĩa được cung cấp)
Andrew Hill

7

Nếu cơ sở dữ liệu của bạn có mức độ tương thích từ 130 trở lên thì bạn có thể sử dụng hàm STRINGinksLIT cùng với các mệnh đề OFFSET FETCH để lấy mục cụ thể theo chỉ mục.

Để lấy mục tại chỉ mục N (không dựa trên), bạn có thể sử dụng mã sau

SELECT value
FROM STRING_SPLIT('Hello John Smith',' ')
ORDER BY (SELECT NULL)
OFFSET N ROWS
FETCH NEXT 1 ROWS ONLY

Để kiểm tra mức độ tương thích của cơ sở dữ liệu của bạn , hãy thực thi mã này:

SELECT compatibility_level  
FROM sys.databases WHERE name = 'YourDBName';

Thủ thuật nằm trong OFFSET 1 ROWS, sẽ bỏ qua mục đầu tiên và sẽ trả về mục thứ hai. Nếu các chỉ mục của bạn dựa trên 0 và @X là biến giữ chỉ mục vật phẩm bạn muốn tìm nạp, bạn có thể chắc chắn làm OFFSET @X ROWS
Gorgi Rankovski

Được rồi, đã không sử dụng điều này trước đây ... Rất vui được biết ... Tôi vẫn thích xmlcách tiếp cận dựa trên -split, vì nó cho phép tìm nạp loại giá trị an toàn và không cần truy vấn phụ, nhưng đây là một truy vấn phụ tốt một. +1 từ phía tôi
Shnugo

3
vấn đề ở đây là STRINGinksLIT không đảm bảo thứ tự của các kết quả được trả về. Vì vậy, mục 1 của bạn có thể hoặc không thể là mục của tôi 1.
user1443098

@GorgiRankovski, Sử dụng STRING_SPLIT nhu cầu cho v2016 +. Trong trường hợp này tốt hơn là sử dụng OPENJSONhoặc JSON_VALUE. Bạn có thể muốn kiểm tra câu trả lời của tôi
Shnugo

6

Tôi đã tìm kiếm giải pháp trên mạng và các công việc dưới đây cho tôi. Ref .

Và bạn gọi hàm như thế này:

SELECT * FROM dbo.split('ram shyam hari gopal',' ')

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[Split](@String VARCHAR(8000), @Delimiter CHAR(1))       
RETURNS @temptable TABLE (items VARCHAR(8000))       
AS       
BEGIN       
    DECLARE @idx INT       
    DECLARE @slice VARCHAR(8000)        
    SELECT @idx = 1       
    IF len(@String)<1 OR @String IS NULL  RETURN       
    WHILE @idx!= 0       
    BEGIN       
        SET @idx = charindex(@Delimiter,@String)       
        IF @idx!=0       
            SET @slice = LEFT(@String,@idx - 1)       
        ELSE       
            SET @slice = @String       
        IF(len(@slice)>0)  
            INSERT INTO @temptable(Items) VALUES(@slice)       
        SET @String = RIGHT(@String,len(@String) - @idx)       
        IF len(@String) = 0 break       
    END   
    RETURN       
END

Bạn không thể dễ dàng truy cập mục thứ N bằng chức năng này.
Bjorn Lindqvist

6

Một phần khác nhận được phần thứ nhất của chuỗi bằng hàm delimet:

create function GetStringPartByDelimeter (
    @value as nvarchar(max),
    @delimeter as nvarchar(max),
    @position as int
) returns NVARCHAR(MAX) 
AS BEGIN
    declare @startPos as int
    declare @endPos as int
    set @endPos = -1
    while (@position > 0 and @endPos != 0) begin
        set @startPos = @endPos + 1
        set @endPos = charindex(@delimeter, @value, @startPos)

        if(@position = 1) begin
            if(@endPos = 0)
                set @endPos = len(@value) + 1

            return substring(@value, @startPos, @endPos - @startPos)
        end

        set @position = @position - 1
    end

    return null
end

và cách sử dụng:

select dbo.GetStringPartByDelimeter ('a;b;c;d;e', ';', 3)

Trả về:

c

Tôi thích giải pháp này như là một tùy chọn để trả về một chuỗi con thay vì nhận một bảng được phân tích cú pháp mà sau đó bạn cần phải chọn. Sử dụng một kết quả bảng có công dụng của nó, nhưng với những gì tôi cần thì nó hoạt động hoàn hảo.
James H

5

Thử cái này:

CREATE function [SplitWordList]
(
 @list varchar(8000)
)
returns @t table 
(
 Word varchar(50) not null,
 Position int identity(1,1) not null
)
as begin
  declare 
    @pos int,
    @lpos int,
    @item varchar(100),
    @ignore varchar(100),
    @dl int,
    @a1 int,
    @a2 int,
    @z1 int,
    @z2 int,
    @n1 int,
    @n2 int,
    @c varchar(1),
    @a smallint
  select 
    @a1 = ascii('a'),
    @a2 = ascii('A'),
    @z1 = ascii('z'),
    @z2 = ascii('Z'),
    @n1 = ascii('0'),
    @n2 = ascii('9')
  set @ignore = '''"'
  set @pos = 1
  set @dl = datalength(@list)
  set @lpos = 1
  set @item = ''
  while (@pos <= @dl) begin
    set @c = substring(@list, @pos, 1)
    if (@ignore not like '%' + @c + '%') begin
      set @a = ascii(@c)
      if ((@a >= @a1) and (@a <= @z1))  
        or ((@a >= @a2) and (@a <= @z2))
        or ((@a >= @n1) and (@a <= @n2))
      begin
        set @item = @item + @c
      end else if (@item > '') begin
        insert into @t values (@item)
        set @item = ''
      end
    end 
    set @pos = @pos + 1
  end
  if (@item > '') begin
    insert into @t values (@item)
  end
  return
end

Kiểm tra nó như thế này:

select * from SplitWordList('Hello John Smith')

Tôi đã trải qua nó và nó hoàn toàn giống như những gì tôi muốn! thậm chí tôi cũng có thể tùy chỉnh nó để bỏ qua các ký tự đặc biệt mà tôi chọn!
Vikas

5

Ví dụ sau sử dụng CTE đệ quy

Cập nhật ngày 18 tháng 9 năm 2013

CREATE FUNCTION dbo.SplitStrings_CTE(@List nvarchar(max), @Delimiter nvarchar(1))
RETURNS @returns TABLE (val nvarchar(max), [level] int, PRIMARY KEY CLUSTERED([level]))
AS
BEGIN
;WITH cte AS
 (
  SELECT SUBSTRING(@List, 0, CHARINDEX(@Delimiter,  @List + @Delimiter)) AS val,
         CAST(STUFF(@List + @Delimiter, 1, CHARINDEX(@Delimiter, @List + @Delimiter), '') AS nvarchar(max)) AS stval, 
         1 AS [level]
  UNION ALL
  SELECT SUBSTRING(stval, 0, CHARINDEX(@Delimiter, stval)),
         CAST(STUFF(stval, 1, CHARINDEX(@Delimiter, stval), '') AS nvarchar(max)),
         [level] + 1
  FROM cte
  WHERE stval != ''
  )
  INSERT @returns
  SELECT REPLACE(val, ' ','' ) AS val, [level]
  FROM cte
  WHERE val > ''
  RETURN
END

Bản trình diễn trên SQLFiddle


2


    Alter Function dbo.fn_Split
    (
    @Expression nvarchar(max),
    @Delimiter  nvarchar(20) = ',',
    @Qualifier  char(1) = Null
    )
    RETURNS @Results TABLE (id int IDENTITY(1,1), value nvarchar(max))
    AS
    BEGIN
       /* USAGE
            Select * From dbo.fn_Split('apple pear grape banana orange honeydew cantalope 3 2 1 4', ' ', Null)
            Select * From dbo.fn_Split('1,abc,"Doe, John",4', ',', '"')
            Select * From dbo.fn_Split('Hello 0,"&""&&&&', ',', '"')
       */

       -- Declare Variables
       DECLARE
          @X     xml,
          @Temp  nvarchar(max),
          @Temp2 nvarchar(max),
          @Start int,
          @End   int

       -- HTML Encode @Expression
       Select @Expression = (Select @Expression For XML Path(''))

       -- Find all occurences of @Delimiter within @Qualifier and replace with |||***|||
       While PATINDEX('%' + @Qualifier + '%', @Expression) > 0 AND Len(IsNull(@Qualifier, '')) > 0
       BEGIN
          Select
             -- Starting character position of @Qualifier
             @Start = PATINDEX('%' + @Qualifier + '%', @Expression),
             -- @Expression starting at the @Start position
             @Temp = SubString(@Expression, @Start + 1, LEN(@Expression)-@Start+1),
             -- Next position of @Qualifier within @Expression
             @End = PATINDEX('%' + @Qualifier + '%', @Temp) - 1,
             -- The part of Expression found between the @Qualifiers
             @Temp2 = Case When @End < 0 Then @Temp Else Left(@Temp, @End) End,
             -- New @Expression
             @Expression = REPLACE(@Expression,
                                   @Qualifier + @Temp2 + Case When @End < 0 Then '' Else @Qualifier End,
                                   Replace(@Temp2, @Delimiter, '|||***|||')
                           )
       END

       -- Replace all occurences of @Delimiter within @Expression with '</fn_Split><fn_Split>'
       -- And convert it to XML so we can select from it
       SET
          @X = Cast('<fn_Split>' +
                    Replace(@Expression, @Delimiter, '</fn_Split><fn_Split>') +
                    '</fn_Split>' as xml)

       -- Insert into our returnable table replacing '|||***|||' back to @Delimiter
       INSERT @Results
       SELECT
          "Value" = LTRIM(RTrim(Replace(C.value('.', 'nvarchar(max)'), '|||***|||', @Delimiter)))
       FROM
          @X.nodes('fn_Split') as X(C)

       -- Return our temp table
       RETURN
    END

2

Bạn có thể tách một chuỗi trong SQL mà không cần một hàm:

DECLARE @bla varchar(MAX)
SET @bla = 'BED40DFC-F468-46DD-8017-00EF2FA3E4A4,64B59FC5-3F4D-4B0E-9A48-01F3D4F220B0,A611A108-97CA-42F3-A2E1-057165339719,E72D95EA-578F-45FC-88E5-075F66FD726C'

-- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes
SELECT 
    x.XmlCol.value('.', 'varchar(36)') AS val 
FROM 
(
    SELECT 
    CAST('<e>' + REPLACE(@bla, ',', '</e><e>') + '</e>' AS xml) AS RawXml
) AS b 
CROSS APPLY b.RawXml.nodes('e') x(XmlCol);

Nếu bạn cần hỗ trợ các chuỗi tùy ý (với các ký tự đặc biệt xml)

DECLARE @bla NVARCHAR(MAX)
SET @bla = '<html>unsafe & safe Utf8CharsDon''tGetEncoded ÄöÜ - "Conex"<html>,Barnes & Noble,abc,def,ghi'

-- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes
SELECT 
    x.XmlCol.value('.', 'nvarchar(MAX)') AS val 
FROM 
(
    SELECT 
    CAST('<e>' + REPLACE((SELECT @bla FOR XML PATH('')), ',', '</e><e>') + '</e>' AS xml) AS RawXml
) AS b 
CROSS APPLY b.RawXml.nodes('e') x(XmlCol); 

1

Tôi biết đó là một Câu hỏi cũ, nhưng tôi nghĩ ai đó có thể hưởng lợi từ giải pháp của tôi.

select 
SUBSTRING(column_name,1,CHARINDEX(' ',column_name,1)-1)
,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name))
    ,1
    ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)-1)
,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name))
    ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)+1
    ,LEN(column_name))
from table_name

FIDDLE SQL

Ưu điểm:

  • Nó phân tách tất cả 3 dấu phân cách chuỗi con bằng ''.
  • Không được sử dụng vòng lặp while, vì nó làm giảm hiệu suất.
  • Không cần Pivot vì tất cả chuỗi con kết quả sẽ được hiển thị trong một Hàng

Hạn chế:

  • Người ta phải biết tổng số không. của không gian (chuỗi con).

Lưu ý : giải pháp có thể cung cấp chuỗi con tối đa cho N.

Để vượt qua giới hạn chúng ta có thể sử dụng sau đây ref .

Nhưng một lần nữa giải pháp trên không thể được sử dụng trong một bảng (Actaully tôi không thể sử dụng nó).

Một lần nữa tôi hy vọng giải pháp này có thể giúp một số người.

Cập nhật: Trong trường hợp Bản ghi> 50000 không nên sử dụng LOOPSvì nó sẽ làm giảm Hiệu suất


1

Giải pháp dựa trên tập hợp thuần túy sử dụng TVFvới đệ quy CTE. Bạn có thể JOINAPPLYchức năng này cho bất kỳ tập dữ liệu.

create function [dbo].[SplitStringToResultSet] (@value varchar(max), @separator char(1))
returns table
as return
with r as (
    select value, cast(null as varchar(max)) [x], -1 [no] from (select rtrim(cast(@value as varchar(max))) [value]) as j
    union all
    select right(value, len(value)-case charindex(@separator, value) when 0 then len(value) else charindex(@separator, value) end) [value]
    , left(r.[value], case charindex(@separator, r.value) when 0 then len(r.value) else abs(charindex(@separator, r.[value])-1) end ) [x]
    , [no] + 1 [no]
    from r where value > '')

select ltrim(x) [value], [no] [index] from r where x is not null;
go

Sử dụng:

select *
from [dbo].[SplitStringToResultSet]('Hello John Smith', ' ')
where [index] = 1;

Kết quả:

value   index
-------------
John    1

1

Hầu như tất cả các câu trả lời khác đang thay thế chuỗi bị chia tách gây lãng phí chu kỳ CPU và thực hiện phân bổ bộ nhớ không cần thiết.

Tôi trình bày một cách tốt hơn nhiều để thực hiện phân tách chuỗi ở đây: http://www.digitalruby.com/split-opes-sql-server/

Đây là mã:

SET NOCOUNT ON

-- You will want to change nvarchar(MAX) to nvarchar(50), varchar(50) or whatever matches exactly with the string column you will be searching against
DECLARE @SplitStringTable TABLE (Value nvarchar(MAX) NOT NULL)
DECLARE @StringToSplit nvarchar(MAX) = 'your|string|to|split|here'
DECLARE @SplitEndPos int
DECLARE @SplitValue nvarchar(MAX)
DECLARE @SplitDelim nvarchar(1) = '|'
DECLARE @SplitStartPos int = 1

SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos)

WHILE @SplitEndPos > 0
BEGIN
    SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, (@SplitEndPos - @SplitStartPos))
    INSERT @SplitStringTable (Value) VALUES (@SplitValue)
    SET @SplitStartPos = @SplitEndPos + 1
    SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos)
END

SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, 2147483647)
INSERT @SplitStringTable (Value) VALUES(@SplitValue)

SET NOCOUNT OFF

-- You can select or join with the values in @SplitStringTable at this point.

0

Giải pháp CTE đệ quy với đau máy chủ, kiểm tra nó

Thiết lập lược đồ MS SQL Server 2008 :

create table Course( Courses varchar(100) );
insert into Course values ('Hello John Smith');

Truy vấn 1 :

with cte as
   ( select 
        left( Courses, charindex( ' ' , Courses) ) as a_l,
        cast( substring( Courses, 
                         charindex( ' ' , Courses) + 1 , 
                         len(Courses ) ) + ' ' 
              as varchar(100) )  as a_r,
        Courses as a,
        0 as n
     from Course t
    union all
      select 
        left(a_r, charindex( ' ' , a_r) ) as a_l,
        substring( a_r, charindex( ' ' , a_r) + 1 , len(a_R ) ) as a_r,
        cte.a,
        cte.n + 1 as n
    from Course t inner join cte 
         on t.Courses = cte.a and len( a_r ) > 0

   )
select a_l, n from cte
--where N = 1

Kết quả :

|    A_L | N |
|--------|---|
| Hello  | 0 |
|  John  | 1 |
| Smith  | 2 |

0

Mặc dù tương tự như câu trả lời dựa trên xml của josejuan, tôi thấy rằng việc xử lý đường dẫn xml chỉ một lần, sau đó xoay vòng hiệu quả hơn vừa phải:

select ID,
    [3] as PathProvidingID,
    [4] as PathProvider,
    [5] as ComponentProvidingID,
    [6] as ComponentProviding,
    [7] as InputRecievingID,
    [8] as InputRecieving,
    [9] as RowsPassed,
    [10] as InputRecieving2
    from
    (
    select id,message,d.* from sysssislog cross apply       ( 
          SELECT Item = y.i.value('(./text())[1]', 'varchar(200)'),
              row_number() over(order by y.i) as rn
          FROM 
          ( 
             SELECT x = CONVERT(XML, '<i>' + REPLACE(Message, ':', '</i><i>') + '</i>').query('.')
          ) AS a CROSS APPLY x.nodes('i') AS y(i)
       ) d
       WHERE event
       = 
       'OnPipelineRowsSent'
    ) as tokens 
    pivot 
    ( max(item) for [rn] in ([3],[4],[5],[6],[7],[8],[9],[10]) 
    ) as data

chạy trong 8:30

select id,
tokens.value('(/n[3])', 'varchar(100)')as PathProvidingID,
tokens.value('(/n[4])', 'varchar(100)') as PathProvider,
tokens.value('(/n[5])', 'varchar(100)') as ComponentProvidingID,
tokens.value('(/n[6])', 'varchar(100)') as ComponentProviding,
tokens.value('(/n[7])', 'varchar(100)') as InputRecievingID,
tokens.value('(/n[8])', 'varchar(100)') as InputRecieving,
tokens.value('(/n[9])', 'varchar(100)') as RowsPassed
 from
(
    select id, Convert(xml,'<n>'+Replace(message,'.','</n><n>')+'</n>') tokens
         from sysssislog 
       WHERE event
       = 
       'OnPipelineRowsSent'
    ) as data

chạy trong 9:20


0
CREATE FUNCTION [dbo].[fnSplitString] 
( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE(splitdata NVARCHAR(MAX) 
) 
BEGIN 
    DECLARE @start INT, @end INT 
    SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) 
    WHILE @start < LEN(@string) + 1 BEGIN 
        IF @end = 0  
            SET @end = LEN(@string) + 1

        INSERT INTO @output (splitdata)  
        VALUES(SUBSTRING(@string, @start, @end - @start)) 
        SET @start = @end + 1 
        SET @end = CHARINDEX(@delimiter, @string, @start)

    END 
    RETURN 
END

VÀ SỬ DỤNG CNTT

select *from dbo.fnSplitString('Querying SQL Server','')

0

nếu bất cứ ai muốn chỉ có một phần của văn bản tách biệt có thể sử dụng

chọn * từ fromSplitStringSep ('Word1 wordr2 word3', '')

CREATE function [dbo].[SplitStringSep] 
(
    @str nvarchar(4000), 
    @separator char(1)
)
returns table
AS
return (
    with tokens(p, a, b) AS (
        select 
        1, 
        1, 
        charindex(@separator, @str)
        union all
        select
            p + 1, 
            b + 1, 
            charindex(@separator, @str, b + 1)
        from tokens
        where b > 0
        )
        select
            p-1 zeroBasedOccurance,
            substring(
                @str, 
                a, 
                case when b > 0 then b-a ELSE 4000 end) 
            AS s
        from tokens
  )

0

Tôi đã phá hủy điều này,

declare @x nvarchar(Max) = 'ali.veli.deli.';
declare @item nvarchar(Max);
declare @splitter char='.';

while CHARINDEX(@splitter,@x) != 0
begin
    set @item = LEFT(@x,CHARINDEX(@splitter,@x))
    set @x    = RIGHT(@x,len(@x)-len(@item) )
     select @item as item, @x as x;
end

sự chú ý duy nhất bạn nên là dấu chấm '.' kết thúc của @x luôn luôn ở đó.


0

dựa trên giải pháp @NothingsImpossible, hoặc, đúng hơn, nhận xét về câu trả lời được bình chọn nhiều nhất (ngay dưới câu trả lời được chấp nhận), tôi đã tìm thấy cách nhanh chóng và bẩn thỉu sau đây giải pháp đáp ứng nhu cầu của riêng tôi - nó có lợi ích duy nhất trong miền SQL.

đưa ra một chuỗi "thứ nhất, thứ hai, thứ ba, thứ tư, thứ năm", giả sử, tôi muốn nhận được mã thông báo thứ ba. điều này chỉ hoạt động nếu chúng ta biết chuỗi sẽ có bao nhiêu mã thông báo - trong trường hợp này là 5. vì vậy cách hành động của tôi là cắt hai mã thông báo cuối cùng (truy vấn bên trong), và sau đó cắt hai mã thông báo đầu tiên đi ( truy vấn bên ngoài)

Tôi biết rằng điều này là xấu và bao gồm các điều kiện cụ thể tôi đã có, nhưng tôi đang đăng nó chỉ trong trường hợp ai đó thấy nó hữu ích. chúc mừng

select 
    REVERSE(
        SUBSTRING(
            reverse_substring, 
            0, 
            CHARINDEX(';', reverse_substring)
        )
    ) 
from 
(
    select 
        msg,
        SUBSTRING(
            REVERSE(msg), 
            CHARINDEX(
                ';', 
                REVERSE(msg), 
                CHARINDEX(
                    ';',
                    REVERSE(msg)
                )+1
            )+1,
            1000
        ) reverse_substring
    from 
    (
        select 'first;second;third;fourth;fifth' msg
    ) a
) b

điều này chỉ hoạt động nếu chúng ta biết chuỗi sẽ có bao nhiêu mã thông báo - giới hạn phá vỡ ...
Shnugo

0
declare @strng varchar(max)='hello john smith'
select (
    substring(
        @strng,
        charindex(' ', @strng) + 1,
        (
          (charindex(' ', @strng, charindex(' ', @strng) + 1))
          - charindex(' ',@strng)
        )
    ))

0

Bắt đầu với SQL Server 2016, chúng tôi string_split

DECLARE @string varchar(100) = 'Richard, Mike, Mark'

SELECT value FROM string_split(@string, ',')

Điều này tốt và tốt, nhưng nó không giải quyết được câu hỏi về kết quả thứ n.
Johnie Karr

STRING_SPLITkhông đảm bảo trả lại cùng một thứ tự. Nhưng OPENJSONcó (xem câu trả lời của tôi (phần cập nhật) )
Shnugo
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.