मैं एक स्ट्रिंग कैसे विभाजित कर सकता हूं ताकि मैं आइटम x तक पहुंच पाऊं?


493

SQL सर्वर का उपयोग करते हुए, मैं एक स्ट्रिंग को कैसे विभाजित कर सकता हूं ताकि मैं आइटम x तक पहुंच पाऊं?

एक तार लें "हैलो जॉन स्मिथ"। मैं स्ट्रिंग को अंतरिक्ष से कैसे विभाजित कर सकता हूं और आइटम को इंडेक्स 1 पर एक्सेस कर सकता हूं जिसे "जॉन" वापस करना चाहिए?


3
देखें stackoverflow.com/questions/314824/... रूप में अच्छी तरह
जारोड डिक्सन

5
अंतर्निहित SQL सर्वर 2016 msdn.microsoft.com/en-us/library/mt684588.aspx
टिम एबेल

4
यहाँ सबसे अधिक उत्तर हैं - कम से कम मेरे लिए - काफी पुराने जमाने और आउट-डेटेड। प्रक्रियात्मक लोकिग, लूप, रिकर्सन, सीएलआर, फ़ंक्शंस, कोड की कई लाइनें ... अधिक अप-टू-डेट दृष्टिकोण खोजने के लिए "सक्रिय" उत्तरों को पढ़ना दिलचस्प हो सकता है ।
शन्नो

मैंने अधिक अप-टू-डेट दृष्टिकोण के साथ एक नया उत्तर जोड़ा है: stackoverflow.com/a/49669994/632604
Gorgi Rankovski

किसी सूची का nth तत्व प्राप्त करने का प्रयास करें -> portosql.wordpress.com/2019/05/27/enesimo-elemento-lista
José Diz

जवाबों:


191

आप 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

1
क्यों SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( RTRIM( LTRIM( @p_SourceText)))और क्या नहीं SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( @p_SourceText)?
बेथ

12
@GateKiller यह समाधान यूनिकोड का समर्थन नहीं करता है और यह हार्ड कोडेड न्यूमेरिक (18,3) का उपयोग करता है जो इसे एक व्यवहार्य "पुन: प्रयोज्य" फ़ंक्शन नहीं बनाता है।
फिलिप डी वोस

4
यह काम करता है लेकिन बहुत सारी मेमोरी और अपशिष्ट सीपीयू आवंटित करता है।
jjxtra

2
SQL सर्वर 2016 के रूप में, अब एक अंतर्निहित फ़ंक्शन है STRING_SPLITजो एक स्ट्रिंग को विभाजित करेगा और एक-स्तंभ तालिका परिणाम लौटाएगा जिसका उपयोग आप एक SELECTबयान या अन्य जगहों पर कर सकते हैं ।
qJake

उन लोगों के लिए बहुत बुरा है जिनके लिए मैं 2016 में काम कर रहा हूं। लेकिन, मैं इस बात को ध्यान में रखूंगा कि वे कभी भी अपने जूते से बाहर निकलते हैं। अंतरिम में महान समाधान। मैंने इसे एक फ़ंक्शन के रूप में लागू किया और एक तर्क के रूप में सीमांकक जोड़ा।
ब्रैंडन ग्रिफिन

355

मुझे विश्वास नहीं है कि SQL सर्वर में एक अंतर्निहित विभाजन फ़ंक्शन है, इसलिए UDF के अलावा, केवल एक अन्य उत्तर जो मुझे पता है कि PARSENAME फ़ंक्शन को हाईजैक करना है:

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

PARSENAME एक स्ट्रिंग लेता है और इसे अवधि वर्ण पर विभाजित करता है। यह एक संख्या को अपने दूसरे तर्क के रूप में लेता है, और यह संख्या निर्दिष्ट करती है कि स्ट्रिंग के किस सेगमेंट में वापस लौटना है (पीछे से आगे की ओर काम करना)।

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

स्पष्ट समस्या तब होती है जब स्ट्रिंग में पहले से ही एक अवधि होती है। मुझे अभी भी लगता है कि यूडीएफ का उपयोग करना सबसे अच्छा तरीका है ... कोई अन्य सुझाव?


102
धन्यवाद शाऊल ... मुझे कहना चाहिए कि यह समाधान वास्तव में वास्तविक विकास के लिए एक बुरा समाधान है। PARSENAME केवल चार भागों की अपेक्षा करता है, इसलिए चार भागों से अधिक के साथ स्ट्रिंग का उपयोग करने से यह NULL वापस लौटता है। यूडीएफ समाधान स्पष्ट रूप से बेहतर हैं।
नाथन बेडफोर्ड

33
यह एक महान हैक है, और मुझे रोता भी है कि कुछ ऐसा है जो वास्तविक भाषाओं में कुछ इतना सरल है।
फैक्टर मिस्टिक

36
अनुक्रमणिका को "सही" तरीके से काम करने के लिए, अर्थात् 1 से शुरू करके, मैंने आपके अपहरण को REVERSE: REVERSE (PARSENAME (REPLERS (REVERSE (हैलो हैलो जॉन स्मिथ)), '', '')) के साथ अपहृत किया है। , 1)) - नमस्कार
NothingsImpossible

3
@FactorMystic फर्स्ट नॉर्मल फॉर्म के लिए जरूरी है कि आप एक ही फील्ड में कई वैल्यूज न डालें। यह सचमुच RDBMS का पहला नियम है। किसी SPLIT()फ़ंक्शन को आपूर्ति नहीं की जाती है क्योंकि यह खराब डेटाबेस डिज़ाइन को प्रोत्साहित करता है, और इस प्रारूप में संग्रहीत डेटा का उपयोग करने के लिए डेटाबेस को कभी भी अनुकूलित नहीं किया जाएगा। आरडीबीएमएस डेवलपर्स को बेवकूफ चीजें करने में मदद करने के लिए बाध्य नहीं है जिन्हें इसे संभालने के लिए नहीं बनाया गया है। सही उत्तर हमेशा "आपके डेटाबेस को सामान्य करेगा जैसे हमने आपको 40 साल पहले बताया था।" खराब डिज़ाइन के लिए न तो SQL और न ही RDBMS को दोषी माना जाता है।
बेकन बिट्स

8
@BaconBits जब मैं सिद्धांत में सहमत हूं, तो इस तरह के अभ्यास उपकरण में उपयोगी होते हैं जब आपके द्वारा आए किसी व्यक्ति द्वारा निर्मित खराब डिजाइन को सामान्य करते हैं।
टिम अबेल

110

सबसे पहले, एक फ़ंक्शन बनाएं (सीटीई का उपयोग करके, सामान्य टेबल एक्सप्रेशन एक अस्थायी तालिका की आवश्यकता के साथ दूर होता है)

 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

फिर, इसे किसी भी तालिका के रूप में उपयोग करें (या इसे अपने मौजूदा संग्रहीत स्टोर के भीतर फिट करने के लिए संशोधित करें)।

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

उपयोग वही रहता है।


14
यह सुरुचिपूर्ण है लेकिन केवल 100 तत्वों के लिए काम करता है क्योंकि पुनरावृत्ति की गहराई की सीमा।
Pking

4
@Pking, नहीं, डिफ़ॉल्ट है 100(अनंत लूप को रोकने के लिए)। उपयोग MAXRECURSION संकेत प्रत्यावर्तन स्तरों की संख्या (परिभाषित करने के लिए 0करने के लिए 32767, 0कि "कोई सीमा नहीं" - सर्वर को कुचलने सकता है)। BTW, की तुलना में बेहतर जवाब है PARSENAME, क्योंकि यह सार्वभौमिक है :-)। +1
मिशैल पावगा

maxrecursionइस समाधान में जोड़ना इस प्रश्न और इसके उत्तरों को ध्यान में रखता है कि maxrecursionटेबल-वैल्यूएड-फंक्शन के अंदर सीटीई के लिए विकल्प कैसे सेटअप करें
मिशाल पोवागा

विशेष रूप से, क्रिस्पोल द्वारा उत्तर का संदर्भ - उसकी विधि इसे कुछ हद तक धीमा कर देती है, लेकिन अधिकांश अन्य विकल्पों की तुलना में सरल है।
हिगिंस

मामूली बिंदु लेकिन उपयोग समान नहीं रहता है क्योंकि आपने कॉलम का नाम बदल दिया है, इसलिए sअब परिभाषित नहीं है
टिम एबेल

62

यहाँ अधिकांश समाधान लूप या पुनरावर्ती CTEs का उपयोग करते हैं। एक सेट-आधारित दृष्टिकोण बेहतर होगा, मैं वादा करता हूं, यदि आप एक सीमांकक का उपयोग अंतरिक्ष के अलावा कर सकते हैं:

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

नमूना उपयोग:

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

परिणाम:

----
blat

आप idxफ़ंक्शन के लिए एक तर्क के रूप में भी आप चाहते हैं जोड़ सकते हैं, लेकिन मैं पाठक के लिए एक अभ्यास के रूप में छोड़ दूँगा।

आप के साथ ऐसा नहीं कर सकते सिर्फ देशी STRING_SPLITसमारोह एसक्यूएल सर्वर 2016 में कहा, कोई गारंटी नहीं कि उत्पादन मूल सूची के क्रम में रेंडर किया जाएगा क्योंकि वहाँ। दूसरे शब्दों में, यदि आप 3,6,1परिणाम में पास होते हैं तो संभवतः उसी क्रम में होंगे, लेकिन यह हो सकता है 1,3,6। मैंने यहाँ अंतर्निहित कार्य को बेहतर बनाने में समुदाय की सहायता के लिए कहा है:

पर्याप्त गुणात्मक प्रतिक्रिया के साथ, वे वास्तव में इनमें से कुछ को बढ़ाने पर विचार कर सकते हैं:

विभाजन कार्यों पर अधिक, क्यों (और सबूत है कि) जबकि लूप और पुनरावर्ती CTEs स्केल नहीं करते हैं, और बेहतर विकल्प, यदि आवेदन परत से आने वाले तारों को विभाजित करते हैं:

SQL सर्वर 2016 या इसके बाद के संस्करण पर, आपको देखना चाहिए STRING_SPLIT()और STRING_AGG():


1
सबसे अच्छा जवाब, IMHO। कुछ अन्य जवाबों में 100 की एसक्यूएल पुनरावृत्ति सीमा का मुद्दा है, लेकिन इस मामले में नहीं। बहुत तेज और बहुत सरल कार्यान्वयन। +2 बटन कहां है?
T-moty

5
मैंने उपयोग के साथ इस फ़ंक्शन को शब्दशः आज़माया: select * from DBO.SplitString('Hello John smith', ' ');और आउटपुट का उत्पादन किया गया: वैल्यू हैलो
एलो ल्लो

2
@AaronBertrand GateKiller द्वारा पोस्ट की गई मूल समस्या में एक अंतरिक्ष परिसीमन शामिल है।
12


1
@ मिचेल हां, यह सच है। यदि आपके पास ALTER SCHEMA की अनुमति नहीं है, तो भी आपके पास चयन करने के लिए कोई तालिका नहीं होगी, और यदि आपके पास चयन की अनुमति नहीं है तो आप इसमें से चयन नहीं कर पाएंगे। आप हमेशा किसी को आपके लिए फ़ंक्शन बनाने के लिए कह सकते हैं। । या इसे कहीं भी बना सकते हैं आप इसे बना सकते हैं (यहां तक ​​कि अस्थायी रूप से, टेम्पर्ड में कहते हैं)। और 2016+ पर आपको STRING_SPLIT () का उपयोग करना चाहिए, न कि किसी फ़ंक्शन को वैसे भी खुद बनाना होगा।
एरोन बर्ट्रेंड

38

स्ट्रिंग पार्सिंग करने के लिए आप संख्या तालिका का लाभ उठा सकते हैं।

एक भौतिक संख्या तालिका बनाएँ:

    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

उपयोग (मेरे लैपटॉप पर 40 के दशक में 3mil पंक्तियों को आउटपुट करता है)

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

साफ - सफाई

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

यहां प्रदर्शन आश्चर्यजनक नहीं है, लेकिन एक मिलियन रो टेबल पर एक फ़ंक्शन को कॉल करना सबसे अच्छा विचार नहीं है। यदि कई पंक्तियों पर एक स्ट्रिंग विभाजन का प्रदर्शन कर रहा हूं तो मैं फ़ंक्शन से बचूंगा।


2
सबसे अच्छा समाधान आईएमओ, दूसरों के पास किसी तरह की सीमा है .. यह तेज है और कई तत्वों के साथ लंबे तार को पार्स कर सकता है।
Pking

आप क्यों उतरते हुए एन ऑर्डर करते हैं? यदि वहाँ तीन आइटम हैं, और हमने 1 पर नंबर देना शुरू कर दिया है, तो पहला आइटम नंबर 3 होगा, और अंतिम नंबर 1 होगा। यदि इसे descहटा दिया गया तो क्या यह अधिक सहज परिणाम नहीं देगा ?
हैट्रिक -

1
सहमत, asc दिशा में अधिक सहज होगा। मैं परसेनमे () सम्मेलन का अनुसरण कर रहा था, जिसमें
डेस

3
यह कैसे महान होगा के रूप में कुछ स्पष्टीकरण
टिम एबेल

पार्स करने के लिए 3 फ़ील्ड्स की 100 मिलियन पंक्तियों पर एक परीक्षण में, ufn_ParseArray 25 मिनट के बाद समाप्त नहीं हुआ, जबकि REVERSE(PARSENAME(REPLACE(REVERSE('Hello John Smith'), ' ', '.'), 1)) @NothingsImpossible ने 1.5min में पूरा किया। @hello_earth 4 से अधिक क्षेत्रों के साथ लंबे तार पर आपके समाधान की तुलना कैसे होगी?
wwmbes

31

यह प्रश्न एक स्ट्रिंग विभाजन दृष्टिकोण के बारे में नहीं है , लेकिन एनटीएच तत्व को कैसे प्राप्त किया जाए , इसके बारे में ।

यहां सभी उत्तर पुनरावृत्ति, CTEएस, मल्टीपल CHARINDEX, REVERSEऔर PATINDEX, आविष्कार कार्यों का उपयोग करते हुए, सीएलआर विधियों के लिए कॉल, नंबर टेबल, CROSS APPLYएस ... के कुछ प्रकार के स्ट्रिंग विभाजन कर रहे हैं ... अधिकांश उत्तर कोड की कई लाइनों को कवर करते हैं।

लेकिन - अगर आप वास्तव में nth तत्व प्राप्त करने के लिए एक दृष्टिकोण से ज्यादा कुछ नहीं चाहते हैं - तो इसे वास्तविक वन-लाइनर के रूप में किया जा सकता है , कोई 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- सर्वर 2016+ के लिए अद्यतन

अफसोस है कि डेवलपर्स इस हिस्से के सूचकांक को वापस करना भूल गए STRING_SPLIT । लेकिन, SQL- सर्वर 2016+ का उपयोग करते हुए, वहाँ है JSON_VALUEऔर OPENJSON

साथ JSON_VALUEहम सूचकांक 'सरणी के रूप में स्थिति में पारित कर सकते हैं।

के लिए प्रलेखनOPENJSON स्पष्ट रूप से कहा गया है:

जब OPENJSON किसी JSON सरणी को पार्स करता है, तो फ़ंक्शन JSON पाठ में तत्वों की अनुक्रमणिकाओं को कुंजी के रूप में वापस करता है।

की तरह एक स्ट्रिंग 1,2,3कोष्ठक की तुलना में अधिक की जरूरत है कुछ भी नहीं: [1,2,3]
शब्दों की एक स्ट्रिंग की तरह this is an exampleहोना चाहिए ["this","is","an","example"]
ये बहुत आसान स्ट्रिंग ऑपरेशन हैं। बस इसे आज़माएं:

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

- इस स्थिति को सुरक्षित स्ट्रिंग-स्प्लिटर ( शून्य-आधारित ) के लिए देखें:

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

में इस पोस्ट मैं विभिन्न दृष्टिकोण का परीक्षण किया और पाया कि, OPENJSONहै वास्तव में तेजी से। यहां तक ​​कि प्रसिद्ध "सीमांकित सीप्लेट 8k ()" विधि की तुलना में बहुत तेज ...

अद्यतन 2 - मान टाइप-सुरक्षित प्राप्त करें

हम किसी सरणी के भीतर केवल दोगुने का उपयोग करके किसी सरणी का उपयोग कर सकते हैं [[]]। यह एक टाइप WITH-क्लॉज के लिए अनुमति देता है :

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

पुन: यदि आपके स्ट्रिंग में निषिद्ध वर्ण शामिल हो सकते हैं ... तो आप बस इस तरह सबस्ट्रिंग लपेट सकते हैं <x><![CDATA[x<&>x]]></x>
सलमान ए

@ सलमान, हाँ, -सुविधाएँ CDATAइससे भी निपट सकती हैं ... लेकिन कलाकारों के जाने के बाद (बदले में बचकर निकले text())। मुझे हुड के नीचे जादू पसंद नहीं है , इसलिए मैं (SELECT 'Text with <&>' AS [*] FOR XML PATH(''))- दृष्टिकोण को पसंद करूंगा । यह मुझे साफ दिखता है और वैसे भी होता है ... (कुछ और सीडीएटीए और एक्सएमएल के बारे में )।
शन्नो

22

यहां एक यूडीएफ है जो इसे करेगा। यह सीमांकित मानों की एक तालिका लौटाएगा, इस पर सभी परिदृश्यों की कोशिश नहीं की है, लेकिन आपका उदाहरण ठीक काम करता है।


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

संपादित करें: एक लीन> 1 के साथ परिसीमा को संभालने के लिए अद्यतित समाधान:


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

Dbo.ethos_SplitString_fn ('आदमी, विक्स, यहाँ था', '), आईडी भाग ----------- ------------ से चयन के लिए काम नहीं किया -------------------------------------- १ लड़का २ बाती
गाइ

2
लेन के साथ बाहर देखें () क्योंकि यह सही संख्या पर वापस नहीं आएगा यदि इसके तर्क में रिक्त स्थान है।, उदाहरण के लिए लेन ('-') = 2.
रोरी

पर काम नहीं करता है: dbo.SplitString से चुनें * ('फू, फू परीक्षण ,,, फू', ',')
cbp

1
Cbp के लिए ठीक करें .. @myString = substring (@ मिस्ट्रिंग, @ iSpaces + len (@deliminator), len (@myString) - charindex (@ deliminator, @ myString, 0) का चयन करें
Alxwest

16

यहां मैं समाधान का एक सरल तरीका पोस्ट करता हूं

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

मुझे यह समाधान पसंद आया। परिणामों के भीतर निर्दिष्ट कॉलम के आधार पर एक स्केलर मान वापस करने के लिए इसे विस्तारित किया।
एलन

मैं स्ट्रिंग में एक 'और' के साथ जल गया, इसका उपयोग करके विभाजित किया गया
कीथल

10

मेरी राय में आप लोग इसे बहुत जटिल बना रहे हैं। बस एक सीएलआर यूडीएफ बनाएं और उसके साथ किया जाए।

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
मुझे लगता है कि यह बहुत अधिक जटिल है, क्योंकि मुझे विजुअल स्टूडियो की आवश्यकता है, फिर सर्वर पर सीएलआर को सक्षम करें, फिर प्रोजेक्ट बनाएं और संकलित करें, और अंत में इसका उपयोग करने के लिए, विधानसभाओं को डेटाबेस में जोड़ें। लेकिन फिर भी एक दिलचस्प जवाब है।
गुइलर्मो गुतिरेज

3
@ guillegr123, यह जटिल नहीं है। आप बस (मुफ्त में!), SQL # डाउनलोड और इंस्टॉल कर सकते हैं, जो SQLCLR फ़ंक्शंस और प्रोक्स की लाइब्रेरी है। आप इसे SQLsharp.com से प्राप्त कर सकते हैं । हां, मैं लेखक हूं लेकिन String_Split नि: शुल्क संस्करण में शामिल है।
सोलोमन रुट्ज़की

10

का उपयोग करने 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

1
मैंने आपके उत्तर का उपयोग किया लेकिन काम नहीं किया, लेकिन मैंने संशोधित किया और इसने सभी के साथ काम किया, मैं sql 2005 का उपयोग कर रहा हूं
परी

9

मैं फ़्रेडरिक के उत्तर का उपयोग करता हूं लेकिन यह SQL सर्वर 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

यह वास्तव में बहुत अच्छा है मैंने कभी एसक्यूएल सामान में देखा है, यह मेरी नौकरी के लिए काम करता है और मैं इसकी सराहना करता हूं, धन्यवाद!
अब्दुर्रहमान I

मैं वास्तव में उत्साहित था जब मैंने इसे देखा क्योंकि यह इतना साफ और समझने में आसान लग रहा था, लेकिन दुर्भाग्य से आप इसे यूडीएफ के अंदर नहीं डाल सकते EXECEXECस्पष्ट रूप से एक संग्रहीत कार्यविधि को कॉल करता है, और आप UDFs में संग्रहीत कार्यविधियों का उपयोग नहीं कर सकते हैं।
क्रिस्टन हैमैक

यह पूरी तरह से काम करता है !! मैं यहाँ से एक फंक्शन (स्प्लिटस्ट्रीम_मोडेन) का उपयोग करना चाह रहा था: sqlperformance.com/2012/07/t-sql-queries/split-strings#comments जो ऐसा करता है और डेटा को विभाजित करने और वापस आने में एक मिनट का समय लग रहा था। केवल 4 खाता संख्याओं का उपयोग करते समय पंक्तियाँ। मैंने आपके संस्करण का परीक्षण खाता संख्याओं पर डेटा के साथ तालिका में एक बाईं ओर से किया और इसे 2 या 3 सेकंड की तरह लिया! विशाल अंतर और निर्दोष रूप से काम करता है! अगर संभव हो तो मैं इसे 20 वोट दूंगा!
मैट

8

यह पैटर्न ठीक काम करता है और आप सामान्यीकरण कर सकते हैं

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

नोट FIELD , 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

बंटवारे और सभी भागों कास्टिंग।


यह यहां एकमात्र समाधान है जो आपको विशिष्ट प्रकारों के लिए कास्ट करने की अनुमति देता है, और मध्यम रूप से कुशल है (सीएलआर अभी भी सबसे कुशल है, लेकिन यह दृष्टिकोण लगभग 9 मिनट (aws m3 सर्वर, 4k iops) में 8gb, 10 टोकन, 10M पंक्ति तालिका को संभालता है प्रावधानित ड्राइव)
एंड्रयू हिल

7

यदि आपके डेटाबेस में संगतता स्तर 130 या अधिक है तो आप OFFSET FETCH के साथ STRING_SPLIT फ़ंक्शन का उपयोग कर सकते हैं सूचकांक द्वारा विशिष्ट आइटम प्राप्त करने के लिए क्लॉज़ ।

इंडेक्स एन (शून्य आधारित) पर आइटम प्राप्त करने के लिए , आप निम्न कोड का उपयोग कर सकते हैं

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

चाल OFFSET 1 ROWS में है, जो पहले आइटम को छोड़ देगा और दूसरा आइटम वापस कर देगा। यदि आपके सूचकांक 0-आधारित हैं और @X वह चर है जिसे आप प्राप्त करना चाहते हैं, तो आइटम सूचकांक को पकड़ना है, तो आप निश्चित रूप से OFFSET @X ROWS
Gorgi Rankovski

ठीक है, इससे पहले इसका इस्तेमाल नहीं किया था ... यह जानकर अच्छा लगा ... मैं अभी भी xml-split आधारित दृष्टिकोण को पसंद करूंगा , क्योंकि यह मूल्य प्रकार-सुरक्षित लाने की अनुमति देता है और उप-क्वेरी की आवश्यकता नहीं है, लेकिन यह एक है अच्छा था। मेरी तरफ से +1
शन्नुगो

3
यहां समस्या यह है कि STRING_SPLIT लौटे परिणामों के आदेश की गारंटी नहीं देता है। तो आपका आइटम 1 मेरा आइटम 1 हो सकता है या नहीं भी हो सकता है
13:14 पर user1443098

@ GorgiRankovski, STRING_SPLITv2016 + की माँगों का उपयोग करते हुए । इस मामले में इसका उपयोग करना बेहतर है OPENJSONया JSON_VALUE। आप मेरे उत्तर की जाँच
Shnugo

6

मैं नेट पर समाधान की तलाश कर रहा था और मेरे लिए नीचे काम करता है। संदर्भ

और आप फ़ंक्शन को इस तरह कहते हैं:

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

आप इस फ़ंक्शन का उपयोग करके आसानी से Nth आइटम तक नहीं पहुंच सकते।
ब्योर्न लिंडक्विस्ट

6

अभी तक एक और स्ट्रिंग के n'th हिस्सा 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

मैं एक विकल्प के रूप में इस विकल्प को पसंद करता हूं कि एक पार्स की गई तालिका प्राप्त करने का विरोध करने के लिए एक एकल सबस्ट्रिंग को वापस करने के लिए जिसे आपको चयन करने की आवश्यकता है। तालिका परिणाम का उपयोग करने से इसके उपयोग होते हैं, लेकिन इसके लिए मुझे जो चाहिए वह पूरी तरह से काम करता है।
जेम्स एच।

5

इसे इस्तेमाल करे:

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

मैं इसके माध्यम से गया हूँ और यह पूरी तरह से जैसा मैं चाहता हूँ! यहां तक ​​कि मैं इसे विशेष वर्णों की अनदेखी करने के लिए भी अनुकूलित कर सकता हूं जो मैं चुनता हूं!
विकास

5

निम्न उदाहरण एक पुनरावर्ती 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 पर डेमो


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

आप फ़ंक्शन की आवश्यकता के बिना 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); 

1

मुझे पता है कि यह एक पुराना प्रश्न है, लेकिन मुझे लगता है कि कोई मेरे समाधान से लाभ उठा सकता है।

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 FIDDLE

लाभ:

  • यह '' द्वारा सभी 3 सब-स्ट्रिंग्स डेलिमिनेटर को अलग करता है।
  • लूप का उपयोग नहीं करना चाहिए, क्योंकि यह प्रदर्शन को कम करता है।
  • पिवट करने की कोई आवश्यकता नहीं है क्योंकि सभी परिणामी उप-स्ट्रिंग को एक पंक्ति में प्रदर्शित किया जाएगा

सीमाएं:

  • किसी को कुल पता होना चाहिए। रिक्त स्थान (उप-स्ट्रिंग)।

नोट : समाधान N को उप-स्ट्रिंग दे सकता है।

सीमा को पार करने के लिए हम निम्नलिखित रेफरी का उपयोग कर सकते हैं ।

लेकिन फिर से उपरोक्त समाधान एक तालिका में उपयोग नहीं किया जा सकता है (Actaully मैं इसका उपयोग करने में सक्षम नहीं था)।

फिर से मुझे उम्मीद है कि यह समाधान कुछ-कुछ मदद कर सकता है।

अपडेट: रिकॉर्ड्स> 50000 के मामले में इसका उपयोग करना उचित नहीं है LOOPSक्योंकि यह प्रदर्शन को नीचा दिखाएगा


1

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

1

लगभग सभी अन्य उत्तर स्ट्रिंग को विभाजित कर रहे हैं जो सीपीयू चक्रों को बर्बाद करता है और अनावश्यक मेमोरी आवंटन करता है।

मैं एक स्ट्रिंग विभाजन करने के लिए एक बेहतर तरीका कवर करता हूं: 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.

0

सर्वर दर्द के साथ पुनरावर्ती सीटीई समाधान, इसका परीक्षण करें

एमएस SQL ​​सर्वर 2008 स्कीमा सेटअप :

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

क्वेरी 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

परिणाम :

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

0

जबकि josejuan द्वारा xml आधारित उत्तर के समान, मैंने पाया कि केवल एक बार xml पथ को संसाधित करना, फिर धुरी को मध्यम रूप से अधिक कुशल बनाना था:

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

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

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

और इसका उपयोग करें

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

0

अगर कोई भी अलग पाठ का केवल एक हिस्सा प्राप्त करना चाहता है तो इसका उपयोग कर सकता है

चयन करें * 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

मैंने इस पर भरोसा किया,

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

एकमात्र ध्यान आपको डॉट होना चाहिए। ' @x का वह छोर हमेशा होना चाहिए।


0

@NothingsImpossible समाधान पर निर्माण, या, बल्कि, सबसे अधिक वोट किए गए उत्तर पर टिप्पणी करें (केवल एक स्वीकृत के नीचे), मैंने पाया कि निम्न त्वरित और गंदा समाधान मेरी खुद की जरूरतों को पूरा करता है - इसका केवल SQL डोमेन के भीतर होने का एक लाभ है।

एक तार दिया "पहला; दूसरा; तीसरा; चौथा; पांचवा", कहते हैं, मैं तीसरा टोकन प्राप्त करना चाहता हूं। यह तभी काम करता है जब हम जानते हैं कि स्ट्रिंग में कितने टोकन हैं - इस मामले में यह 5. है, इसलिए मेरी कार्रवाई का तरीका अंतिम दो टोकन दूर (आंतरिक क्वेरी) काटना है, और फिर पहले दो टोकन दूर करना है ( बाहरी प्रश्न)

मुझे पता है कि यह बदसूरत है और उन विशिष्ट स्थितियों को शामिल करता है जो मैं अंदर था, लेकिन इसे सिर्फ इसलिए पोस्ट कर रहा हूं जब कोई इसे उपयोगी पाता है। चियर्स

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

यह तभी काम करता है जब हम जानते हैं कि स्ट्रिंग में कितने टोकन हैं - एक
टूटी हुई

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

0

SQL सर्वर 2016 के साथ शुरू हम string_split

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

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

यह अच्छी तरह से और अच्छा है, लेकिन यह nth परिणाम प्राप्त करने के सवाल को संबोधित नहीं करता है।
जॉनी कर्र

STRING_SPLITउसी आदेश को वापस करने की गारंटी नहीं देता है। लेकिन OPENJSONकरता है (मेरा उत्तर (अपडेट अनुभाग देखें) )
शन्नू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.