Używając programu SQL Server, w jaki sposób podzielić ciąg, aby uzyskać dostęp do elementu x?
Weź ciąg „Hello John Smith”. Jak mogę podzielić ciąg według spacji i uzyskać dostęp do elementu o indeksie 1, który powinien zwrócić „John”?
sql
sql-server
tsql
split
GateKiller
źródło
źródło
Odpowiedzi:
Rozwiązanie w funkcji SQL User Defined Function służące do parsowania łańcucha rozdzielanego może być pomocne (z projektu Code ).
Możesz użyć tej prostej logiki:
źródło
SET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( RTRIM( LTRIM( @p_SourceText)))
nieSET @p_SourceText = RTRIM( LTRIM( @p_SourceText)) SET @w_Length = DATALENGTH( @p_SourceText)
?STRING_SPLIT
, która podzieli ciąg i zwróci wynik tabeli z jedną kolumną, której można użyć wSELECT
instrukcji lub w innym miejscu.Nie wierzę, że SQL Server ma wbudowaną funkcję podziału, więc oprócz UDF jedyną znaną odpowiedzią jest przejęcie funkcji PARSENAME:
PARSENAME bierze ciąg i dzieli go na znak kropki. Jako drugi argument przyjmuje liczbę, która określa, który segment łańcucha ma zostać zwrócony (działa od tyłu do przodu).
Oczywistym problemem jest to, że ciąg zawiera już kropkę. Nadal uważam, że korzystanie z UDF jest najlepszym sposobem ... jakieś inne sugestie?
źródło
SPLIT()
Funkcja nie jest dostarczana, ponieważ zachęca słabe projekt bazy danych, a baza danych nie zostanie zoptymalizowany do korzystania z danych zapisanych w tym formacie. RDBMS nie jest zobowiązany do pomocy programistów robić głupie rzeczy, że zostało zaprojektowane nie do uchwytu. Prawidłowa odpowiedź będzie zawsze brzmiała: „Normalizuj bazę danych, jak mówiliśmy 40 lat temu”. Ani SQL, ani RDBMS nie są winne złego projektu.Najpierw utwórz funkcję (za pomocą CTE wspólne wyrażenie tabelowe eliminuje potrzebę tworzenia tabeli tymczasowej)
Następnie użyj go jako dowolnej tabeli (lub zmodyfikuj w celu dopasowania do istniejącego przechowywanego proc) w ten sposób.
Aktualizacja
Poprzednia wersja nie działała dla ciągu wejściowego dłuższego niż 4000 znaków. Ta wersja zajmuje się ograniczeniem:
Użycie pozostaje takie samo.
źródło
100
(aby zapobiec nieskończonej pętli). Użyj podpowiedzi MAXRECURSION, aby zdefiniować liczbę poziomów rekurencji (0
do32767
,0
to „no limit” - może zniszczyć serwer). BTW, znacznie lepsza odpowiedź niżPARSENAME
, ponieważ jest uniwersalna :-). +1maxrecursion
do tego rozwiązania, pamiętaj o tym pytaniu i jego odpowiedziach. Jak ustawićmaxrecursion
opcję CTE w funkcji wycenianej w tabeli .s
nie jest już zdefiniowanyWiększość rozwiązań tutaj wykorzystuje pętle while lub rekurencyjne CTE. Podejście oparte na zestawie będzie lepsze, obiecuję, jeśli możesz użyć separatora innego niż spacja:
Przykładowe użycie:
Wyniki:
Możesz także dodać
idx
żądaną funkcję jako argument do funkcji, ale zostawię to jako ćwiczenie czytelnikowi.Nie można tego zrobić tylko z funkcją natywną
STRING_SPLIT
dodaną w SQL Server 2016, ponieważ nie ma gwarancji, że dane wyjściowe będą renderowane w kolejności oryginalnej listy. Innymi słowy, jeśli zdasz,3,6,1
wynik będzie prawdopodobnie w tej kolejności, ale może być1,3,6
. Poprosiłem o pomoc społeczności w ulepszeniu wbudowanej funkcji tutaj:Przy wystarczającej jakościowej informacji zwrotnej mogą rozważyć wprowadzenie niektórych z tych ulepszeń:
Więcej informacji na temat funkcji podziału, dlaczego (i udowodnij to), podczas gdy pętle i rekurencyjne CTE nie skalują się, i lepsze alternatywy, jeśli dzielenie ciągów pochodzących z warstwy aplikacji:
Jednak w SQL Server 2016 lub nowszym powinieneś przyjrzeć się
STRING_SPLIT()
iSTRING_AGG()
:źródło
select * from DBO.SplitString('Hello John smith', ' ');
i uzyskano wynik: Wartość Hello ello llo lo o John ohn hn n smith mith ith thMożesz wykorzystać tabelę liczb do parsowania łańcucha.
Utwórz tabelę liczb fizycznych:
Utwórz tabelę testową z 1000000 wierszami
Utwórz funkcję
Użycie (wyprowadza 3mil wierszy w 40s na moim laptopie)
sprzątać
Wydajność tutaj nie jest niesamowita, ale wywołanie funkcji w tabeli zawierającej milion wierszy nie jest najlepszym pomysłem. Wykonując ciąg podzielony na wiele wierszy, unikałbym tej funkcji.
źródło
desc
zostały usunięte?REVERSE(PARSENAME(REPLACE(REVERSE('Hello John Smith'), ' ', '.'), 1))
od @NothingsImpossible zakończono w 1,5 minuty. @hello_earth Jak Twoje rozwiązanie porównałoby się z dłuższymi ciągami z więcej niż 4 polami?To pytanie nie dotyczy podejścia opartego na dzieleniu łańcucha , ale sposobu uzyskania n-tego elementu .
Wszystkie odpowiedzi są tu robi jakieś rozszczepienie łańcucha za pomocą rekurencji,
CTE
s, stwardnienieCHARINDEX
,REVERSE
aPATINDEX
funkcje wymyślanie, wezwanie do metod numerycznych CLR, stoły,CROSS APPLY
s ... Większość odpowiedzi obejmują wiele linii kodu.Ale - jeśli naprawdę nie chcesz niczego więcej niż podejście do uzyskania n-tego elementu - można to zrobić jako prawdziwy jeden liniowiec , bez UDF, nawet bez wyboru podrzędnego ... I jako dodatkowa korzyść: wpisz bezpieczny
Uzyskaj część 2 oddzieloną spacją:
Oczywiście możesz użyć zmiennych dla separatora i pozycji (użyj,
sql:column
aby pobrać pozycję bezpośrednio z wartości zapytania):Jeśli Twój ciąg może zawierać niedozwolone znaki (szczególnie jeden spośród
&><
), nadal możesz to zrobić w ten sposób. Po prostu użyjFOR XML PATH
najpierw swojego ciągu, aby domyślnie zastąpić wszystkie zabronione znaki pasującą sekwencją ucieczki.Jest to bardzo szczególny przypadek, jeśli - dodatkowo - separatorem jest średnik . W takim przypadku najpierw zastępuję separator na „# DLMT #”, a na końcu zastępuję go tagami XML:
AKTUALIZACJA dla SQL-Server 2016+
Niestety programiści zapomnieli zwrócić indeks części
STRING_SPLIT
. Ale przy użyciu SQL Server 2016+ istniejeJSON_VALUE
iOPENJSON
.Za pomocą
JSON_VALUE
możemy podać pozycję jako tablicę indeksu.Dla dokumentacja stwierdza wyraźnie:
OPENJSON
Ciąg jak
1,2,3
potrzebuje niczego więcej niż nawiasach:[1,2,3]
.Ciąg takich słów
this is an example
musi być["this","is","an","example"]
.Są to bardzo łatwe operacje na łańcuchach. Po prostu wypróbuj:
- Zobacz to dla dzielnika łańcuchów bezpiecznego pozycjonowania (od zera ):
W tym poście przetestowałem różne podejścia i stwierdziłem, że
OPENJSON
jest to naprawdę szybkie. Nawet znacznie szybszy niż słynna metoda „delimitedSplit8k ()” ...AKTUALIZACJA 2 - Uzyskaj wartości bezpieczne dla typu
Możemy użyć tablicy w tablicy po prostu używając podwójnej
[[]]
. Pozwala to naWITH
wpisanie klauzuli:źródło
<x><![CDATA[x<&>x]]></x>
.CDATA
mogą sobie z tym poradzić ... Ale po obsadzie zniknęły (zmieniono na ucieczkętext()
niejawnie). Nie podoba mi się magia pod maską , więc wolę(SELECT 'Text with <&>' AS [*] FOR XML PATH(''))
podejście -. Wydaje mi się to czystsze i tak się dzieje ... (Więcej o CDATA i XML ).Oto UDF, który to zrobi. Zwróci tabelę wartości rozdzielanych, nie wypróbowałem na niej wszystkich scenariuszy, ale twój przykład działa dobrze.
Nazwałbyś to tak:
Edycja: Zaktualizowano rozwiązanie do obsługi ograniczników z długością> 1 jak w:
źródło
Tutaj zamieszczam prosty sposób rozwiązania
Wykonaj taką funkcję
źródło
Moim zdaniem, robicie to zbyt skomplikowane. Wystarczy utworzyć CLR UDF i gotowe.
źródło
Co z używaniem
string
ivalues()
oświadczeniem?Zestaw wyników osiągnięty.
źródło
Korzystam z odpowiedzi frederic, ale to nie działało w SQL Server 2005
I zmodyfikowano go i używam
select
zunion all
i działaA zestaw wyników to:
źródło
EXEC
.EXEC
niejawnie wywołuje procedurę przechowywaną i nie można używać procedur przechowywanych w UDF.Ten wzór działa dobrze i możesz uogólniać
uwaga POLA , INDEKS i TYP .
Niech trochę tabela z identyfikatorami, takimi jak
Następnie możesz pisać
dzielenie i odlewanie wszystkich części.
źródło
Jeśli Twoja baza danych ma poziom zgodności 130 lub wyższy, możesz użyć funkcji STRING_SPLIT wraz z klauzulami OFFSET FETCH , aby uzyskać określony element według indeksu.
Aby uzyskać pozycję o indeksie N (zero), możesz użyć następującego kodu
Aby sprawdzić poziom zgodności bazy danych , wykonaj ten kod:
źródło
xml
używałam tego wcześniej ... Dobrze wiedzieć ... Nadal wolałbym podejście oparte na -split, ponieważ pozwala ono pobrać wartość bezpieczną dla typu i nie wymaga sub-zapytania, ale jest to dobry. +1 z mojej stronySTRING_SPLIT
żądań dla v2016 +. W takim przypadku znacznie lepiej jest użyćOPENJSON
lubJSON_VALUE
. Może chcesz sprawdzić moją odpowiedźSzukałem rozwiązania w sieci, a poniższe działa dla mnie. Nr ref .
I wywołujesz funkcję w ten sposób:
źródło
Jeszcze inna zdobądź n-tą część ciągu przez funkcję delimera:
i wykorzystanie:
który zwraca:
źródło
Spróbuj tego:
Przetestuj tak:
źródło
W poniższym przykładzie użyto rekurencyjnego CTE
Aktualizacja 18.09.2013
Demo na SQLFiddle
źródło
źródło
Możesz podzielić ciąg znaków w SQL bez potrzeby używania funkcji:
Jeśli potrzebujesz obsługi dowolnych ciągów znaków (ze znakami specjalnymi xml)
źródło
Wiem, że to stare pytanie, ale myślę, że ktoś może skorzystać z mojego rozwiązania.
FIDDLE SQL
Zalety:
Ograniczenia:
Uwaga : rozwiązanie może dać ciąg podrzędny do N.
Aby pokonać ograniczenie, możemy skorzystać z następującego ref .
Ale znowu powyższego rozwiązania nie można użyć w tabeli (Actaully nie byłem w stanie go użyć).
Ponownie mam nadzieję, że to rozwiązanie może komuś pomóc.
Aktualizacja: W przypadku rekordów> 50000 nie zaleca się używania,
LOOPS
ponieważ spowoduje to obniżenie wydajnościźródło
Rozwiązanie oparte na czystym zestawie
TVF
z rekurencyjnymCTE
. MożeszJOIN
iAPPLY
ta funkcja do dowolnego zestawu danych.Stosowanie:
Wynik:
źródło
Prawie wszystkie inne odpowiedzi zastępują dzielony ciąg, który marnuje cykle procesora i wykonuje niepotrzebne przydziały pamięci.
Omawiam tutaj znacznie lepszy sposób wykonania podziału strun: http://www.digitalruby.com/split-string-sql-server/
Oto kod:
źródło
Rekurencyjne rozwiązanie CTE z bólem serwera, przetestuj je
Konfiguracja schematu MS SQL Server 2008 :
Zapytanie 1 :
Wyniki :
źródło
chociaż podobny do odpowiedzi opartej na xml autorstwa josejuan, stwierdziłem, że przetwarzanie ścieżki xml tylko raz, następnie przestawianie było umiarkowanie bardziej wydajne:
pobiegł w 8:30
pobiegł w 9:20
źródło
I UŻYWAJ GO
źródło
jeśli ktoś chce uzyskać tylko jedną część oddzielnego tekstu, może to wykorzystać
wybierz * z fromSplitStringSep („Word1 wordr2 word3”, „”)
źródło
Zdewoluowałem to,
jedyną uwagę, na którą powinieneś zwrócić uwagę, jest kropka ”. koniec @x zawsze powinien być na miejscu.
źródło
Opierając się na @NothingsImpossible rozwiązanie, a raczej komentować najczęściej głosowaną odpowiedź (tuż poniżej zaakceptowanej), znalazłem następujący szybki i brudny rozwiązanie, które spełnia moje własne potrzeby - ma tę zaletę, że znajduje się wyłącznie w domenie SQL.
biorąc ciąg „pierwszy; drugi; trzeci; czwarty; piąty”, powiedzmy, chcę uzyskać trzeci token. działa to tylko wtedy, gdy wiemy, ile tokenów będzie miał łańcuch - w tym przypadku jest to 5. więc moim sposobem działania jest odrąbanie dwóch ostatnich tokenów (zapytanie wewnętrzne), a następnie oderwanie dwóch pierwszych tokenów ( zapytanie zewnętrzne)
wiem, że jest to brzydkie i obejmuje określone warunki, w których byłem, ale publikuję to na wypadek, gdyby ktoś uznał to za przydatne. Twoje zdrowie
źródło
źródło
Począwszy od SQL Server 2016 my string_split
źródło
STRING_SPLIT
nie gwarantuje zwrotu tego samego zamówienia. AleOPENJSON
tak (patrz moja odpowiedź (sekcja aktualizacji) )