设计一个应用系统似乎并不难,但是要想使系统达到最优化的性能并不是一件容易的事。 在开发工具、数据库设计、应用程序的结构、查询设计、接口选择等方面有多种选择,这取决于特定的应用需求以及开发队伍的技能。本文以SQL Server为例,从后台数据库的角度讨论应用程序性能优化技巧,并且给出了一些有益的建议。
1 数据库设计
要在良好的SQL Server方案中实现最优的性能,最关键的是要有1个很好的数据库设计方案。在实际工作中,许多SQL Server方案往往是由于数据库设计得不好导致性能很差。所以,要实现良好的数据库设计就必须考虑这些问题。
1.1 逻辑库规范化问题
一般来说,逻辑数据库设计会满足规范化的前3级标准:
1.第1规范:没有重复的组或多值的列。
2.第2规范:每个非关键字段必须依赖于主关键字,不能依赖于1个组合式主关键字的某些组成部分。
3.第3规范:1个非关键字段不能依赖于另1个非关键字段。
遵 守这些规则的设计会产生较少的列和更多的表,因而也就减少了数据冗余,也减少了用于存储数据的页。但表关系也许需要通过复杂的合并来处理,这样会降低系统 的性能。某种程度上的非规范化可以改善系统的性能,非规范化过程可以根据性能方面不同的考虑用多种不同的方法进行,但以下方法经实践验证往往能提高性能。
1.如果规范化设计产生了许多4路或更多路合并关系,就可以考虑在数据库实体(表)中加入重复属性(列)。
2.常用的计算字段(如总计、最大值等)可以考虑存储到数据库实体中。
比 如某一个项目的计划管理系统中有计划表,其字段为:项目编号、年初计划、二次计划、调整计划、补列计划…,而计划总数(年初计划+二次计划+调整计划+补 列计划)是用户经常需要在查询和报表中用到的,在表的记录量很大时,有必要把计划总数作为1个独立的字段加入到表中。这里可以采用触发器以在客户端保持数 据的一致性。
3.重新定义实体以减少外部属性数据或行数据的开支。相应的非规范化类型是:
(1)把1个实体(表)分割成2个表(把所有的属性分成2组)。这样就把频繁被访问的数据同较少被访问的数据分开了。这种方法要求在每个表中复制首要关键字。这样产生的设计有利于并行处理,并将产生列数较少的表。
(2) 把1个实体(表)分割成2个表(把所有的行分成2组)。这种方法适用于那些将包含大量数据的实体(表)。在应用中常要保留历史记录,但是历史记录很少用 到。因此可以把频繁被访问的数据同较少被访问的历史数据分开。而且如果数据行是作为子集被逻辑工作组(部门、销售分区、地理区域等)访问的,那么这种方法 也是很有好处的。
1.2 生成物理数据库
要想正确选择基本物理实现策略,必须懂得数据库访问格式和硬件资源的操作特点,主要是内存和磁盘子系统I/O。这是一个范围广泛的话题,但以下的准则可能会有所帮助。
1.与每个表列相关的数据类型应该反映数据所需的最小存储空间,特别是对于被索引的列更是如此。比如能使用smallint类型就不要用integer类型,这样索引字段可以被更快地读取,而且可以在1个数据页上放置更多的数据行,因而也就减少了I/O操作。
2.把1个表放在某个物理设备上,再通过SQL Server段把它的不分簇索引放在1个不同的物理设备上,这样能提高性能。尤其是系统采用了多个智能型磁盘控制器和数据分离技术的情况下,这样做的好处更加明显。
3.用SQL Server段把一个频繁使用的大表分割开,并放在2个单独的智能型磁盘控制器的数据库设备上,这样也可以提高性能。因为有多个磁头在查找,所以数据分离也能提高性能。
4.用SQL Server段把文本或图像列的数据存放在1个单独的物理设备上可以提高性能。1个专用的智能型的控制器能进一步提高性能。
2 与SQL Server相关的硬件系统
与SQL Server有关的硬件设计包括系统处理器、内存、磁盘子系统和网络,这4个部分基本上构成了硬件平台,Windows NT和SQL Server运行于其上。
2.1 系统处理器(CPU)
根 据自己的具体需要确定CPU结构的过程就是估计在硬件平台上占用CPU的工作量的过程。从以往的经验看,CPU配置最少应是1个80586/100处理 器。如果只有2~3个用户,这就足够了,但如果打算支持更多的用户和关键应用,推荐采用Pentium Pro或PⅡ级CPU。
2.2 内存(RAM)
为SQL Server方案确定合适的内存设置对于实现良好的性能是至关重要的。SQL Server用内存做过程缓存、数据和索引项缓存、静态服务器开支和设置开支。SQL Server最多能利用2GB虚拟内存,这也是最大的设置值。还有一点必须考虑的是Windows NT和它的所有相关的服务也要占用内存。
Windows NT为每个WIN32应用程序提供了4GB的虚拟地址空间。这个虚拟地址空间由Windows NT虚拟内存管理器(VMM)映射到物理内存上,在某些硬件平台上可以达到4GB。SQL Server应用程序只知道虚拟地址,所以不能直接访问物理内存,这个访问是由VMM控制的。Windows NT允许产生超出可用的物理内存的虚拟地址空间,这样当给SQL Server分配的虚拟内存多于可用的物理内存时,会降低SQL Server的性能。
这些地址空间是专门为SQL Server系统设置的,所以如果在同一硬件平台上还有其它软件(如文件和打印共享,应用程序服务等)在运行,那么应该考虑到它们也占用一部分内存。一般 来说硬件平台至少要配置32MB的内存,其中,Windows NT至少要占用16MB。1个简单的法则是,给每一个并发的用户增加100KB的内存。例如,如果有100个并发的用户,则至少需要32MB+100用户 *100KB=42MB内存,实际的使用数量还需要根据运行的实际情况调整。可以说,提高内存是提高系统性能的最经济的途径。
2.3 磁盘子系统
设计1个好的磁盘I/O系统是实现良好的SQL Server方案的一个很重要的方面。这里讨论的磁盘子系统至少有1个磁盘控制设备和1个或多个硬盘单元,还有对磁盘设置和文件系统的考虑。智能型SCSI-2磁盘控制器或磁盘组控制器是不错的选择,其特点如下:
(1)控制器高速缓存。
(2)总线主板上有处理器,可以减少对系统CPU的中断。
(3)异步读写支持。
(4)32位RAID支持。
(5)快速SCSI—2驱动。
(6)超前读高速缓存(至少1个磁道)。
3 检索策略
在 精心选择了硬件平台,又实现了1个良好的数据库方案,并且具备了用户需求和应用方面的知识后,现在应该设计查询和索引了。有2个方面对于在SQL Server上取得良好的查询和索引性能是十分重要的,第1是根据SQL Server优化器方面的知识生成查询和索引;第2是利用SQL Server的性能特点,加强数据访问操作。
3.1 SQL Server优化器
Microsoft SQL Server数据库内核用1个基于费用的查询优化器自动优化向SQL提交的数据查询操作。数据操作查询是指支持SQL关键字WHERE或HAVING的查 询,如SELECT、DELETE和UPDATE。基于费用的查询优化器根据统计信息产生子句的费用估算。
了解优化器数据处理过程的简 单方法是检测SHOWPLAN命令的输出结果。如果用基于字符的工具(例如isql),可以通过键入SHOW SHOWPLAN ON来得到SHOWPLAN命令的输出。如果使用图形化查询,比如SQL Enterprise Manager中的查询工具或isql/w,可以设定配置选项来提供这一信息。
SQL Server的优化通过3个阶段完成:查询分析、索引选择、合并选择。
1.查询分析
在 查询分析阶段,SQL Server优化器查看每一个由正规查询树代表的子句,并判断它是否能被优化。SQL Server一般会尽量优化那些限制扫描的子句。例如,搜索和/或合并子句。但是不是所有合法的SQL语法都可以分成可优化的子句,如含有SQL不等关系 符“<>”的子句。因为“<>”是1个排斥性的操作符,而不是1个包括性的操作符,所在扫描整个表之前无法确定子句的选择范围会 有多大。当1个关系型查询中含有不可优化的子句时,执行计划用表扫描来访问查询的这个部分,对于查询树中可优化的SQL Server子句,则由优化器执行索引选择。
2.索引选择
对于每个可优化的子句,优化器都查看数据库系统表,以确定是 否有相关的索引能用于访问数据。只有当索引中的列的1个前缀与查询子句中的列完全匹配时,这个索引才被认为是有用的。因为索引是根据列的顺序构造的,所以 要求匹配是精确的匹配。对于分簇索引,原来的数据也是根据索引列顺序排序的。想用索引的次要列访问数据,就像想在电话本中查找所有姓为某个姓氏的条目一 样,排序基本上没有什么用,因为你还是得查看每一行以确定它是否符合条件。如果1个子句有可用的索引,那么优化器就会为它确定选择性。
所以在设计过程中,要根据查询设计准则仔细检查所有的查询,以查询的优化特点为基础设计索引。
(1)比较窄的索引具有比较高的效率。对于比较窄的索引来说,每页上能存放较多的索引行,而且索引的级别也较少。所以,缓存中能放置更多的索引页,这样也减少了I/O操作。
(2)SQL Server优化器能分析大量的索引和合并可能性。所以与较少的宽索引相比,较多的窄索引能向优化器提供更多的选择。但是不要保留不必要的索引,因为它们 将增加存储和维护的开支。对于复合索引、组合索引或多列索引,SQL Server优化器只保留最重要的列的分布统计信息,这样,索引的第1列应该有很大的选择性。
(3)表上的索引过多会影响UPDATE、INSERT和DELETE的性能,因为所有的索引都必须做相应的调整。另外,所有的分页操作都被记录在日志中,这也会增加I/O操作。
(4)对1个经常被更新的列建立索引,会严重影响性能。
(5)由于存储开支和I/O操作方面的原因,较小的自组索引比较大的索引性能更好一些。但它的缺点是要维护自组的列。
(6)尽量分析出每一个重要查询的使用频度,这样可以找出使用最多的索引,然后可以先对这些索引进行适当的优化。
(7)查询中的WHERE子句中的任何列都很可能是个索引列,因为优化器重点处理这个子句。
(8)对小于1个范围的小型表进行索引是不划算的,因为对于小表来说表扫描往往更快而且费用低。
(9)与“ORDER BY”或“GROUP BY”一起使用的列一般适于做分族索引。如果“ORDER BY”命令中用到的列上有分簇索引,那么就不会再生成1个工作表了,因为行已经排序了。“GROUP BY”命令则一定产生1个工作表。
(10)分簇索引不应该构造在经常变化的列上,因为这会引起整行的移动。在实现大型交易处理系统时,尤其要注意这一点,因为这些系统中数据往往是频繁变化的。
3.合并选择
当 索引选择结束,并且所有的子句都有了一个基于它们的访问计划的处理费用时,优化器开始执行合并选择。合并选择被用来找出一个用于合并子句访问计划的有效顺 序。为了做到这一点,优化器比较子句的不同排序,然后选出从物理磁盘I/O的角度看处理费用最低的合并计划。因为子句组合的数量会随着查询的复杂度极快地 增长,SQL Server查询优化器使用树剪枝技术来尽量减少这些比较所带来的开支。当这个合并选择阶段结束时,SQL Server查询优化器已经生成了1个基于费用的查询执行计划,这个计划充分利用了可用的索引,并以最小的系统开支和良好的执行性能访问原来的数据。
3.2 高效的查询选择
从以上查询优化的3个阶段不难看出,设计出物理I/O和逻辑I/O最少的方案并掌握好处理器时间和I/O时间的平衡,是高效查询设计的主要目标。也就是说,希望设计出这样的查询:充分利用索引、磁盘读写最少、最高效地利用了内存和CPU资源。
以下建议是从SQL Server优化器的优化策略中总结出来的,对于设计高效的查询是很有帮助的。
1.如果有独特的索引,那么带有“=”操作符的WHERE子句性能最好,其次是封闭的区间(范围),再其次是开放的区间。
2. 从数据库访问的角度看,含有不连续连接词(OR和IN)的WHERE子句一般来说性能不会太好。所以,优化器可能会采用R策略,这种策略会生成1个工作 表,其中含有每个可能匹配的执行的标识符,优化器把这些行标志符(页号和行号)看做是指向1个表中匹配的行的“动态索引”。优化器只需扫描工作表,取出每 一个行标志符,再从数据表中取得相应的行,所以R策略的代价是生成工作表。
3.包含NOT、<>、或! =的WHERE子句对于优化器的索引选择来说没有什么用处。因为这样的子句是排斥性的,而不是包括性的,所以在扫描整个原来数据表之前无法确定子句的选择性。
4.限制数据转换和串操作,优化器一般不会根据WHERE子句中的表达式和数据转换式生成索引选择。例如:
paycheck * 12>36000 or substring(lastname,1,1)=“L”
如果该表建立了针对paycheck和lastname的索引,就不能利用索引进行优化,可以改写上面的条件表达式为:
paycheck<36000/12 or lastname like “L%”
5.WHERE子句中的本地变量被认为是不被优化器知道和考虑的,例外的情况是定义为储备过程输入参数的变量。
6. 如果没有包含合并子句的索引,那么优化器构造1个工作表以存放合并中最小的表中的行。然后再在这个表上构造1个分簇索引以完成一个高效的合并。这种作法的 代价是工作表的生成和随后的分族索引的生成,这个过程叫REFORMATTING。 所以应该注意RAM中或磁盘上的数据库tempdb的大小(除了 SELECT INTO语句)。另外,如果这些类型的操作是很常见的,那么把tempdb放在RAM中对于提高性能是很有好处的。
4 性能优化的其他考虑
上面列出了影响SQL Server的一些主要因素,实际上远不止这些。操作系统的影响也很大,在Windows NT下,文件系统的选择、网络协议、开启的服务、SQL Server的优先级等选项也不同程度上影响了SQL Server的性能。
影响性能的因素是如此的多,而应用又各不相同,找出1个通用的优化方案是不现实的,在系统开发和维护的过程中必须针对运行的情况,不断加以调整。事实上,绝大部分的优化和调整工作是在与客户端独立的服务器上进行的,因此也是现实可行的。
相当长一段时间以来,在64 位平台上运行SQL Server一直是提高数据库性能和扩展性的一种选择,不过配置方面的选项有限,而且不是没有问题。举例说,SQL Server 2000只能在昂贵的安腾系列处理器上面运行;而且SQL Server的客户端工具与64位平台不兼容。另一方面,SQL Server 2005却提供了新的选项可以充分利用64位架构的强大功能;而且完全没有在过去导致人们不太需要64位的问题。
使用SQL Server的公司为什么应当改用64位架构?
要 解答这个问题,最重要的答案就是,64位平台与32位系统相比,大大提高了内存访问能力。32位系统最多只能本地访问4GB的内存。32位的SQL Server系统使用地址窗口扩展(AWE)及相关技术后,最多可以访问64GB的内存,不过地址虚拟技术带来了开销:AWE需要创建虚拟“窗口”来访问 更高内存。访问高端内存的每个请求都必须通过这个窗口进行,开销要比请求访问本地内存大得多。因而,在高使用率情况下,访问更大内存的功能实际上妨碍了而 不是有助于性能。此外,AWE内存只是被SQL Server用于缓冲器缓存,而不是用于过程缓存,而且不会有助于对利用许多即席查询(ad-hoc query)的服务器进行优化。AWE内存也不会被用于帮助内存中的排序、散列连接(hash join)或者其他数据密集型操作。
如 今的64位系统最多可本地访问512GB的内存。这意味着,性能不会受到地址窗口的影响,额外内存可以供任何SQL Server缓存而不仅仅是缓冲器缓存使用。这种增加内存的功能在许多情况下直接提高了性能。由于更多的数据保存在缓存里面,势必会减少磁盘的I/O操 作。你还会注意到使用中间排序、散列连接或者指针的查询在性能上得到提高。所有这些在内存里面进行求值要比换到磁盘上进行求值来得快。
为什么64位采用迟缓?
有 人不由得会想:既然好处这么显著,为什么到目前为止64位SQL Serve的采用似乎很迟缓?SQL Server 2000的64位选项很有限,因为SQL Server 2000惟一支持的64位配置就是安腾服务器运行在Windows Server 2003上面。也没有哪个SQL Server 2000客户端工具可在64位服务器上面运行,包括企业管理器、查询分析器和SQL Profiler。连数据转换服务(DTS)软件包也无法在64位服务器上运行,这意味着DTS无法充分利用64位的更强功能。
SQL Server 2005 64位架构有什么优点?
SQL Server 2005为企业带来了64位架构的优点,而与以往相比价位较低、功能较多。最重要的是,SQL Server 2005支持可以安装在安腾和价格低得多的x64服务器两种平台上。所以,除了节省费用外,数据库管理员现在就可以使用英特尔处理器或者AMD处理器。
SQL Server 2005客户端工具与64位服务器完全兼容,所有SQL Server支持服务都可以在64位配置环境下与SQL Server 2005一起使用,这包括:分析服务、SQL Server集成服务、报表服务和通知服务。所有这些服务都能够利用访问更多内存的功能,有助于提高安装的SQL Server的性能、满足业务集成需求。
哪种安装环境应当升级至64位?
升级主要有两个市场:需要向上扩展的32位单服务器安装环境;以及需要合并的32位多服务器安装环境。每种场景都有明显的优点。
表明单服务器安装配置可能属于向上扩展类别的最明显迹象就是,深度查询磁盘活动、较低的缓冲器缓存命中率以及较短的页面生命周期。所有这些问题都可以使用性能计数器来评估,可通过能够访问更多内存的64位系统来加以解决。
另 一方面,确定多服务器安装环境是不是非常适合合并来得困难一点。应当进行认真测试,评估所有数据库总共需要多少内存、处理器能不能处理所有数据库的并发查 询、磁盘系统能不能处理同时读写带来的更大压力。做出这个决策比升级单一服务器困难得多,不过就整体的管理简易性而言,会获得巨大回报。
改 用64位会在SQL Server的性能和扩展性方面带来重大影响。SQL Server 2005提供的选项使得从32位进行升级合理得多。如果你投资新硬件用于新的数据库管理系统(DBMS),就应当调查分析64位选项,尤其是基于价格较低 的x64位处理器的那些选项。
我要不要使用新的XML数据类型把所有XML数据保存在SQL Server 2005里面?
XML酷似CLR用户定义类型(UDT),它现在是SQL Server 2005中新的第一类数据类型。开发人员现在可能会忍不住使用这种数据类型,以免编写代码把XML数据“分割”到表里面(即不是使用OPENXML往表里面批量载入数据)。
遗 憾的是,像这样使用XML数据类型存在与使用用户定义类型表示数据同样的许多问题。开发人员应当把性能记在心头,因为对XML列的一个节点进行查询需要引 擎对表中每一行的不同XML查询进行求值。与使用CLR UDT一样,还存在规范化问题。在过多使用XML数据类型的数据库里面,要确保数据完整性极其困难。
今天在做图书馆的博硕士论文数据库时,想要用到存储过程,在存储过程中想用到字 符串替换功能。先到网上找了一下,基本上所有相关的文章都说在SQL Server中没发现子串全部替换的字符串函数。难道还真的要我自己写一个这样的函数不成?我不死心,于是去查中国铁道出版社出的《SQL Server2000宝典》,在172页字符串函数那部分,我看到了有一个Replace()函数的介绍。不过那个例子举的不太好。我再打开SQL Server的联机手册,在其中查找Replace,它的说明如下:
Replace:用第三个表达式替换第一个字符串表达式中出现的所有第二个给定字符串表达式。
语法
Replace ( 'string_expression1' , 'string_expression2' , 'string_expression3' )
参数:
'string_expression1'
待搜索的字符串表达式。string_expression1 可以是字符数据或二进制数据。
'string_expression2'
待查找的字符串表达式。string_expression2 可以是字符数据或二进制数据。
'string_expression3'
替换用的字符串表达式。string_expression3 可以是字符数据或二进制数据。
返回类型
如果 string_expression(1、2 或 3)是支持的字符数据类型之一,则返回字符数据。如果 string_expression(1、2 或 3)是支持的 binary 数据类型之一,则返回二进制数据。
示例:
下例用 xxx 替换 abcdefghi 中的字符串 cde。
SELECT REPLACE( ' abcdefghicde ' , ' cde ' , ' xxx ' )
GO
下面是结果集:
abxxxfghixxx
(1 row(s) affected)
原来如此!看来“尽信网不如无网”啊,网上的一些文章都是你抄我我抄你的,对人的误导太大了。还是看官方的参考手册好啊。
SQL Server上内置了加密术用来保护各种类型的敏感数据。在很多时候,这个加密术对于你来说是完全透明的;当数据被存储时候被加密,它们被使用的时候就会自动加密。在其他的情况下,你可以选择数据是否要被加密。SQL Server可以加密下列这些组件:
·密码
·存储过程,视图,触发器,用户自定义函数,默认值,和规则。
·在服务器和用户之间传输的数据
密码加密术
SQL Server自动将你分配给登陆和应用角色的密码加密。尽管当你可以从主数据库中直接察看系统表格而不需要密码。你不能给对这种情况作出任何修改,事实上,你根本不能破坏它。
定义加密术
在有些时候,如果对对象进行加密是防止将一些信息分享给他人。例如,一个存储进程可能包含所有者的商业信息,但是这个信息不能和让其他的人看到,即使他 们公开的系统表格并可以看到对象的定义。这就是为什么SQL Server允许你在创建一个对象的时候进行加密。为了加密一个存储进程,使用下面形式的CREAT PROCEDURE 语句:
CREATE PROCEDURE procedurename [;number]
[@parameter datatype
[VARYING][ = defaultvalue][OUTPUT]]
[, …]
[WITH RECOMPILE | ENCRYPTION | RECOMPILE, ENCRYPTION]
我们关心的仅仅是可选的WITH参数。你可以详细说明ARECOMPILE或者ENCRYPTION,或者你可以同时说明它们。 ENCRYPTION关键字保护SQL Server它不被公开在进程中。结果,如果ENCRYPTION在激活的时候系统存储进程sp_helptext就会被忽视,这个存储进程将被存储在用 户创建进程的文本中。如果你不想要加密,你可以使用ALTER PROCEDURE,忽略WITH ENCRYPTION子句来重新创建一个进程。
为了能够使用加密术。用户和服务器都应该使用TCP/IP NetworkLibraries用来连接。运行适当的Network Utility和检查Force protocol encryption,看下表,用户和服务器之间的连接将不会被加密。
加密也不能完全自由。当连接确定后,要继续其他的构造,并且用户和服务器必须运行代码来解释加密和解释的包裹。这里将需要一些开销并且当在编译码的时候会使进程慢下来。如果网络包裹在你控制范围之外,使用这种做法是非常好的。
加密术中缺少什么?
你可以注意到在这个列表中缺少一些被加密的东西:你表格中的数据。在你存储数据之前,SQL Server不会提供任何内置的工具来加密你的数据。如果你需要保护存储在SQL Server上的数据,我们给你两条建议:第一,你可以利用GRANT 和DENY关键字来控制你想哪个用户可以在SQL Server中读取的数据。
第二.如果你真的想对数据加密,不要设法加密码。你可以利用被测试过的商业产品的算法。
SQL 注入攻击
SQL 注入攻击是一个常规性的攻击,它可以允许一些不法用户检索你的数据,改变服务器的设置,或者在你不小心的时候黑掉你的服务器。SQL 注入攻击不是SQL Server问题,而是不适当的程序。如果你想要运行这些程序的话,你必须明白这冒着一定的风险。
测点定位弱点
SQL 注入的脆弱点发生在程序开发员构造一个WHERE 子句伴随着用户的输入的时候。比如,一个简单的ASP程序允许用户输入一个顾客的ID然后检索公司的全部人员的名字,如果顾客ID如果作为ASP页面的请 求串的一部分返回,那么开发员可以编写下面的代码获得数据:
strConn = " Provider=SQLOLEDB;Data Source=(local); " & _
" Database=Northwind;Integrated Security=SSPI "
Set cnn = Server.CreateObject( " ADODB.Connection " )
cnn.Open strConn
strQuery = " SELECT ContactName FROM Customers " & _
“WHERE CustomerID = ' " & Request.Form("CustID") & " '"
Set rstResults = cnn.Execute(strQuery)
Response.Write(rstResults.Fields( " ContactName " ).Value)
现在你知道什么地方有问题了吧?如果用户知道一个用户的ID,他可以通过检索来获得全部的相应的名字。现在明白了?
获得额外的数据
当然,对于一个攻击程序,尽管它不知道任何顾客的ID,甚至不用去猜,它也可以获得数据。为了完成这个工作,它将下面的文本输入到应用程序调用顾客ID的textbox中:
customer ID:
' UNION ALL SELECT ContactName FROM Customers
WHERE CustomerID <> '
如果你输入了这个代码,你将会看到返回一个询问语句:
SELECT ContactName FROM Customers
WHERE CustomerID = ''
UNION ALL SELECT ContactName FROM Customers
WHERE CustomerID <> ''
通过获得空和非空顾客的ID并集,这个查询语句会返回数据库中所有的相关姓名。事实上,这个UNION技术可以被用来获得你数据库中大多数信息,看看这个CustomerID的值:
' UNION ALL SELECT FirstName ' ' LastName FROM
Employees WHERE LastName <> '
它将SQL语句变成:
SELECT ContactName FROM Customers
WHERE CustomerID = ''
UNION ALL SELECT FirstName ' ' LastName FROM
Employees WHERE LastName <> ''
看,那就是攻击程序从你的数据库获得的第一个雇员的名字。
更多的攻击程序
如果SQL注入仅仅只有数据暴光这个弱点就已经够糟糕的了,但是,实际上一个良好的攻击程序可以通过这个弱点获取你数据库中所有的资料。看下面这个例子:
' ;DROP TABLE Customers;--
SQL语句变成:
SELECT ContactName FROM Customers
WHERE CustomerID = ''
; DROP TABLE Customers; -- '
这个分号使语句和SQL Server隔离,所以,这里实际上是两个语句。第一个语句不存在的名字,第二个则撤消的整个Customers表。两个—SQL Server注释符,它可以使子句不发生语法错误。
使用这个技术的变异,一个攻击程序可以在任何SQL语句或者存储过程上运行。通过使用xp_cmdshell扩展存储过程,一个攻击程序同样可以在操作系统命令下运行,显然,这是一个严重的漏洞。
保护自己的数据库
现在,你知道如何防范SQL注入攻击了吗?首先,你不能在用户输入中构造WHERE子句,你应该利用参数来使用存储进程。在最初的ASP页面下,重新写 的部分将和刚才我们在表中所看到的东西相似。即使你认为在你的应用程序中没有脆弱点,你应该遵守最小特权原则。使用我们建议的其他安全技术允许你的用户仅 仅访问他们能够访问的。在你没有发现你数据库脆弱点的时候,只有这样,不会使你的数据库崩溃。
最后的建议
这就是全部的SQL Server安全系列。也许你现在不是一个全面的专家,但是你已经了解了很多反面。下一步就是你要保护你SQL Server数据,记住你在这里所学到的知识,并利用到你的数据库中保证你的数据不被那些黑客攻击。
如何在程序中动态配置连接SQL Server的ODBC,以便增加程序的可移植性呢?下面给出一个例子以供参考。
建立一个system DSN:
GetMem(WinDir, 256 );
GetWindowsDirectory(WinDir, 128 );
///////////////////////////////////////////// /
MyReg: = TRegistry.Create;
MyReg.RootKey: = HKEY_LOCAL_MACHINE;
if not MyReg.KeyExists( ' SoftWareODBCODBC.INI_ODBC_Name ' )then
begin
MyReg.OpenKey( ' SoftWareODBCODBC.INI_ODBC_Name ' ,True);
end
else
begin
MyReg.OpenKey( ' SoftWareODBCODBC.INI_ODBC_Name ' ,False);
end;
MyReg.WriteString( ' Database ' , ' _dataBaseName ' );
MyReg.WriteString( ' Driver ' ,WinDir + ' System32SQLSRV32.dll ' );
MyReg.WriteString( ' LastUser ' ,_帐号);
MyReg.WriteString( ' Server ' , ' (local) ' );
MyReg.CloseKey;
////////////////////////////////////
if not MyReg.KeyExists( ' SoftWareODBCODBC.INIODBC Data Sources ' )then
begin
MyReg.OpenKey( ' SoftWareODBCODBC.INIODBC Data Sources ' ,True);
end
else
begin
MyReg.OpenKey( ' SoftWareODBCODBC.INIODBC Data Sources ' ,False);
end;
MyReg.WriteString( ' _ODBC_Name ' , ' SQL Server ' );
MyReg.CloseKey;
MyReg.Free;
其中带“_”的为自定义的字符串。
1.通过工具DTS的设计器进行导入或导出
DTS的设计器功能强大,支持多任务,也是可视化界面,容易操作,但知道的人一般不多,如果只是进行SQL Server数据库中部分表的移动,用这种方法最好,当然,也可以进行全部表的移动。在SQL Server Enterprise Manager中,展开服务器左边的+,选择数据库,右击,选择All tasks/Import Data...(或All tasks/Export Data...),进入向导模式,按提示一步一步走就行了,里面分得很细,可以灵活的在不同数据源之间复制数据,很方便的。而且可以另存成DTS包,如果 以后还有相同的复制任务,直接运行DTS包就行,省时省力。也可以直接打开DTS设计器,方法是展开服务器名称下面的Data Transformation Services,选Local Packages,在右边的窗口中右击,选New Package,就打开了DTS设计器。值得注意的是:如果源数据库要拷贝的表有外键,注意移动的顺序,有时要分批移动,否则外键主键,索引可能丢失,移 动的时候选项旁边的提示说的很明白,或者一次性的复制到目标数据库中,再重新建立外键,主键,索引。
其实建立数据库时,建立外键,主键,索引的文件应该和建表文件分开,而且用的数据文件也分开,并分别放在不同的驱动器上,有利于数据库的优化。
2. 利用Bcp工具
这种工具虽然在SQL Server7的版本中不推荐使用,但许多数据库管理员仍很喜欢用它,尤其是用过SQL Server早期版本的人。Bcp有局限性,首先它的界面不是图形化的,其次它只是在SQL Server的表(视图)与文本文件之间进行复制,但它的优点是性能好,开销小,占用内存少,速度快。有兴趣的朋友可以查参考手册。
3. 利用备份和恢复
先对源数据库进行完全备份,备份到一个设备(device)上,然后把备份文件复制到目的服务器上(恢复的速度快),进行数据库的恢复操作,在恢复的数 据库名中填上源数据库的名字(名字必须相同),选择强制型恢复(可以覆盖以前数据库的选项),在选择从设备中进行恢复,浏览时选中备份的文件就行了。这种 方法可以完全恢复数据库,包括外键,主键,索引。
4. 直接拷贝数据文件
把数据库的数据文件(*.mdf)和日志文件(*.ldf)都拷贝到目的服务器,在SQL Server Query Analyzer中用语句进行恢复:
EXEC sp_attach_db @dbname = ’test’,
@filename1 = ’d:mssql7data est_data.mdf’,
@filename2 = ’d:mssql7data est_log.ldf’
这样就把test数据库附加到SQL Server中,可以照常使用。如果不想用原来的日志文件,可以用如下的命令:
EXEC sp_detach_db @dbname = ’test’
EXEC sp_attach_single_file_db @dbname = ’test’,
@physname = ’d:mssql7data est_data.mdf’
这个语句的作用是仅仅加载数据文件,日志文件可以由SQL Server数据库自动添加,但是原来的日志文件中记录的数据就丢失了。
5. 在应用程序中定制
可以在应用程序(PB、VB)中执行自己编写的程序,也可以在Query Analyzer中执行,这种方法比较灵活,其实是利用一个平台连接到数据库,在平台中用的主要时SQL语句,这种方法对数据库的影响小,但是如果用到远 程链接服务器,要求网络之间的传输性能好,一般有两种语句:
1 >select ... into new_tablename where ...
2 >insert (into) old_tablename select ... from ... where ...
区别是前者把数据插入一个新表(先建立表,再插入数据),后者是把数据插入已经存在的一个表中,我个人喜欢后者,因为在编程的结构上,应用的范围上,第二条语句强于前者。
6. SQL Server的复制功能
SQL Server提供了强大的数据复制功能,也是最不易掌握的,具体应用请参考相关资料,值得注意的是要想成功进行数据的复制工作,有些条件是必不可少的:
1>SQL Server Agent必须启动,MSDTC必须启动。
2>所有要复制的表必须有主键。
3>如果表中有text或image数据类型,必须使用with log选项,不能使用with no_log选项。
另外max text repl size选项控制可以复制的文本和图像数据的最大规模,超过这个限制的操作将失败。
4>在要进行复制的计算机上,应该至少是隐含共享,即共享名是C$或D$…。
5>为SQL Server代理使用的Windows NT帐号不能是一个本地的系统帐号,因为本地的系统帐号不允许网络存取。
6>如果参与复制的服务器在另外的计算机域中,必须在这些域之间建立信任关系。本人从事的工作是数据库管理员,要维护多台服务器中的数据库,经常把某台服务器中的某个数据库移动到另外一台服务器,对数据的移动有些心得体会,希望和大家共同交流
SQL Server 2005已经发布,许多单位在考虑升级,但它们不知道如何开始着手,或者不知道如何及时地从一个阶段进入到另一个阶段。本文大致介绍了十个重要步骤,让用户者遵循正确的步骤,升级至SQL Server 2005。
一、证明升级的必要性
找到进行SQL Server 2005升级的关键因素也许很简单,但要说服技术和业务管理人员进行实施却是个难题。调查和教育是良好的开端。要考虑目前的平台遇到过的所有问题,然后确 定单单SQL Server 2005(或者连同其他额外产品)会不会解决业务和技术上的难题。把调查结果汇报给上层管理人员。
二、成本和投资回报
升级决策的一个方面免不了归结为将成本与时间跟业务改进与竞争优势进行比较。要找出升级带来的实际成本和效益很难,因为成本和效益都分为有形的和无形 的。列一份清单,比较有形成本(如许可费、项目预计时间和硬件费)和效益(如事务处理成本降低、支持成本减少、缩短流程时间)。这项任务颇有难度,不过为 了得到纯粹由财务驱动的决策,这些信息是无价的。
三、项目规划
一旦投入了 时间和精力向管理人员竭力介绍SQL Server升级带来的效益,随后就要证明你的调查结果。首先要进行项目规划,列出重要的升级阶段,包括交付时间和必要的角色与职责。然后,你必须决定哪 个部门负责升级项目、哪个部门为项目分配预算。设定预期目标,让你的队伍知道:这项技术不仅对贵组织来说是新的,而且对整个行业来说也是新的。他们需要时 间尽快上手、进行合理的测试、完成项目,从而给组织带来效益。先从简单的试点项目开始着手,从失误当中吸取教训,以便将来改进,然后开始升级比较大、比较 重要的系统。
四、项目队伍
项目规划应当定义重要任务,不过对队伍成员进行 教育、根据现有项目权衡预期目标需要花费时间。队伍必须包括:项目支持者、项目经理、数据库管理员、开发人员、网络管理员、测试人员和使用人员等。在项目 的早期阶段,一定不要忽视任何小组。即便队伍成员不需要立即参与项目,也要征求他们的意见,根据他们的工作日程来安排需要参与项目的时间。
五、人员培训
为了合理地开发及管理基于SQL Server的应用系统,就要安排时间对队伍成员进行培训,无论通过网上、自定进度还是去学校上课。要向管理人员强调培训的重要性和效益。如果不具备必要 知识,就不可能满足企业要求。请访问微软SQL Server 2005的免费培训资源(http://msdn.microsoft.com/virtuallabs/sql/default.aspx)。
六、评估新特性
认为SQL Server 2005是微软有史以来发布过的各款SQL Server当中最具有创新的版本。升级项目规划应当在以后阶段利用充分它的新特性。应当对SQL Server 2005具有的一些重要新特性有所了解。
七、评估当前环境
微软在去年9月份发布的 SQL Server 2005社区技术预览版(CTP)当中包括了SQL Server 2005升级顾问。这个应用程序会扫描SQL Server7.0和2000环境,以确认哪些不推荐的功能和配置更改应在升级之前加以改正。
八、测试环境
鉴于从SQL Server 7.0/2000到2005 发生了众多变化,你必须进行功能测试、负载测试和回归测试,功能测试可以检验应用程序不会在升级之后崩溃,能够继续正常工作。负载测试可以确保应用程序在 用户数量相同或者升级之后数量更多的情况下拥有可接受的性能。回归测试可以确保应用程序向后兼容,不会破坏任何外部接口。
九、如何升级
所有的升级,尤其是数据库升级,需要两个实现计划。第一个就是一旦设计、开发和测试工作完成,就立即升级。第二个就是,万一实现计划遇到不可预料的错误,就启用恢复原状计划,从而确保业务操作持续进行,不会延长停机时间。
SQL Server中有很多自带的系统表,这些表都有各自的作用,下面的表格则分别描述了这些系统表的所属及其作用,仅供参考。
sysaltfiles | 主数据库 | 保存数据库的文件 |
syscharsets | 主数据库 | 字符集与排序顺序 |
sysconfigures | 主数据库 | 配置选项 |
syscurconfigs | 主数据库 | 当前配置选项 |
sysdatabases | 主数据库 | 服务器中的数据库 |
syslanguages | 主数据库 | 语言 |
syslogins | 主数据库 | 登陆帐号信息 |
sysoledbusers | 主数据库 | 链接服务器登陆信息 |
sysprocesses | 主数据库 | 进程 |
sysremotelogins | 主数据库 | 远程登录帐号 |
syscolumns | 每个数据库 | 列 |
sysconstrains | 每个数据库 | 限制 |
sysfilegroups | 每个数据库 | 文件组 |
sysfiles | 每个数据库 | 文件 |
sysforeignkeys | 每个数据库 | 外部关键字 |
sysindexs | 每个数据库 | 索引 |
sysmenbers | 每个数据库 | 角色成员 |
sysobjects | 每个数据库 | 所有数据库对象 |
syspermissions | 每个数据库 | 权限 |
systypes | 每个数据库 | 用户定义数据类型 |
sysusers | 每个数据库 | 用户 |
如何来查询SQL Server中数据库的各个表所占用的空间?
下面来介绍一下两种用来方法。
方法1:
select object_name(id) tablename, 8 * reserved / 1024 reserved,rtrim( 8 * dpages / 1024 ) + ' Mb ' used, 8 * (reserved - dpages) / 1024 unused, 8 * dpages / 1024 - rows / 1024 * minlen / 1024 free,
rows, * from sysindexes
where indid = 1
order by reserved desc
方法2:
exec sp_MSforeachtable " exec sp_spaceused '?' "
1.运行 SQL Server 安装程序来安装"SQL Server 2000 组件",并选择"安装数据库服务器",然后在 SQL Server 安装向导的"欢迎"屏幕上单击"下一步"按钮。
2. 在"计算机名"对话框中,"本地计算机"是默认选项,本地计算机名显示在编辑框中。单击"下一步"按钮。
3.在"安装选项"对话框中单击"创建 SQL Server 新实例或安装客户端工具",然后单击"下一步"按钮。
4.按照"用户信息"和相关屏幕上的指导进行操作。
5.在"安装定义"对话框中,单击"服务器和客户端工具"选项,然后单击"下一步"按钮。
6.在"实例名称"对话框中:
a.若要创建区分大小写的默认实例,请接受"默认"复选框并单击"下一步"按钮。
b.若要创建区分大小写的命名实例,请清除"默认"复选框并键入实例名称。
7.在"安装类型"对话框中单击"自定义"选项,然后单击"下一步"按钮。
8.在"选择组件"、"服务帐户"和"身份验证模式"对话框中,更改或接受默认设置,然后单击"下一步"按钮。
9.在"排列规则设置"对话框中有两个选项:
a.若要使 Windows 区域设置排序规则区分大小写,请选择"排列规则指示器",然后从列表中选择正确的排序规则指示器。清除"二进制"复选框,然后选择"区分大小写"复选框。
b.若要使 SQL 排序规则区分大小写,请选择"SQL 排序规则",然后键入正确的排序规则名称。
有关排序规则选项的更多信息,请单击"帮助"按钮。完成选项的设置后,请单击"下一步"按钮。
10. 在随后出现的各对话框中,更改或接受默认设置,然后单击"下一步"按钮。
11.在完成选项的指定后,请在"开始复制文件"对话框中单击"下一步"按钮。
12.在"选择授权模式"对话框中,按照许可协议进行选择,然后单击"继续"按钮开始安装。
13.有关授权的信息,请单击"帮助"按钮或向系统管理员进行咨询
任何数据库系统都无法避免崩溃的状况,即使你使用了Clustered,双机热备……仍然无法完全根除系统中的单点故障,何况对于大部分用户来说,无法承受这样昂贵的硬件投资。所以,在系统崩溃的时候,如何恢复原有的宝贵数据就成为一个极其重要的问题了。
在恢复的时候,最理想的情况就是你的数据文件和日志文件都完好无损了,这样只需要sp_attach_db,把数据文件附加到新的数据库上即可,或者在 停机的时候把所有数据文件(一定要有master等)都copy到原有路径下也行,不过一般不推荐这样的做法,sp_attach_db比较好,虽然麻烦 许多。
但是呢,一般数据库崩溃的时候系统是未必能有时间把未完成的事务和脏页等写入磁盘的,这样的情况sp_attach_db就会失 败。那么,寄期望于DBA制定了一个良好的灾难恢复计划吧。按照你的恢复计划,还原最新的完全备份,增量备份或者事务日志备份,然后如果你的活动事务日志 还能读得出来的话,恭喜你!你可以还原到崩溃前的状态。
一般的单位都是没有专职的DBA的,如果没有可用的备份,更可能是最近一次备份的时间过于久远而导致不可接受的数据损失,而且你的活动事务日志也处于不可用的状态,那就是最麻烦的情况了。
不幸的很的是,一般数据库崩溃都是由于存储子系统引起的,而这样的情况是几乎不可能有可用的日志用于恢复的。
那么就只好试一下这些方案了。当然,是要求至少你的数据文件是存在的,要是数据文件、日志文件和备份都没有了的话,别找我,你可以到楼顶上去唱“神啊,救救我吧”。
首先,你可以试一下sp_attach_single_file_db,试着恢复一下你的数据文件,虽然能恢复的可能性不大,不过假如这个数据库刚好执行了一个checkpoint的话,还是有可能成功的。
如果你没有好到有摸彩票的手气,最重要的数据库没有像你期盼的那样attach上去,不要气馁,还是有别的方案的。
我们可以试着重新建立一个log,先把数据库设置为emergency mode,sysdatabases的status为32768 就表示数据库处于此状态。
不过系统表是不能随便改的,设置一下先
Use Master
Go
sp_configure ' allow updates ' , 1
reconfigure with override
Go
然后
update sysdatabases set status = 32768 where name = ''
现在,祈求满天神佛的保佑吧,重新建立一个log文件。成功的机会还是相当大的,系统一般都会认可你新建立的日志。如果没有报告什么错误,现在就可以松一口气了。
虽然数据是恢复了,可是别以为事情就算完成了,正在进行的事务肯定是丢失了,原来的数据也可能受到一些损坏。
先把SQL Server 重新启动一下,然后检查你的数据库吧。
先设置成单用户模式,然后做dbcc
sp_dboption '' , ' single user ' , ' true '
DBCC CHECKDB( '' )
如果没有什么大问题就可以把数据库状态改回去了,记得别忘了把系统表的修改选项关掉。update sysdatabases set status = 28 where name = '' ,当然你的数据库状态可能不是这个,自己改为合适的值吧。也可以用
sp_resetstatus
go
sp_configure ' allow updates ' , 0
reconfigure with override
Go
checkdb的时候可能报告有一些错误,这些错误的数据你可能就只好丢弃了。
checkdb有几种修复选项,自己看着用吧,不过最后你可能还是得REPAIR_ALLOW_DATA_LOSS,完成所有修复。
chekcdb并不能完成所有的修复,我们需要更进一步的修复,用DBCC CHECKTABLE对每一个表做检查吧。
表的列表可以用sysobjects里面得到,把OBJECTPROPERTY是IsTable的全部找出来检查一下吧,这样能够基本上解决问题了,如果还报告错误,试着把数据select into到另一张表检查一下。
这些都做完了之后,把所有索引、视图、存储过程、触发器等重新建立一下。DBCC DBREINDEX也许可以帮你一些忙。
(1)char、varchar、text和nchar、nvarchar、ntext
char和varchar的长度都在1到8000之间,它们的区别在于:
char:是定长字符数据,,速度快。
varchar:是变长字符数据,节省储存空间,但是存取速度慢。
所谓定长就是长度固定的,当输入的数据长度没有达到指定的长度时将自动以英文空格在其后面填充,使长度达到相应的长度;而变长字符数据则不会以空格填充。
text:存储可变长度的非Unicode数据,最大长度为2^31-1(2,147,483,647)个字符。
后面三种数据类型和前面的相比,从名称上看只是多了个字母"n",它表示存储的是Unicode数据类型的字符。写过程序的朋友对Unicode应该很 了解。字符中,英文字符只需要一个字节存储就足够了,但汉字众多,需要两个字节存储,英文与汉字同时存在时容易造成混乱,Unicode字符集就是为了解 决字符集这种不兼容的问题而产生的,它所有的字符都用两个字节表示,即英文字符也是用两个字节表示。nchar、nvarchar的长度是在1到4000 之间。和char、varchar比较:nchar、nvarchar则最多存储4000个字符,不论是英文还是汉字;而char、varchar最多能 存储8000个英文,4000个汉字。可以看出使用nchar、nvarchar数据类型时不用担心输入的字符是英文还是汉字,较为方便,但在存储英文时 数量上有些损失。
(2)datetime和smalldatetime
datetime:从1753年1月1日到9999年12月31日的日期和时间数据,精确到百分之三秒。
smalldatetime:从1900年1月1日到2079年6月6日的日期和时间数据,精确到分钟。
(3)bitint、int、smallint、tinyint和bit
bigint:从-2^63(-9223372036854775808)到2^63-1(9223372036854775807)的整型数据。
int:从-2^31(-2,147,483,648)到2^31-1(2,147,483,647)的整型数据。
smallint:从-2^15(-32,768)到2^15-1(32,767)的整数数据。
tinyint:从0到255的整数数据。
bit:1或0的整数数据。
(4)decimal和numeric
这两种数据类型是等效的。都有两个参数:p(精度)和s(小数位数)。p指定小数点左边和右边可以存储的十进制数字的最大个数,p必须是从 1到38之间的值。s指定小数点右边可以存储的十进制数字的最大个数,s必须是从0到p之间的值,默认小数位数是0。
(5)float和real
float:从-1.79^308到1.79^308之间的浮点数字数据。
real:从-3.40^38到3.40^38之间的浮点数字数据。在SQL Server中,real的同义词为float(24)。
1. 大容量装载COM接口。如果需要将文档的实体和属性析取到关系表中,最快的方法就是使用SQL Server 2000 Extensible Markup Language 3.0 Service Pack 1(SQLXML 3.0 SP1)提供的大容量装载COM接口。大容量状态COM接口包含在SQLXML 3.0 SP1的免费下载中。
2.textcopy.exe命令行实用工具。如果不希望将文档的实体和属性析取到关系表中,您可以使用textcopy.exe命令行实用工具。Textcopy.exe是将文本和image数据类型从单一服务器行或列移入或移出的优秀工具。
3.数据转换服务(DTS)。如果XML文档很简单,您可以使用DTS将信息逐行析取到表中。这一方法要求您将XML文件定义为输入数据源,将数据库表定义为输出数据源,并编写ActiveX脚本剖析"<"和">"方式的字符输入,以析取实体、属性及其值。
如何配置SQL Server来做远程备份?
以下介绍一下配置的方法:(ASE版本12.0以上)
在本地机器上:
1.修改interfaces文件,添加远程机器上Backup Server的条目(Backup Server的名称,地址,端口号):服务器名称不能使用别名,UNIX平台使用dscp实用工具添加,Windows平台使用dsedit实用工具添加
2.在sysservers系统表中添加远程Backup Server名
sp_addserver SYB_BACKUP, null ,BACKUP_SERVER_NAME
go
3.使用sp_helpserver查看至少存在以下3个条目:本地ASE名称 ,本地Backup Server名称,远程Backup Server名称
4.在本地机器上实现远程备份:
dump database DATABASE_NAME to " remote_machine_path/... " at REMOTE_BACKUP_SERVER_NAME
go
在本地机器上恢复备份:
load database DATABASE_NAME from " remote_machine_path/... " at REMOTE_BACKUP_SERVER_NAME
go
注:
1.如果要实现远程的双向备份,请在远程机器上做与本地机器相同的配置.
2.本地的Backup Server的名称要求与远程的Backup Server的名称不一样.
其实所有的死锁最深层的原因就是一个:资源竞争
表现一:
一个用户A 访问表A(锁住了表A),然后又访问表B,另一个用户B 访问表B(锁住了表B),然后企图访问表A,这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B,才能继续,好了他老人家就只好老老实实在这等 了,同样用户B要等用户A释放表A才能继续这就死锁了。
解决方法:
这种死锁是由于你的程序的BUG产生的,除了调整你的程序的逻辑别无他法
仔细分析你程序的逻辑:
1:尽量避免同时锁定两个资源
2: 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源.
表现二:
用户A读一条纪录,然后修改该条纪录。这是用户B修改该条纪录,这里用户A的事务里锁的性质由共享锁企图上升到独占锁(for update),而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现 了死锁。
这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。
解决方法:
让用户A的事务(即先读后写类型的操作),在select 时就是用Update lock
语法如下:
select * from table1 with(updlock) where ....
开发人员的噩梦——删除重复记录
想必每一位开发人员都有过类似的经历,在对数据库进行查询或统计的时候不时地会碰到由于表中存在重复的记录而导致查询和统计结果不准确。解决该问题的办法就是将这些重复的记录删除,只保留其中的一条。
在SQL Server中除了对拥有十几条记录的表进行人工删除外,实现删除重复记录一般都是写一段代码,用游标的方法一行一行检查,删除重复的记录。因为这种方法 需要对整个表进行遍历,所以对于表中的记录数不是很大的时候还是可行的,如果一张表的数据达到上百万条,用游标的方法来删除简直是个噩梦,因为它会执行相 当长的一段时间。
四板斧——轻松消除重复记录
殊不知在SQL Server中有一种更为简单的方法,它不需要用游标,只要写一句简单插入语句就能实现删除重复记录的功能。为了能清楚地表述,我们首先假设存在一个产品信息表Products,其表结构如下:
假设产品Chang和Tofu的记录在产品信息表中存在重复。现在要删除这些重复的记录,只保留其中的一条。步骤如下:
第一板斧——建立一张具有相同结构的临时表
第二板斧——为该表加上索引,并使其忽略重复的值
方法是在企业管理器中找到上面建立的临时表Products _temp,单击鼠标右键,选择所有任务,选择管理索引,选择新建。然后设置索引选项。
第三板斧——拷贝产品信息到临时表
此时SQL Server会返回如下提示:
服务器: 消息 3604,级别 16,状态 1,行 1 已忽略重复的键。 它表明在产品信息临时表Products_temp中不会有重复的行出现。 第四板斧——将新的数据导入原表 将原产品信息表Products清空,并将临时表Products_temp中数据导入,最后删除临时表Products_temp。
这样就完成了对表中重复记录的删除。无论表有多大,它的执行速度都是相当快的,而且因为几乎不用写语句,所以它也是很安全的。
小提示:上述方法中删除重复记录取决于创建唯一索引时选择的字段,在实际的操作过程中读者务必首先确认创建的唯一索引字段是否正确,以免将有用的数据删除。 |