How do I split a string so I can access item x?
-
08-06-2019 - |
Frage
Using SQL Server, how do I split a string so I can access item x?
Take a string "Hello John Smith".Wie kann ich split the string by space and access the item at index 1 sollte return "John"?
Lösung
Sie finden die Lösung in SQL-Benutzer-Definiert Funktion Analysiert eine durch Trennzeichen Getrennte Zeichenfolge hilfreich (aus Das Code-Projekt).
Sie können mit diesem einfachen Logik:
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
Andere Tipps
Ich glaube nicht, dass SQL Server verfügt über eine integrierte split-Funktion, also andere als eine UDF, die einzige andere Antwort, die ich weiß, ist, entführen die PARSENAME Funktion:
SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 2)
PARSENAME nimmt eine Zeichenfolge und spaltet es auf den Punkt.Sie nimmt eine Zahl als zweites argument, und diese Zahl gibt an, welches segment der Zeichenfolge zurück (die Arbeit von vorne nach hinten).
SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 3) --return Hello
Offensichtliches problem ist, wenn der string bereits einen Punkt enthält.Ich denke immer noch mit einem UDF ist der beste Weg...andere Vorschläge?
Erstellen Sie zunächst eine Funktion (mit CTE, common table expression nicht mit der Notwendigkeit für eine temp-Tabelle)
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
Dann verwenden Sie es wie jede Tabelle (oder ändern Sie es zu passen in Ihre vorhandene gespeicherte Prozedur) wie dieser.
select s
from dbo.SplitString('Hello John Smith', ' ')
where zeroBasedOccurance=1
Update
Vorherige version würde nicht für Eingabe-Zeichenfolge länger als 4000 Zeichen.Diese version kümmert sich um die Beschränkung:
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
Die Nutzung bleibt die gleiche.
Die meisten Lösungen, die Sie hier verwenden, while-Schleifen oder rekursiven CTEs.Eine set-basierte Ansatz überlegen sein, ich verspreche es:
CREATE FUNCTION [dbo].[SplitString]
(
@List NVARCHAR(MAX),
@Delim VARCHAR(255)
)
RETURNS TABLE
AS
RETURN ( SELECT [Value] FROM
(
SELECT
[Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects) AS x
WHERE Number <= LEN(@List)
AND SUBSTRING(@Delim + @List, [Number], LEN(@Delim)) = @Delim
) AS y
);
Mehr auf split-Funktionen, warum (und der Beweis, dass), während Schleifen und rekursive CTEs nicht skalierbar, und bessere alternativen, wenn splitting strings kommen aus der Anwendungsschicht:
- Split Saiten der richtige Weg ist – oder der nächste beste Weg,
- Splitting Strings :Follow-Up
- Splitting Strings :Jetzt mit weniger T-SQL
- Vergleich von string-splitting - / Verkettung Methoden
- Die Verarbeitung einer Liste von ganzen zahlen :mein Ansatz
- Aufteilen einer Liste von ganzen zahlen :ein weiterer roundup
- Mehr auf aufzuteilen :benutzerdefinierte Trennzeichen, die Verhinderung von Duplikaten und der Aufrechterhaltung der Ordnung
- Entfernen von Duplikaten von Zeichenfolgen in SQL Server
Auf SQL Server 2016 oder höher, allerdings sollte man sich ansehen, STRING_SPLIT()
und STRING_AGG()
:
Nutzen Sie eine Reihe Tabelle zu tun, die die Zeichenfolge analysieren.
Erstellen Sie eine physische zahlen Tabelle:
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
Erstellen von test-Tabelle mit 1000000 Zeilen
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
Erstellen Sie die Funktion
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
Nutzung (Ausgänge 3mil Zeilen in den 40er Jahren auf meinem laptop)
select *
from #yak
cross apply dbo.ufn_ParseArray(array, ',', 1)
cleanup
drop table dbo.Numbers;
drop function [dbo].[ufn_ParseArray]
Performance hier ist nicht erstaunlich, aber das aufrufen einer Funktion über eine million Zeilen der Tabelle ist nicht die beste Idee.Wenn Sie einen string aufteilen, die über viele Zeilen würde ich vermeiden, die Funktion.
Diese Frage ist nicht über einen string-split-Ansatz, aber ungefähr wie man das N-te element.
Alle Antworten hier tun einige Art von string-splitting mit Rekursion, CTE
s, mehrere CHARINDEX
, REVERSE
und PATINDEX
, erfinden-Funktionen aufrufen, für die CLR-Methoden, Anzahl Tische, CROSS APPLY
s ...Die meisten Antworten decken viele Zeilen code.
Aber wenn du das wirklich will nichts mehr, als ein Ansatz, um das N-te element - dies kann durchgeführt werden als real one-liner, keine UDF, nicht einmal eine sub-wählen Sie...Und als extra-Vorteil: sichere Typ
Teil 2 durch ein Leerzeichen getrennt:
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)')
Natürlich Sie können Variablen verwenden für Trennzeichen und position (verwenden Sie sql:column
zum abrufen der position direkt aus einer Abfrage, die den Wert):
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)')
Wenn Ihre Zeichenfolge enthalten könnte verbotene Zeichen (vor allem unter &><
), Sie immer noch kann es tun auf diese Weise.Verwenden Sie einfach FOR XML PATH
auf Ihrem ersten string ersetzen alle Verbotenen Zeichen mit der Montage escape-Sequenz implizit.
Es ist ein sehr spezieller Fall, wenn - darüber hinaus - Ihr Trennzeichen ist das Semikolon.In diesem Fall Ersetze ich das Trennzeichen zuerst '#DLMT#' und ersetzen Sie diese, um die XML-tags zu guter Letzt:
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)');
UPDATE für SQL-Server 2016+
Mit bedauern die Entwickler vergessen zu Rückkehr die Teil der index mit STRING_SPLIT
.Aber, mithilfe von SQL-Server 2016+, es ist OPENJSON
.
Die Dokumentation klar erklärt:
Wenn OPENJSON analysiert, die ein JSON-array die Funktion liefert die Indizes der Elemente in der JSON-text als Schlüssel.
Eine Zeichenfolge wie 1,2,3
nichts mehr braucht als Klammern: [1,2,3]
.
Eine Zeichenfolge der Wörter, wie this is an example
werden muss ["this","is","an"," example"]
.
Dies sind sehr einfache string-Operationen.Probieren Sie es einfach aus:
DECLARE @str VARCHAR(100)='Hello John Smith';
SELECT [value]
FROM OPENJSON('["' + REPLACE(@str,' ','","') + '"]')
WHERE [key]=1 --zero-based!
Hier ist eine benutzerdefinierte Funktion, die es tun.Es wird eine Tabelle zurückgeben, die von der durch Trennzeichen getrennte Werte, habe nicht versucht, alle Szenarien, aber Ihr Beispiel funktioniert gut.
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
Sie würde es so nennen:
Select * From SplitString('Hello John Smith',' ')
Edit:Aktualisierte Lösung zu behandeln delimters mit len>1, wie in :
select * From SplitString('Hello**John**Smith','**')
Hier Stelle ich einen einfachen Weg Lösung
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
Führen Sie die Funktion wie diese
select * from dbo.split('Hello John Smith',' ')
Meiner Meinung nach, Sie Jungs werden machen es viel zu kompliziert.Erstellen Sie einfach eine CLR-UDF-und mit ihm getan werden.
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()));
}
};
Was Sie über die Verwendung string
und values()
- Anweisung?
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
Ergebnis erzielt.
id item
1 Hello
2 John
3 Smith
Verwende ich die Antwort von frederic aber das hat nicht funktioniert in SQL Server 2005
Ich habe es geändert und ich bin mit select
mit union all
und es funktioniert
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
Und das Ergebnis ist:
id item
1 Hello
2 John
3 Smith
4 how
5 are
6 you
Dieses Muster funktioniert gut und man kann verallgemeinern
Convert(xml,'<n>'+Replace(FIELD,'.','</n><n>')+'</n>').value('(/n[INDEX])','TYPE')
^^^^^ ^^^^^ ^^^^
Hinweis FELD, INDEX und TYP.
Lassen Sie uns eine Tabelle mit IDS wie
sys.message.1234.warning.A45
sys.message.1235.error.O98
....
Dann können Sie schreiben
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
Spaltung und Gießen Sie alle Teile.
Wenn Ihre Datenbank-Kompatibilitätsgrad 130 oder höher, dann können Sie die STRING_SPLIT - Funktion zusammen mit OFFSET-FETCH - Klauseln, um die spezifische Element von index.
Um das Element an index N (null basiert), können Sie den folgenden code verwenden
SELECT value
FROM STRING_SPLIT('Hello John Smith',' ')
ORDER BY (SELECT NULL)
OFFSET N ROWS
FETCH NEXT 1 ROWS ONLY
Um zu überprüfen die Kompatibilitätsgrad der Datenbank, führen Sie diesen code:
SELECT compatibility_level
FROM sys.databases WHERE name = 'YourDBName';
Ich war auf der Suche nach der Lösung auf die Netto-und die unten funktioniert für mich.Ref.
Und rufen Sie die Funktion wie folgt :
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
Noch ein weiteres n ' th-Teil-string delimeter Funktion:
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
und die Verwendung von:
select dbo.GetStringPartByDelimeter ('a;b;c;d;e', ';', 3)
das gibt:
c
Versuchen Sie dies:
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
Testen Sie es, wie diese:
select * from SplitWordList('Hello John Smith')
Das folgende Beispiel verwendet eine rekursive CTE
Update 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
Demo auf SQLFiddle
Alter Function dbo.fn_Split
(
@Expression nvarchar(max),
@Delimiter nvarchar(20) = ',',
@Qualifier char(1) = Null
)
RETURNS @Results TABLE (id int IDENTITY(1,1), value nvarchar(max))
AS
BEGIN
/* USAGE
Select * From dbo.fn_Split('apple pear grape banana orange honeydew cantalope 3 2 1 4', ' ', Null)
Select * From dbo.fn_Split('1,abc,"Doe, John",4', ',', '"')
Select * From dbo.fn_Split('Hello 0,"&""&&&&', ',', '"')
*/
-- Declare Variables
DECLARE
@X xml,
@Temp nvarchar(max),
@Temp2 nvarchar(max),
@Start int,
@End int
-- HTML Encode @Expression
Select @Expression = (Select @Expression For XML Path(''))
-- Find all occurences of @Delimiter within @Qualifier and replace with |||***|||
While PATINDEX('%' + @Qualifier + '%', @Expression) > 0 AND Len(IsNull(@Qualifier, '')) > 0
BEGIN
Select
-- Starting character position of @Qualifier
@Start = PATINDEX('%' + @Qualifier + '%', @Expression),
-- @Expression starting at the @Start position
@Temp = SubString(@Expression, @Start + 1, LEN(@Expression)-@Start+1),
-- Next position of @Qualifier within @Expression
@End = PATINDEX('%' + @Qualifier + '%', @Temp) - 1,
-- The part of Expression found between the @Qualifiers
@Temp2 = Case When @End < 0 Then @Temp Else Left(@Temp, @End) End,
-- New @Expression
@Expression = REPLACE(@Expression,
@Qualifier + @Temp2 + Case When @End < 0 Then '' Else @Qualifier End,
Replace(@Temp2, @Delimiter, '|||***|||')
)
END
-- Replace all occurences of @Delimiter within @Expression with '</fn_Split><fn_Split>'
-- And convert it to XML so we can select from it
SET
@X = Cast('<fn_Split>' +
Replace(@Expression, @Delimiter, '</fn_Split><fn_Split>') +
'</fn_Split>' as xml)
-- Insert into our returnable table replacing '|||***|||' back to @Delimiter
INSERT @Results
SELECT
"Value" = LTRIM(RTrim(Replace(C.value('.', 'nvarchar(max)'), '|||***|||', @Delimiter)))
FROM
@X.nodes('fn_Split') as X(C)
-- Return our temp table
RETURN
END
Ich weiß, es ist eine alte Frage ist, aber ich glaube, einigen kann man profitieren Sie von meiner Lösung.
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
Vorteile:
- Es trennt alle 3 sub-strings deliminator mit ' '.
- Muss man nicht nutzen, while-Schleife, wie es verringert die Leistung.
- Keine Notwendigkeit zu Pivot-wie alle daraus resultierenden sub-string angezeigt wird eine Zeile
Einschränkungen:
- Man muss wissen, die insgesamt keine.Leerzeichen (sub-string).
Hinweis:die Lösung geben kann, sub-string bis zu N.
Zu überwand die Einschränkung, die wir verwenden können die folgenden ref.
Aber wieder die oben Lösung kann nicht verwendet werden in einer Tabelle (Actaully ich war nicht in der Lage, es zu benutzen).
Auch ich hoffe, diese Lösung helfen kann, etwas ein.
Update: Im Falle der Datensätze > 50000 ist es nicht ratsam verwenden LOOPS
wie es mindert die Leistung
Fast alle anderen Antworten split-code ersetzen die Zeichenfolge aufgeteilt wird, die unnötig CPU-Zyklen und führt unnötige Speicherzuweisungen.
Ich Decke ein viel besserer Weg, um einen string aufteilen hier: http://www.digitalruby.com/split-string-sql-server/
Hier ist der code:
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.
Können Sie split eine Zeichenfolge in SQL ohne Funktion:
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);
Wenn Sie brauchen, um Unterstützung für beliebige Zeichenfolgen (mit xml-Sonderzeichen)
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);
Reine set-basierte Lösung verwenden TVF
mit rekursiven CTE
.Sie können JOIN
und APPLY
diese Funktion für jedes dataset.
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
Verwendung:
select *
from [dbo].[SplitStringToResultSet]('Hello John Smith', ' ')
where [index] = 1;
Ergebnis:
value index
-------------
John 1
Beginnend mit SQL Server 2016 wir string_split
DECLARE @string varchar(100) = 'Richard, Mike, Mark'
SELECT value FROM string_split(@string, ',')
Ein moderner Ansatz mit STRING_SPLIT, erfordert SQL Server 2016 und höher.
DECLARE @string varchar(100) = 'Hello John Smith'
SELECT
ROW_NUMBER() OVER (ORDER BY value) AS RowNr,
value
FROM string_split(@string, ' ')
Ergebnis:
RowNr value
1 Hello
2 John
3 Smith
Jetzt ist es möglich, th N-te element aus der Zeilen-Nummer.
Aaron Bertrand Antwort ist großartig, aber fehlerhaft.Es nicht ordnungsgemäß verarbeiten ein Leerzeichen als Trennzeichen verwendet wird (wie das Beispiel in der ursprünglichen Frage), da die Länge-Funktion-Streifen Leerzeichen.
Das folgende ist der code, mit einer kleinen Anpassung können in den Raum Trennzeichen:
CREATE FUNCTION [dbo].[SplitString]
(
@List NVARCHAR(MAX),
@Delim VARCHAR(255)
)
RETURNS TABLE
AS
RETURN ( SELECT [Value] FROM
(
SELECT
[Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects) AS x
WHERE Number <= LEN(@List)
AND SUBSTRING(@Delim + @List, [Number], LEN(@Delim+'x')-1) = @Delim
) AS y
);
Hier ist eine Funktion, werden erfüllen die Frage, ist das Ziel von teilen eines string und der Zugriff auf Element X:
CREATE FUNCTION [dbo].[SplitString]
(
@List VARCHAR(MAX),
@Delimiter VARCHAR(255),
@ElementNumber INT
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE @inp VARCHAR(MAX)
SET @inp = (SELECT REPLACE(@List,@Delimiter,'_DELMTR_') FOR XML PATH(''))
DECLARE @xml XML
SET @xml = '<split><el>' + REPLACE(@inp,'_DELMTR_','</el><el>') + '</el></split>'
DECLARE @ret VARCHAR(MAX)
SET @ret = (SELECT
el = split.el.value('.','varchar(max)')
FROM @xml.nodes('/split/el[string-length(.)>0][position() = sql:variable("@elementnumber")]') split(el))
RETURN @ret
END
Verwendung:
SELECT dbo.SplitString('Hello John Smith', ' ', 2)
Ergebnis:
John
EINFACHE LÖSUNG FÜR DIE ANALYSE VON VOR-UND NACHNAME
DECLARE @Name varchar(10) = 'John Smith'
-- Get First Name
SELECT SUBSTRING(@Name, 0, (SELECT CHARINDEX(' ', @Name)))
-- Get Last Name
SELECT SUBSTRING(@Name, (SELECT CHARINDEX(' ', @Name)) + 1, LEN(@Name))
In meinem Fall (und in vielen anderen scheint es...), habe ich eine Liste von vor-und Nachnamen getrennt durch ein Leerzeichen.Diese können direkt verwendet werden in einer select-Anweisung, um zu analysieren, vor-und Nachnamen.
-- i.e. Get First and Last Name from a table of Full Names
SELECT SUBSTRING(FullName, 0, (SELECT CHARINDEX(' ', FullName))) as FirstName,
SUBSTRING(FullName, (SELECT CHARINDEX(' ', FullName)) + 1, LEN(FullName)) as LastName,
From FullNameTable
Ich weiß, das ist spät, aber ich hatte vor kurzem diese Anforderung und kam mit dem folgenden code.Ich habe nicht eine Wahl, die zur Verwendung von Benutzer-definierten Funktion.Hoffe, das hilft.
SELECT
SUBSTRING(
SUBSTRING('Hello John Smith' ,0,CHARINDEX(' ','Hello John Smith',CHARINDEX(' ','Hello John Smith')+1)
),CHARINDEX(' ','Hello John Smith'),LEN('Hello John Smith')
)
Gut, mir ist nicht alles, das einfacher, aber hier ist der code, den ich verwenden, um die split a comma-delimited input variable in einzelne Werte, und legen Sie es in eine table-variable.Ich bin sicher, Sie könnten ändern Sie diese leicht nach split basierend auf einem Platz und dann eine grundlegende SELECT-Abfrage für die Tabelle variable Holen Sie sich Ihre Ergebnisse.
-- Create temporary table to parse the list of accounting cycles.
DECLARE @tblAccountingCycles table
(
AccountingCycle varchar(10)
)
DECLARE @vchAccountingCycle varchar(10)
DECLARE @intPosition int
SET @vchAccountingCycleIDs = LTRIM(RTRIM(@vchAccountingCycleIDs)) + ','
SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)
IF REPLACE(@vchAccountingCycleIDs, ',', '') <> ''
BEGIN
WHILE @intPosition > 0
BEGIN
SET @vchAccountingCycle = LTRIM(RTRIM(LEFT(@vchAccountingCycleIDs, @intPosition - 1)))
IF @vchAccountingCycle <> ''
BEGIN
INSERT INTO @tblAccountingCycles (AccountingCycle) VALUES (@vchAccountingCycle)
END
SET @vchAccountingCycleIDs = RIGHT(@vchAccountingCycleIDs, LEN(@vchAccountingCycleIDs) - @intPosition)
SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1)
END
END
Das Konzept ist so ziemlich das gleiche.Eine andere alternative ist die Nutzung der .NET-Kompatibilität in SQL Server 2005 selbst.Sie können im wesentlichen schreiben Sie selbst eine einfache Methode .NET, die teilen den string und dann freigelegt, die als eine gespeicherte Prozedur/Funktion.
Dies ist etwas, was ich Tat, um eine bestimmte token in einer Zeichenfolge.(Getestet in MSSQL 2008)
Zuerst erstellen Sie die folgenden Funktionen:(gefunden in: hier
CREATE FUNCTION dbo.SplitStrings_Moden
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
E42(N) AS (SELECT 1 FROM E4 a, E2 b),
cteTally(N) AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(@List,1)))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
WHERE (SUBSTRING(@List,t.N,1) = @Delimiter OR t.N = 0))
SELECT Item = SUBSTRING(@List, s.N1, ISNULL(NULLIF(CHARINDEX(@Delimiter,@List,s.N1),0)-s.N1,8000))
FROM cteStart s;
und
create FUNCTION dbo.getToken
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255),
@Pos int
)
RETURNS varchar(max)
as
begin
declare @returnValue varchar(max);
select @returnValue = tbl.Item from (
select ROW_NUMBER() over (order by (select null)) as id, * from dbo.SplitStrings_Moden(@List, @Delimiter)
) as tbl
where tbl.id = @Pos
return @returnValue
end
dann können Sie es verwenden, wie:
select dbo.getToken('1111_2222_3333_', '_', 1)
die Rückkehr 1111