كيف يمكنني تقسيم سلسلة لذا يمكن الوصول إليها في البند العاشر ؟

StackOverflow https://stackoverflow.com/questions/2647

  •  08-06-2019
  •  | 
  •  

سؤال

باستخدام SQL Server, كيف يمكنني تقسيم سلسلة لذا يمكن الوصول إليها في البند العاشر ؟

اتخاذ سلسلة "مرحبا جون سميث".كيف يمكنني تقسيم سلسلة من الفضاء و الوصول إلى العنصر في الفهرس 1 الذي ينبغي أن عودة "جون"?

هل كانت مفيدة؟

المحلول

قد تجد الحل في SQL دالة معرفة من قبل المستخدم إلى تحليل سلسلة محددة مفيدة (من رمز المشروع).

يمكنك استخدام هذا المنطق البسيط:

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

نصائح أخرى

أنا لا أصدق SQL Server لديه المدمج في تقسيم وظيفة أخرى من UDF ، فقط إجابة أخرى أعرفه هو أن خطف PARSENAME وظيفة:

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

PARSENAME يأخذ سلسلة انشقاقات على فترة حرف.فإنه يأخذ عددا كما في الحجة الثانية ، أن عدد يحدد الجزء من سلسلة العودة (العمل من الخلف إلى الأمام).

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

مشكلة واضحة عندما السلسلة يحتوي بالفعل على فترة.ما زلت أعتقد باستخدام UDF هو أفضل وسيلة...أي اقتراحات أخرى ؟

أولا إنشاء وظيفة (باستخدام CTE, الجدول المشترك التعبير لا بعيدا مع الحاجة إلى الجدول المؤقت)

 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

ثم استخدامه مثل أي الجدول (أو تعديل لتناسب ضمن القائمة الخاصة بك المخزنة proc) مثل هذا.

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

التحديث

الإصدار السابق سوف تفشل لإدخال سلسلة أطول من 4000 حرف.هذا الإصدار يعتني الحصر:

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

الاستخدام لا يزال هو نفسه.

معظم الحلول هنا استخدام حين الحلقات أو متكررة CTEs.مجموعة القائم على النهج سوف تكون متفوقة ، أعدك:

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

أكثر على تقسيم الوظائف ، لماذا (والدليل على ذلك) في حين الحلقات التكرارية CTEs لا نطاق ، بدائل أفضل ، إذا كان تقسيم سلاسل قادمة من طبقة التطبيقات:

في SQL Server عام 2016 أو أعلاه ، رغم ذلك ، يجب أن ننظر في STRING_SPLIT() و STRING_AGG():

يمكنك الاستفادة من عدد طاولة للقيام سلسلة تحليل.

إنشاء المادية أرقام الجدول:

    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

إنشاء جدول الاختبار مع 1000000 الصفوف

    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

إنشاء وظيفة

    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

الاستخدام (النواتج 3mil الصفوف في 40s على جهاز الكمبيوتر المحمول)

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

تنظيف

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

الأداء هنا ليست مذهلة ، ولكن استدعاء دالة أكثر من مليون صف الجدول ليست أفضل فكرة.إذا كان أداء سلسلة الانقسام على العديد من الصفوف وأود أن تجنب وظيفة.

هذا السؤال هو لا حول سلسلة نهج تقسيم, ولكن عن كيفية الحصول على أقصى عنصر.

كل الإجابات هنا هي القيام ببعض النوع من سلسلة تقسيم استخدام العودية ، CTEs متعددة CHARINDEX, REVERSE و PATINDEX, واختراع وظائف الدعوة CLR طرق, عدد الجداول ، CROSS APPLYs ...معظم الإجابات تغطي العديد من الأسطر من التعليمات البرمجية.

ولكن إذا كنت حقا لا أريد شيئا أكثر من نهج للحصول على أقصى عنصر - ويمكن أن يتم ذلك كما واحد حقيقي-بطانة, لا UDF, ولا حتى حدد الفرعية...و كميزة اضافية: نوع آمنة

الحصول على 2 جزء محدد من الفضاء:

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

بالطبع يمكنك استخدام المتغيرات بالنسبة محدد و موقف (استخدام sql:column استرداد موضع مباشرة من استعلام القيمة):

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

إذا كانت السلسلة قد تشمل ممنوع الشخصيات (وخاصة بين &><) لا يمكنك أن تفعل ذلك بهذه الطريقة.مجرد استخدام FOR XML PATH على السلسلة الأولى ليحل محل كل ممنوع الشخصيات مع تركيب تسلسل الهروب ضمنا.

انها حالة خاصة جدا إذا كان - بالإضافة إلى ذلك - جهاز محدد الفاصلة المنقوطة.في هذه الحالة لا محل محدد الأولى إلى '#DLMT#', واستبدال هذه علامات 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)');

تحديث SQL Server 2016+

للأسف المطورين نسيت أن عودة جزء من مؤشر مع STRING_SPLIT.ولكن باستخدام SQL-Server 2016+ هناك OPENJSON.

على الوثائق بوضوح:

عندما OPENJSON يوزع سلمان مجموعة ، ترجع الدالة الفهارس العناصر في النص JSON المفاتيح.

مثل سلسلة 1,2,3 تحتاج إلى شيء أكثر من الأقواس: [1,2,3].
سلسلة من الكلمات مثل this is an example يحتاج إلى ["this","is","an"," example"].
هذه هي سهلة جدا سلسلة عمليات.مجرد محاولة ذلك:

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

SELECT [value]
FROM OPENJSON('["' + REPLACE(@str,' ','","') + '"]')
WHERE [key]=1 --zero-based!

هنا هو UDF التي سوف نفعل ذلك.فإنه سيعود جدول محدد القيم لم يحاكم كل السيناريوهات على ذلك ولكن على سبيل المثال الخاص بك يعمل بشكل جيد.


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

كنت أسميها مثل هذا:


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

تحرير:تحديث حل للتعامل مع delimters مع ليون>1 كما في :


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

هنا أنشر طريقة بسيطة الحل

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


وتنفيذ وظيفة مثل هذا

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

في رأيي أنتم مما يجعلها معقدة جدا.مجرد إنشاء CLR UDF وينبغي القيام به معها.

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()));
  }
};

ماذا عن استخدام string و values() البيان ؟

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

نتيجة مجموعة تحقيقها.

id  item
1   Hello
2   John
3   Smith

أنا استخدم الجواب فريدريك ولكن هذا لم ينجح في SQL Server 2005

أنا تعديله و أنا باستخدام select مع union all ويعمل

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

والنتيجة-مجموعة:

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

هذا النمط يعمل بشكل جيد و يمكنك التعميم

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

ملاحظة المجال, مؤشر و نوع.

السماح لبعض الجدول مع معرفات مثل

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

ثم يمكنك كتابة

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

تقسيم و صب كل أجزاء.

إذا كان لديك قاعدة بيانات التوافق مستوى 130 أو أعلى ثم يمكنك استخدام STRING_SPLIT وظيفة جنبا إلى جنب مع تعويض الجلب شروط الحصول على العنصر المحدد من قبل المؤشر.

للحصول على هذا البند في مؤشر N (صفر) ، يمكنك استخدام التعليمات البرمجية التالية

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

للتحقق من مستوى توافق قاعدة البيانات الخاصة بك, تنفيذ هذا الكود:

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

كنت أبحث عن الحل على الشبكة أدناه يعمل بالنسبة لي.المرجع.

و يمكنك استدعاء الدالة مثل هذا :

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

آخر الحصول على ن أخذ جزء من سلسلة من delimeter وظيفة:

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

و الاستخدام:

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

والتي ترجع:

c

جرب هذا:

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

اختبار مثل هذا:

select * from SplitWordList('Hello John Smith')

يستخدم المثال التالي العودية CTE

التحديث 18.09.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

التجريبي على SQLFiddle



    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

أنا أعرف أنها مسألة قديمة, ولكن أعتقد أن بعض واحد يمكن أن تستفيد من الحل.

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

SQL كمان

المزايا:

  • أنه يفصل كل 3 السلاسل الفرعية deliminator من قبل ' '.
  • يجب عدم استخدام حين حلقة ، كما أنه يقلل من الأداء.
  • لا تحتاج إلى محور كل الناتجة sub-string سيتم عرضها في صف واحد

القيود:

  • واحد يجب أن تعرف مجموعه لا.المساحات (sub-string).

ملاحظة:الحل يمكن أن تعطي sub-string يصل إلى N.

أن تغلبت الحد يمكننا استخدام التالية المرجع.

ولكن مرة أخرى فوق الحل لا يمكن استخدامها في الجدول (أكتاولي لم أكن قادرة على استخدامها).

مرة أخرى أتمنى أن هذا الحل يمكن أن تساعد بعض واحد.

تحديث: في حالة السجلات > 50000 ليس من المستحسن استخدام LOOPS كما أنها سوف تتحلل الأداء

تقريبا جميع إجابات أخرى تقسيم البرمجية استبدال سلسلة يتم تقسيم والتي النفايات دورات وحدة المعالجة المركزية وأداء الذاكرة لا لزوم لها مخصصات.

انا تغطية أفضل طريقة للقيام string split هنا: http://www.digitalruby.com/split-string-sql-server/

هنا هو رمز:

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.

يمكنك تقسيم سلسلة في SQL دون الحاجة إلى الدالة:

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

إذا كنت بحاجة إلى دعم التعسفي السلاسل (مع 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); 

مجموعة النقي القائم على الحل باستخدام TVF مع العودية CTE.يمكنك JOIN و APPLY هذه الوظيفة إلى أي بيانات.

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

الاستخدام:

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

النتيجة:

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

بدءا من SQL Server 2016 نحن string_split

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

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

النهج الحديث باستخدام STRING_SPLIT, يتطلب ملقم SQL 2016 وما فوق.

DECLARE @string varchar(100) = 'Hello John Smith'

SELECT
    ROW_NUMBER() OVER (ORDER BY value) AS RowNr,
    value
FROM string_split(@string, ' ')

النتيجة:

RowNr   value
1       Hello
2       John
3       Smith

فمن الممكن الآن الحصول على ال nth عنصر من رقم الصف.

هارون برتراند الجواب هو رائع ولكن معيبة.لا دقة التعامل مع الفضاء محدد (كما هو المثال في السؤال الأصلي) لأن طول وظيفة شرائح مسافات زائدة.

التالية هي رمز له ، مع تعديل صغير للسماح مساحة محدد:

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

هنا هي الدالة التي سيتم إنجاز السؤال المتمثل في تقسيم سلسلة والوصول إلى البند العاشر:

CREATE FUNCTION [dbo].[SplitString]
(
   @List       VARCHAR(MAX),
   @Delimiter  VARCHAR(255),
   @ElementNumber INT
)
RETURNS VARCHAR(MAX)
AS
BEGIN

       DECLARE @inp VARCHAR(MAX)
       SET @inp = (SELECT REPLACE(@List,@Delimiter,'_DELMTR_') FOR XML PATH(''))

       DECLARE @xml XML
       SET @xml = '<split><el>' + REPLACE(@inp,'_DELMTR_','</el><el>') + '</el></split>'

       DECLARE @ret VARCHAR(MAX)
       SET @ret = (SELECT
              el = split.el.value('.','varchar(max)')
       FROM  @xml.nodes('/split/el[string-length(.)>0][position() = sql:variable("@elementnumber")]') split(el))

       RETURN @ret

END

الاستخدام:

SELECT dbo.SplitString('Hello John Smith', ' ', 2)

النتيجة:

John

الحل بسيط لتحليل الاسم الأول والأخير

DECLARE @Name varchar(10) = 'John Smith'

-- Get First Name
SELECT SUBSTRING(@Name, 0, (SELECT CHARINDEX(' ', @Name)))

-- Get Last Name
SELECT SUBSTRING(@Name, (SELECT CHARINDEX(' ', @Name)) + 1, LEN(@Name))

في حالتي (و في العديد من الآخرين على ما يبدو...), لدي قائمة من الأسماء الأولى والأخيرة مفصولة مسافة واحدة.هذا يمكن استخدامها مباشرة داخل select إلى إعراب الاسم الأول والأخير.

-- i.e. Get First and Last Name from a table of Full Names
SELECT SUBSTRING(FullName, 0, (SELECT CHARINDEX(' ', FullName))) as FirstName,
SUBSTRING(FullName, (SELECT CHARINDEX(' ', FullName)) + 1, LEN(FullName)) as LastName,
From FullNameTable

وأنا أعلم به في وقت متأخر ، ولكن مؤخرا كان هذا الشرط جاء مع رمز أدناه.أنا لم يكن لديك خيار استخدام دالة معرفة من قبل المستخدم.ويساعد هذا الأمل.

SELECT 
    SUBSTRING(
                SUBSTRING('Hello John Smith' ,0,CHARINDEX(' ','Hello John Smith',CHARINDEX(' ','Hello John Smith')+1)
                        ),CHARINDEX(' ','Hello John Smith'),LEN('Hello John Smith')
            )

حسنا, لي ليس كل ما أبسط, ولكن هنا هو رمز يمكنني استخدام لتقسيم بفواصل إدخال متغير في القيم الفردية ، ووضعها في جدول متغير.أنا متأكد من أنك يمكن تعديل هذا قليلا إلى الانقسام على أساس مسافة ثم القيام الأساسي حدد الاستعلام ضد هذا الجدول متغير للحصول على النتائج الخاصة بك.

-- Create temporary table to parse the list of accounting cycles.
DECLARE @tblAccountingCycles table
(
    AccountingCycle varchar(10)
)

DECLARE @vchAccountingCycle varchar(10)
DECLARE @intPosition int

SET @vchAccountingCycleIDs = LTRIM(RTRIM(@vchAccountingCycleIDs)) + ','
SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)

IF REPLACE(@vchAccountingCycleIDs, ',', '') <> ''
BEGIN
    WHILE @intPosition > 0
    BEGIN
        SET @vchAccountingCycle = LTRIM(RTRIM(LEFT(@vchAccountingCycleIDs, @intPosition - 1)))
        IF @vchAccountingCycle <> ''
        BEGIN
            INSERT INTO @tblAccountingCycles (AccountingCycle) VALUES (@vchAccountingCycle)
        END
        SET @vchAccountingCycleIDs = RIGHT(@vchAccountingCycleIDs, LEN(@vchAccountingCycleIDs) - @intPosition)
        SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)
    END
END

هذا المفهوم هو الى حد كبير نفس.واحدة البديل الآخر هو الاستفادة .صافي التوافق داخل SQL Server 2005 نفسها.يمكنك أساسا تكتب لنفسك طريقة بسيطة في .الشبكة التي من شأنها أن تقسيم السلسلة ثم فضح هذا الإجراء المخزن/وظيفة.

هذا شيء فعلته من أجل الحصول على محدد المميز في سلسلة.(اختبار في MSSQL 2008)

أولا خلق الوظائف التالية:(وجدت في: هنا

CREATE FUNCTION dbo.SplitStrings_Moden
(
   @List NVARCHAR(MAX),
   @Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
  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),
       E2(N)        AS (SELECT 1 FROM E1 a, E1 b),
       E4(N)        AS (SELECT 1 FROM E2 a, E2 b),
       E42(N)       AS (SELECT 1 FROM E4 a, E2 b),
       cteTally(N)  AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(@List,1))) 
                         ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
       cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
                         WHERE (SUBSTRING(@List,t.N,1) = @Delimiter OR t.N = 0))
  SELECT Item = SUBSTRING(@List, s.N1, ISNULL(NULLIF(CHARINDEX(@Delimiter,@List,s.N1),0)-s.N1,8000))
    FROM cteStart s;

و

create FUNCTION dbo.getToken
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255),
@Pos int
)
RETURNS varchar(max)
as 
begin
declare @returnValue varchar(max);
select @returnValue = tbl.Item from (
select ROW_NUMBER() over (order by (select null)) as id, * from dbo.SplitStrings_Moden(@List, @Delimiter)
) as tbl
where tbl.id = @Pos
return @returnValue
end

ثم يمكنك استخدامه هكذا:

select dbo.getToken('1111_2222_3333_', '_', 1)

والتي العودة 1111

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top