【发布时间】:2011-07-17 19:47:53
【问题描述】:
与使用 ORM(nHibernate、EF 等)处理某些 CRUD 操作相比,存储过程的目的是什么?要调用存储过程,我们只需要传递几个参数,并使用 ORM 发送整个 SQL 查询,但这只是性能和安全问题,还是有更多优势?
我问这个是因为我从未使用过存储过程(我只是用 ORM 编写所有 SQL 语句并执行它们),并且一位客户告诉我,我将在下一个项目中使用存储过程,我正在尝试弄清楚何时使用它们。
【问题讨论】:
与使用 ORM(nHibernate、EF 等)处理某些 CRUD 操作相比,存储过程的目的是什么?要调用存储过程,我们只需要传递几个参数,并使用 ORM 发送整个 SQL 查询,但这只是性能和安全问题,还是有更多优势?
我问这个是因为我从未使用过存储过程(我只是用 ORM 编写所有 SQL 语句并执行它们),并且一位客户告诉我,我将在下一个项目中使用存储过程,我正在尝试弄清楚何时使用它们。
【问题讨论】:
我偶然发现了这个相当古老的问题,但令我震惊的是,存储过程最重要的好处竟然没有被提及。
安全和资源保护
使用 SP,您可以将该 SP 的执行权授予用户。用户可以执行 SP 并且只能执行该 SP。您甚至不必授予用户对所用表的读取或写入权限。用户甚至不必知道使用的表。
使用 ORM,您必须授予对使用的表和用户的读取或/和写入访问权限。用户可以从您授予权限的所有表中读取所有数据,甚至可以将它们组合到查询中,如果您愿意的话,还可以运行会在数据库服务器上产生繁重负载的查询。
这在应用程序开发和数据库开发由不同团队完成并且数据库由多个应用程序使用的情况下特别有用。
【讨论】:
您应该使用存储过程的主要(我想说“唯一”)原因是您是否真的需要性能。
在数据库中创建快速执行复杂任务的“函数”似乎很诱人。但它很快就会失控。
我曾使用过将大量业务逻辑封装在 SQL 中的应用程序,因此几乎不可能重构任何东西。数以百计的存储过程对于使用 ORM 的开发人员来说是黑盒子。
此类应用程序变得脆弱、难以调试且难以理解。通过允许业务逻辑存在于存储过程中,您允许 SQL 开发人员在一个比 ORM 更难工作、记录和调试的工具中做出他们不应该做出的设计选择。我见过处理支付处理的存储过程。真正核心的东西。一个应用程序变得如此重要以至于没人敢碰它的东西,都是因为某个具有良好 SQL 技能的人花了 5 年时间编写了一个脚本来快速修复某个东西,它从未迁移到 ORM 并最终成长为一个无法管理的怪物,充满了没有人理解的复杂逻辑。开发人员最终不得不盲目地相信它所做的一切。更糟糕的是,它几乎总是在测试覆盖范围之外,因此您可能会在部署时破坏一切,即使您的测试通过了模拟数据,但一些古老的存储过程突然开始起作用。
滥用存储过程是您积累的最糟糕的技术债务之一。作为持久层的数据库不应该用于业务逻辑。您应该尽可能严格地保持这种区别。
当然,在某些情况下,ORM 的性能会很糟糕,或者根本不支持您需要的 SQL 功能。如果在原始 SQL 中做事确实不可避免,那么您才应该考虑存储过程。
我见过存储过程地狱。你不想这样。
【讨论】:
正如 le dorfier 提到的,应该使用存储过程(和/或视图)的主要原因之一是在数据库及其客户端(Web 应用程序、报告、ETL 等)之间提供一个抽象层
这个“DB API”可以更轻松地更改/重构您的数据库,而不必影响客户端。
请参阅 - Why use stored procs - 进行更深入的讨论
【讨论】:
在某些情况下,存储过程具有显着的性能优势。通常,由 Linq 和其他 ORM 生成的查询可能效率低下,但仍然足以满足您的目的。一些 RBDMS(如 SQL Server)会缓存存储过程的执行计划,从而节省查询时间。对于您经常使用的更复杂的查询,这种性能节省可能至关重要。
不过,对于大多数普通的 CRUD,我发现如果 ORM 可用并且它的操作满足您的需求,则使用它通常会更好地维护可维护性。 Entity Framework 在 .NET 世界中的大部分时间都非常适合我(与 Linq 结合使用),我非常喜欢 Propel 的 PHP。
【讨论】:
存储过程通常用 SQL 的方言编写(T-SQL for SQL Server、PL-SQL Oracle 等)。那是因为他们为 SQL 添加了额外的功能以使其更强大。 另一方面,你有一个 ORM,比如说 NH 生成 SQL。
ORM 生成的 SQL 语句与编写 T-SQL 存储过程的速度或能力不同。 这就是进退两难的地方:我是否需要与 SQL 数据库供应商绑定的超快速应用程序,难以维护,或者我是否需要灵活,因为我需要针对多个数据库,并且我更喜欢通过编写 HQL 查询而不是 SQL 来缩短开发时间那些?
存储过程比 SQL 语句更快,因为它们在数据库引擎中预编译,并缓存了执行计划。您无法在 NH 中执行此操作,但您有其他选择,例如使用缓存级别 1 或 2。
另外,尝试使用 NH 进行批量操作。存储过程在这些情况下工作得很好。您需要考虑 SP 与数据库进行更深层次的对话。
选择可能并不那么明显,因为这完全取决于您正在处理的场景。
【讨论】:
我发现它们的主要用途是实现抽象层并封装查询逻辑。就像我用过程语言编写函数一样。
【讨论】:
我主要坚持使用 linq to sql 作为 ORM,我认为它很棒,但存储过程仍有一席之地。主要是当我要运行的查询非常复杂,有很多连接(尤其是外连接,这在 Linq 中)、子查询中可能有很多聚合、递归 CTE 和其他类似场景时,我会使用它。
不过对于一般的杂物,没有必要。
【讨论】: