出处:http://www.codeproject.com/cs/database/sqldodont.asp
仅供学习省事用,翻译不准,详细了解请看原文。
假设你第一次主导一个基于SQL Server的项目,而以前是基于Access。那么你可能有一些SQL Server的性能上的问题,不知道怎么来处理。如果你想简要地知道一些关于SQL Server及其数据访问层解决方案的指导方针,那么这篇文章就是你所需要的。
即使你不使用SQL Server,这里的大部分指导方针也都可以运用到其它的数据库管理系统,如Sybase、Oracle。当然,这里没有T-SQL的诀窍,也没有针对你的SQL Server疑问的灵丹妙药。我想说的是健全的设计相关的一些建议,这些建议来自于我之前几年的学习和一次次设计失误的积累。
了解你的工具
请不要轻视这个工具。这是本文的重点。你将诧异有许多编程人员不了解所有T-SQL命令和所有SQL Server有用工具。“什么?我需要花费一个月的时间来学习我从来不使用的SQL命令??”你不应该这样说,应当花上一个周末来浏览MSDN上的所有T-SQL命令,从而学到许多应该做和不应该做的。这样,不久以后,当你在设计一个查询语句的时候,你就会知道:“这个命令我还不熟悉”,然后回到MSDN去看它正确的语法。
在本文中,假设你已经了解T-SQL的语法或者能从MSDN找到。
不要使用游标(由于作者针对的目标是初学者,所以这么说)
让我强调一下:不要使用游标。他们是一个完整系统的性能杀手。许多初学者使用游标,却不知道牺牲了多少性能!游标使用内存,以某种方式锁定表,并且效率低下。更糟糕的是,游标和你的数据库管理员的许多性能优化相抵触。你知道每个FETCH的执行性能与一条SELECT语句一样吗?换句话说,如果你对10000条记录的表使用游标,相当与对该表做10000次的SELECT查询!如果你使用几个SELECT、UPDATE或DELECT来取代游标,操作将会快得多。
SQL程序员初学者因为游标的好用和类似编程的代码而使用它。嗯,不幸的是,这会导致糟糕的性能。SQL的最终目的是说明你想做什么,而不是你应该怎么去做它。
我曾经重写过一个基于游标的存储过程,用一些基本的SQL查询语句来取代它。相关的表有100000条记录,基于游标的存储过程执行一次需要40分钟,而新的存储过程仅仅需要10秒,你应该看看写基于游标的存储过程的可怜的程序员的表情!
有时候,创建可以一个小的应用程序来获得全部数据,处理后更新到服务器,这样反而会更快!T-SQL没有想像中那么好的循环性能。
如果你正在看这篇文章,我想说一下:使用游标是不好的,我从来没有看到游标能合适地使用到哪,除了用于数据库管理员的某些工作。因为大部分时候,管理员知道他们在做什么,但是读者你应该不是一个专门的数据库管理员,不是吗?
规范化你的数据表
有两个可以不做规范化数据库的一般理由:性能和纯属偷懒。不久以后你将为你的懒惰付出代价,对于性能,如果不是速度很慢,就不应该去刻意优化它。我经常看到许多程序员因为速度慢而使数据库不规范。而更经常的,修改后的结果反而是更加地慢。数据库管理系统设计用于规范化的数据库,所以请规范化地设计你的数据库。
不要使用SELECT *
改变老习惯是很难的,我知道,坦白地说我也经常使用SELECT *,但是请尽可能地仅写出你所需要的列,这样有许多好处:
1.减少内存消耗,节省带宽
2.轻松的安全设计
3.让查询优化器能通过索引读取出所有需要的列
了解你的数据怎样存取的
你能为你的数据库做的正确事情之一是一个健壮的索引设计。这项工作简直是一项艺术。每当你为数据表增加一个索引,SELECT操作将更加快,但是INSERT和DELETE操作将变得更慢。建立和维护一个索引有许多工作要做。如果你为了加速表的SELECT而增加一些索引时,你将发现在UPDATE时表将长时间锁定以更新索引。因此,问题在于:这个表用于干吗?读取还是更新数据?这个问题要谨慎考虑,特别对于DELETE和UPDATE操作,因为两个操作经常包含一个SELECT作为WHERE部分的更新或删除条件。
不要为如“性别”之类的列创建索引
这是徒劳的。首先,让我们了解索引是怎样来加速表存取的。你知道索引是通过将表按一个标准快速地进行划分的方法来实现的。如果你为类似于“性别”列创建一个索引,整个表将划分为两个部分:男性和女性。如果你的表有1000000条记录,它能进行什么优化呢?记住,维持一个索引是很慢的。应该按照从最稀松的列(即极少值相同)到最不稀松的列的顺序来设计你的索引,如姓名+省份+性别。
使用事务
特别为运行时间较长的查询。它将在错误发生的时候拯救你。进行数据编程一段时间后,你会发现总有一些意外的状况使你的存储过程崩溃。
注意死锁
请总是按照相同的顺序存取你的数据表。当你使用存储过程和事务的时候,你将明白这一点。如果你要锁定表A后,锁定表B,请在你的所有存储过程中总是按照这个顺序锁定。如果你在另外的存储过程中,先锁定表B,然后锁定表B,将因为你锁定顺序的粗心设计导致麻烦的死锁。
不要开启一个大的数据集
编程论坛中一个常见的问题是:“我怎样才能迅速地填充100000个项到下拉列表框中?”。嗯,这个非常不正确的。你不能怎么做,也不应该这么做。最起码,你的用户很讨厌从100000个项中选择一项,你需要一个更加友好的用户界面,你应该显示给你的用户不超过100或200条的数据。
不要使用服务器端游标
除非你知道你在干吗。客户端游标经常(不总是)花费更少的网络资源和服务器资源,缩短锁定时间。
使用带参数的查询
有时我在编程论坛上,看到类似如下的问题:“我的查询因为一些字符(如单引号)失败了,我怎么才能避免呢?”通常的回答是:“改单引号为双引号。”错了,这仅仅是一个问题的解决,其它字符还有可能引起查询失败,而且有可能引入严重的安全错误。另外,它还会破坏SQL Server缓存系统。应该学习怎样使用带参数的查询,彻底避免发生类似的错误。
总是使用一个大的数据库做测试
程序员开发时,通常使用一个小的测试数据库,而最终用户使用的是一个大数据库。这是错误的:磁盘很便宜,而使用小数据库性能问题将很晚才会发现。
不要使用INSERT语句导入大量的数据
除非确实需要,使用DTS或者BCP工具,你将有一个比较灵活和快速的解决方案。
注意超时
查询数据库时,默认的超时是很短的,如15秒或30秒。记得告诉数据库,查询可以长时间运行而不是15秒或30秒,尤其当你的数据库增长时。
不要忽视同时发生的编辑
有时两个用户可能同时编辑同一条记录。当写入时,后面的写用户将成功,而前面用户的更改将丢失。很容易发现这种情况:创建一个时间戳列,并且当你写入的时候检查它。如果可能,合并这些改变。如果两个更新有冲突,及时地提示用户。
在往明细表中插入数据时,不要执行SELECT Max(ID) FROM 主表
这是又一个经常发生的错误。当有两个用户同时插入数据时,SELECT查询将无效。尽可能使用SCOPE_IDENTITY,IDENT_CURRENT和@@IDENTITY之一来避免导致的触发器脏读问题。
尽量避免可空列
尽可能地。可空列在每一行多消耗一个特殊的字节,当查询数据时将产生更多的资源消耗。数据访问层的编程会很棘手,因为每次你存取可空列时都必须检查它。
我并不是说可空列像有些人说的是恶魔的化身。我相信它们能很好的使用,并且当“可空的数据”是你的业务规则的一部分时,它们能简化你的代码。但是,有时候可空列被使用在下列情形:
CustomerName1
CustomerAddress1
CustomerEmail1
CustomerName2
CustomerAddress2
CustomerEmail3
CustomerName1
CustomerAddress2
CustomerEmail3
那么,这是极其糟糕的。请不要这样做,将你的数据表规范化。这样数据表将更加灵活和快捷,减少可空列。
不要使用Text数据类型
除非你真的因为大型数据使用它。对查询来说,如果不正确地使用,Text数据类型是不灵活的,速度慢的,而且将浪费大量的空间。有时一个VARCHAR类型能更好地处理你的数据。
不要使用临时表
除非真正地需要。通常地,一个子查询可以用一个临时表来取代。它们可以减少花销,但是当在COM+下编程时使用数据库连接池,临时表将总是存在,这将令人头疼。在SQL Server2000中,另外一个选择是提供一个写在存储过程里的小型表,它将驻留在内存中。
学习怎样看懂一个查询执行计划
SQL Server查询分析器是你的朋友,你能从中学习到许多东西,如语句如何被执行,查询和索引能怎样影响性能。
使用参照完整性
它能非常的节约时间。定义所有该有的主键,唯一约束和外键。每个在服务器上建立的验证以后都会节省你的时间。
结论
正如我前面说的,本文不是一个完全的SQL Server性能和最佳实践的向导。那些内容足够写一本书了。但是,我相信本文是一个很好的开端,如果你遵循这些习惯,在以后的实践中将减少很多的麻烦。
作者自述:
I develop software since I was 11. In the past 20 years, I developed software and used very different machines and languages, since Z80 based ones (ZX81, MSX) to mainframe computers. I still have passion for ASM, though no use for it anymore. Professionally, I developed systems for managing very large databases, mainly on Sybase and SQL Server. Most of the solutions I write are for the financial market, focused on credit systems.To date, I learned about 20 computer languages. As the moment, I'm in love with C# and the .NET framework, although I only can say I’m very proficient at C#, VB.NET(I’m not proud of this), T/SQL, C++ and libraries like ATL and STL.I hate doing user interfaces, whether Web based or not, and I’m quite good at doing server side work and reusable components.I’m the technical architect and one of the authors of Crivo, the most successful automated credit and risk assessment system available in Brazil.