SOLID存储过程和函数

时间:2013-09-08 22:31:02

标签: sql sql-server oop design-patterns stored-procedures

我想在我正在使用的应用程序中开始更多地使用存储过程。该应用程序搜索十几个数据库。应用程序将信息存储在自己的数据库中。

我在考虑将业务逻辑偏移到特定数据库中的存储过程。因此,如果逻辑对所有外部数据库都是通用的,则将其保存在应用程序(.NET)中。如果逻辑特定于数据库,则创建存储过程。

我不确定SOLID如何与存储过程和函数一起工作,因为没有接口或抽象。以下帖子似乎建议您尝试合并查询:http://ledgersmbdev.blogspot.co.uk/2013/02/building-solid-databases-interface.html。例如,如果存储过程有四个SQL语句,那么为什么不尝试将它们组合成一个SQL语句呢?这是帖子的意思吗?这是一种SOLID方法吗?

2 个答案:

答案 0 :(得分:3)

  

我正在考虑将业务逻辑偏移到存储过程中   特定数据库。因此,如果逻辑对所有外部都是通用的   数据库然后将其保存在应用程序(.NET)中。如果逻辑是   特定于数据库然后创建存储过程。

这句话引起了我的极大关注。当你说逻辑是特定于数据库时,我认为你指的是上述十二个中的一个,这是一个设计缺陷。数据库只是信息存储,它们不应该要求任何“特殊”逻辑来访问它们在任何视图结构之外。此外,如果需要以非设置方式操作数据,而不是将其放入应用程序中。当它们可以偏移到您的应用程序时,这不会使您的执行计算。

设计应用程序时,必须确保不要变形数据库以忽略关系模型。根据我的经验,这是使您的应用程序无法管理和缓慢的最佳方法之一。为了澄清,业务逻辑不应该存在于数据库中,这使得其他人难以使用您的数据。反对这一点的典型论点是,“我是唯一一个使用数据的人”,我说这是一个可怕的设计理由。

继续,您应该尝试确定关系(集)模型中实际工作的内容,并构建可以查询该数据的应用程序。而不是构建适合您的应用程序的数据库。话虽如此,SOLID 不使用关系模型,因为它们不是面向对象
    WIKI

  

在计算机编程中,SOLID(单一责任,开放式,   Liskov替换,接口隔离和依赖倒置)   是Michael Feathers为“第一个”引入的助记符   罗伯特·C·马丁1 [2]早期确定的五项原则   2000s [3]代表了面向对象的五个基本原则   编程和设计。

评论更新

  

应用程序链接来自不同系统的信息并决定   当一组记录可以删除时(这些是通用规则)   适用于所有系统)。然后有数据库特定的规则   必须在删除前应用。我在考虑抵消   本地业务规则到存储过程。 -

查看此评论,我不完全了解审计/软删除之外的数据库特定规则。我同意特定于数据库的规则可以设置为数据库中的管理存储过程,在这种情况下,当您遇到以下问题时,必须绘制该行:

我的应用程序会查询历史数据,除非它早于6 months,然后必须从脱机存储中检索它。

6 months之后删除此数据的选项是允许应用程序通过某些业务逻辑清除它或创建一个计划任务来执行此清理,因为它是一个正常的数据库操作来删除元组。

我的论点是将其放在数据库中,并禁止您的应用程序调用这些过程。实际上,您的应用程序甚至不应该知道数据库存在于正确抽象的应用程序中。因此,如果这是您提出的示例,那么我的解决方案如下:

1) Create stored procedures in the database that only a maintenance based user can invoke,   NOT THE APPLICATION  
2) Create a database scheduled task to run these based on your data needs.  

答案 1 :(得分:1)

我同意不将业务逻辑放入SQL的概念以及SQL是基于集合的语言的概念。

将SOLID视为一些非常好的编程实践的OO特定实现,并将它们应用于您编写的任何代码,包括SQL。良好的表格模式设计将包含SOLID创意。

我花了太多时间调试大型复杂的存储过程,所以如果你能避免它,请这样做。如果您必须编写存储过程代码,那么SOLID可以帮助您。

例如,我有一个大型存储过程,它会查询几十个表,并返回一个数据集,供打印机用来发送“欢迎信”。这是一个伪装成“报告”并用SQL编写的业务流程的典型示例。

从SOLID镜头看这个操作:

它应该有一个单一的责任和一个改变的理由。

代码既可以确定谁收到一封信,也可以确定该信中的数据。如果流程的任何一个方面发生变化,您必须更新并重新测试整个系统。一个坚实的原则是拥有一个确定谁得到一封信及其中的内容的功能。

这可能就像拥有一个返回客户列表的查询一样简单。将其作为ID或TVP数组馈送到另一个存储过程中以收集数据。

打开/关闭

在大多数SP(存储过程)代码中,整个过程是硬编码或“数据驱动”。数据驱动通常是针对此原则的SQL工作。 SQL流程由一个CASE进行修改,该CASE以字段值或SQL文本字段中的SQL为基础进行EVAL'd。

我的问题的SOLID方法可以在我们刚刚创建的“主”SP中看到现在我有一个调用 GET_CUSTOMERS 的SP和另一个调用 GET_DATA 的SP。此基本上控制工作流,并且工作流不应更改。主人关闭进行修改。

如果我们需要增强系统以发送电子邮件,并且电子邮件具有不同的数据字段,那么我可以通过调用 GET_DATA_FOR_EMAIL 将调用换出 GET_DATA 可供修改

主人有一个责任。如果工作流程发生更改,则此主服务器会根据规则#1进行更改。

利斯科夫替代原则

这是非常OO,但为了说明一点,我们可以说 GET_CUSTOMERS SP在这里被视为可替换的对象。我应该能够进行不同的查找,比如 GET_CUSTOMERS_WHO_NEED_DIFFERENT_LETTER ,并且能够重用我的代码。或者 GET_CUSTOMERS_FOR_INTERNAL_TEST ?如果系统的所有组件都是一次性且不可重复使用,那么系统将难以维护和理解。这可能是一个延伸。少考虑OO对象继承,更多地考虑具有可重用的类似参数的函数。

依赖性倒置原则

这是我见过的最大失败。不要在selection和where子句周围编写代码。我的系统开始于* SELECT * FROM表,table,table,其中customers.letter-sent = false *(实际超过1k行)我希望以查找所有客户并向他们发送信件结束。从抽象和代码开始,最高级别控制SP中的工作流程。

这个想法是抽象是'找到客户'并返回一个客户列表。我应该能够将该逻辑交换为代码中的其他内容。我经常看到的违反这种情况的方法是让一个“查找客户”写入连接在customers表上的表,然后是另一个读取该表的SP。在这种情况下,您的抽象泄漏到实现中,您从可重用代码转到为特定表发送字母的系统。

SQL非常强大,并且很容易将在抽象中应该分开的步骤组合在一起来实现。

存储过程是一个很好的工具,可以帮助团队交换性能的可读性,并以可扩展性为代价带来便利。它仍然是代码,从其他语言窃取模式只会对你的整体设计有所帮助。

如果您的需求很简单,通常您不需要设计模式。如果您的代码超过50行左右,请尝试将其移至应用层和/或考虑可靠的重新分解。