2011-08-24 115 views
19

作为一名开发人员,主要编写c#我在编写c#代码时采用了一些良好的实践。当我有时编写存储过程时,我无法将这些实践应用于存储过程代码。构建存储过程的最佳实践

有几次我继承了噩梦存储过程代码,前三到四层存储过程设置了一些临时表并且大多互相调用。没有真正的工作完成,只有几行代码。然后最后有一个叫做“最终”的存储过程,这是一个3000-5000行SQL代码的巨大怪物。这段代码通常有很多代码的味道,如代码复制,错综复杂的控制流(又称意大利面条),以及一种方法,它们将相互堆叠的东西叠加在一起,没有明确的分离,其中一部分工作开始并结束(甚至没有评论作为除数)。

我也注意到使用了从中间临时表中选择的out注释选择语句。为了调试目的,选择可以重新打开,但是在任何调用代码期望返回结果集的特定顺序之前需要将选择移除。

显然我的同事队友也分享我缺乏良好的SQL编写实践。

所以......(这里是真正的问题)......编写模块化可维护存储过程的最佳实践是什么?

欢迎使用自制的练习和参考书籍/博客。方法以及帮助完成特定任务的工具。

让我们总结一下,我还没有发现好的做法

一些地区
  • 模块化和封装(存储过程通过临时表真的要走的路?通信)
    • 在C#中我用的组件,类以及用访问修饰符修饰的方法来完成这一点。
  • 调试/测试(不是修改调试的目标更好?)
    • 调试工具?
    • 调试跟踪?
    • 测试夹具?
  • 使用代码的代码
    • 在C#我重构的结构,打破了更小的方法,做各仅有一个逻辑任务强调的代码/逻辑/数据/控制流。
  • 代码重复

主要是我遇到的SQL Server作为DBMS但DBMS无关的答案或答案指出其他DBMS的功能:ES在上述情况下,这种帮助也欢迎。

为了给出一些背景知识:我遇到的大多数大型存储过程都是在报表场景中,其中的基础只是从大型表中创建一些汇总值。但是一路上你需要排除某些异常表中出现的一些值,在一些尚未完成的东西表中添加一些值,与去年相比(你能想象处理产品变更部门的丑陋代码几年之间?)等

+2

由于这些原因以及无法在SCM中使用存储过程,我已经避免了所有这些,除了最必要和最明显的用途(触发器等)。我们将业务逻辑保留在业务层(.NET)中,并将数据库用于这一数据库。 – gahooa

+0

理想情况下,存储过程不应调用其他存储过程。这是BL层的目的。不幸的是,如果你已经拥有一个充满嵌套SP调用的数据库,那么这种情绪并没有真正的帮助。您是否在寻求如何使当前模型更易维护的建议,或者有关如何重构系统以更好地遵循最佳实践的建议? –

+0

@gahooa我也尽可能在.NET中放置了一部分(部分原因是因为那是我的家庭舞台),但在某些情况下,例如继承大型代码库时,或者当您拥有大量数据时,您或多或少都会被卡住程序。 –

回答

13

我写了很多复杂的存储过程。有些事情我会考虑最佳实践:

不要在存储过程中使用动态SQl,除非您使用大量可能需要或可能不需要的参数执行搜索过程(然后它是目前最好的解决方案之一)。如果必须在proc中使用动态SQl,则始终需要调试输入参数,并且如果设置了调试参数,则会打印创建的SQL语句而不是执行它。这将节省数小时的调试时间!

如果您在proc(插入/更新/删除)中执行多个操作查询,请使用Try Cacth块和事务处理。给输入参数添加一个测试参数,当它被设置为1时,总是回滚整个事务。在回滚到测试模式之前,我通常会有一个部分返回我正在影响的表中的值,以确保我认为我对数据库所做的事实上是我所做的。或者你可以按照如下所示进行检查。这就像将当前注释掉的选择放在下面的代码(并取消注释)一样简单,只要你有@test参数。

If @test =1 
Begin 
Select * from table1 where field1 = @myfirstparameter 
End 

现在,你不必去通过和发表意见,并取消注释每次一次测试。

应将@test或@debuig始终设置为默认值0,并放在列表中最后一位。这样添加它们不会破坏proc的现有调用。

考虑为进行插入/更新/删除的procs设置日志记录和/或错误记录表。如果您在执行时记录表变量中的步骤和/或错误,那么在回滚插入到日志记录表后它们仍然可用。了解复杂过程的哪一部分失败以及错误是什么可以在以后非常宝贵。

尽可能不要嵌套存储的特效。如果您需要在循环中运行多个记录,请将存储的proc替换为具有表值参数的proc,并将proc设置为以基于集合而非单独的记录方式运行。如果表值参数有一条记录或多条记录,这将起作用。

如果你有一个复杂的选择与很多子查询或派生表,请考虑使用CTE。重构任何相关的子查询或游标以更好地执行基于集合的代码。总是用一组数据来考虑而不是一个记录。

在任何可以想象的情况下,不要嵌套视图。性能下降比任何少量节省的开发时间差得多。并且相信我,嵌套视图不会节省维护时间,因为更改需要位于视图链中最远的视图。

所有存储的特效(和其他数据库代码)都应该在源代码控制中。

表变量适用于较小的数据集,但临时表(真正的以#或##表示不是表的变量)对于大型数据集的性能可能更好。如果使用临时表,则在您不再需要时删除它们。尽量避免使用全局临时表。

学习编写高性能SQL。编写SQL的过程通常会比SQL更容易,而SQL一旦掌握了技术知识就不会发生。如果你编写复杂的存储过程,没有理由不知道哪种技术比其他技术更好。了解如何确保您的查询是可靠的。避免游标,相关的子查询,标量函数和其他按行排序的行。

+0

RE嵌套视图:我继承了嵌套视图的系统,并且在SQL Server上执行性能调查时并没有引起任何问题。也许它曾经是一个问题,但在MSSQL 2008和以上版本中,查询规划器非常聪明并且有效地将视图内联。 –

+0

我们遇到过这个问题的服务器是SQl Server 2008服务器。 T可能取决于tehw veiws如何编写。我们遇到的另一个问题是意见变得难以维持,最终我们实际上达到了系统的硬性限制,而这些意见根本无法运行。我坚持我所说的,使用视图来调用其他视图是一种不好的做法。 – HLGEM

5

通过临时表进行通讯有时会产生巨大的代码异味。这样的过程通常不能由用户在不干扰彼此的情况下运行(如果您为不同的过程重复使用临时表名称并且它们不被重新创建,或者如果您使用两个不同表的同名模式)。他们可能很难排除故障 - 像任何功能一样,必要时使用它们,并且不存在更好的替代方案。暂时使用实际表格也可能会产生问题。

存储过程在SQL Server中相互传递数据(超过参数)可能会产生问题。现在有一些表值参数,以前用proc完成的许多事情现在可以使用内联表值函数或(通常优先于)多语句表值函数完成。

在SQL Server中,避免在大型行集上大量使用标量函数和多语句表值函数 - 它们执行得并不好,因此在C#中看起来很明显的模块化技术在这里并不适用。

我建议你看看Ken Henderson's Guru's Guide to SQL Server Stored Procedures - 发表于2002年,它仍然有丰富的数据库应用程序设计的有用信息。

+0

也许我是误解,但临时表可以在没有问题的情况下与多个用户一起运行。但是也许你在谈论一个特例? –

+0

@Mark SQLDev你是正确的 - 本地临时表是连接本地 - 我发现临时表(和表变量)难以调试。而且,当然,您正在决定要实现的内容以及何时实现,而不是将其交给优化器。你需要跟踪命名,以便它不会发生冲突等。 –

2

这是一个很好的问题。作为一名C#开发人员,我不得不涉足SQL,似乎SQL本身就阻碍了我习惯于C#的最佳实践。

公用表表达式很适合隔离存储过程中的查询,但您只能使用它们一次!这导致你定义视图,但是你已经失去了封装。

来自一个存储过程的结果集很难在另一个存储过程中使用,因此您可能会倾向于编写表值函数。这增加了你的权限维护负担,并迫使你编写函数'两次' - 一次作为函数,另一次作为调用函数的过程。否则,根据是否是程序,您有不同的DAL接口。

所有这些都导致我随着时间的推移,坚持在数据库中简单的CRUD存储过程(不会互相调用)以及在关系非常复杂时进行少量孤立的查询。更多的BI东西。其他一切都在BLL中。

从物理上看,SQL是按照函数或它们围绕并在源代码控制中进行管理的表在单独文件中隔离的。

避免SELECT *并支持指定列。当您更改表格并且不触碰所有特效时,可以节省运行时间问题。是的,有一个重新编译过程,但它会错过一些,尤其是如果意见涉及。另外,SELECT *几乎总是会返回比您真正需要的更多的列,这会浪费带宽。

+0

SQL最佳实践definitly与您习惯使用C#不一样。尝试在关系世界中使用c#最佳实践是数据库性能不佳的原因之一。数据库旨在最大限度地提高性能,而不是简化维护性。 – HLGEM

+0

当您需要在proc中多次使用它们时,您可以使用临时表而不是CTE。 – HLGEM