performancecsvt-sqlazure-sql-databasetable-variable

Performance bottleneck for SQL Table-Valued Function for CSVtoTable


I am working on a query where I need to convert a CSV value holding few ID's sepearated by ,.

Currently I am using a Table-Values Function taken from here:

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

I use it in my view like this:

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

Now I already know that this is going to cause performance issues when operating on a large number of rows in dataset i.e. > 1000. Because the function stores the parsed data to a Table variable, and in case of the same function being called 5 times for every row, will definitely cause issues.

Question: Is there any way to modify the code for better performance? Better if the same is being called in the view itself.

Update : Adding visual of sample data for reference

enter image description here

SQL server version I am running is:

Microsoft SQL Azure (RTM) - 12.0.2000.8 

Solution

  • Here is one option where you concatenate the 5 columns and call a more efficient parser once.

    Example

    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
    

    The UDF if Interested:

    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','||')