c# - 什么时候写 "ad hoc sql"比存储过程更好

标签 c# sql sql-server-2008

这个问题在这里已经有了答案:





What are the pros and cons to keeping SQL in Stored Procs versus Code [closed]

(47 个回答)


5年前关闭。




我的应用程序中有 100% 的临时 sql。我的一个 friend 建议我转换为存储过程以获得额外的性能和安全性。这在我的脑海中提出了一个问题,除了速度和安全性之外,还有其他理由坚持使用即席 sql 查询吗?

最佳答案

SQL Server 缓存临时查询的执行计划,因此(扣除第一次调用所花费的时间)这两种方法在速度方面是相同的。

通常,使用存储过程意味着获取应用程序所需的一部分代码(T-SQL 查询)并将其放在不受源代码控制的地方(可以,但通常不是)并且其他人可以在您不知情的情况下对其进行更改。

将查询放在像这样的中心位置可能是一件好事,这取决于有多少不同的应用程序需要访问它们所代表的数据。我通常发现将应用程序使用的查询保留在应用程序代码本身中要容易得多。

在 1990 年代中期,传统观点认为 SQL Server 中的存储过程是处理性能关键情况的方法,而在当时确实如此。然而,这个 CW 背后的原因很长一段时间都没有成立。

更新:此外,经常在关于存储过程的可行性的争论中,需要防止 SQL 注入(inject)被调用以保护 procs。当然,没有人认为通过字符串连接组装临时查询是正确的做法(尽管如果您连接用户输入,这只会使您面临 SQL 注入(inject)攻击)。显然,ad hoc 查询应该被参数化,不仅是为了防止 sql 注入(inject)攻击的底层怪物,而且只是为了让你作为程序员的生活更轻松(除非你喜欢弄清楚何时使用 single引用您的值(value)观)。

更新 2:我做了更多的研究。基于 this MSDN white paper ,看来答案完全取决于您对查询的“临时”的含义。例如,像这样的简单查询:

SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

... 将缓存其执行计划。此外,因为查询不包含某些不符合条件的元素(就像从一个表中简单的 SELECT 之外的几乎任何东西),SQL Server 实际上会“自动参数化”查询并用参数替换文字常量“5”,并缓存参数化版本的执行计划。这意味着,如果您随后执行此临时查询:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23

...它将能够使用缓存的执行计划。

不幸的是,自动参数化的不合格查询元素列表很长(例如,忘记使用 DISTINCTTOPUNIONGROUP BYOR 等),所以你真的不能指望这是为了性能。

如果您确实有一个不会被自动参数化的“ super 复杂”查询,例如:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5 OR ITEM_COUNT < 23

...它仍然会被查询的确切文本缓存,因此如果您的应用程序使用相同的文字“硬编码”值重复调用此查询,则第一个查询之后的每个查询都将重新使用缓存的执行计划(和因此与存储过程一样快)。

如果文字值发生变化(基于用户操作,例如过滤或排序查看的数据),那么查询将不会从缓存中受益(除非偶尔它们不小心与最近的查询完全匹配)。

从“ad-hoc”查询缓存中获益的方法是将它们参数化。在 C# 中动态创建查询,如下所示:
int itemCount = 5;
string query = "DELETE FROM tblSTUFF WHERE ITEM_COUNT > " + 
        itemCount.ToString();

是不正确的。正确的方法(使用 ADO.Net)是这样的:
using (SqlConnection conn = new SqlConnection(connStr))
{
    SqlCommand com = new SqlCommand(conn);
    com.CommandType = CommandType.Text;
    com.CommandText = 
        "DELETE FROM tblSTUFF WHERE ITEM_COUNT > @ITEM_COUNT";
    int itemCount = 5;
    com.Parameters.AddWithValue("@ITEM_COUNT", itemCount);
    com.Prepare();
    com.ExecuteNonQuery();
}

该查询不包含文字并且已经完全参数化,因此使用相同参数化语句的后续查询将使用缓存计划(即使使用不同的参数值调用)。请注意,此处的代码实际上与您用于调用存储过程的代码相同(唯一的区别是 CommandType 和 CommandText),因此在某种程度上归结为您希望该查询的文本“存活”的位置"(在您的应用程序代码或存储过程中)。

最后,如果通过“临时”查询,您的意思是您正在使用不同的列、表、过滤参数等动态构建查询,例如:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`

SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS 
    WHERE AGE >= 18 AND LASTNAME LIKE '%What the`
    ORDER BY LASTNAME DESC

...那么你几乎不能用存储过程来做到这一点(没有 EXEC 在上流社会中不会被提及的黑客),所以这一点没有实际意义。

更新 3:这是使用存储过程的唯一真正好的与性能相关的原因(无论如何我都能想到)。如果您的查询是一个长时间运行的查询,其中编译执行计划的过程花费的时间比实际执行时间长得多,并且该查询仅被很少调用(例如月度报告),那么将其放入存储过程可能使 SQL Server 将已编译的计划在缓存中保留足够长的时间,以便它在下个月左右仍然存在。不管是真是假,我都打不过。

关于c# - 什么时候写 "ad hoc sql"比存储过程更好,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2734007/

相关文章:

PHP 和 SQLite 3,多个返回行

SQL选择,匹配一列但不匹配另一列

asp.net - 在 VPS 服务器上配置和托管 asp.net 网站?

c# - C# Web 代理中的 SSL;如何确定请求是否为 SLL?

mysql - SQL 从其他表中选择具有相应值的列

c# - 在 Windows 下以编程方式禁用网络摄像头

java - JPQL 查询转换为 SQL 查询

sql-server - 存储过程中 DROP 和 SELECT INTO 的默认架构

c# - 赋值不会使用 lambda 表达式修改 List ForEach 函数中的变量

c# - 将 Entity Framework 的多对多关系保存到 MySQL