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
SQL server version I am running is:
Microsoft SQL Azure (RTM) - 12.0.2000.8
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','||')