【问题标题】:How can I convert split function to inline table valued udf in SQL server?如何在 SQL Server 中将拆分函数转换为内联表值 udf?
【发布时间】:2015-04-30 07:26:32
【问题描述】:

假设我有这个查询(伪):

Select T.a,
       T.b, 
       (select top 1 element from fn_split(c,',') where element=T.element)
From largeTable T

fn_split 为每一行运行,我想使用值为 udf 的内联表,这样性能会更好。

注意:fn_split 只需通过, 拆分创建一个表:

但是查看内联表值 udf 结构

create FUNCTION [dbo].[fn_...]
(
    ...
)
RETURNS table
AS
RETURN  SELECT ...(!!!)

它应该立即将 select 作为第一条语句返回!

但是如果我的 UDF 看起来像这样呢:

CREATE FUNCTION [dbo].[FN_Split] 
(
    @InDelimitedString varchar(max),
    @InDelimiter       varchar(10) 
)
RETURNS 
    @tblArray TABLE 
    (
        ElementID  smallint IDENTITY(1,1), 
        Element    varchar(1000)
    )

AS
BEGIN

    DECLARE @StrPos smallint,
    @StrStart smallint,
    @DelimiterLength smallint

    SET @DelimiterLength = LEN(@InDelimiter)    

    WHILE LEN(@InDelimitedString) > 0
        BEGIN
         --Removed for clarity . do some CHARINDEX  manipulation ETc.
        END

    RETURN 
END

问题:

我无法立即返回 select,但我仍然想将 fn_split 更改为内联表值 udf。

我该怎么做?

【问题讨论】:

    标签: sql-server sql-server-2008-r2 inline-table-function


    【解决方案1】:

    问题在于您的拆分功能。它以RBAR 的方式进行拆分。您应该使用基于集合的拆分器。这是 Jeff Moden 的 DelimitedSplit8k,它是目前最快的分离器之一:

    CREATE FUNCTION [dbo].[DelimitedSplit8K](
        @pString VARCHAR(8000), @pDelimiter CHAR(1)
    )
    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)
    ,cteTally(N) AS(
        SELECT TOP (ISNULL(DATALENGTH(@pString), 0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
    )
    ,cteStart(N1) AS(
        SELECT 1 UNION ALL 
        SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString, t.N, 1) = @pDelimiter
    ),
    cteLen(N1, L1) AS(
    SELECT 
        s.N1,
        ISNULL(NULLIF(CHARINDEX(@pDelimiter, @pString, s.N1),0) - s.N1, 8000)
    FROM cteStart s
    )
    SELECT 
        ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
    FROM cteLen l
    

    注意:请务必查看文章以了解更新的功能


    有关更多拆分功能,请阅读 Aaron Bertrand 爵士的以下文章:

    【讨论】:

    • 这个内联表值是 udf 吗??
    • 天哪,我喜欢那些 CTE 的
    猜你喜欢
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多