假设您出于某种原因不能使用 LINQ,将查询放在存储过程中是一种更好的做法,还是对数据库执行即席查询(例如,为了参数而使用 SQL Server)也是一种很好的做法?
22 回答
根据我主要编写 WinForms 客户端/服务器应用程序的经验,这些是我得出的简单结论:
使用存储过程:
- 对于任何复杂的数据工作。如果您要执行真正需要游标或临时表的操作,通常在 SQL Server 中执行此操作是最快的。
- 当您需要锁定对数据的访问时。如果您不向用户(或角色或其他)授予表访问权限,则可以确定与数据交互的唯一方法是通过您创建的 SP。
使用临时查询:
- 当您不需要限制数据访问(或以其他方式这样做)时用于 CRUD。
- 用于简单搜索。为一堆搜索条件创建 SP 是一件痛苦且难以维护的事情。如果您可以生成相当快的搜索查询,请使用它。
在我的大多数应用程序中,我都使用了 SP 和 ad-hoc sql,尽管我发现我使用 SP 的次数越来越少,因为它们最终成为像 C# 一样的代码,只是更难进行版本控制、测试和维护。我建议使用 ad-hoc sql,除非你能找到不这样做的具体原因。
除了 SQL Server,我无法与其他任何东西交谈,但性能参数在那里并不显着有效,除非您使用的是 6.5 或更早版本。大约十年来,SQL Server 一直在缓存 ad-hoc 执行计划。
我认为这是必须维护数据库的人和开发用户界面的人之间的基本冲突。
作为一名数据人员,我不会考虑使用通过即席查询访问的数据库,因为它们很难有效地调整或管理。我如何知道对架构的更改会产生什么影响?此外,出于安全原因,我不认为用户应该被授予对数据库表的直接访问权限(我不仅指 SQL 注入攻击,还因为它是一个基本的内部控制,不允许直接权限并要求所有用户仅使用为应用程序设计的 proc。这是为了防止可能的欺诈。任何允许直接插入、更新或删除表权限的金融系统都存在巨大的欺诈风险。这是一件坏事。)。
数据库不是面向对象的,从面向对象的角度来看看起来不错的代码从数据库的角度来看可能非常糟糕。
我们的开发人员告诉我们,他们很高兴我们所有的数据库访问都是通过 procs,因为它可以更快地修复以数据为中心的错误,然后只需在生产环境中运行 proc,而不是创建代码的新分支并重新编译和重新加载到生产中。我们要求我们所有的过程都处于颠覆状态,所以源代码控制根本不是问题。如果它不在 Subversion 中,它会定期被 dbas 删除,因此使用源代码控制没有阻力。
存储过程代表一个软件契约,它封装了对数据库采取的行动。过程中的代码,甚至数据库本身的模式都可以更改,而不会影响已编译、部署的代码,因此过程的输入和输出保持不变。
通过在应用程序中嵌入查询,您将自己与数据模型紧密耦合。
出于同样的原因,简单地创建存储过程也不是好的做法,这些存储过程只是对数据库中每个表的 CRUD 查询,因为这仍然是紧密耦合。相反,这些过程应该是庞大的、粗粒度的操作。
从安全的角度来看,最好在应用程序中禁止 db_datareader 和 db_datawriter,只允许访问存储过程。
存储过程绝对是要走的路……它们是编译好的,事先有执行计划,你可以对它们进行权限管理。
我不了解存储过程的整个源代码控制问题。你绝对可以对它们进行源代码控制,只要你有一点纪律。
始终从作为存储过程源的 .sql 文件开始。编写代码后将其置于版本控制中。下次您要编辑存储过程时,请从源代码管理中获取它,而不是从数据库中获取它。如果您遵循这一点,您将拥有与您的代码一样好的源代码控制。
我想在这里引用 Oracle 的 Tom Kyte 的话……这是他关于在哪里编写代码的规则……虽然有点不相关,但我想很高兴知道。
- 从 PL/SQL 中的存储过程开始...
- 如果您认为在 PL/SQL 中使用存储过程无法完成某些事情,请使用 Java 存储过程。
- 如果您认为使用 Java 存储过程无法完成某些事情,请考虑使用 Pro*c。
- 如果您认为使用 Pro*C 无法实现某些目标,您可能需要重新考虑需要完成什么。
在我们的应用程序中,有一层代码提供查询的内容(有时是对存储过程的调用)。这使我们能够:
- 轻松将所有查询置于版本控制之下
- 对不同数据库服务器的每个查询进行所需的更改
- 在我们的代码中消除了相同查询代码的重复
访问控制是在中间层实现的,而不是在数据库中,所以我们不需要存储过程。这在某些方面是临时查询和存储过程之间的中间道路。
我在另一篇文章中的回答:存储过程更易于维护,因为:
- 每当您想更改某些 SQL 时,您不必重新编译您的 C# 应用程序
- 您最终会重用 SQL 代码。
当您尝试构建可维护的应用程序时,代码重复是您可以做的最糟糕的事情!
当您发现需要在多个地方更正的逻辑错误时会发生什么?您更容易忘记更改复制和粘贴代码的最后一个位置。
在我看来,性能和安全性的提升是一个额外的优势。您仍然可以编写不安全/低效的 SQL 存储过程。
更容易移植到另一个数据库 - 无需移植
编写所有存储过程以在另一个数据库中创建并不是很困难。事实上 - 它比导出表更容易,因为没有需要担心的主键/外键。
两者都有说服力的论据 - 存储过程都位于中央存储库中,但(可能)难以迁移,临时查询更容易调试,因为它们与您的代码一样,但它们也可能更难在代码。
存储过程更有效的论点不再成立。 链接文本
为存储过程与动态查询做一个谷歌将显示出体面的论点,并且可能最适合您做出自己的决定......
应该尽可能多地使用存储过程,如果您将 SQL 编写到代码中,您已经让自己在未来感到头疼。编写 SPROC 所花费的时间与编写代码所花费的时间大致相同。
考虑一个在中等负载下运行良好的查询,但一旦它进入全时生产,您的优化不佳的查询会影响系统并使其陷入困境。在大多数 SQL 服务器中,您不是唯一使用它的应用程序/服务。你的应用程序现在已经把一群愤怒的人带到你家门口。
如果您在 SPROC 中进行查询,您还可以让友好的 DBA 管理和优化,而无需重新编译或破坏您的应用程序。请记住,DBA 是该领域的专家,他们知道该做什么,不该做什么。利用他们更多的知识是有意义的!
编辑:有人说重新编译是一个懒惰的借口!是的,让我们看看当您必须重新编译并将您的应用程序部署到 1000 台桌面时您感觉多么懒惰,这一切都是因为 DBA 告诉您您的临时查询占用了太多服务器时间!
这里需要考虑一些事情:无论如何,谁需要存储过程?
显然,这取决于您自己的需求和偏好,但是在面向公众的环境中使用临时查询时要考虑的一件非常重要的事情是安全性。始终对它们进行参数化,并注意 SQL 注入攻击等典型漏洞。
有人说重新编译是一个懒惰的借口!是的,让我们看看当您必须重新编译并将您的应用程序部署到 1000 台桌面时您感觉多么懒惰,这一切都是因为 DBA 告诉您您的临时查询占用了太多服务器时间!
如果让 1000 个桌面直接连接到数据库,它是一个好的系统架构吗?
存储过程很棒,因为它们可以在不重新编译的情况下更改。我会尽可能多地使用它们。
我只对根据用户输入动态生成的查询使用 ad-hoc。
由于其他人提到的原因,使用分析器或 proc 的一部分调整 proc 更容易。这样您就不必告诉某人运行他的应用程序来找出发送到 SQL Server 的内容
如果您确实使用临时查询,请确保它们已参数化
参数化的 SQL 或 SPROC ...从性能的角度来看并不重要...您可以查询优化任何一个。
对我来说,SPROC 剩下的最后一个好处是,我可以通过只授予我的登录权限来执行 sprocs 来消除很多 SQL 权限管理......如果你使用参数化 SQL,那么带有连接字符串的登录有更多的权限(写任何例如,他们也可以访问其中一个表上的一种选择语句)。
不过,我仍然更喜欢参数化 SQL ......
我还没有找到任何令人信服的论据来使用临时查询。尤其是那些与你的 C#/Java/PHP 代码混在一起的。
sproc 性能参数没有实际意义——前 3 个顶级 RDBM 使用查询计划缓存并且已经使用了一段时间。它已被记录在案……还是 1995 年仍然存在?
然而,在你的应用程序中嵌入 SQL 也是一个糟糕的设计——代码维护对于许多人来说似乎是一个缺失的概念。
如果一个应用程序可以使用 ORM 从头开始(新开发的应用程序很少!)这是一个很好的选择,因为您的类模型驱动您的数据库模型 - 并节省了大量时间。
如果 ORM 框架不可用,我们会采用混合方法来创建 SQL 资源 XML 文件,以便在需要时查找 SQL 字符串(然后由资源框架缓存它们)。如果 SQL 需要任何小的操作,它在代码中完成 - 如果需要主要的 SQL 字符串操作,我们重新考虑该方法。
这种混合方法便于开发人员进行管理(也许我们是少数,因为我的团队足够聪明,可以阅读查询计划)并且部署是从 SVN 进行的简单检查。此外,它使切换 RDBM 更容易 - 只需换出 SQL 资源文件(当然不像 ORM 工具那么容易,但可以连接到遗留系统或不受支持的数据库)
取决于你的目标是什么。例如,如果您想检索项目列表并且它在应用程序的整个运行期间发生一次,那么使用存储过程可能不值得。另一方面,重复运行并需要(相对)较长时间执行的查询是数据库存储的绝佳候选者,因为性能会更好。
如果您的应用程序几乎完全存在于数据库中,那么存储过程是不费吹灰之力的。如果您正在编写一个数据库只是无关紧要的桌面应用程序,那么临时查询可能是更好的选择,因为它将您的所有代码保存在一个地方。
@Terrapin:我认为您断言不必重新编译应用程序来进行修改这一事实使存储过程成为一个更好的选择是不可行的。可能有理由选择存储过程而不是临时查询,但在没有其他任何令人信服的情况下,编译问题似乎是懒惰而不是真正的原因。
我的经验是 90% 的查询和/或存储过程根本不应该编写(至少手工编写)。
数据访问应该以某种方式自动生成。您可以决定是在编译时静态生成过程还是在运行时动态生成过程,但是当您想将列添加到表(对象的属性)时,您应该只修改一个文件。
我更喜欢将所有数据访问逻辑保留在程序代码中,其中数据访问层执行直接的 SQL 查询。另一方面,数据管理逻辑我以触发器、存储过程、自定义函数等形式放入数据库中。我认为值得数据库化的一个例子是数据生成——假设我们的客户有一个名字和一个姓氏。现在,用户界面需要一个 DisplayName,它源自一些重要的逻辑。对于这一代,我创建了一个存储过程,然后在更新行(或其他源数据)时由触发器执行。
似乎存在这种有点普遍的误解,即数据访问层是数据库,有关数据和数据访问的所有内容都“仅仅因为”而进入那里。这完全是错误的,但我看到很多设计都源于这个想法。不过,也许这是一种局部现象。
在看到这么多设计糟糕的 SP 之后,我可能只是对 SP 的想法不感兴趣。例如,我参与的一个项目对他们遇到的每个表和每个可能的查询都使用了一组 CRUD 存储过程。在这样做时,他们只是添加了另一个完全没有意义的层。甚至想到这样的事情都是痛苦的。
这些天我几乎没有使用存储过程。我只将它们用于无法在代码中轻松完成的复杂 sql 查询。
主要原因之一是存储过程不适用于 OR 映射器。
这些天来,我认为您需要一个很好的理由来编写不使用某种 OR 映射器的业务应用程序/信息系统。
存储过程作为代码块工作,因此代替即席查询它可以快速工作。另一件事是存储过程提供重新编译选项,这是 SQL 的最佳部分,您只需将其用于存储过程,在即席查询中就没有这样的功能。
查询和存储过程的一些结果是不同的,这是我个人的经验。使用强制转换和隐蔽功能进行检查。
大项目必须使用存储过程来提高性能。
我的项目中有 420 个程序,对我来说效果很好。我在这个项目上工作了 3 年。
因此,任何交易都只能使用程序。
如果让 1000 个桌面直接连接到数据库,它是一个好的系统架构吗?
不,显然不是,这可能是一个糟糕的例子,但我认为我试图说明的观点很明确,您的 DBA 负责管理您的数据库基础设施,这是他们的专长,在代码中填充 SQL 会锁住他们和他们的专长的大门。