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','||')
我正在处理一个查询,我需要转换一个 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','||')