9

我有一个包含许多存储过程的生产 SQL-Server 数据库(报告)。SP 以不同的方式向外部世界公开
- 一些用户可以直接访问 SP,
- 有些通过 WebService 公开
- 而另一些则通过 DCOM 层封装为接口。

用户群很大,我们不确切知道哪个用户集使用哪种方法访问数据库。
我们收到来自用户组的频繁(大约每隔一个月 1 个)请求,通过向输出添加一列或向现有输出添加一组列来修改现有 SP,其他所有内容保持不变。
我们最初通过修改现有 SP 并将新请求的列添加到输出的末尾来开始执行此操作。但这破坏了其他一些用户群构建的自定义工具,因为他们的工具具有硬编码的列数,因此添加列意味着他们也必须修改他们的工具。

此外,对于某些列,需要复杂的逻辑才能将该列放入报告中,这意味着 SP 性能下降,影响所有用户 - 甚至那些不需要新列的用户。

我们正在考虑各种方法来解决这个问题:

1 控制流的默认参数

通过添加标志作为默认参数来控制代码路径,更新现有 SP 并控制新功能。通过使用默认参数,如果 Parameter 的值设置为 true,则仅调用新功能。默认情况下,它设置为 False。

优势

  • 不需要新对象。
  • 正在进行的维护不受影响。
  • 测试开销仍在控制之中。

坏处

  • 由于现有 SP 已修改,因此需要对现有功能和新功能进行测试。
  • 由于我们不知道客户端工具如何调用 SP,我们永远无法确定我们没有破坏任何东西。
  • 如果相同的报告再次被更多请求修改,将很难处理——这意味着更多的标志和代码将变得不可读。

2 新建存储过程

将为更改 SP 的签名(输入/输出)的任何需求创建一个新的存储过程。
新的 SP 将为现有的东西调用原始存储过程,并在其之上添加新需求的逻辑。

优势

  • 这样做的好处是不会对现有程序产生影响,因此不需要对旧逻辑进行测试。

坏处

  • 每当请求更改时,都需要在数据库中创建新对象。这将是数据库维护的开销。

执行计划是否会因添加新参数而改变?如果是,那么这可能会对未请求新列的用户产生不利影响。
考虑到 SP 是数据库的公共接口,如果我们选择选项 2,接口应该是不可变的?
最佳实践是什么,还是取决于具体情况,选择选项时的主要驱动因素是什么?

提前致谢!

4

7 回答 7

4

从劣势引用您的第一个选项:

如果相同的报告再次被更多请求修改,将很难处理——这意味着更多的标志和代码将变得不可读。

我个人认为这是不修改现有存储过程以适应新列的最大原因。

当错误出现具有多个分支的存储过程时,调试会变得非常困难。同样正如您所暗示的,执行计划可以随着分支/if 语句而改变。(sql 在运行查询和在存储过程中运行该查询时使用不同的执行计划?

这与面向对象的编码非常相似,您的直觉是正确的,最好扩展现有对象而不是修改它们。

我会选择方法#2。您将拥有更多对象,但至少在出现问题时,您将能够知道受影响的存储过程的范围/影响有限。

随着时间的推移,我学会了水平而不是垂直地增长对象/数据结构。换句话说,只是做一些新的东西,不要让现有的东西越来越大。

于 2013-08-30T16:24:39.033 回答
1

#2 可能是比 #1 更好的选择,特别是考虑到 #1 的第 3 个缺点,因为需求在大多数情况下都在不断变化。我有这种感觉,因为这里的劣势比任何一方的优势都占主导地位。

于 2013-09-06T09:09:38.953 回答
1

使用#2 更容易。可为空的 SP 参数会产生一些非常难以定位的错误。虽然,我确实不时雇用他们。

尤其是当您开始加入空值和 ANSI 设置时。您编写查询的方式将极大地改变结果。吻。(让事情变得简单愚蠢)。

此外,如果它是用于报告或显示的参数化搜索,我可能会考虑将数据超快速地提取到支持 LINQ 的对象中。然后您可以搜索内存中的列表,而不是从数据库中重新获取。

于 2013-09-16T20:08:30.410 回答
1

好的。#2。确实。毫无疑问。

#1 说:“改变现有程序”,导致事情中断。这怎么可能是好事!你的客户会讨厌你。您的代码变得越来越复杂,这意味着越来越难以避免破坏导致更多仇恨的事物。它会非常缓慢,并且无法调整。等等。

对于#2,你有一个稳定的界面。没有仇恨。耶!说真的,“耶”就像“我还有工作!” 而不是“嘘,我因为惹恼了我的客户而被解雇了”。严重地。永远不要仅仅因为这个原因而做#1。你知道这是真的。你知道的!

话虽如此,记录人们在做什么。将用户 ID 作为参数。记录下来。了解您的用户。找到那些使用旧的蹩脚代码的人,并在必要时很好地要求他们升级。

您避免使用 2 号的理由是扩散。但这只是一个问题,如果你不测试东西。如果您确实正确地测试了东西,那么无论如何都会在您的测试中发生扩散。如果必须,您可以随时调整 #2 中的内容,或者至少隔离性能问题。

如果胖程序真的很棒,那么用胖人的更苗条版本改装瘦身版本。在 SQL 中,这很棘手,但复制/粘贴并减少您的选择列列表是可行的。一般来说,我只是懒得这样做。生命太短暂。拥有真正好的测试代码是一种更好的时间投资,而且数据模式往往很少以破坏现有查询的方式发生变化。

好的。吐槽一下。严肃的消息。做#2,或者至少不要做#1,否则你会被解雇,或被讨厌,或两者兼而有之。我想不出比这更好的理由了。

于 2013-09-04T16:22:50.987 回答
0

选项 #2 是可供选择的选项。

您自己提到了(缺点)优点。

当您考虑根据需求更改向 db 添加新对象时,请仅添加不会使您的新 SP 变得更大且难以维护的必要对象。

于 2013-09-07T11:12:38.610 回答
0

我也会投票给#2。我见过一些将#1 发挥到极致的存储过程:SP 有一个参数@Option和几个参数@param1, @param2, .... 最终结果是这是一个试图扮演许多角色的单个存储过程存储过程。

#2 的主要缺点是存储过程更多。找到您正在寻找的人可能更难,但我认为这是为您获得的其他优势付出的小代价。

我还想确保您不只是复制和粘贴原始存储过程并添加一些列。我也见过太多这样的。如果只添加几列,则可以调用原始存储过程并加入新列。如果这些列以前很容易获得,这肯定会导致性能损失,但是您不必更改原始存储过程(重构以实现良好的性能并且不重复代码),也不必维护两个副本代码(复制和粘贴以提高性能)。

于 2013-09-02T23:53:37.670 回答
0

我将根据您提供的选项建议其他几个选项。

替代选项#1:添加另一个变量,但不是将其作为默认变量,而是基于客户名称的变量。这样客户 A 可以获得他的专业报告,而客户 B 可以获得他略有不同的定制报告。这增加了大量工作,因为“主要”部分的更新必须复制到所有专业客户。您可以通过分支“if”语句来做到这一点。

备选方案#2:添加新的存储过程,只需将客户的名称添加到存储过程中。维护方面,这可能有点困难,但它会达到相同的最终结果,每个客户都有自己的报告类型。

于 2013-09-03T18:29:19.663 回答