纯粹动态调用SQL存储过程的方法(sp_executesql中的动态参数)?

时间:2014-08-19 21:22:31

标签: sql-server ssms

当从外部语言(例如c#)调用SQL Server存储过程时,编码调用的方式使得给定的存储过程可以在元数据中完全描述并使用通用函数调用。调用命令对象上的参数 collection 可以支持不同过程之间不同数量的参数。

如果想要在SQL Server中完全实现类似的功能,如果使用sp_executesql(可以在这里找到一个相关的示例:Dynamic Search Conditions in T‑SQL)....你可以获得最多那里的方式,但主要问题是函数调用中的参数必须是硬编码的。

文章中的例子:

EXEC sp_executesql @sql, @paramlist,                               
               @orderid, @fromdate, @todate, @minprice,        
               @maxprice,  @custid, @custname, @city, @region, 
               @country, @prodid, @prodname   

在此示例中,SQL语句存储在@sql中,参数 list 存储在@paramList中,以下是实际参数列表。< / p>

@sql@paramList都是简单的nvarchar变量,可以通过编程方式设置(通过读取元数据并分配给变量),但实际参数本身是硬编码的。

所以,问题是:

有没有办法指定实际参数&amp;这样的值,使整个函数的实现完全通用?

2 个答案:

答案 0 :(得分:0)

参数列表可以以逗号分隔(或特殊字符分隔)列表的形式发送,然后可以将列表解析到表中。 在这个例子中,我使用&#34;,&#34;和&#34; =&#34;。

这是我最初的解决方案:

DECLARE @List VARCHAR(MAX) = 'a=1,b=3,c=hey,d=12/05/10,val5='
DECLARE @Delimiter1 VARCHAR(1) = ','
DECLARE @Delimiter2 VARCHAR(1) = '='
----
SELECT y.i.value('(./text())[1]', 'nvarchar(4000)') pass1
INTO #Buffer
FROM ( 
    SELECT x = CONVERT(XML, '<i>' 
        + REPLACE(@List, @Delimiter1, '</i><i>') 
        + '</i>').query('.')
) a 
CROSS APPLY x.nodes('i') y(i)

SELECT ROW_NUMBER()OVER(ORDER BY(SELECT 1)) rn,y.i.value('(./text())[1]', 'nvarchar(4000)') pass2
INTO #PreResult
FROM ( 
    SELECT x = CONVERT(XML, '<i>' 
        + REPLACE(b.pass1, @Delimiter2, '</i><i>') 
        + '</i>').query('.')
    FROM #Buffer b
    WHERE b.pass1 LIKE '%=%' AND b.pass1 NOT LIKE '%=%=%' -- to make sure assignment has place and there is no double or more assignments
) a 
CROSS APPLY x.nodes('i') y(i)

SELECT @List '@List'
--SELECT '' '#Buffer',* FROM #Buffer b
--SELECT '' '#PreResult',* FROM #PreResult p

SELECT p.pass2 [Variable],p2.pass2 [Value]
FROM #PreResult p
INNER JOIN #PreResult p2 ON p2.rn = p.rn + 1
WHERE p.rn%2 > 0

DROP TABLE #Buffer
DROP TABLE #PreResult

更聪明的人:

DECLARE @List VARCHAR(MAX) = 'a=1,b=3,c=hey,d=12/05/10,val5='

DECLARE @Delimiter1 VARCHAR(1) = ','
DECLARE @Delimiter2 VARCHAR(1) = '='

SELECT v.v.value('(./text())[1]', 'nvarchar(4000)') [Variable],n.n.value('(./text())[1]', 'nvarchar(4000)') [Value]
FROM ( 
    SELECT x = CONVERT(XML, 
        '<a><v>' + REPLACE(REPLACE(@List,@Delimiter1,'</n></a><a><v>'),@Delimiter2,'</v><n>') + '</n></a>'
    ).query('.')
) a 
CROSS APPLY x.nodes('a') y(a)
CROSS APPLY y.a.nodes('v') v(v)
CROSS APPLY y.a.nodes('n') n(n)

最好的方法是使用参数列表发送XML,然后将此XML解析到表中。

如果您有任何疑问,请与我们联系。

<强>更新: 所以在这里你只需要提供一个值 - 参数列表及其值。 在查询中,您可以随心所欲地做任何事情。

DECLARE @sql NVARCHAR(MAX),@paramlist NVARCHAR(MAX)

SET @sql = N'
DECLARE @Delimiter1 VARCHAR(1) = '',''
DECLARE @Delimiter2 VARCHAR(1) = ''=''

SELECT v.v.value(''(./text())[1]'', ''NVARCHAR(4000)'') [Variable],n.n.value(''(./text())[1]'', ''NVARCHAR(4000)'') [Value]
INTO #Values
FROM ( 
    SELECT x = CONVERT(XML, 
        ''<a><v>'' + REPLACE(REPLACE(@List,@Delimiter1,''</n></a><a><v>''),@Delimiter2,''</v><n>'') + ''</n></a>''
    ).query(''.'')
) a 
CROSS APPLY x.nodes(''a'') y(a)
CROSS APPLY y.a.nodes(''v'') v(v)
CROSS APPLY y.a.nodes(''n'') n(n)

/*Do whatever you want with the values*/
/*There even could be a stored proc call based on parameters provided*/
SELECT v.Value FROM #Values v WHERE v.Variable = ''c''


DROP TABLE #Values
'
SET @paramlist = '@list nvarchar(max)'

DECLARE @List VARCHAR(MAX) = 'a=1,b=3,c=hey,d=12/05/10,val5='

EXEC sp_executesql @sql, @paramlist, @list=@List

答案 1 :(得分:0)

可以这样做,但我还不知道是否存在性能影响,并且一些方法对sql注入开放。

这里显示了一些例子,在第二个问题中,使用不同的语法(其中一些有利于纯动态SQL,有些则不是),具体询问性能:

Performance differences calling sp_executesql with dynamic SQL vs parameters