sql将字符串拆分为始终相同的数量

时间:2018-08-16 14:40:52

标签: sql sql-server function user-defined-functions

我需要使用deliminiter(例如,)将字符串拆分成多个段

在任何情况下,我都需要相同数量的块,如果文本太短,该函数应返回空字符串。

我想将此存储为Scalar函数,因为与表值函数相比,最好在需要的地方使用它。

我正在寻找像myFunction这样的函数(inputstring,chunknumber)

例如:Inputstring = abc,def,ghi

预期的样本结果:

myFunction(inputstring,1)->'abc'

myFunction(inputstring,3)->'ghi'

myFunction(inputstring,5)->''

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

这是返回位置编号的基于集合的拆分器的关键所在。值得庆幸的是,杰夫·摩登(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)