Performance differences calling sp_executesql with dynamic SQL vs parameters
给出:
1 2 3 4 5
| CREATE PROCEDURE [dbo].[my_storedproc]
@param1 int, @param2 varchar(100)
AS
<<whatever>>
GO |
这些不同的执行方法之间是否存在已知的性能差异?:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| -- Method #1:
declare @param1 int = 1
declare @param2 varchar(100) = 'hello'
exec my_storedproc @param1, @param2
-- Method #2:
exec my_storedproc @param1=1, @param2='hello'
-- Method #3:
declare @param1 int = 1
declare @param2 varchar(100) = 'hello'
declare @procname nvarchar(100) = N'my_storedproc @param1, @param2'
declare @params nvarchar(4000) = N'@param1 int, @param2 varchar(100)'
exec sp_executesql @procname, @params, @param1, @param2
-- Method #4:
declare @procname nvarchar(4000) = N'my_storedproc @param1=1, @param2=''hello'''
exec sp_executesql @procname
-- Method #5:
declare @procname nvarchar(4000) = N'my_storedproc 1, ''hello'''
exec sp_executesql @procname
-- Method #6:
declare @procname nvarchar(4000) = N'my_storedproc 1, ''hello'''
exec (@procname) |
"你为什么问?"你问?我正在尝试找到一种完全基于元数据来一般地执行存储过程的方法,该控制存储过程将物理执行所有其他配置的(在元数据中)存储过程,除了元数据中定义的内容外,对它们一无所知。在此控制器SP中,我无法(从任何实际意义上)知道并声明可能必须调用的每个可能存储的proc所需的特定物理参数(及其所需的数据类型)-我正在尝试找到一种执行它们的方法完全通用,同时仍然希望保持良好的性能(重用查询计划等)。
- 我不知道为什么这么多人认为这是个好主意。您所描述的是可以执行任何其他存储过程的单个存储过程。这就像在.NET中创建可以执行任何操作的单个方法一样。当然可以,但是代价是性能。每当您需要对存储过程执行任何操作时,它首先都必须解析一堆东西来弄清楚实际要做什么。
-
有趣的是,SQL人员实际上在这里不能理解其基本原理-这不仅不疯狂,它还是ORM工具所基于的原理。"这就像在.NET中创建一个可以执行任何操作的单一方法",不是,您仍然有单独的存储过程。"代价将是性能。"是吗?这就是问题所在。这就是批评者对带有ORM的动态SQL的评价,事实证明它们是不正确的。
-
是的,但是ORM是动态生成sql的。而且我还没有看到可以生成出色的sql的ORM。这非常复杂,这就是为什么sql总是如此难以管理的原因。我认为带有这种附加抽象层的sql会更加疯狂。我会非常谨慎地使用您描述的方法3或4,因为在不使用参数的情况下很难防止sql注入。而且方法1的通用性不足以实现您要执行的操作。
-
据我所知,1和2是相同的,仅必须编译外部查询(假设该过程具有缓存的计划),3、4和5也相同,因为您必须编译两个SQL,外部查询和内部查询(需要一个更好的词),可能不会对单个查询造成重大损害,但是您可能会得到plan肿的计划缓存。另外3和4看起来很难使类型安全。当您尝试执行称为[my_storedproc 1, ''hello'']的存储过程时,第六种方法将失败。
-
如果6应该是exec (N'my_storedproc 1, ''hello'''),则它与5相同。您打算如何调用泛型存储过程,该泛型存储过程将调用所有其他过程?如果想要像行为那样的ORM,那么为什么要重新发明轮子,那么为什么不使用ORM呢?
-
方法4是BY FAR对我来说最容易实现的方法,但关注点是性能和SQL注入。但是,这是一个完全内部的系统,将设置适当的权限,并且我将对注射进行一些基本检查,因此不必过于担心。我更担心较重查询的性能。
-
@GarethD这将按需(代理数据服务器方案)或按计划的过程(非工作时间数据提取方案)调用-它不是ORM,它只是基于相同的原理。
-
大多数人将SSIS用于每晚的SQL作业。这不是一个选择吗?您调用的存储过程真的会每天变化很多,而您无法设置类似的东西吗?
-
我建议,如果您要做的事情可能会影响整个系统的性能,那么您首先应该进行一些深入的研究。在您阅读有关SQL Server内部知识的书籍,关于SQL Server性能调整的书籍以及关于如何阅读执行计划的书籍之前,您不应该尝试了解这一点。您需要了解它是如何创建执行计划的以及其打算开展业务的方式的含义。您使用的是哪个SQL Server后端,也会有所不同,因为它们已经改变了版本之间对这些内容的解释方式。
-
要开始使用,请:amazon.com/Microsoft-Server-Internals-Developer-Reference/dp??/…
-
上面针对SQL Server的不同版本,本书有不同版本,但是alawys寻找由Karen Delaney编写的版本。
-
@Jeremy这样设计的目的是使技能相对较低的管理员可以注册新的数据源,从而避免了像SSIS专业人士这样的复杂技能的需求。
这6个选项之间实际上应该没有性能差异,因为它们全部执行存储过程,而不是直接执行任何SQL语句。
但是,没有比在您自己的系统上测试性能更好的指示了。您已经有6个测试用例,因此尝试每个用例应该不难。
Within this controller SP, I cannot (in any practical sense) know and declare the specific physical parameters (with their required data types) required for every possible stored proc that might have to be called
为什么不呢?我看不到为什么无法基于以下两个查询之一的输出动态生成方法2和3的SQL:
1 2 3 4 5 6 7 8
| SELECT OBJECT_NAME(sp.[object_id]), *
FROM sys.parameters sp
WHERE sp.[object_id] = OBJECT_ID(N'dbo.my_storedproc');
SELECT isp.*
FROM INFORMATION_SCHEMA.PARAMETERS isp
WHERE isp.[SPECIFIC_NAME] = N'my_storedproc'
AND isp.[SPECIFIC_SCHEMA] = N'dbo'; |
使用该信息,您可以创建一个表,以包含每个proc的每个参数的各种参数值。实际上,您甚至可以将其设置为对所有变体都具有" global "值的某些参数,然后某些参数值就是特定proc的变体。
- 所有SQL都存储为元数据,它是普通SQL和存储过程调用的混合,其中一些引用远程SQL服务器。
-
@tbone问题文本和示例仅涉及此设置所调用的存储过程。如果安装程序还将调用动态SQL,则这6个选项中的一些选项之间可能会有性能差异。然后当然,即使有参数,也无法通过我列出的两个查询来发现这些参数。
-
我认为,将这个问题移到SQLCLR可以消除我对此问题(包括通过sp_executesql通过参数实现的动态sql性能)的担忧。您在两个查询示例中都提出了要点,但我还必须考虑基于非SP的查询文本。但是,通过SQLCLR正确构造查询之后,我现在希望我正在执行的性能特征与任何标准ORM基本上相同(大多数DBA似乎讨厌它,但是我认为并没有太多理由)只要正确使用它们。)
-
@tbone这不是实际的工作原理。执行计划与查询的全文相关(使用二进制比较:所有内容均敏感)。非参数化SQL(没有参数的EXEC和SqlCommand)将为任何参数值的任何更改生成新计划,因为这会更改查询文本。参数化的SQL(带有参数的sp_executesql和SqlCommand)可以重用计划,因为更改参数值不会更改查询文本。 SQLCLR不会改变这一点。 ORM通常使用sp_executesql,但不允许对查询进行微调,因此为什么不喜欢它们。
-
我认为,只要我将动态sql呈现为参数化查询,就可以在许多情况下(至少从我所读的情况下)获得计划重用。但是,我认为您必须遵循一些非常严格的准则。我认为就我而言,这是过早的优化,但我希望对此有所了解。
-
@tbone是的,我在上一条评论中说的是:"参数化的SQL(带有参数的sp_executesql和带有SqlCommand通过SqlParamater的参数的)可以重用计划,因为更改参数值不会更改查询文本"。我不确定您认为还需要其他哪些准则。但是,将sp_executesql与参数一起使用,使用ORM(只要将sp_executesql与参数一起使用)或使用SQLCLR(将是SqlCommand)和SqlParameter之间没有区别。这些都是参数化查询。