什么是更好的 - 将可选参数添加到现有SP或添加新SP?

时间:2013-08-30 04:54:35

标签: sql-server database database-design stored-procedures

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

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

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

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

1用于控制流量的默认参数

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

  

优势

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

缺点

  • 由于现有SP已修改,因此需要测试现有功能以及新功能。
  • 由于我们没有了解客户端工具如何调用SP,因此我们无法确定我们是否已经破坏了任何内容。
  • 如果同一报告再次被更多请求修改将很难处理 - 意味着更多标志和代码将变得不可读。

2新存储过程

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

  

优势

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

缺点

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

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

提前谢谢!

7 个答案:

答案 0 :(得分:3)

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

  

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

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

当bug出现具有多个分支的存储过程时,调试变得非常困难。另外,正如您所暗示的那样,执行计划可以使用分支/ if语句进行更改。 (sql using different execution plans when running a query and when running that query inside a stored procedure?

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

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

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

答案 1 :(得分:1)

确定。 #2。当然。毫无疑问。

#1说:“改变现有程序”,导致事情破裂。这绝不是件好事!你的客户会讨厌你。你的代码变得越来越复杂,意味着越来越难以避免破坏导致更多仇恨的东西。它将会非常缓慢,无法调整。等等。

对于#2,你有一个稳定的界面。没有仇恨。好极了!说真的,“yay”就像“我还有工作!”而不是“嘘,我因为烦恼我的客户而被解雇”。认真。因为这个原因,永远不会做#1。你知道这是真的。你知道的!

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

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

如果更胖的程序真的很棒,那么使用更薄的胖版本来改装瘦版本。在SQL中这很棘手,但复制/粘贴和缩小选择列列表是有效的。一般来说,我只是不愿意这样做。生命太短暂。拥有非常好的测试代码是一种更好的时间投入,数据模式往往很少以破坏现有查询的方式发生变化。

好。咆哮。严肃的消息。做#2,或者至少不要做#1,否则你会被解雇,或者被憎恨,或两者兼而有之。我想不出比这更好的理由。

答案 2 :(得分:1)

#2可能是比#1更好的选择,特别是考虑到#1的缺点,因为要求在大多数时间内不断变化。我觉得这是因为这里的劣势比任何一方都有优势。

答案 3 :(得分:1)

更容易选择#2。可以为空的SP参数可以创建一些非常难以定位的错误。虽然,我不时使用它们。

特别是当你开始进入nulls和ANSI设置的连接时。编写查询的方式将显着改变结果。吻。 (保持简单愚蠢)。

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

答案 4 :(得分:0)

我也会投票给#2。我已经看到了一些存储过程,它们将#1推向极致:SP具有参数@Option和一些参数@param1@param2,....净效果是这是一个尝试扮演许多存储过程角色的存储过程。

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

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

答案 5 :(得分:0)

我将基于您提供的选项建议其他几个选项。

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

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

答案 6 :(得分:0)

选项#2是可供选择的。

你自己提到(dis)优势。

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