我有一个函数,可以将数字的字符串列表转换为整数表:
USE [IFRS_Temp]
GO
/****** Object: UserDefinedFunction [dbo].[CSVToTable] Script Date: 01/12/2019 3:36:10 PM ******/
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
declare @listOfIDs varchar(1000);
SET @listOfIDs = '5, 6, 7, 8, 9, 15, 28, 31, 49, 51, 59, 61';
select id from [dbo].[CSVToTable] (@listOfIDs) --this code is ok5
结果正确:
6
7
8
9
15
28
31
49
51
59
61
这会引发错误:
exec('select id from [dbo].[CSVToTable] ('+@listOfIDs+')') -- error
结果:
过程或函数dbo.CSVToTable指定的参数过多。
我需要第二个查询,因为我的查询是动态的。 谢谢
答案 0 :(得分:6)
简单
EXECUTE ('select id from [dbo].[CSVToTable] ('''+@listOfIDs+''')')
declare @listOfIDs varchar(1000);
或者,这是更好的方法
SET @listOfIDs = '5, 6, 7, 8, 9, 15, 28, 31, 49, 51, 59, 61';
EXECUTE sp_executesql N'select id from [dbo].[CSVToTable] (@listOfIDs)',
N'@listOfIDs VARCHAR(1000)',
@listOfIDs;
过程或函数dbo.CSVToTable指定的参数过多。
因为您确实传递了太多参数,然后需要传递更多的参数,才能理解此运行此查询并查看真正传递给函数的信息
SELECT 'select id from [dbo].[CSVToTable] ('+@listOfIDs+')';
这将返回(这是您真正要执行的操作)
select id from [dbo].[CSVToTable] (5, 6, 7, 8, 9, 15, 28, 31, 49, 51, 59, 61)
代替(这就是您所需要的)
SELECT 'select id from [dbo].[CSVToTable] ('''+@listOfIDs+''')';
sp_executesql
比exec
好? 简而言之,EXEC
将迫使您将所有变量连接到一个字符串中,这是最糟糕的事情,并且使您的代码完全可以接受 SQL注入。请参见 Bad Habits to Kick : Using EXEC() instead of sp_executesql
,这并不意味着sp_executesql
是100%安全的,但允许在{{1}时对语句进行参数化 }不会,因此就SQL注入而言,它比EXEC()
更加安全。
最后,由于您标记了sql-server并且未指定版本,因此建议您使用 SPLIT_STRING()
函数(2016+)而不是您使用的函数,如果您没有2016+版本,而没有使用EXEC
循环创建自己的版本以获得更好的性能,则导致WHILE
循环执行速度较慢,因此应避免使用它。
示例:
答案 1 :(得分:0)
您的SQL存在一些问题,但让我们从以下内容开始:
exec('select id from [dbo].[CSVToTable] ('+@listOfIDs+')') -- error
失败的原因是因为它将SQL转换为:
select id from [dbo].[CSVToTable] (1,2,3,4)
您可以很快看到为什么这是一个问题。而是将值作为参数传递:
EXEC sp_executesql N'SELECT id FROM [dbo].[CSVToTable] (@IDs);', N'@IDs varchar(1000)', @IDs = @listOfIDs;
这将使您的代码免于注入,但是,您的函数CSVToTable
使用WHILE
意味着这将导致性能下降。如果您使用的是SQL Server 2016+,则可以访问SPLIT_SPLIT
。它会比迭代过程快得多。
如果没有,我建议看看SQL Server 2008的DelimitedSplit8K或SQL Server 2012和2014的DelimitedSplit8k_lead。您会发现它们在性能上要好得多。