SQL Server 存储过程性能影响

Posted

技术标签:

【中文标题】SQL Server 存储过程性能影响【英文标题】:SQL Server stored procedure performance impact 【发布时间】:2018-06-18 04:54:31 【问题描述】:

我想编写可用于多用途的存储过程。谁能帮助我从这些方法中找到在 SQL Server 中编写存储过程的最佳实践?它们各自对性能有何影响?

方法 1: 参数@KEY、@VALUE

IF @KEY = 'ISACTIVE'
BEGIN 
    SELECT ID, STATECODE, STATENAME, ISACTIVE 
    FROM STATE 
    WHERE ISACTIVE = @VALUE
END
ELSE IF @KEY = 'STATECODE'
BEGIN
    SELECT ID, STATECODE, STATENAME, ISACTIVE 
    FROM STATE  
    WHERE STATECODE = @VALUE
END
ELSE
BEGIN
    SELECT ID, STATECODE, STATENAME, ISACTIVE 
    FROM STATE
END

方法:2 参数@ISACTIVE,@STATECODE

SELECT ID, STATECODE, STATENAME, ISACTIVE 
FROM STATE
WHERE ISACTIVE = ISNULL(@ISACTIVE, ISACTIVE) 
  AND STATECODE = ISNULL(@STATECODE,STATECODE)

方法:3

参数@KEY、@VALUE

DECLARE @SQLQRY NVARCHAR(500)
SET @SQLQRY = 'SELECT ID, STATECODE, STATENAME, ISACTIVE FROM STATE WHERE ' + @KEY +' = ' + @VALUE
EXEC @SQLQRY

就性能和可重用性而言,哪一种方法最好?

或者还有其他选择可以实现吗?

【问题讨论】:

您应该进行基准测试....但如果这是您的瓶颈,您将遇到更大的问题.... 这张桌子的尺寸是多少?这是来自内存,但我认为如果表大小小于 64 KB,索引不会产生任何影响,因为这是 SQL Server 读取的最小数据集。也就是说,绝对不推荐方法 3,因为它容易发生 sql 注入。 就可重用性而言,第三种方法是“好的”,但就性能而言,我建议使用方法 1,因为 SP 是编译的一种,因此 SQL Server 可以在编译时决定执行计划取决于你的条件。第三种方法中存在运行时错误的可能性,在第三种方法中,由于动态查询,执行计划将在运行时确定。在方法 2 中,如果不是,则 NULL 检查可能需要一些时间。表中的行数和检查条件更多。方法 1 的唯一缺点是,如果条件没有增加,您将需要编写更多代码。 就个人而言 - 过去做过很多这样的事情 - 我会选择 2-3 个专门的查询,即使它们(现在/看起来)相似。更容易阅读 - 更容易扩展 - 更容易调试 - 更容易优化。 选项 1 的问题是参数 @VALUE 的数据类型将根据指定的键而有所不同。最好将参数类型与引用的列相匹配。 【参考方案1】:

我肯定会采用方法 2,在我看来,它既可维护又可永久使用。

方法 1 中,您正在复制代码,因此在维护方面它不是最好的方法。

方法 3 中,您使用动态 sql 查询,这不是最好的处理方式,因为它不提供智能感知支持,并且限制了 SQL 服务器的优化能力性能。

如果您遇到任何性能问题,请考虑在“ISACTIVE”和“STATECODE”列上引入索引。如果需要,还可以分析查询执行计划并使用分析器进行进一步调查。

【讨论】:

【参考方案2】:

我想写一个多用途的存储过程。

考虑到这种愿望(尽管我对这种需求有顾虑),这取决于

方法 1: 随着业务规则变得复杂,您将开始重复大量代码。当您意识到时,您有多个重复选择,可能带有多个连接或EXISTS 子句,并且可能另一个必须修改某些内容的程序员忘记编辑其中一个查询。当您开始同时应用多个过滤器时,事情会变得更加复杂(例如SELECT where IsActive = 0 AND StateCode = 'AR')。这种方法可以更轻松地根据您要过滤的键返回不同的结果集。 方法 2: 您可以将过滤器堆叠在一起,您只需编写一次 SELECT,但如果您有很多行并且过滤器适用于不同的表,您将特别影响性能。另一个问题是您可能必须为比较的两端提供替换值:如果我们只想选择在StateCode 上具有NULL 的记录,那么您将必须执行类似ISNULL(StateCode, 'SomeImpossibleStateCode') = ISNULL(@StateCode, 'SomeImpossibleStateCode') 或类似的过滤器(@StateCode IS NULL AND StateCode IS NULL) OR (@StateCode = StateCode)方法 3: 由于它是动态的,因此更难维护,依赖表/列不会出现在 sysdepends 之类的视图上,您​​可能需要采取额外措施来防止 SQL 注入。但是,它提供了完全避免条件过滤器的灵活性,这将使您的查询更简单、更高效。您甚至可以接收一个表格作为输入参数,其中包括您要应用的过滤器、链接它们的逻辑运算符(ANDOR)以及过滤器优先级。

结论:

如果您的表不大并且您可能搜索的键数量很少,方法 2 将是最好的,但您必须测试性能。

方法 3 似乎最适合处理所有场景,包括性能。

我绝对会避免 方法 1,因为它很容易出现维护错误。如果您觉得不同的键需要返回不同的列或应用不同的查询条件,那么是时候将您的全能 SP 拆分为不同的了。

【讨论】:

【参考方案3】:

必读:Dynamic Search Conditions in T‑SQL,作者 Erland Sommarskog。方法 2 不好,但可以成为最好的。

这里是链接文章的简短摘要。

方法 1 从性能的角度来看是好的。优化器可以生成良好的计划并使用可用的索引。 这里的问题是查询的可能变体/排列的数量。如果您有三个参数 Key1、Key2、Key3,它将变成一个相当难看的嵌套 IF 三层深度。如果再添加几个参数,从维护的角度来看,这将变得不切实际。

方法 2 从维护的角度来看是好的,但从性能的角度来看,“原样”是不好的。即使@ISACTIVE 为NULL,ISACTIVE = ISNULL(@ISACTIVE, ISACTIVE) 也会被评估,并且可能会妨碍索引的有效使用。

查询应该这样重写:

SELECT ID, STATECODE, STATENAME, ISACTIVE 
FROM STATE
WHERE
    (ISACTIVE = @ISACTIVE OR @ISACTIVE IS NULL) 
    AND (STATECODE = @STATECODE OR @STATECODE IS NULL)
OPTION(RECOMPILE);

OPTION(RECOMPILE) 非常重要。本质上,它允许优化器将参数的实际值内联到查询中,简化逻辑表达式并生成计划,这与方法 1 中的一样好或更好。有关详细信息,请参阅文章。

方法 3 会产生好的计划,就像方法 1 中那样,虽然不像方法 2 那样最好。维护也不错,尽管动态 SQL 有其困难。如果你走这条路,你应该阅读 Erland The Curse and Blessings of Dynamic SQL 的另一篇文章。

【讨论】:

以上是关于SQL Server 存储过程性能影响的主要内容,如果未能解决你的问题,请参考以下文章

SQL 存储过程中注释的性能影响

SQL Server 存储过程性能测试

SQL Server 2005:“保护”存储过程免受 MS Access 使用的 FMTONLY 模式的影响

理解性能的奥秘——应用程序中慢,SSMS中快——SQL Server如何编译存储过程

SQL Server 存储过程

SQL Server 存储过程