Gat*_*ler 480 sql t-sql sql-server split
使用SQL Server,如何拆分字符串以便访问项目x?
拿一个字符串"Hello John Smith".如何按空格分割字符串并访问索引1处应该返回"John"的项目?
Nat*_*ord 352
我不相信SQL Server有内置的拆分功能,所以除了UDF之外,我知道的唯一其他答案是劫持PARSENAME函数:
SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 2)
Run Code Online (Sandbox Code Playgroud)
PARSENAME接受一个字符串并将其拆分为句点字符.它需要一个数字作为它的第二个参数,并且该数字指定要返回的字符串的哪个段(从后到前工作).
SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 3) --return Hello
Run Code Online (Sandbox Code Playgroud)
显而易见的问题是字符串已经包含句点.我仍然认为使用UDF是最好的方法......任何其他建议?
Jon*_*tor 186
您可以在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
Run Code Online (Sandbox Code Playgroud)
vzc*_*czc 108
首先,创建一个函数(使用CTE,公共表表达式不需要临时表)
create function dbo.SplitString
(
@str nvarchar(4000),
@separator char(1)
)
returns table
AS
return (
with tokens(p, a, b) AS (
select
1,
1,
charindex(@separator, @str)
union all
select
p + 1,
b + 1,
charindex(@separator, @str, b + 1)
from tokens
where b > 0
)
select
p-1 zeroBasedOccurance,
substring(
@str,
a,
case when b > 0 then b-a ELSE 4000 end)
AS s
from tokens
)
GO
Run Code Online (Sandbox Code Playgroud)
然后,将它用作任何表(或修改它以适合您现有的存储过程),就像这样.
select s
from dbo.SplitString('Hello John Smith', ' ')
where zeroBasedOccurance=1
Run Code Online (Sandbox Code Playgroud)
更新
对于长度超过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
Run Code Online (Sandbox Code Playgroud)
用法保持不变.
Aar*_*and 60
这里的大多数解决方案都使用循环或递归CTE.基于集合的方法将是优越的,我保证:
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
);
Run Code Online (Sandbox Code Playgroud)
有关分割函数的更多信息,为什么(并证明)while循环和递归CTE不能缩放,以及更好的替代方法,如果分割来自应用程序层的字符串:
但是,在SQL Server 2016或更高版本上,您应该查看STRING_SPLIT()
并STRING_AGG()
:
Nat*_*erl 37
您可以利用Number表来进行字符串解析.
创建物理数字表:
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
Run Code Online (Sandbox Code Playgroud)
创建具有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
Run Code Online (Sandbox Code Playgroud)
创建功能
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
Run Code Online (Sandbox Code Playgroud)
用法(在我的笔记本电脑上输出40s的40m行)
select *
from #yak
cross apply dbo.ufn_ParseArray(array, ',', 1)
Run Code Online (Sandbox Code Playgroud)
清理
drop table dbo.Numbers;
drop function [dbo].[ufn_ParseArray]
Run Code Online (Sandbox Code Playgroud)
这里的性能并不令人惊讶,但调用超过一百万行表的函数并不是最好的主意.如果执行一个字符串拆分多行,我会避免该函数.
Shn*_*ugo 28
这个问题不是关于字符串拆分方法,而是关于如何获取第n个元素.
这里所有的答案都使用递归,做某种类型的字符串分裂CTE
,多发性CHARINDEX
,REVERSE
并且PATINDEX
,发明的功能,请致电CLR方法,数表,CROSS APPLY
S ^ ......大多数的答案涉及多行代码.
但是 - 如果你真的只想要获得第n个元素的方法 - 这可以作为真正的单行,没有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)')
Run Code Online (Sandbox Code Playgroud)
当然,您可以使用变量作为分隔符和位置(用于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)')
Run Code Online (Sandbox Code Playgroud)
如果您的字符串可能包含禁用字符(尤其是其中一个字符&><
),您仍然可以这样做.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)');
Run Code Online (Sandbox Code Playgroud)
很遗憾,开发人员忘记返回部分索引STRING_SPLIT
.但是,使用SQL-Server 2016+,有JSON_VALUE
.
该文件明确规定:
当OPENJSON解析JSON数组时,该函数将JSON文本中元素的索引作为键返回.
一个字符串只OPENJSON
需要括号:JSON_VALUE
.
也就是说像一个字符串OPENJSON
需要是1,2,3
.
这些是非常简单的字符串操作.试试吧:
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,']'));
Run Code Online (Sandbox Code Playgroud)
bre*_*dan 21
这是一个UDF,它会做到这一点.它将返回一个分隔值的表,没有尝试过它的所有场景,但你的例子工作正常.
CREATE FUNCTION SplitString
(
-- Add the parameters for the function here
@myString varchar(500),
@deliminator varchar(10)
)
RETURNS
@ReturnTable TABLE
(
-- Add the column definitions for the TABLE variable here
[id] [int] IDENTITY(1,1) NOT NULL,
[part] [varchar](50) NULL
)
AS
BEGIN
Declare @iSpaces int
Declare @part varchar(50)
--initialize spaces
Select @iSpaces = charindex(@deliminator,@myString,0)
While @iSpaces > 0
Begin
Select @part = substring(@myString,0,charindex(@deliminator,@myString,0))
Insert Into @ReturnTable(part)
Select @part
Select @myString = substring(@mystring,charindex(@deliminator,@myString,0)+ len(@deliminator),len(@myString) - charindex(' ',@myString,0))
Select @iSpaces = charindex(@deliminator,@myString,0)
end
If len(@myString) > 0
Insert Into @ReturnTable
Select @myString
RETURN
END
GO
Run Code Online (Sandbox Code Playgroud)
你会这样称呼它:
Select * From SplitString('Hello John Smith',' ')
Run Code Online (Sandbox Code Playgroud)
编辑:更新解决方案以处理len> 1的分隔符,如下所示:
select * From SplitString('Hello**John**Smith','**')
Run Code Online (Sandbox Code Playgroud)
小智 15
在这里,我发布了一种简单的解决方案
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
Run Code Online (Sandbox Code Playgroud)
执行这样的功能
select * from dbo.split('Hello John Smith',' ')
Run Code Online (Sandbox Code Playgroud)
Dam*_*ake 10
在我看来,你们让它变得太复杂了.只需创建一个CLR UDF并完成它.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;
public partial class UserDefinedFunctions {
[SqlFunction]
public static SqlString SearchString(string Search) {
List<string> SearchWords = new List<string>();
foreach (string s in Search.Split(new char[] { ' ' })) {
if (!s.ToLower().Equals("or") && !s.ToLower().Equals("and")) {
SearchWords.Add(s);
}
}
return new SqlString(string.Join(" OR ", SearchWords.ToArray()));
}
};
Run Code Online (Sandbox Code Playgroud)
小智 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
Run Code Online (Sandbox Code Playgroud)
达到了结果集.
id item
1 Hello
2 John
3 Smith
Run Code Online (Sandbox Code Playgroud)
我使用frederic的答案,但这在SQL Server 2005中不起作用
我修改它,我正在使用select
它union all
,它的工作原理
DECLARE @str varchar(max)
SET @str = 'Hello John Smith how are you'
DECLARE @separator varchar(max)
SET @separator = ' '
DECLARE @Splited table(id int IDENTITY(1,1), item varchar(max))
SET @str = REPLACE(@str, @separator, ''' UNION ALL SELECT ''')
SET @str = ' SELECT ''' + @str + ''' '
INSERT INTO @Splited
EXEC(@str)
SELECT * FROM @Splited
Run Code Online (Sandbox Code Playgroud)
结果集是:
id item
1 Hello
2 John
3 Smith
4 how
5 are
6 you
Run Code Online (Sandbox Code Playgroud)
这种模式工作正常,你可以概括
Convert(xml,'<n>'+Replace(FIELD,'.','</n><n>')+'</n>').value('(/n[INDEX])','TYPE')
^^^^^ ^^^^^ ^^^^
Run Code Online (Sandbox Code Playgroud)
注意FIELD,INDEX和TYPE.
让一些带有标识符的表格
sys.message.1234.warning.A45
sys.message.1235.error.O98
....
Run Code Online (Sandbox Code Playgroud)
然后,你可以写
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
Run Code Online (Sandbox Code Playgroud)
分裂和铸造所有零件.
如果数据库的兼容级别为130或更高,则可以使用STRING_SPLIT函数和OFFSET FETCH子句按索引获取特定项.
要获取索引N(从零开始)的项目,可以使用以下代码
SELECT value
FROM STRING_SPLIT('Hello John Smith',' ')
ORDER BY (SELECT NULL)
OFFSET N ROWS
FETCH NEXT 1 ROWS ONLY
Run Code Online (Sandbox Code Playgroud)
SELECT compatibility_level
FROM sys.databases WHERE name = 'YourDBName';
Run Code Online (Sandbox Code Playgroud)
我正在网上寻找解决方案,以下对我有用. 参考.
你调用这个函数:
SELECT * FROM dbo.split('ram shyam hari gopal',' ')
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
还有另一个通过分隔函数得到字符串的第n部分:
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
Run Code Online (Sandbox Code Playgroud)
和用法:
select dbo.GetStringPartByDelimeter ('a;b;c;d;e', ';', 3)
Run Code Online (Sandbox Code Playgroud)
返回:
c
Run Code Online (Sandbox Code Playgroud)
试试这个:
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
Run Code Online (Sandbox Code Playgroud)
像这样测试:
select * from SplitWordList('Hello John Smith')
Run Code Online (Sandbox Code Playgroud)
以下示例使用递归CTE
更新于 2013年9月18日
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
Run Code Online (Sandbox Code Playgroud)
在SQLFiddle上演示
归档时间: |
|
查看次数: |
821655 次 |
最近记录: |