sql将字符串拆分为始终相同数量的片段
Posted
技术标签:
【中文标题】sql将字符串拆分为始终相同数量的片段【英文标题】:sql split string into always same amount of pieces 【发布时间】:2018-08-16 14:40:51 【问题描述】:我需要使用分隔符(例如,)将字符串拆分为多个部分
在任何情况下我都需要相同数量的块,如果文本太短,函数应该返回空字符串。
我想将其存储为标量函数,因为与表值函数相比,它更适合在需要的地方使用。
我正在寻找类似 myFunction (inputstring, chunknumber) 的函数
例如:输入字符串 = abc, def, ghi
预期的样本结果:
myFunction (inputstring, 1) --> 'abc'
myFunction (inputstring, 3) --> 'ghi'
myFunction (inputstring, 5) --> ''
【问题讨论】:
您使用的是哪个 dbms? MS SQL Server 2014 标量函数比表值函数更好吗?假设您的表函数是内联的(单个语句),它将比标量函数快得多。 【参考方案1】:在 SQL Server 中,您可以使用以下用户定义函数:
CREATE FUNCTION [dbo].[myFunction]
(
@String VARCHAR(MAX),
@ChunkNumber INT
)
RETURNS VARCHAR (100)
AS
BEGIN
DECLARE @Incrementer AS INT = 1;
DECLARE @SliceValue VARCHAR(MAX);
DECLARE @Delimiter AS CHAR(1) = ',';
DECLARE @ReturnObject AS VARCHAR (100) = '';
DECLARE @SplitedValues TABLE (Id INT IDENTITY NOT NULL, Item VARCHAR (100));
IF LEN(@String) < 1 OR @String IS NULL
RETURN @ReturnObject
WHILE @Incrementer != 0
BEGIN
SET @Incrementer = CHARINDEX(@Delimiter, @String);
IF @Incrementer != 0
SET @SliceValue = LEFT(@String, @Incrementer - 1)
ELSE
SET @SliceValue = @String
IF(LEN(@SliceValue) > 0)
INSERT INTO @SplitedValues(Item) VALUES (@SliceValue)
SET @String = RIGHT(@String, LEN(@String) - @Incrementer)
IF LEN(@String) = 0 BREAK
END
SELECT @ReturnObject = Item FROM @SplitedValues WHERE Id = @ChunkNumber;
RETURN @ReturnObject
END
示例执行:
SELECT [dbo].[myFunction] ('abc, def, ghi', 5) -- returns empty
SELECT [dbo].[myFunction] ('abc, def, ghi', 3) -- returns ghi
SELECT [dbo].[myFunction] ('abc, def, ghi', 1) -- returns abc
【讨论】:
对分离器使用循环可能是最糟糕的方法。有这么多基于集合的拆分器将把这扇门吹走。【参考方案2】:这里是返回位置编号的基于集合的拆分器至关重要的地方。谢天谢地 Jeff Moden 有这样一个分离器。 http://www.sqlservercentral.com/articles/Tally+Table/72993/
他的分离器的代码是:
CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
(@pString VARCHAR(8000), @pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
-- enough to cover VARCHAR(8000)
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
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
;
现在对于您的情况,您需要另一个功能。没问题,我们可以利用他的分离器来解决这个问题。但是我们还需要一个计数表。希望您拥有其中之一,但如果没有,这里就是我使用的那个。我将其保留为视图,因为它非常有用,而且速度快如闪电。我最初也是从 Jeff Moden 那里了解到的这项技术。
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
GO
现在我们可以开始创建新函数了。有了这两个其他组件就非常简单了。
create function GetChunkValue
(
@Inputstring varchar(100)
, @ChunkNumber int
) returns table as
return
select ChunkValue = isnull(s.Item, '')
from cteTally t
left join dbo.DelimitedSplit8K(@Inputstring, ',') s on s.ItemNumber = t.N
where t.N = @ChunkNumber
最后是一些使用它的示例代码。
select *
from GetChunkValue('abc, def, ghi', 2)
【讨论】:
以上是关于sql将字符串拆分为始终相同数量的片段的主要内容,如果未能解决你的问题,请参考以下文章
如何在一定数量的字符后将 NSString 拆分为多个字符串
当内容在单引号中时,使用 PowerShell 将 SQL 文件/字符串拆分为批处理排除拆分