如何分割字符串以便可以访问项目x?
使用SQL Server,如何分割字符串以便可以访问项x?
取一个字符串" Hello John Smith"。如何按空格分割字符串并访问索引1的项目,该项目应返回" John"?
解决方案
回答
我们可能会发现" SQL用户定义函数"中的解析定界字符串的解决方案很有用(来自The Code Project)。
我们可以使用以下简单逻辑:
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
回答
好吧,我的并不是那么简单,但这是我用来将逗号分隔的输入变量拆分为单个值,然后将其放入表变量中的代码。我确定我们可以对此进行一些修改,以基于空格进行拆分,然后针对该表变量执行基本的SELECT查询以获取结果。
-- 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
这个概念几乎是相同的。另一种选择是利用SQL Server 2005本身内的.NET兼容性。我们基本上可以在.NET中编写一个简单的方法,该方法将拆分字符串,然后将其公开为存储过程/函数。
回答
这是将要执行的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
我们可以这样称呼它:
Select * From SplitString('Hello John Smith',' ')
编辑:更新了解决方案,以处理len> 1的delimters,如:
select * From SplitString('Hello**John**Smith','**')
回答
试试这个:
CREATE function [SplitWordList] ( @list varchar(8000) ) returns @t table ( Word varchar(50) not null, Position int identity(1,1) not null ) as begin declare @pos int, @lpos int, @item varchar(100), @ignore varchar(100), @dl int, @a1 int, @a2 int, @z1 int, @z2 int, @n1 int, @n2 int, @c varchar(1), @a smallint select @a1 = ascii('a'), @a2 = ascii('A'), @z1 = ascii('z'), @z2 = ascii('Z'), @n1 = ascii('0'), @n2 = ascii('9') set @ignore = '''"' set @pos = 1 set @dl = datalength(@list) set @lpos = 1 set @item = '' while (@pos <= @dl) begin set @c = substring(@list, @pos, 1) if (@ignore not like '%' + @c + '%') begin set @a = ascii(@c) if ((@a >= @a1) and (@a <= @z1)) or ((@a >= @a2) and (@a <= @z2)) or ((@a >= @n1) and (@a <= @n2)) begin set @item = @item + @c end else if (@item > '') begin insert into @t values (@item) set @item = '' end end set @pos = @pos + 1 end if (@item > '') begin insert into @t values (@item) end return end
像这样测试它:
select * from SplitWordList('Hello John Smith')
回答
我不相信SQL Server具有内置的拆分功能,因此除了UDF之外,我知道的唯一其他答案是劫持PARSENAME函数:
SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 2)
PARSENAME接受一个字符串并将其分割为句点字符。它以数字作为第二个参数,该数字指定要返回的字符串段(从后到前)。
SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 3) --return Hello
明显的问题是字符串已经包含句点。我仍然认为使用UDF是最好的方法...还有其他建议吗?
回答
首先,创建一个函数(使用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
然后,将其用作任何表(或者对其进行修改以适合我们现有的存储过程),如下所示。
select s from dbo.SplitString('Hello John Smith', ' ') where zeroBasedOccurance=1
更新
如果输入字符串超过4000个字符,则先前版本将失败。此版本解决了以下限制:
create function dbo.SplitString ( @str nvarchar(max), @separator char(1) ) returns table AS return ( with tokens(p, a, b) AS ( select cast(1 as bigint), cast(1 as bigint), charindex(@separator, @str) union all select p + 1, b + 1, charindex(@separator, @str, b + 1) from tokens where b > 0 ) select p-1 ItemIndex, substring( @str, a, case when b > 0 then b-a ELSE LEN(@str) end) AS s from tokens ); GO
用法保持不变。
回答
我们可以利用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
创建具有1000000行的测试表
create table #yak (i int identity(1,1) primary key, array varchar(50)) insert into #yak(array) select 'a,b,c' from dbo.Numbers n cross join dbo.Numbers nn go
创建功能
create function [dbo].[ufn_ParseArray] ( @Input nvarchar(4000), @Delimiter char(1) = ',', @BaseIdent int ) returns table as return ( select row_number() over (order by n asc) + (@BaseIdent - 1) [i], substring(@Input, n, charindex(@Delimiter, @Input + @Delimiter, n) - n) s from dbo.Numbers where n <= convert(int, len(@Input)) and substring(@Delimiter + @Input, n, 1) = @Delimiter ) go
用法(在我的笔记本电脑上以40秒的速度输出300万行)
select * from #yak cross apply dbo.ufn_ParseArray(array, ',', 1)
清理
drop table dbo.Numbers; drop function [dbo].[ufn_ParseArray]
这里的性能并不令人惊讶,但是调用百万行表中的函数并不是最好的主意。如果将字符串拆分成许多行,我将避免使用该函数。
回答
没有代码,但请阅读有关此内容的权威文章。其他答案中的所有解决方案都是本文列出的解决方案:SQL Server 2005和更高版本中的数组和列表
我个人最常使用Numbers表解决方案,因为它适合我的工作...