X öğesine erişebilmek için bir dizeyi nasıl bölerim?


493

SQL Server'ı kullanarak, x öğesine erişebilmek için bir dizeyi nasıl bölebilirim?

"Merhaba John Smith" dizesini al. Dizeyi boşluğa nasıl bölerim ve dizin 1'deki "John" döndürmesi gereken öğeye nasıl erişebilirim?



5
sql server 2016 itibarıyla yerleşik msdn.microsoft.com/en-us/library/mt684588.aspx
Tim Abell

4
Buradaki en yüksek cevaplar - en azından benim için - oldukça eski moda ve oldukça eskimiş. Prosedürel yer, döngüler, özyineleme, CLR, fonksiyonlar, birçok kod satırı ... Daha güncel yaklaşımlar bulmak için "aktif" cevapları okumak ilginç olabilir .
Shnugo

Daha güncel bir yaklaşımla yeni bir cevap ekledim: stackoverflow.com/a/49669994/632604
Gorgi Rankovski

Deneyin Bir listenin n'inci elementini edinin -> portosql.wordpress.com/2019/05/27/enesimo-elemento-lista
José Diz

Yanıtlar:


191

Sınırlandırılmış bir dizeyi ayrıştırmak için SQL Kullanıcı Tanımlı İşlevinde ( Kod Projesi'nden ) çözümü bulabilirsiniz .

Bu basit mantığı kullanabilirsiniz:

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
neden SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( RTRIM( LTRIM( @p_SourceText)))olmasın SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( @p_SourceText)?
Beth

12
@GateKiller Bu çözüm Unicode'u desteklemez ve sabit "yeniden kullanılabilir" bir işlev yapmayan sabit kodlu sayısal (18,3) kullanır.
Filip De Vos

4
Bu çalışır, ancak çok fazla bellek ayırır ve CPU israfına neden olur.
jjxtra

2
SQL Server 2016'dan itibaren, artık bir STRING_SPLITdizeyi ayıracak ve bir sütunda kullanabileceğiniz tek sütunlu bir tablo sonucu döndürecek yerleşik bir işlev var.SELECT deyimde veya başka yerde .
qJake

Çalıştığım adamlar çok kötü 2016'da değil. Ama, ayakkabılarından çıkma ihtimalini göz önünde bulundurursam. Arada harika bir çözüm. Bir işlev olarak uyguladım ve argüman olarak ayırıcı ekledim.
Brandon Griffin

355

SQL Server yerleşik bir bölünmüş işlevi olduğuna inanmıyorum, bu yüzden bir UDF dışında, bildiğim tek cevap PARSENAME işlevini ele geçirmektir:

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

PARSENAME bir dize alır ve nokta karakterine böler. Bir sayıyı ikinci bağımsız değişkeni olarak alır ve bu sayı dizenin hangi segmentinin döndürüleceğini belirtir (arkadan öne doğru).

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

Açık sorun, dizenin zaten bir nokta içermesidir. Hala bir UDF kullanmanın en iyi yol olduğunu düşünüyorum ... başka herhangi bir öneriniz var mı?


102
Teşekkürler Saul ... Bu çözümün gerçek gelişim için gerçekten kötü bir çözüm olduğunu belirtmeliyim. PARSENAME yalnızca dört bölüm bekler, bu nedenle dört bölümden fazla bir dize kullanılması NULL döndürmesine neden olur. UDF çözümlerinin daha iyi olduğu açıktır.
Nathan Bedford

33
Bu büyük bir hack ve aynı zamanda gerçek dillerde çok basit bir şey için böyle bir şeyin gerekli olduğunu ağlatmamı sağlıyor.
Factor Mystic

36
Dizinlerin "doğru" şekilde çalışmasını sağlamak için, yani 1'den başlayarak, kaçırma işlemini TERS: REVERSE (PARSENAME (REPLACE (REVERSE ('Hello John Smith'), '', ') ile kaçırdım. , 1)) - Merhaba döndürür
Hiçbir şey

3
@FactorMystic İlk Normal Form , tek bir alana birden fazla değer eklememenizi gerektirir. Kelimenin tam anlamıyla bir RDBMS'nin ilk kuralı. Bir SPLIT()işlev sağlanmaz çünkü veritabanı tasarımı zayıftır ve veritabanı hiçbir zaman bu biçimde saklanan verileri kullanacak şekilde optimize edilmez. RDBMS, geliştiricilerin ele almaması için tasarlanan aptalca şeyler yapmalarına yardımcı olmakla yükümlü değildir . Doğru cevap her zaman "Veritabanınızı size 40 yıl önce söylediğimiz gibi normalleştirin" olacaktır . Ne SQL ne de RDBMS kötü tasarım için sorumlu değildir.
Bacon Bits

8
@BaconBits, teoride hemfikir olduğumda, bunun gibi araçlar, sizden önce gelen biri tarafından üretilen kötü bir tasarımı normalleştirirken faydalıdır.
Tim Abell

110

İlk olarak, bir işlev oluşturun (CTE kullanarak ortak tablo ifadesi geçici tabloya olan ihtiyacı ortadan kaldırır)

 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

Ardından, bunu herhangi bir tablo olarak kullanın (veya varolan depolanmış proc'unuza sığacak şekilde değiştirin).

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

Güncelleme

4000 karakterden uzun giriş dizesi için önceki sürüm başarısız olur. Bu sürüm sınırlamaya dikkat eder:

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

Kullanım aynı kalır.


14
Zarif ama özyineleme derinliği sınırı nedeniyle sadece 100 eleman için çalışıyor.
Pking

4
@Pking, hayır, varsayılan değerdir 100(sonsuz döngüyü önlemek için). Kullanım MAXRECURSION ipucu yineleme seviyesi sayısı (tanımlamak 0için 32767, 0"herhangi bir sınırlama" bir - server ezilme olabilir). BTW, çok daha iyi bir cevap PARSENAME, çünkü evrensel :-). +1
Michał Powaga

Ekleme maxrecursionbu çözüm akılda tutmak bu soru ve cevaplara Nasıl kurulur maxrecursionbir Tablo Değerli-Function içinde bir CTE seçenek .
Michał Powaga

Özellikle, Crisfole tarafından verilen cevaba referans verin - yöntemi biraz yavaşlar, ancak diğer seçeneklerden daha basittir.
AHiggins

küçük bir nokta, ancak sütun adını değiştirdiğiniz için kullanım aynı kalmıyor, bu yüzden sartık tanımlanmadı
Tim Abell

62

Buradaki çözümlerin çoğu döngüler veya özyinelemeli CTE'ler kullanırken kullanılır. Alan tabanlı bir sınırlayıcı kullanabiliyorsanız, set tabanlı bir yaklaşım daha üstün olacaktır, söz veriyorum:

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

Örnek kullanım:

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

Sonuçlar:

----
blat

Ayrıca idx, işlevi bağımsız değişken olarak istediğinizi ekleyebilirsiniz , ancak bunu okuyucuya bir alıştırma olarak bırakacağım.

Sen ile bunu yapamaz sadece yerli STRING_SPLITfonksiyonu çıkışı orijinal liste sırasına göre icra edileceğini garantisi yoktur, çünkü SQL Server 2016 yılında ekledi. Başka bir deyişle, başarılı olursa 3,6,1muhtemelen bu sırayla olacak sonuç, ancak olabilir olmak 1,3,6. Topluluğun yerleşik işlevi iyileştirme konusunda yardım istedim:

Yeterli niteliksel geribildirimle, aslında bu geliştirmelerden bazılarını yapmayı düşünebilirler:

Bölünmüş işlevler hakkında daha fazla bilgi için, döngüler ve özyinelemeli CTE'ler ölçeklenmezken neden (ve bunun kanıtı) ve uygulama katmanından gelen bölme dizeleri geliyorsa daha iyi alternatifler:

Yukarıdaki SQL Server 2016 veya üzerinde olsa da, size bakmak gerekir STRING_SPLIT()ve STRING_AGG():


1
En iyi cevap, IMHO. Diğer bazı cevaplarda SQL özyineleme sınırı 100'dür, ancak bu durumda yoktur. Çok hızlı ve çok basit bir uygulama. +2 düğmesi nerede?
T-moty

5
Bu işlevi kullanımı ile kelimesi kelimesine denedim: select * from DBO.SplitString('Hello John smith', ' ');ve üretilen çıktı: Değer Merhaba ello llo lo o John ohn hn n smith mith th h
wwmbes

2
@AaronBertrand GateKiller tarafından yayınlanan orijinal sorun bir boşluk sınırlayıcı içerir.
wwmbes

1
@ user1255933 Adreslendi.
Aaron Bertrand

1
@Michael Evet, bu doğru. Ayrıca ALTER SCHEMA iznine sahip olmasaydı seçmek için bir tablo olmazdı ve her zaman olabilir SEÇ izni yoksa ondan seçmek mümkün olmaz birine sormak sizin için işlevi oluşturmak için . Ya da oluşturabileceğiniz bir yerde oluşturun (geçici olarak bile, tempdb'de söyleyin). Ve 2016+ sürümünde kendinizi yine de yaratmanız gereken bir işlevi değil, STRING_SPLIT () kullanmalısınız.
Aaron Bertrand

38

Dize ayrıştırma işlemi yapmak için bir Sayı tablosundan yararlanabilirsiniz.

Fiziksel sayılar tablosu oluşturun:

    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 satırlı test tablosu oluşturma

    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

İşlevi oluştur

    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

Kullanım (dizüstü bilgisayarımda 40'larda 3mil satır çıktı)

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

Temizlemek

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

Buradaki performans şaşırtıcı değil, ancak bir milyon satırlık tablodan fazla bir işlevi çağırmak en iyi fikir değil. Birçok dizeye bölünmüş bir dize gerçekleştirirseniz, işlevi önlemek.


2
En iyi çözüm IMO, diğerleri bir tür sınırlama var .. Bu hızlı ve birçok unsuru ile uzun dizeleri ayrıştırabilir.
Pking

Niye azalan sipariş veriyorsunuz? Orada üç madde varsa ve 1 olarak numaralandırmaya başlamışsak, ilk öğe 3, sonuncusu 1 olacaktır. descKaldırılırsa daha sezgisel sonuçlar vermez mi ?
balta - SOverflow ile yapıldı

1
Anlaşıldı, yükseliş yönünde daha sezgisel olurdu. Desc - parsename () sözleşmesini takip ediyordum
Nathan Skerl

3
bunun nasıl işe yarayacağına dair bazı açıklamalar
Tim Abell

Ayrıştırılacak 3 alana kadar 100 milyon satırlık bir testte, ufn_ParseArray 25 dakika sonra bitmedi, @NothingsImpossible'den 1,5 dakika REVERSE(PARSENAME(REPLACE(REVERSE('Hello John Smith'), ' ', '.'), 1)) içinde tamamlandı. @hello_earth Çözümünüz 4'ten fazla alana sahip daha uzun dizelerle nasıl karşılaştırılır?
wwmbes

31

Bu soru dize bölünmüş bir yaklaşımla değil , n'inci öğenin nasıl alınacağıyla ilgilidir .

Buradaki tüm cevaplar özyineleme kullanarak dize bölünme çeşit yapıyoruz, CTEs, multipl CHARINDEX, REVERSEve PATINDEX, icat fonksiyonlar, CLR yöntemlerle, sayı tabloları, çağrı CROSS APPLYs ... En cevapları satır sayısı oldukça fazla kapsamaktadır.

Ancak - nth elementi elde etmek için gerçekten bir yaklaşımdan başka bir şey istemiyorsanız - bu gerçek bir astar , UDF yok, bir alt seçim bile değil ... Ve ekstra bir fayda olarak: güvenli yazın

Bölüm 2'yi boşlukla sınırlandırın:

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

Elbette , sınırlayıcı ve konum için değişkenleri kullanabilirsiniz ( sql:columnkonumu doğrudan bir sorgu değerinden almak için kullanı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)')

Dizeniz yasak karakterler (özellikle de bunlardan biri &><) içeriyorsa , bunu yine de yapabilirsiniz. FOR XML PATHTüm yasak karakterleri örtülü kaçış dizisiyle değiştirmek için önce dizenizi kullanın .

Ek olarak - sınırlayıcınız noktalı virgülse çok özel bir durumdur . Bu durumda sınırlayıcıyı önce '# DLMT #' olarak değiştiririm ve bunu sonunda XML etiketlerine değiştiririm:

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+ için GÜNCELLEME

Maalesef geliştiriciler parçanın dizinini geri getirmeyi unuttu STRING_SPLIT. Ancak, SQL-Server 2016+ kullanarak JSON_VALUEve vardır OPENJSON.

İle JSON_VALUEindeks dizisi olarak pozisyona geçebiliriz.

İçin dokümantasyon açıkça belirtmektedir:OPENJSON

OPENJSON bir JSON dizisini ayrıştırdığında, işlev JSON metnindeki öğelerin dizinlerini anahtar olarak döndürür.

Gibi bir dize 1,2,3daha parantez daha ihtiyaçları şey: [1,2,3].
Gibi bir kelime dizisi this is an exampleolmalı ["this","is","an","example"].
Bunlar çok kolay dize işlemleri. Sadece deneyin:

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

- Güvenli bir dize ayırıcı ( sıfır tabanlı ) için bunu görün:

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

Gelen bu yazı Çeşitli yaklaşımlar test edilmiş ve bu, buldum OPENJSONgerçekten hızlı. Ünlü "delimitedSplit8k ()" yönteminden bile çok daha hızlı ...

GÜNCELLEME 2 - Değerleri güvenli hale getirin

Bir dizinin içindeki bir diziyi yalnızca ikiye katlayarak kullanabiliriz [[]]. Bu, yazılı bir WITHcümleye izin verir :

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: dizeniz yasak karakterler içeriyorsa ... alt dizeleri bu şekilde silebilirsiniz <x><![CDATA[x<&>x]]></x>.
Salman A

@SalmanA, evet, CDATA-bölümler bununla da başa çıkabilir ... Ama oyuncu kadrosundan sonra gitti ( text()örtük olarak kaçtı ). Kaputun altındaki sihri sevmiyorum , bu yüzden (SELECT 'Text with <&>' AS [*] FOR XML PATH(''))- yaklaşımını tercih ederim . Bu bana daha temiz görünüyor ve yine de oluyor ... ( CDATA ve XML hakkında biraz daha ).
Shnugo

22

İşte bunu yapacak bir UDF. Sınırlandırılmış değerlerin bir tablosunu döndürür, üzerindeki tüm senaryoları denememiştir, ancak örneğiniz iyi çalışır.


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

Buna şöyle derdiniz:


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

Düzenleme: len> 1 ile ayırıcıları aşağıdaki gibi işlemek için güncellenmiş çözüm:


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

Dbo.ethos_SplitString_fn ('adam, fitiller, buradaydı', ',') id bölümünden select * için çalışmadı ----------- ------------ -------------------------------------- 1 erkek 2 fitil
Erkek

2
argümanı sondaki boşluklar varsa doğru sayı döndürmeyeceğinden len () ile dikkat edin., örneğin len ('-') = 2.
Rory

Çalışmıyor: dbo.SplitString (* foo, foo testi ,,,, foo ',', ')' den * seçin
cbp

1
Cbp için düzeltme .. Seç @myString = substring (@ mystring, @ iSpaces + len (@deliminator), len (@myString) - charindex (@ ayırıcı, @ myString, 0))
Alxwest

16

Burada basit bir çözüm yolu gönderiyorum

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


İşlevi böyle yürüt

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

Bu çözümü beğendim. Sonuçlarda belirtilen sütuna göre skaler bir değer döndürmek için genişletin.
Alan

Bunu kullanarak bölünmüş dize bir '&' ile yandı
KeithL

10

Bence siz bunu çok karmaşık hale getiriyorsunuz. Sadece bir CLR UDF oluşturun ve onunla bitirin.

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
Sanırım bu çok karmaşık, çünkü Visual Studio'ya sahip olmalıyım, sonra sunucuda CLR'yi etkinleştirmeliyim, sonra projeyi oluşturup derledim ve son olarak kullanmak için derlemeleri veritabanına ekleyin. Ama yine de ilginç bir cevap.
Guillermo Gutiérrez

3
@ guillegr123, karmaşık olması gerekmez. Sadece SQLCLR fonksiyonlarının ve proc'larının bir kütüphanesi olan SQL # 'ı (ücretsiz!) İndirip kurabilirsiniz. SQLsharp.com adresinden alabilirsiniz . Evet, ben yazarım ama String_Split Ücretsiz versiyona dahil edilmiştir.
Solomon Rutzky

10

Kullanmaya stringve values()açıklamaya ne dersiniz ?

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

Sonuç kümesi elde edildi.

id  item
1   Hello
2   John
3   Smith

1
Cevabını kullandım ama işe yaramadı, ama değiştirdim ve bu sendika ile çalıştı, sql 2005 kullanıyorum
melek

9

Frederic yanıt kullanın ama bu SQL Server 2005'te işe yaramadı

Değiştirdim ve selectbirlikte kullanıyorum union allve işe yarıyor

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

Ve sonuç kümesi:

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

Bu gerçekten sql şeyler gördüğüm harika, işim için çalıştı ve bunu takdir, teşekkürler!
Abdurrahman I.

Bunu gördüğümde çok heyecanlandım çünkü çok temiz ve anlaşılması kolay görünüyordu, ama ne yazık ki bunu bir UDF'nin içine koyamazsınız EXEC. EXECörtülü olarak saklı yordamı çağırır ve UDF'lerde saklı yordamları kullanamazsınız.
Kristen Hammack

Bu mükemmel çalışıyor !! Buradan bir işlev (SplitStrings_Moden) kullanarak bakıyordu: sqlperformance.com/2012/07/t-sql-queries/split-strings#comments bunu yapan ve veri bölmek ve geri almak için bir buçuk dakika sürüyordu yalnızca 4 hesap numarası kullanırken satırlar. Sürümünüzü tablodaki sol birleşimle hesap numaralarındaki verilerle test ettim ve 2 veya 3 saniye sürdü! Büyük fark ve kusursuz çalışıyor! Mümkünse 20 oy verirdim!
MattE

8

Bu desen iyi çalışıyor ve genelleştirebilirsiniz

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

not ALAN , İNDEKS ve TÜR .

Izin vermek gibi tanımlayıcıları ile bazı tablo izin

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

Sonra yazabilirsiniz

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üm parçaları bölme ve döküm.


Bu, belirli türlere yayın yapmanıza izin veren ve orta derecede verimli olan tek çözümdür (CLR hala en verimlidir, ancak bu yaklaşım yaklaşık 9 dakikada 8gb, 10 jeton, 10M satır tablosunu işler (aws m3 sunucusu, 4k iops provizyonlu sürüş)
Andrew Hill

7

Veritabanınız 130 veya daha yüksek uyumluluk düzeyine sahipse , belirli bir öğeyi dizine göre almak için STRING_SPLIT işlevini OFFSET FETCH yan tümceleriyle birlikte kullanabilirsiniz .

Öğeyi dizin N'ye (sıfır tabanlı) almak için aşağıdaki kodu kullanabilirsiniz

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

Veritabanınızın uyumluluk seviyesini kontrol etmek için şu kodu yürütün:

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

Hile, ilk öğeyi atlayacak ve ikinci öğeyi döndürecek OFSET 1 SATIRLAR'da. Dizinleriniz 0 tabanlıysa ve @X almak istediğiniz öğe dizinini tutan değişkense, OFFSET @X ROWS
Gorgi Rankovski

Tamam, bunu daha önce kullanmadı ... Bilmek güzel ... Hala xml-split tabanlı yaklaşımı tercih ederim , çünkü bu değer tipi-güvenli getirilmesini sağlar ve bir alt sorguya ihtiyaç duymaz, ancak bu bir İyi bir.
Yanımdan

3
buradaki sorun, STRING_SPLIT'in döndürülen sonuçların sırasını garanti etmemesidir. Bu nedenle, öğeniz 1,
öğem

@GorgiRankovski, STRING_SPLITv2016 + için talepleri kullanma . Bu durumda OPENJSONveya kullanmak çok daha iyidir JSON_VALUE. Cevabımı kontrol etmek
Shnugo

6

Ben net bir çözüm arıyordum ve aşağıdaki benim için çalışıyor. Ref .

Ve işlevi şöyle çağırırsınız:

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

Bu işlevi kullanarak N. Öğeye kolayca erişemezsiniz.
Björn Lindqvist

6

Sınırlayıcı işleviyle dizenin başka bir parçası olsun:

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

ve kullanımı:

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

döndüren:

c

Bu çözümü, daha sonra seçmeniz gereken ayrıştırılmış bir tablo almak yerine tek bir alt dize döndürmek için bir seçenek olarak seviyorum. Bir tablo sonucu kullanmanın faydaları vardır, ancak ihtiyacım olan şey için bu mükemmel çalıştı.
James H

5

Bunu dene:

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

Bu şekilde test edin:

select * from SplitWordList('Hello John Smith')

Ben geçtim ve mükemmel bir şekilde istediğim gibi! hatta seçtiğim özel karakterleri göz ardı etmek için özelleştirebilirim!
Vikas

5

Aşağıdaki örnek, özyinelemeli bir CTE kullanır

Güncelleme 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

Üzerinde Demosu 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'de bir işleve ihtiyaç duymadan bir dizeyi bölebilirsiniz:

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

Rasgele dizeleri (xml özel karakterleriyle) desteklemeniz gerekiyorsa

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

Bunun eski bir soru olduğunu biliyorum, ama bazılarının benim çözümümden faydalanabileceğini düşünüyorum.

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

Avantajları:

  • Tüm 3 alt dizgi sınırlayıcısını '' ile ayırır.
  • Performans döngüsünü azalttığı için while döngüsü kullanılmamalıdır.
  • Ortaya çıkan tüm alt dize bir Satırda görüntüleneceğinden Pivot eklemeye gerek yoktur

Sınırlamalar:

  • Kişi toplam no bilmeli. boşluk (alt dize).

Not : çözüm N'ye kadar alt dize verebilir.

Sınırlamanın üstesinden gelmek için aşağıdaki ref .

Ama yine de yukarıdaki çözüm bir tabloda kullanılamaz (Actaully i kullanamadım).

Yine umarım bu çözüm bazılarına yardımcı olabilir.

Güncelleme: Kayıtlar> 50000 olması durumunda , Performansı düşüreceği için kullanılması önerilmezLOOPS


1

TVFÖzyinelemeli saf set tabanlı çözüm CTE. Bunu JOINve APPLYherhangi bir veri kümesine bu işlevi yapabilirsiniz .

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

Kullanımı:

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

Sonuç:

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

1

Diğer tüm cevaplar, CPU döngülerini harcayan ve gereksiz bellek ayırma işlemleri gerçekleştiren bölünmüş dizenin yerini alıyor.

Ben burada bir dize bölünmüş yapmak için çok daha iyi bir yol kapsar: http://www.digitalruby.com/split-string-sql-server/

İşte kod:

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

Sunucu ağrısı ile özyinelemeli CTE çözümü, test edin

MS SQL Server 2008 Şema Kurulumu :

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

Sorgu 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

Sonuçlar :

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

0

josejuan tarafından xml tabanlı cevap benzer iken, ben sadece bir kez xml yolu işleme, sonra pivoting orta derecede daha verimli bulundu:

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 da koştu

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

koştu 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

VE KULLANIN

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

0

eğer biri ayrılmış metnin sadece bir kısmını almak isterse bunu kullanabilir

seçinSplitStringSep ('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

Bunu geliştirdim,

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

tek dikkat etmeniz gereken nokta '.' @x'in sonu her zaman orada olmalıdır.


0

imkansız bir çözüm, ya da daha çok, en çok oylanan cevap üzerine yorum (kabul edilenin hemen altında), aşağıdaki hızlı ve kirli çözümün kendi ihtiyaçlarımı karşıladığını gördüm - sadece SQL etki alanında olmanın bir yararı var.

bir dize verildi "birinci; ikinci; üçüncü; dördüncü; beşinci", diyelim, üçüncü belirteci almak istiyorum. Bu, yalnızca dizenin kaç jetonu olacağını bildiğimizde işe yarar - bu durumda 5'tir. bu yüzden benim eylemim son iki jetonu kesmek (iç sorgu) ve daha sonra ilk iki jetonu kesmek ( dış sorgu)

Ben bu çirkin olduğunu ve içinde bulunduğum belirli koşulları kapsar biliyorum, ama sadece birisi yararlı bulursa gönderirim. şerefe

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.