我同意不将业务逻辑放入 SQL 的概念以及 SQL 作为基于集合的语言的概念。
将 SOLID 视为一些非常好的编程实践的 OO 特定实现,并将它们应用于您编写的任何代码,包括 SQL。良好的表模式设计将包含 SOLID 思想。
我花了太多时间调试大型复杂的存储过程,所以如果你能避免它,请这样做。如果您必须编写存储过程代码,那么 SOLID 可以帮助您。
例如,我有一个大型存储过程,它查询几十个表,它返回一个数据集供打印机使用以发送 'welcome letters'。这是伪装成“报告”并用 SQL 编写的业务流程的典型示例。
从 SOLID 镜头看这个操作:
它应该有一个单一的责任和一个单一的改变理由。
该代码既确定谁收到了一封信,也确定了该信中包含哪些数据。如果过程的任一方面发生变化,您必须更新并重新测试整个系统。一个可靠的原则是有一个函数来确定谁收到一封信以及信中的内容。
这可以像使用单个查询返回客户列表一样简单。将其作为 ID 或 TVP 数组输入另一个存储过程以收集数据。
打开关闭
在大多数 SP(存储过程)代码中,整个过程要么是硬编码的,要么是“数据驱动的”。数据驱动通常是针对这一原则的 SQL 努力。SQL 流由 CASE 修改,该 CASE 将字段值或获得 EVAL 的文本字段中的 SQL 进行透视。
在我们刚刚创建的“主”SP 中可以看到解决我的问题的可靠方法 现在我有一个调用GET_CUSTOMERS的 SP和另一个调用GET_DATA的 SP 。这个主人基本上控制着工作流程,那个工作流程不应该改变。主站已关闭以进行修改。
如果我们随后需要增强系统以发送电子邮件,并且电子邮件具有不同的数据字段,那么我可以将调用GET_DATA替换为调用GET_DATA_FOR_EMAIL。 开放修改。
主人只有一个责任。如果工作流程发生变化,则此主节点会根据规则 #1 发生变化。
里氏替换原则
这非常 OO,但是为了说明一点,让我们假设GET_CUSTOMERS SP 在这里可以被视为可替换的对象。我应该能够进行不同的查找,比如GET_CUSTOMERS_WHO_NEED_DIFFERENT_LETTER并且能够重用我的代码。或者也许GET_CUSTOMERS_FOR_INTERNAL_TEST?如果系统的所有组件都是一次性的且不可重复使用,那么系统将更难维护和理解。这可能是一个延伸。少考虑 OO 对象继承,多考虑可重用的具有相似参数的函数。
依赖倒置原则
这是我见过的最大的失败。不要围绕选择和 where 子句编写代码。我的系统以 *SELECT * FROM table, table, table where customers.letter-sent=false* 开始(实际上超过 1k 行),我希望以Find all customers and send them a letter结束。从抽象和代码开始,最高级别控制 SP 中的工作流。
这个想法是抽象是“查找客户”并返回客户列表。我应该能够将该逻辑交换为代码中的其他内容。我经常看到的违规行为是让“查找客户”写入连接到客户表上的表,然后另一个 SP 读取该表。在这种情况下,您的抽象会泄漏到实现中,并且您从可重用代码转到为特定表发送信件的系统。
SQL 非常强大,并且很容易将抽象中应该分开的步骤组合起来,以便在实现中混合在一起。
存储过程是一个很好的工具,它可以帮助团队用可读性换取性能,并以牺牲可伸缩性为代价带来便利。它仍然是代码,从其他语言中窃取模式只会在您的整体设计时有所帮助。
如果您需要一个简单的设计模式,通常您不需要设计模式。如果您的代码超过 50 行左右,请尝试将其移至应用层和/或考虑进行可靠的重构。