SQL Table 的性能瓶颈 - CSVtoTable 的值函数

Performance bottleneck for SQL Table-Valued Function for CSVtoTable

我正在处理一个查询,我需要转换一个 CSV 值,其中包含一些由 , 分隔的 ID。

目前我正在使用取自 here:

的 Table-Values 函数
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[CSVToTable] (@InStr VARCHAR(MAX))
RETURNS @TempTab TABLE
   (id int not null)
AS
BEGIN
    ;-- Ensure input ends with comma
    SET @InStr = REPLACE(@InStr + ',', ',,', ',')
    DECLARE @SP INT
DECLARE @VALUE VARCHAR(1000)
WHILE PATINDEX('%,%', @INSTR ) <> 0 
BEGIN
   SELECT  @SP = PATINDEX('%,%',@INSTR)
   SELECT  @VALUE = LEFT(@INSTR , @SP - 1)
   SELECT  @INSTR = STUFF(@INSTR, 1, @SP, '')
   INSERT INTO @TempTab(id) VALUES (@VALUE)
END
   RETURN
END
GO

我在我看来是这样使用它的:

Select * from SomeTable S where ID in (Select * from CSVtoTable(S.CSVIDs))

现在我已经知道,当对数据集中的大量行(即> 1000)进行操作时,这将导致性能问题。因为该函数将解析后的数据存储到 Table variable,如果同一个函数每行调用 5 次,肯定会出问题。

问题:有没有办法修改代码以获得更好的性能?如果在视图本身中调用相同的内容会更好。

更新 : 添加示例数据的可视化以供参考

SQL服务器版本我运行是:

Microsoft SQL Azure (RTM) - 12.0.2000.8 

这是一个选项,您可以连接 5 列并调用一次更高效的解析器。

例子

Declare @YourTable Table ([N_ID] varchar(50),[CSVIDs] varchar(50),[Bar] varchar(50),[Lorem] varchar(50),[Ipsum] varchar(50))

Insert Into @YourTable 
Values (264, '2,3,4', '1,2,3', '1,2,6', '1,2,3'),
       (265, NULL, NULL, '1,2', NULL)

Select A.* 
From @YourTable A
Cross Apply [dbo].[tvf-Str-Parse-8K] (concat(CSVIDs,',',Bar,',',Lorem,',',ipsum),',') B
Where RetVal = 6

Returns

N_ID    CSVIDs  Bar     Lorem   Ipsum
--------------------------------------
264     2,3,4   1,2,3   1,2,6   1,2,3

UDF 如果感兴趣:

CREATE FUNCTION [dbo].[tvf-Str-Parse-8K] 
    (@String VARCHAR(MAX), @Delimiter VARCHAR(25))
RETURNS TABLE
AS
    RETURN (  
        WITH cte1(N) AS
        (
            SELECT 1 
            FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) N(N)
        ),
        cte2(N) AS
        (
             SELECT TOP (IsNull(DataLength(@String), 0)) 
                 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) 
             FROM
                 (SELECT N = 1 
                  FROM cte1 a, cte1 b, cte1 c, cte1 d) A 
        ),
        cte3(N) AS 
        (
             SELECT 1 
             UNION ALL 
             SELECT t.N + DataLength(@Delimiter) 
             FROM cte2 t 
             WHERE Substring(@String, t.N, DataLength(@Delimiter)) = @Delimiter
        ),
        cte4(N,L) AS 
        (
             SELECT S.N, ISNULL(NULLIF(CharIndex(@Delimiter, @String, s.N), 0) -S.N, 8000) 
             FROM cte3 S
        )
        SELECT
            RetSeq = ROW_NUMBER() OVER (ORDER BY A.N),
            RetVal = LTRIM(RTRIM(SUBSTRING(@String, A.N, A.L)))
        FROM
            cte4 A
    );
    -- Original Source http://www.sqlservercentral.com/articles/Tally+Table/72993/
--Select * from [dbo].[udf-Str-Parse-8K]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse-8K]('John||Cappelletti||was||here','||')