【问题标题】:Stored Procedures and ORM's存储过程和 ORM
【发布时间】:2011-07-17 19:47:53
【问题描述】:

与使用 ORM(nHibernate、EF 等)处理某些 CRUD 操作相比,存储过程的目的是什么?要调用存储过程,我们只需要传递几个参数,并使用 ORM 发送整个 SQL 查询,但这只是性能和安全问题,还是有更多优势?

我问这个是因为我从未使用过存储过程(我只是用 ORM 编写所有 SQL 语句并执行它们),并且一位客户告诉我,我将在下一个项目中使用存储过程,我正在尝试弄清楚何时使用它们。

【问题讨论】:

    标签: stored-procedures


    【解决方案1】:

    我偶然发现了这个相当古老的问题,但令我震惊的是,存储过程最重要的好处竟然没有被提及。

    安全和资源保护

    使用 SP,您可以将该 SP 的执行权授予用户。用户可以执行 SP 并且只能执行该 SP。您甚至不必授予用户对所用表的读取或写入权限。用户甚至不必知道使用的表。

    使用 ORM,您必须授予对使用的表和用户的读取或/和写入访问权限。用户可以从您授予权限的所有表中读取所有数据,甚至可以将它们组合到查询中,如果您愿意的话,还可以运行会在数据库服务器上产生繁重负载的查询。

    这在应用程序开发和数据库开发由不同团队完成并且数据库由多个应用程序使用的情况下特别有用。

    【讨论】:

      【解决方案2】:

      您应该使用存储过程的主要(我想说“唯一”)原因是您是否真的需要性能。

      在数据库中创建快速执行复杂任务的“函数”似乎很诱人。但它很快就会失控。

      我曾使用过将大量业务逻辑封装在 SQL 中的应用程序,因此几乎不可能重构任何东西。数以百计的存储过程对于使用 ORM 的开发人员来说是黑盒子。

      此类应用程序变得脆弱、难以调试且难以理解。通过允许业务逻辑存在于存储过程中,您允许 SQL 开发人员在一个比 ORM 更难工作、记录和调试的工具中做出他们不应该做出的设计选择。我见过处理支付处理的存储过程。真正核心的东西。一个应用程序变得如此重要以至于没人敢碰它的东西,都是因为某个具有良好 SQL 技能的人花了 5 年时间编写了一个脚本来快速修复某个东西,它从未迁移到 ORM 并最终成长为一个无法管理的怪物,充满了没有人理解的复杂逻辑。开发人员最终不得不盲目地相信它所做的一切。更糟糕的是,它几乎总是在测试覆盖范围之外,因此您可能会在部署时破坏一切,即使您的测试通过了模拟数据,但一些古老的存储过程突然开始起作用。

      滥用存储过程是您积累的最糟糕的技术债务之一。作为持久层的数据库不应该用于业务逻辑。您应该尽可能严格地保持这种区别。

      当然,在某些情况下,ORM 的性能会很糟糕,或者根本不支持您需要的 SQL 功能。如果在原始 SQL 中做事确实不可避免,那么您才应该考虑存储过程。

      我见过存储过程地狱。你不想这样。

      【讨论】:

        【解决方案3】:

        正如 le dorfier 提到的,应该使用存储过程(和/或视图)的主要原因之一是在数据库及其客户端(Web 应用程序、报告、ETL 等)之间提供一个抽象层

        这个“DB API”可以更轻松地更改/重构您的数据库,而不必影响客户端。

        请参阅 - Why use stored procs - 进行更深入的讨论

        【讨论】:

        • 您将“'DB API'”放在引号中的确切原因是这很糟糕的确切原因。您正在将域逻辑与持久层耦合。因此,您不能使用隐喻的“DB API”而侥幸逃脱。您还假设另一种方法是将直接 ORM 或硬编码 SQL 直接放入 Web 应用程序或接近表示层的东西中。正确的替代方案(无论是使用 ORM 还是手动 ADO.Net 等)是将您的业务或数据逻辑正确地抽象到它自己的层或层中。
        【解决方案4】:

        在某些情况下,存储过程具有显着的性能优势。通常,由 Linq 和其他 ORM 生成的查询可能效率低下,但仍然足以满足您的目的。一些 RBDMS(如 SQL Server)会缓存存储过程的执行计划,从而节省查询时间。对于您经常使用的更复杂的查询,这种性能节省可能至关重要。

        不过,对于大多数普通的 CRUD,我发现如果 ORM 可用并且它的操作满足您的需求,则使用它通常会更好地维护可维护性。 Entity Framework 在 .NET 世界中的大部分时间都非常适合我(与 Linq 结合使用),我非常喜欢 Propel 的 PHP。

        【讨论】:

        • 大多数 RDBMS 缓存适合特定模式的每个查询的执行计划,并且从十多年前开始。只为存储过程而不为动态 SQL 缓存执行计划已多次被证明是错误的,甚至在 SQL Server 帮助中也如此说明。
        • 这不仅仅是关于缓存或执行计划。这是因为在 DBMS 的原生 SQL 变体中可以使用一些技巧和技术来大量优化查询和存储过程。这种类型的 SQL 不能使用 ORM 方法轻松编写。我一次又一次地看到它。底线是使用 DBMS 的本机 SQL 语言的存储过程可以比任何 ORM 生成的 SQL 快几个数量级。然而,ORM 方法更易于维护。大多数 ORM 开发人员都不是高级 SQL 方面的专家,并且对此没有任何理解/经验。
        • 根据文档msdn.microsoft.com/en-us/library/cc293623.aspx,可能值得注意的是,正确参数化的 sql 查询(文档中的“准备好的查询”)确实会被缓存和重用
        • @Manachi 您的评论假定编写存储过程的人是高级 SQL 专家。这通常也不是真的。给定两个案例:一个流行的 ORM 由数十人(如果不是数百人)开发以支持数千或数十万个用例,与一个由一两个人编写的存储过程,你认为哪一个人最专业适用于它?
        【解决方案5】:

        存储过程通常用 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 与数据库进行更深层次的对话。

        选择可能并不那么明显,因为这完全取决于您正在处理的场景。

        【讨论】:

        • 在大多数情况下,一个像样的 ORM 生成的 SQL 是完美的,只是当事情变得复杂时,它们才会崩溃。
        • @Paul,这就是我想要解释的。做一些不那么花哨的子查询+一些公式,事情可能会爆炸。无论如何,这是 CreateSQLQuery 节省一天的时间。
        • ORM 生成 SQL 的速度和能力与直接的 T-SQL 相同。 (它们还在 SQL Server 中被预编译和重用)。它也更容易和更快地编写,风险更小,更易于维护。
        • Paul 和 Ernst - 直接 SQL 和存储过程有可能比 ORM 生成的 SQL 快几个数量级,如果两者都以最好的方式编写的话。这是事实。诚然,这是可维护性与性能之间的巨大权衡。但请永远不要声称速度/功率是相同的:)
        • 95% 的时间我发现自己在做“从表中选择字段 [根据正确索引的字段进行过滤]”在这种情况下,那些认为输入更多代码 == 更多的开发人员更喜欢存储过程增值。但是 procs 对于其余情况可能非常有用。注意有多少关于这个主题的帖子往往是全有或全无。
        【解决方案6】:

        我发现它们的主要用途是实现抽象层并封装查询逻辑。就像我用过程语言编写函数一样。

        【讨论】:

          【解决方案7】:

          我主要坚持使用 linq to sql 作为 ORM,我认为它很棒,但存储过程仍有一席之地。主要是当我要运行的查询非常复杂,有很多连接(尤其是外连接,这在 Linq 中)、子查询中可能有很多聚合、递归 CTE 和其他类似场景时,我会使用它。

          不过对于一般的杂物,没有必要。

          【讨论】:

          • 好点,我不记得使用连接了。在 LINQ 中,使用连接确实很困难。顺便说一句,您知道是否所有主要的 ORM(EF、nHibernate)都可以调用存储过程并将其返回给与其关联的实体吗?
          • @guilhermeGeek EF4 具有此功能。 EF 允许您避免大多数显式连接,前提是您正确设置了主键和外键,但如果您确实需要 Linq 中的连接,主要是在语法上很尴尬。您可能会发现您不需要将连接指定为 Join,而可能需要指定为 Select 或 Linq 中的 SelectMany。
          • @Andrew,在 EF4 和 L2S 中,假设架构是使用正确的键正确定义的,但是当您无法更改或控制 DDL 时,或者当外部大量需要连接和其他更复杂的查询,有时在 linq 中纠正它不值得在可读性或性能方面付出努力,有些事情根本不可能。
          • @guilhermeGeek,Linq to SQL 将存储过程映射到实体,但在存储过程中使用动态 sql 时可能会有些麻烦。
          • 同意。如果您没有可靠的 DDL,那么尝试使用 Linq 可能会很痛苦。据说,您可以在 EF 中通过使用不太明显的数据库映射自己定义类来缓解这种情况,但我自己不必尝试这样做。我很幸运:如果数据模型坏了,我可以修复它,而不是尝试在代码中解决它。
          猜你喜欢
          • 2011-11-23
          • 1970-01-01
          • 2017-04-23
          • 2011-12-11
          • 2011-06-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-06-25
          相关资源
          最近更新 更多