存储过程是全球最大的IT软件咨询公司之一的不良做法?


148

我在全球三大IT咨询公司之一的一个项目中工作,一名DBA告诉我,公司的最佳实践的状态存储过程不是“最佳实践”。这与我学到的一切都太相反了。

存储过程为您提供代码重用和封装(软件开发的两个支柱),安全性(您可以为单个存储的proc授予/撤消权限),保护您免受SQL注入攻击并还有助于提高速度(尽管DBA表示从SQL Server 2008开始,即使常规SQL查询运行了足够的时间,它们也会被编译。

我们正在使用敏捷软件开发方法开发复杂的应用程序。任何人都可以考虑为什么不想使用存储的proc的充分理由吗?我的猜测是,DBA不想维护那些存储的proc,但是似乎有太多负面因素无法证明这种设计决策是正确的。


3
它增加了什么代码重用?如果您的客户端使用其他数据库该怎么办。必须丢弃所有这些SP并从头开始。不要保护您免受sql注入。在大多数情况下,速度是最小的。
钻机2012年

32
请记住,大多数大型IT咨询公司都有动机在使自己的资产蒙受损失的同时最大化可计费时间。在这些公司中有影响力的老手也往往是参与者和官僚,而不是技术人员。我会从咨询公司中拿出一些钱来做这样的事情-我已经不止一次地通过完善其“最佳”做法来使咨询公司摆脱困境。
ConcernedOfTunbridgeWells

1
@Rig代码重用与针对任何语言的功能一样被添加-通过将代码包装在可重用的容器中。当然,只要您不执行已构建的字符串,存储过程实际上就可以保护您免受SQL注入。要说速度是最小的,似乎根本没有任何根据。大多数情况不会在性能收益上归为同一类,但差异很大。
Garet Claborn 2015年

1
@GaretClaborn但是,重新构建应用程序层的可能性要比多年的历史数据库数据高得多。无论如何,在任何非平凡的应用程序上。如果这样做,您将花费数月时间移植存储过程丰富的代码。除了在极端情况下之外,向项目中再添加一个依赖项几乎没有什么好处。那些确实存在,但是在大多数情况下,这只是增加了项目敏捷性和代码重用的障碍。
钻机

2
从我们几乎专门使用sps的背景来看,我可以告诉您远离它们并使用ORM之类的实体框架的好处。业务逻辑被封装在过程中的次数太多了。虽然可以使用某些工作和/或第三方工具对proc进行版本控制。在TFS或GIT之类的框架中,这样做并不容易。发出的数据库代码与RDBMS提供程序无关。因此,您以后可以不用担心而关闭RDBMS提供程序。
ewahner

Answers:


280

根据我从事大型项目的经验,您必须非常清楚业务逻辑的用途。如果您允许单个开发人员将业务逻辑放在他们认为合适的业务对象层或存储过程中的环境,那么大型应用程序将变得非常难以理解和维护。

存储过程对于加快某些数据库操作非常有用。我的体系结构决定是将所有逻辑保留在应用程序的业务层中,并以有针对性的方式采用存储过程来提高性能,以进行基准测试表明有必要。


37
我看不到那么简单。对我来说,这就是所有业务逻辑。具有或不具有存储过程的数据库都提供某些服务并提供某些保证。理想情况下,错误的应用程序代码应该不可能使数据库处于不一致状态。如果需要存储过程来保持这种一致性,则可以使用它们。
凯文·克莱恩

12
@kevin cline:定义“不一致状态”。我同意诸如引用完整性之类的数据库功能很有价值,并且可以大大减少发生严重损坏的应用程序错误的可能性。但是,一般而言,“一致数据”的定义取决于正确执行业务规则。
Eric J.

16
将我的百万加到Mayo的百万。分布式业务逻辑使您摆脱了良好实践的道路,直奔疯癫之路
Nico

27
使用存储过程时,+ 1业务逻辑渗入DAL是一个非常重要的问题。
系统停机时间

30
@ChristopherMahan,我永远都不想使用您设计的数据库。从数据库角度来看,这是最糟糕的做法。数据库通常直接受到数据库的影响。认为有人将使用业务层来更新一百万条记录或随着时间推移发生的其他事情是短视的。导入通常不会经过业务层(是的,我想在业务层一次处理2100万条记录的导入记录)。当您在数据库级别没有约束时,欺诈会容易得多。错误数据几乎可以肯定100%。
HLGEM 2012年

163

一些观察

存储过程为您提供代码重用和封装(软件开发的两个支柱),

仅当您在应该使用它们的上下文中正确使用它们时。关于功能(在结构化编程中)或方法(在面向对象的编程中),可以说相同的主张,但是,我们看到了1K函数和兆资产对象。

工件不会给您带来这些好处。正确使用这些工件可以带来这些好处。

安全性(您可以授予/撤消对单个存储过程的权限),

是。这是一个好点,也是我喜欢存储过程的主要原因之一。与仅使用视图和用户帐户通常可以实现的访问控制相比,它们提供了更好的粒度访问控制。

保护您免受SQL注入攻击,

这不是特定于SP的,因为您可以通过参数化的SQL语句和输入清理获得相同级别的保护。但是,除了“深度安全性”问题之外,我还将使用SP 。

并且还有助于提高速度(尽管DBA表示,从SQL Server 2008开始,如果常规SQL查询运行了足够的时间,则它们也会被编译)。

这是特定于数据库供应商的,但是总的来说,您的DBA是正确的。确实会编译SQL语句(静态或参数化)。如果您希望/需要聚合和计算您无法使用简单的SQL语句完成的数据,但是与SQL紧密集成并且不保证往返于应用程序服务器,则SP可提供帮助。

一个很好的例子是将数据查询到一个或多个临时游标中,以便从中运行另一个SQL本身。您可以在应用服务器中以编程方式进行操作,也可以通过在数据库中进行操作来保存多次往返。

但是,这不应该成为常态。如果您遇到许多情况,则表明数据库设计不正确(或者您正在跨部门从不太兼容的数据库架构中提取数据)。

我们正在使用敏捷软件开发方法开发复杂的应用程序。

敏捷性与软件工程流程和需求管理有关,而与技术无关。

任何人都可以考虑为什么不想使用存储的proc的充分理由吗?

错误的问题

这个问题是错误的,等同于询问“是否有充分的理由不使用GOTO”?在这个问题上,我与尼克劳斯·沃斯(Niklaus Wirth)的支持远胜于迪克斯特拉(Dijkstra)。我可以理解Dijkstra的情绪来自何处,但我认为这并非在所有情况下均100%适用。与商店进程和任何技术相同。

如果工具可以很好地用于其预期目的,并且是完成特定任务的最佳工具,那么它就是好工具。否则使用它并不表示该工具是错误的,而是表明焊工不知道他/她在做什么。

正确的问题是“应避免使用哪种类型的存储过程使用模式”。或者,“在什么条件下我(或不应)使用存储过程”。寻找使用技术的原因只是将责任归咎于工具,而不是将工程责任直接归咎于工程师。

换句话说,这是一种无知或无知的声明。

我的猜测是,DBA不想维护那些存储的proc,但是似乎有太多负面因素无法证明这种设计决策是正确的。

然后,他们正在做的事情是将不良的工程决策结果投射到他们使用不足的工具上。

您该怎么办?

我的经验是,在罗马时,就像罗马人一样

不要打架 如果您公司的员工希望将存储过程贴上标签,这是一种不良做法,请让他们使用。但是请注意,这可能是其工程实践中的一个危险信号。

通常在带有大量无能的程序员的组织中将事物标记为不良实践。通过将某些事物列入黑名单,组织试图限制由于自身能力不足而在内部造成的损害。我不拉你

概括是所有失败的源泉。说存储过程(或任何类型的技术)是不好的做法,这是一种概括。概括是无能的解决方案。工程师不能进行公然的概括。他们会根据具体情况进行分析,权衡分析并根据手头的事实执行工程决策和解决方案,以解决问题。

好的工程师不会以这种笼统的方式将事情标记为不良实践。他们着眼于问题,选择合适的工具,进行权衡。换句话说,他们从事工程。

我对如何不使用它们的看法

  • 不要在其中进行数据收集(甚至可能进行一些转换)以外的复杂逻辑。可以在其中放置一些数据按摩逻辑,或者将多个查询的结果聚合在一起。就是这样。超出此范围的任何内容都应视为应该驻留在其他地方的业务逻辑。

  • 不要将它们用作防御SQL注入的唯一机制。您可以将它们留在这里,以防万一坏事发生,但是在它们前面应该有一系列防御性逻辑-客户端验证/清理,服务器端验证/清理,可能转换成对您有意义的类型域模型,最后传递给参数化的语句(可以是参数化的SQL语句或参数化的存储过程。)

  • 不要使数据库成为唯一包含存储过程的地方。您应该像对待C#或Java源代码一样对待您的商店proc。也就是说,源代码控制您存储过程的文本定义。人们对存储过程的抱怨不能被源代码控制-废话,他们只是不知道他们在说什么该死的地狱。

我对如何/在何处使用它们的看法

  • 您的应用程序需要从多个查询或视图中转置或聚合的数据。您可以将其从应用程序卸载到数据库中。在这里,您必须进行性能分析,因为a)数据库引擎在执行这些操作方面比应用程序服务器更有效,但是 b)(有时)应用程序服务器更易于水平扩展。

  • 细粒度的访问控制。您不希望某些笨蛋在数据库中运行笛卡尔联接,但是您不能仅仅禁止人们像这样执行任何SQL语句。一种典型的解决方案是允许在开发和UAT环境中使用任意SQL语句,而在systest和生产环境中禁止使用它们。任何必须使其进入systest或生产环境的语句都会进入存储过程,由开发人员和dbas进行代码审查。

运行不在存储过程中的SQL语句的任何有效需求都将通过不同的用户名/帐户和连接池(对使用情况进行高度监视和劝阻)。

  • 在像Oracle这样的系统中,您可以访问LDAP或创建到外部数据库的符号链接(例如,通过vpn在业务伙伴的db上调用存储过程。)执行意大利面条式代码的简便方法,但这对于所有编程范例都是正确的,有时您有特定的业务/环境要求,这是唯一的解决方案。Store proc有助于将这种烦恼单独封装在一个地方,靠近数据,而不必遍历应用服务器。

您是在数据库上作为存储过程还是在应用服务器上运行此程序,取决于工程师(工程师)必须进行的权衡分析。两种选择都必须进行分析,并通过某种类型的分析进行论证。通过简单地指责另一种替代方法是“不好的做法”而走了一条路,这只是一个la脚的工程解决方案。

  • 在您根本无法扩展应用服务器的情况下(例如,没有新硬件或云实例的预算),但是数据库后端具有足够的容量(这是很多人都愿意承认的典型情况),移动业务逻辑以存储proc。它不漂亮,可能会导致贫乏的领域模型……但随后又要……权衡分析,这是大多数软件黑客都难以接受的事情。

无论这是否成为永久性解决方案,这都是特定于该特定时刻观察到的约束。

希望能帮助到你。


14
这是一个很好的答案。
yfeldblum 2011年

5
好的答案,但这是否具有讽刺意味?“概括是所有失败的源头。”
bedwyr

2
是的,不。我的评论旨在用于OP在其原始问题中提到的特定句子(存储过程不是“最佳实践”。)对存储过程的最佳或不良实践进行粗略描述是一种概括。无视他们可以很好的背景OR);坏的可以(而且往往会导致)拧UPS架构或设计解决方案时
luis.espinal

7
+1表示“通常将标签标记为不良做法,通常是在拥有大量无能的程序员的组织中进行的。” -在那里经历了那段经历,包括一位开发经理告诉我他认为我对一个棘手的问题有很好的解决方案,但是如果他被允许我实施,那么它将为开发人员打开闸门。木偶。
朱莉娅海沃德2014年

1
@Shane你是对的。但是,我相信此答案试图传达的是某些工程师群体倾向于通过拜访不良实践卡片来原谅他们缺乏知识或分析的倾向。不过,对于我们较缺乏经验的人,答案可能会有所改善。
塞萨尔·埃尔南德斯

56

这样做的理由是,依赖于存储过程层会限制可移植性,并将您与某个数据库绑定在一起。也有增加维护成本的原因。我也想对您提出的这一点发表评论:

(存储过程)保护您免受SQL注入攻击

实际上,这是保护您的参数化查询,您可以在纯文本sql查询中轻松地做到这一点。


18
而且,如果您存储的proc正在使用任何类型的动态sql和字符串参数,那么您就回到了起点。
JeffO 2011年

4
区别在于可以基于每个过程为存储过程设置访问权限,对于参数化的SQL查询,您必须依赖程序员的理智,+ "blablabla"因为您必须允许使用普通的SQL,这才是控制的终点。
编码器

19
我从未理解过“将您与某个数据库联系起来”的说法。您多久采用一次程序并将其迁移到完全不同的数据库?
梅森惠勒2012年

11
@MasonWheeler-每次+1。在任何足够大的项目中,您的应用程序最终都是根据给定数据库产品的缺点编写的。无论如何,转换为另一个数据库都成为一项主要工作,因为新数据库将具有不同的可能性!
Michael Kohne 2012年

6
@HLGEM-但是在COTS世界中,一开始会期望有多个DB(实际上,您选择了兼容的DB)。不是移植,而是支持不同的后端,这与移植完全不同。
Michael Kohne 2013年

46

我同意存储proc的某些原因不是最佳做法。

  • 业务和应用程序逻辑应在代码中而不在数据库中。将逻辑放在数据库中令人担忧。
  • 您无法使用其余的应用程序逻辑像常规单元测试项目中那样无缝地测试存储的proc。
  • 在编写代码时,我发现存储的proc不利于测试首次编程。
  • 在IDE中调试程序时,存储的proc不像应用程序代码那样容易调试。
  • SP的版本控制/源代码控制与常规代码

7
您可以像对存储过程进行测试优先编程一样容易。

5
嗯,嗯... 1)使用db存储过程不一定意味着要在其中存储业务逻辑。2)存储的proc是一些最容易进行单元测试的东西。3)存储过程不一定遵循测试优先实践,这是正确的,但并非所有可计算的事物都可以测试优先。4)调试应该不是问题,因为存储过程应该只包含易于验证的SQL语句和游标。同样,调试应该通过首先对代码中的SQL语句进行测试和调试来进行,然后再移至存储过程中……仅是IMO btw。
luis.espinal 2011年

6
您显然不是数据库开发人员。源代码管理(IDE)-如果您使用的是TOAD或类似的IDE,以及版本控制,则非常容易调试SP。
gbjbaanb

6
2)关于单元测试存储过程。关于其他单元测试框架的idk,但至少与MS Test(VisualStudio.TestTools.UnitTesting)结合使用,在存储的proc上运行任何Assert方法至少需要Db连接,这从定义上讲使它比单元更像一个集成测试测试。并且存储的过程可以在全局数据库级别上引用关于数据库的状态。这些可能不是伪造的或具有接口。
T. Webster

3
+1此外,存储过程语言(pl / sql,t-sql,plpgsql等)非常笨拙且冗长。对于我来说,使用脚本语言来建立数据库连接并处理数据库外部的业务逻辑要容易得多。

22

存储过程为您提供代码重用和封装(软件开发的两个支柱),

是的,但是以能够满足其他敏捷设计目标为代价。一方面,它们更难以维护。如果我参与的项目有任何迹象,您最终可能会得到多个不兼容的SP,这些SP基本上完成了相同的工作,而没有任何好处。

保护您免受SQL注入攻击,

不,他们没有。正如我经常听到的那样,我什至无法开始猜测这个想法可能从何而来,这根本不是事实。它可以缓解某些类型的SQL注入攻击,但是如果您首先不使用参数化查询,那将无关紧要。我仍然可以'; DROP TABLE Accounts; -

并且还有助于提高速度(尽管DBA表示,从SQL Server 2008开始,如果常规SQL查询运行了足够的时间,则它们也会被编译)。

通常,在使用准备好的参数化语句时(至少与我使用过的几个DB一起使用),也会对它们进行编译。在您的应用程序开始执行查询时(或特别是当您多次执行相同的准备好的查询时),您认为SP所具有的任何性能优势都完全没有意义。

使用存储过程恕我直言的唯一原因是,当您必须进行一个复杂的,多阶段的查询时,该查询将从多个整理的数据源中提取。SP不应包含低级决策逻辑,并且它们绝不应简单地封装否则为简单的查询。没有好处,只有很多缺点。

听您的DBA。他知道发生了什么事。


1
Red Gate有一个用于SQL Server 的产品SQL Source Control,但我同意,将逻辑推入存储的procs是确保您的重要逻辑不受任何版本控制的绝佳方法。
Carson63000 2011年

17
@greyfade- “我还没有看到SP的源代码控制” -您在跟我开玩笑吗?商店proc只是您在数据库引擎中上载的血腥文本文件(它会对其进行处理,编译并安装以执行)。在我工作过的每个存储proc的地方,我们都将商店proc源代码存储在其中,例如CVS,clearcase或使用的任何SCM。说存储过程不能被源代码控制(因为它们在db中)就像说我的应用程序源代码(Java,C#或其他任何东西)不能被源代码控制,因为它是在生产环境中编译和部署的。
luis.espinal 2011年

2
@ luis.espinal:我没有说他们不能处于源代码控制中。我只是说我不知道​​专门用于维护SP历史的工具,这意味着要在数据库中维护该历史。请不要只因为您看错了而对我at之以鼻。
greyfade 2011年

1
所有opur存储的proc都在源代码管理下,只是因为您过去曾看到过不良的表述并不意味着它们是使用存储proc所固有的。
HLGEM 2011年

1
@ luis.espinal,是否可以稍后从数据库中检索存储过程的源头,这很典型?如果是这样,您只需拥有一个可以定期将其拔出的工具,然后使用其他工具即可从头开始重新创建安装。偶尔执行一次以确保其准确性。

17

这是几年前我在五巨头之一工作时的官方电话。理由是,由于SP绑定到特定的实现(PL / SQL,T / SQL和...),因此它们不必要地限制了技术选择。

经历了一个大型系统从T / SQL到PL / SQL的迁移之后,我可以理解这一论点。我认为这有点让人难过-究竟有多少个地方突然从一个数据库移动到另一个数据库?


10
@DaveE:对于企业解决方案,您可能是对的。如果要创建打包的软件,则一旦在MSSQL上发布,您最大的希望就是希望它在Oracle上运行。
Eric J.

3
@Eric:太正确了。我现在所处的位置,我们使用了大量的SP,并告诉人们“不”,如果他们不想要MSSQL。能够做到这一点真是太好了。
DaveE

3
@DaveE:销售团队是否希望您说“是”?
Eric J.

2
将一个系统从一个数据库迁移到另一个数据库并不是那么多,而是让一个系统能够使用客户已经拥有的任何数据库系统。大数据库很昂贵。

@EricJ:是的,但是一旦他们看到了佣金的成本,请求就消失了。
DaveE 2011年

17

我为这三家公司工作的所有公司都使用存储过程来实现SQL Server的应用程序逻辑。相反,我还没有真正看到过事情。但是对我来说,它们真是一团糟。通常没有很好的错误处理工具或带有存储过程的代码重用工具。

假设您有一个存储过程返回了要使用的数据集,那么如何在以后的存储过程中使用它呢?SQL Server上的机制不是很好。EXEC INTO ...仅适用于一层或两层嵌套(我现在忘记了)。或者,您必须预定义工作表并对其进行键控。或者,您需要预先创建一个临时表并让过程填充它。但是,如果两个人在两个他们从未计划同时使用的不同过程中将临时表称为同一件事,该怎么办?在任何普通的编程语言中,您都可以从函数返回数组或指向它们之间共享的对象/全局结构(函数语言除外,在函数语言中您将返回数据结构而不是仅更改全局结构... )

代码重用如何?如果您开始将通用表达式放入UDF(甚至更糟的子查询)中,则会使代码停顿下来。您无法调用存储过程来为列执行计算(除非您使用游标,将列值一个接一个地传递,然后以某种方式更新表/数据集)。因此,基本上要获得最佳性能,您需要在整个维护工作的噩梦中到处剪切/粘贴通用表达式...使用编程语言,您可以创建一个函数来生成通用SQL,然后在构建时从任何地方调用它SQL字符串。然后,如果您需要调整公式,则可以在一个地方进行更改...

错误处理如何?SQL Server有许多错误会立即停止存储过程的执行,甚至会导致断开连接。自2005年以来,出现了try / catch,但是仍然存在许多无法捕获的错误。同样,错误处理代码上的代码重复也会发生同样的事情,您真的无法像大多数编程语言一样容易地传递异常或将异常冒泡到更高的层次。

也只是速度。数据集上的许多操作都不面向SET。如果您尝试进行面向行的操作,则要么使用游标,要么使用“游标”(当开发人员经常逐行查询每一行并将内容像游标一样存储到@变量中时)。 ..即使这通常比FORWARD_ONLY游标要慢)。在使用SQL Server 2000之前,我已经运行了1个小时,而我杀死了它。我在Perl中重写了该代码,并在20分钟内完成了代码。当比C慢20-80倍的脚本语言在性能上抽走SQL时,您肯定没有业务用SQL编写面向行的操作。

现在,SQL Server确实具有CLR集成,如果您使用CLR存储过程,则很多此类问题都会消失。但是出于安全考虑,许多DBA都不知道如何编写.NET程序或关闭CLR,并坚持使用Transact SQL。 。

通常,最难扩展的是数据库。如果您所有的业务逻辑都在数据库中,那么当数据库变得太慢时,您将遇到问题。如果您有业务层,则可以添加更多缓存和更多业务服务器以提高性能。传统上,另一台安装Windows / Linux并运行.NET / Java的服务器比购买另一台数据库服务器并为更多SQL Server授予许可要便宜得多。但是,SQL Server现在确实提供了更多的群集支持,最初它实际上没有任何支持。因此,如果您确实有很多钱,则可以添加群集,甚至可以做一些日志传送来制作多个只读副本。但是总的来说,这将比仅在高速缓存中写入内容或其他东西花费更多。

还要看一下Transact-SQL工具。字符串操作?我每天都会上Java String Class / Tokenizer / Scanner / Regex类。哈希表/链接列表/等等。我将使用Java Collection框架等。对于.NET也是相同的。C#和Java比Transact SQL更加进化。 。

此外,存储过程对于处理大型数据集和应用多个查询/条件以将其缩小后再返回到业务层更为有效。如果您必须向客户端应用程序发送一堆庞大的数据集并在客户端上分解数据,那将比仅在服务器上完成所有工作效率低得多。

存储过程也对安全性有好处。您可以削减对基础表的所有访问,仅允许通过存储过程进行访问。使用XML之类的现代技术,您可以具有执行批处理更新的存储过程。然后,通过存储过程控制所有访问,因此只要它们是安全/正确的,数据就可以具有更高的完整性。

由于我们已经在编程语言方面进行了参数化查询,因此SQL注入参数不再真正适用。同样,即使在参数化查询之前,大多数情况下,在大多数情况下也可以使用replace(“'”,“''”)来工作(尽管仍然有一些技巧可以使用字符串末尾来获得所需的内容)。

总的来说,我认为SQL和Transact SQL是查询/更新数据的好语言。但是对于编码任何类型的逻辑,请执行字符串操作(或heck文件操作...。您会对使用xp_cmdshell可以执行的操作感到惊讶。...)请不要。我希望找到一个将来不主要使用存储过程的地方。从代码可维护性的角度来看,这是一场噩梦。如果您要切换平台,也会发生什么(尽管实际上,如果您购买了Oracle / DB2 / Sybase / Sql Server / etc。您也可以通过使用所有可以帮助您的专有扩展来获得所有功能。 ..)。

同样令人惊讶的是,业务逻辑常常是不同的。在理想情况下,您会将所有逻辑放入存储过程中并在应用程序之间共享。但是,逻辑常常会因应用程序而异,并且您的存储过程最终变得过于复杂,人们害怕更改并且不了解其所有含义。使用良好的面向对象语言,您可以编写一个数据访问层,该层具有一些标准接口/钩子,每个应用程序都可以根据自己的需要进行覆盖。


6
但是,我不禁要对整个面向集合的问题与程序问题进行思考。我已经看到数据库游标在各种情况下都用到了,但这种方法简直就是疯了。我亲自将面向显式的基于游标的SQL(在特定情况下为Oracle PL / SQL)替换为面向集合的查询,并看到结果在一秒钟之内返回,而不是8分钟。我花了30分钟的时间来剖析并“获取”该1,000行光标代码。产生的SQL查询简洁,优雅,简单。人们过低且过快地低估了其数据库服务器的功能。
克雷格2014年

12

您如何在服务器上对存储过程进行版本控制?

如果您从版本控制将存储过程重新部署到服务器,则会炸毁存储的执行计划。

存储过程不应直接在服务器上进行修改,否则您如何知道当前正在实际运行的是什么?如果不是,则部署工具需要访问权限以将存储过程写入数据库。您必须在每个构建上进行部署(执行计划可能需要有所不同)

尽管存储过程是不可移植的,但一般来说SQL都不是(从未见过的oracle日期处理-uggghhh)。

因此,如果您希望具有可移植性,请构建一个内部数据访问API。您可以像调用函数一样调用此函数,并且可以在内部使用所需的语言内置参数化查询,并且可以对其进行版本控制。


6
您如何在服务器上对存储过程进行版本控制?-您可以控制商店proc源代码的版本。当需要进行部署时,您可以(从给定的基准)获取存储过程,然后(或您的dba)部署到生产环境。重新部署(无论是测试还是生产)肯定会炸毁存储的执行计划,但这将独立于您是否对SP进行源控制而进行。
luis.espinal 2011年

1
@BarryBrown如果人们可以直接访问服务器并可以更改存储过程,则该方法将无效。我必须有一个监视SP的过程,或者在每次使用前都要进行检查……
Christopher Mahan,2012年

2
如果您只是在服务器上随意更改存储过程而又没有将更改提交给源代码控制,那么即使您不知道这样做,您也会遇到一个过程问题,几乎肯定会影响您的命令式代码开发。
克雷格

1
我过去做过的一件事是将数据库服务器的开发实例放在单个开发人员的工作站上,或者如果不可能,那么至少要有数据库的“开发”和“生产”实例,所有DDL和DML脚本以及示例数据和加载脚本都位于源代码树中它们自己的目录下,并且通常使用MAKE文件从这些脚本中构建数据库。开发人员也可以使用nmake来构建单个存储的proc。如果他们不将其置于源代码控制之下,它将消失在他们身上,他们知道这一点。
克雷格

1
……我并不是故意要在我先前的评论中贬低“……,即使您不知道……”。我要传达的意思是,如果这种事情在存储过程中发生,那么它也可能在项目的其他部分中发生。我个人不喜欢集成开发环境中的集成源代码控制,部分原因是我认为,这使人们在思考对团队和整个项目进行更改并将这些更改提交到源代码管理时真正意味着什么时变得懒惰资料库。我认为这些事情不应是“自动的”。
克雷格2014年

9

这与我学到的一切都太相反了。

您可能需要更多。[笑]严重的是,存储的处理程序已下降至少10年了。自从n层取代客户端服务器以来,几乎已经有了。仅通过采用Java,C#,Python等OO语言加速了这种下降。

这并不是说存储过程没有其拥护者和拥护者-但这是一个长期的讨论和辩论。这不是新事物,并且可能还会持续相当长的一段时间。IMO,存储过程的对手显然是赢家。

存储过程为您提供代码重用和封装(软件开发的两个支柱)

非常真实 但是,体面设计的OO层也是如此。

安全性(您可以授予/撤消对单个存储过程的权限)

尽管可以做到,但由于严重的限制,很少有人这样做。数据库级别的安全性不够精细,无法做出上下文感知的决策。由于性能和管理开销,通常也无法建立每个用户的连接-因此,您的应用程序代码中仍需要一定级别的授权。您可以使用基于角色的登录名,但是您将需要为新角色创建它们,维护您作为哪个角色运行,切换连接以执行“系统级”工作,例如登录等。最后,如果您的应用是拥有-与数据库的连接也拥有。

保护您免受SQL注入攻击

只不过是进行参数化查询。无论如何,您都要这样做。

并且还有助于提高速度(尽管DBA表示,从SQL Server 2008开始,如果常规SQL查询运行了足够的时间,则它们也会被编译)。

我认为这始于MSSQL 7或2000。关于存储proc和内联SQL性能的争论,度量和错误信息很多-我将其全部归类于YAGNI。并且,如果您确实需要它,请进行测试。

我们正在使用敏捷软件开发方法开发复杂的应用程序。任何人都可以考虑为什么不想使用存储的proc的充分理由吗?

我想不出您想要的许多原因。Java / C#/任何第3 GL语言在封装,重用和可扩展性等方面都比T-SQL强大得多。考虑到ORM不错,大多数都是免费的。

另外,考虑到“根据需要分配,但不能更多”的建议,我认为这些天的举证责任在SP倡导者身上。使存储过程繁重的一个常见原因是T-SQL比OO更容易,并且商店比T.SQL具有更好的T-SQL开发人员。或者,DBA停在数据库层,而存储的proc是dev和DBA之间的接口。或者,您正在运送半定制产品,并且存储的过程可以由用户定制。缺乏类似的考虑,我认为这些天任何敏捷软件项目的默认设置都将是ORM。


1
如果您不必从数据库中转移出巨大的数据集来做简单的事情,那么有很多方法可以提高性能。测量并根据需要进行优化。

精确地 存储过程可以像手术刀一样使用。绝对保证数据库服务器内部的I / O比数据库服务器和中间层之间的I / O具有更多的带宽。而且,您不会在中间层编写比在数据库服务器中编写的数据库引擎开发人员更快,更有效的数据联接代码。如果您要将1,000,000行数据传输到中间层以进行联接(我已经肯定地看到过),那您就应该被弄糊涂了……就像那些声称您应该“编写自己的回滚代码”的家伙一样。疯狂。
克雷格

1
不要低估您的数据库服务器。了解如何正确使用它。
克雷格

1
FWIW,您不需要存储的proc就可以在数据库端进行联接。而且,如果将游标用于过程逻辑,则可能已经失去了性能之争。放弃存储过程肯定与放弃SQL或基于集合的解决方案不同。
Mark Brackett 2014年

1
完全正确,我实际上是在支持SQL,而不是专门针对sproc。但是,将SQL嵌入命令式代码中也不一定是幸福的关键,对吗?这常常引起整个ORM争论,然后导致我指出ORM驱动的数据库访问与仅学习如何使用SQL之间的性能比较。我都看到和听到的地方,比方说,甲骨文顾问建议保持所有的负载可能关闭数据库服务器,从而导致重(和昂贵的严重!)中间件系统的糟糕表现。
Craig

4

考虑到以上所有情况,我想再添加一个。SP的选择也可能取决于人们的选择。

当人们在SP中放置非常复杂的逻辑时,我个人感到沮丧,并且我相信此类SP的维护和调试非常复杂。甚至在很多情况下,开发人员本人都面临着在后台代码(例如语言部分)中进行调试比在SP中容易得多的问题。

SP仅应用于简单操作。那是我的选择。


4

我想同时介绍存储过程的一些利弊。我们在LedgerSMB中广泛使用它们,并且我们的规则是,通过一些非常具体的扩展,“如果是查询,则将其存储为proc。”

我们这样做的原因是为了促进跨语言查询的重用。老实说,没有更好的方法。

最后,问题总是在细节上。如果使用得当,存储过程会使事情变得容易得多,而如果使用不当,它们会使事情变得更加困难。

因此,在不利方面。

  1. 正如传统上使用的那样,存储过程很脆弱。单独使用它们可以为您添加代码错误的可能性,这些错误是在您除了调用语法发生更改之外的其他所有意外地方。单独使用会有点问题。层之间的内聚性太大,这会引起问题。

  2. 是的,如果执行任何动态SQL,则可以进行过程内SQL注入。在这个领域过分自信是一件坏事,因此需要在该领域的安全方面拥有丰富的经验。

  3. 由于上面的原因1,对存储过程的接口更改有些问题,但是如果涉及大量的客户端应用程序,这可能会成为一个巨大的噩梦。

以上是很难否认的。他们发生了。支持SP和反对SP的每个人都可能有关于这些的恐怖故事。这些问题并非无法解决,但是如果您不关注它们,就无法解决(在LedgerSMB中,我们使用服务定位器在运行时动态构建SP调用,从而完全避免了上述问题。)只能对其他数据库执行类似的操作)。

积极向上。假设您可以解决上述问题,您将获得:

  1. 设置操作中提高清晰度的可能性。如果查询很大或非常灵活,则尤其如此。这也导致增强的可测试性。

  2. 如果我已经在该领域工作过服务定位器,那么我发现存储过程可以加快开发速度,因为它们使应用程序开发人员摆脱了数据库问题,反之亦然。在做正确的事情上有一些困难,但是做起来并不难。

  3. 查询重用。

哦,您在SP中几乎应该做不到的几件事:

  1. 非交易逻辑。您发送了一封电子邮件,说明订单已经发货,但是交易已回滚...或者现在您正在等待继续使电子邮件服务器联机....更糟糕的是,您回滚交易只是因为您无法到达电子邮件服务器...

  2. 许多小查询松散地串在一起,并散布着程序逻辑。


强烈同意,例如:将非事务性垃圾排除在存储过程之外。在该电子邮件示例中,无论如何,应将电子邮件消息放入队列并进行异步处理。谈论设置自己以使其在负载下产生巨大的性能影响和时髦的行为,从而使数据库事务依赖于邮件服务器的响应吗?kes!
克雷格2014年

3

你为谁工作?

答案可能取决于您雇用的人,咨询公司或公司本身。对于公司而言,最好的选择通常对咨询公司或其他软件供应商来说不是最好的选择。例如,一家精明的公司希望拥有比竞争对手持久的优势。相比之下,软件供应商希望能够以最低的成本为特定行业的所有企业提供相同的解决方案。如果他们在此方面取得成功,那么客户将没有净竞争优势。

在这种特殊情况下,应用程序来来往往,但企业数据库却永远存在。RDBMS要做的主要事情之一是防止垃圾数据进入数据库。这可能涉及存储过程。如果逻辑是好的逻辑并且极不可能每年更改,那么无论使用何种应用程序编写使用该数据库的逻辑,为什么都不应将其保留在数据库中,并使其内部保持一致?几年后,有人会问他们想问数据库的问题,如果阻止垃圾邮件进入数据库,这将是可以回答的。

因此,这可能与您的DBA在咨询公司工作有关。他们使代码的可移植性越强,它们在客户端之间重用代码的可能性就越大。他们可以在应用程序中使用的逻辑越多,公司与供应商的联系就越多。如果他们在此过程中留下了很大的混乱,他们将获得报酬以清理它,或者再也看不到混乱了。无论哪种方式对他们来说都是胜利。

在此处输入图片说明

有关围栏两侧的更多讨论,请阅读恐怖编码上的讨论。FWIW我倾向于那些主张SP的人。


1
该答案的重点是将是否使用存储过程的问题与您为谁工作以及他们的动机是什么相关联。下注。相反,答案应该集中在将存储过程的优缺点与是否使用存储过程联系起来。如果答案集中在SP阻止垃圾进入数据库的想法,那我就不会低估。为了公开的目的,我不同意,但我不会拒绝。
yfeldblum 2011年

您还链接了2004年的一篇文章,恕我直言,此后情况发生了很大变化。OR / M已经变得越来越普遍。Ruby / Rails ActiveRecord,MS推出了linq和EF,Django适用于python等
。– Brook

@Justice,他是对的,不过,storageprocs领域的最佳实践是否取决于公司的性质以及他们所扮演的角色。例如,存储的proc允许您对proc本身而不是直接在表上设置权限。如果您正在进行任何财务工作,并且必须考虑内部控制,那么它们是保护数据免受用户侵害的唯一可行选择。但是,如果您要创建具有多个后端的COTS产品,则它们也是特定于数据库的。如果您是一家咨询公司,则可能需要考虑几种不同的方法,以适应具体情况。
HLGEM 2011年

3
@HLGEM我不反对提出的任何观点。但是我反对答案的论点,即DBA可能在应用程序中添加逻辑的主要原因是因为他是一名顾问,并且打算欺骗客户。它把一个人的道德立场与他是否使用存储过程的选择联系起来。在我看来,上有技术参数面,并在双方的观点将不同的应用程序应用,技术工艺,公司与公司,工业行业。我将首先寻找功劳,然后再去破坏动机。
yfeldblum 2011年

他说他在一家咨询公司工作。与部署到客户站点的存储过程相比,对代码保持更多控制是一个非常合理的理由,这可能是他们的“最佳实践”。可能不是“吸引客户”,而是完全可以控制的问题。
杰西2012年

3

切换数据库品牌并利用相同的存储过程非常困难。

您的团队没有DBA,没有其他人希望与sql有任何关系。

这无非是程序员与DBA撒尿大赛。


2

IMO这取决于。存储过程虽然占了上风,但不是最佳实践,也不是不惜一切代价避免的事情。精明的开发人员知道如何正确评估给定情况并确定存储过程是否是答案。就个人而言,我喜欢使用某种ORM(甚至是诸如原始的Linq到Sql这样的基本ORM),而不是使用存储过程,除了可能用于预定义的报告或类似的报告外,但这确实是个案的基础。


下注者应发表评论。
SandRock'5

2

使用不同的编程语言将业务逻辑划分到不同的层之间,始终是问题的根源。当您必须在世界之间切换时,追踪错误或实施更改会变得更加困难。

就是说,我知道通过将所有业务逻辑放入数据库中存在的PL / SQL包中的公司表现相当不错。这些不是很大的应用程序,但也不是一件容易的事。例如20K-100K LOC。(PL / SQL比T-SQL更适合于这种系统,因此,如果您只了解T-SQL,您可能现在就摇摇头……)


2

这是尚未提及的另一点:

代码生成工具和逆向工程工具无法真正很好地应对存储过程。该工具通常无法分辨proc的作用。proc是否返回结果集?几个结果集?是否从几个表和临时表中获取结果集?proc是封装的update语句,不返回任何内容吗?它是否返回结果集,返回值和一些“控制台输出”?

因此,如果您想使用一种工具来自动创建数据传输对象DTO和DAO层(例如liferay的“服务构建器”),您将不容易做到。

而且,当数据源是SP时,像Hibernate这样的ORM不能真正正常工作。数据访问最多是只读的。


有趣的是,当存储过程本身没有任何麻烦时,代码生成工具显然很难弄清楚存储过程是否返回结果集。
克雷格

2

单独编程,我无法抗拒编写存储过程。

我主要使用MySQL。我以前没有像PostGreSQL那样使用过面向对象的数据库,但是我在MySQL中使用SP所能做的就是稍微抽象了表结构。SP使我能够设计这些原始动作,即使它们下面的数据库发生了变化,其输入和输出也不会改变。

因此,我有一个名为的过程logIn。登录时,您始终只通过usernamepassword。传回的结果是整数userId

如果logIn是一个存储过程,现在我可以添加在登录时出现这种情况,同时作为初始日志中做额外的工作。我发现与嵌入式逻辑一系列SQL语句在存储过程中更容易编写比(主叫环境FETCH)->(获取结果)->(调用环境FETCH)系列,这是在编写逻辑服务器端时必须要做的。


1

我还要指出,存储过程确实在服务器上使用cpu时间。不是很多,但是有些。在工作过程中完成的某些工作可以在应用程序中完成。扩展应用层比扩展数据层更容易。


3
扩展数据库难吗?
JeffO 2011年

1
它至少显著更昂贵的(除非你在MySQL),并在很多地方,我的工作得到另一个SQL Server企业版许可证就像拔牙
奔F。

扩展数据库并不比扩展应用程序层的故事难得多
Brian Ogden

1

我同意Mark的观点,即社区确实已经离开存储过程已有相当一段时间了。尽管原始发布者提出的许多观点一次都有效的原因使我们想使用SP,但是已经有一段时间了,而且正如另一位发布者所说,环境已经改变。例如,我记得一个关于“在一天之内”使用SP的论点是获得的性能提升,因为它们的执行计划是“预编译”的,而每次执行时都必须“重新编译”来自我们代码的动态SQL。随着主要数据库的更改,改进,改编等,情况不再如此。

也就是说,我们在当前项目中使用的是SP。原因仅仅是因为我们在仍支持旧版应用程序的现有数据库之上构建新应用程序。因此,在我们关闭旧版应用程序之前,很难更改架构。我们做出了一个明智的决定,即根据应用程序所需的行为和规则来设计我们的新应用程序,并使用SP以我们希望的方式临时连接数据库,并使SP适应现有的SQL。 。这可以说是先前发布者的观点,即SP使得在数据库级别进行更改变得更容易,而无需更改应用程序代码。使用SP作为Adapter模式的实现对我当然是有意义的(尤其是考虑到当前的项目),

首先,我们打算在更新架构时删除SP。但是,与公司发展中的所有其他事情一样,我们将看看这种情况是否会发生![咧嘴]


0

我只是想简要概述如何建议使用存储过程。我认为这根本不是一个坏习惯,就像其他人所说的那样,应该在适当的情况下使用它们。

我看到一些问题,在这些问题中,为不同应用程序编写程序的功能可能会变得混乱,并使应用程序的业务逻辑分离,并导致数据库变得更加混乱和严格。

因此,我将在特定于数据库的面向关系数据的任务中使用存储过程。换句话说,如果存在用于数据库操作的逻辑与任何应用程序的数据相一致,则可以使用存储过程来保持数据的一致存储(有意义)。我认为很好的例子是:一致的日志记录,一致的维护,使用敏感信息等。

我认为,其他任务将按照数据库的强大数据模型来操纵数据以适合应用程序的需求,然后将它们存储在包含业务逻辑的另一层中。简而言之,为确保一致性而使用的特定于数据库的数据操作可以使用存储过程,其中一致性超出了数据库完整性模式模型的范围。


-1

“对我而言”存储过程适用于OLAP“只读”操作,很少使用。

对于业务规则,OLTP读/写操作我更喜欢Java应用程序服务器。为了简化编码并尽可能减轻主数据库服务器的CPU和内存负载。在此设置中,应用程序服务器中的所有代码都不难检查或记录,并且具有可伸缩性。

对我来说重要的是,与在存储层中进行调试相比,在业务层中进行调试更容易。


您做出了一些假设:OP需要OLAP(问题中未提及);所使用的平台具有Java应用程序服务器(不太可能,因为该标记与SQL Server有关)。您的答案也不会带来其他22个答案尚未涵盖的任何内容
Adam Zuckerman 2015年

我只是说如果我开始一个新项目,将很少使用存储过程进行只读操作,这只是个人选择。我发现在业务逻辑层而不是数据层上执行大多数编码更为方便。
jaizon lubaton 2015年

这似乎并没有提供任何关于之前24个答案中所提出和解释的要点的实质性内容,几乎没有内容值得质疑一个已有4年历史的问题
t

-2

除了不必要地分发业务逻辑,并将您与特定的数据库供应商联系在一起之外,我还坚信要按预期使用技术。数据库就是关系数据存储。用它来存储数据,别无其他。

明智地选择您的工具,从长远来看,您将为自己省下很多麻烦。


如果您要减票,请这样做,但至少要说明原因。
Nico

可能是因为您错了。SP并不意味着您在其中编写代码,而只是在编写数据访问查询(我认为有99%的情况)。此外,仅在数据模型上放置触发器和约束就算是“代码”,即操作逻辑,而不是数据。因此,我的观点是你错了。
gbjbaanb 2012年

除了数据库中的存储数据,您还应在哪里设置转换?
克里斯·特拉弗斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.