导读:如果两个用户进程分别锁定了不同的资源,接着又试图锁定对方所锁定的资源,就会产生死锁。此时,SQL Server数据库将自动地选择并中止其中一个进程以解除死锁,使得另外一个进程能够继续处理。系统将回退被中止的事务,并向被回退事务的用户发送错误信息。 大多数设计良好的应用都会在接收到这个错误信息之后重新提交该事务,此时提交成功的可能性是很大的。但是,如果服务器上经常出现这种情况,就会显著地降低服务器性能。由此可知,减少SQL Server数据库死锁是非常有必要的。 为避免死锁,设计应用应当遵循一定的原则,包括: 让应用每次都以相同的次序访问服务器资源。 在事务期间禁止任何用户输入。应当在事务开始之前收集用户输入。 尽量保持事务的短小和简单。 如合适的话,为运行事务的用户连接指定尽可能低的隔离级别。[适用于6.5,7.0,2000] 此外,对于SQL Server的死锁问题,下面是几则实践中很有用的小技巧。 使用SQL Server Profiler的Create Trace Wizard运行“Identify The Cause of a Deadlock”跟踪来辅助识别死锁问题,它将提供帮助查找数据库产生死锁原因的原始数据。[适用于7.0,2000] 如果无法消除应用中的所有死锁,请确保提供了这样一种程序逻辑:它能够在死锁出现并中止用户事务之后,以随机的时间间隔自动重新提交事务。这里等待时间的随机性非常重要,这是因为另一个竞争的事务也可能在等待,我们不应该让两个竞争的事务等待同样的时间,然后再在同一时间执行它们,这样的话将导致新的死锁。[适用于6.5,7.0,2000] 尽可能地简化所有T-SQL事务。此举将减少各种类型的锁的数量,有助于提高SQL Server应用的整体性能。如果可能的话,应将较复杂的事务分割成多个较简单的事务。[适用于6.5,7.0,2000] 所有条件逻辑、变量赋值以及其他相关的预备设置操作应当在事务之外完成,而不应该放到事务之内。永远不要为了接受用户输入而暂停某个事务,用户输入应当总是在事务之外完成。[适用于6.5,7.0,2000] 在存储过程内封装所有事务,包括BEGIN TRANSACTION和COMMIT TRANSACTION语句。此举从两个方面帮助减少阻塞的锁。首先,它限制了事务运行时客户程序和SQL Server之间的通信,从而使得两者之间的任何消息只能出现于非事务运行时间(减少了事务运行的时间)。其次,由于存储过程强制它所启动的事务或者完成、或者中止,从而防止了用户留下未完成的事务(留下未撤销的锁)。[适用于6.5,7.0,2000] 如果客户程序需要先用一定的时间检查数据,然后可能更新数据,也可能不更新数据,那么最好不要在整个记录检查期间都锁定记录。假设大部分时间都是检查数据而不是更新数据,那么处理这种特殊情况的一种方法就是:先选择出记录(不加UPDATE子句。UPDATE子句将在记录上加上共享锁),然后把它发送给客户。 如果用户只查看记录但从来不更新它,程序可以什么也不做;反过来,如果用户决定更新某个记录,那么他可以通过一个WHERE子句检查当前的数据是否和以前提取的数据相同,然后执行UPDATE。 类似地,我们还可以检查记录中的时间标识列(如果它存在的话)。如果数据相同,则执行UPDATE操作;如果记录已经改变,则应用应该提示用户以便用户决定如何处理。虽然这种方法需要编写更多的代码,但它能够减少加锁时间和次数,提高应用的整体性能。[适用于6.5,7.0,2000] 尽可能地为用户连接指定具有最少限制的事务隔离级别,而不是总是使用默认的READ COMMITTED。为了避免由此产生任何其他问题,应当参考不同隔离级别将产生的效果,仔细地分析事务的特性。[适用于6.5,7.0,2000] 使用游标会降低并发性。为避免这一点,如果可以使用只读的游标则应该使用READ_ONLY游标选项,否则如果需要进行更新,尝试使用OPTIMISTIC游标选项以减少加锁。设法避免使用SCROLL_LOCKS游标选项,该选项会增加由于记录锁定引起的问题。[适用于6.5,7.0,2000] 如果用户抱怨说他们不得不等待系统完成事务,则应当检查服务器上的资源锁定是否是导致该问题的原因。进行此类检查时可以使用SQL Server Locks Object: Average Wait Time (ms),用该计数器来度量各种锁的平均等待时间。 如果可以确定一种或几种类型的锁导致了事务延迟,就可以进一步探究是否可以确定具体是哪个事务产生了这种锁。Profiler是进行这类具体分析的最好工具。[适用于7.0,2000] 使用sp_who和sp_who2(SQL Server Books Online没有关于sp_who2的说明,但sp_who2提供了比sp_who更详细的信息)来确定可能是哪些用户阻塞了其他用户。[适用于6.5,7.0,2000] 试试下面的一个或多个有助于避免阻塞锁的建议:1)对于频繁使用的表使用集簇化的索引;2)设法避免一次性影响大量记录的T-SQL语句,特别是INSERT和UPDATE语句;3)设法让UPDATE和DELETE语句使用索引;4)使用嵌套事务时,避免提交和回退冲突。[适用于6.5,7.0,2000] 这就是我要为大家介绍的减少SQL Server数据库死锁的小技巧,学会了这些技巧,在以后的操作中将会更加方便快捷。 原文链接:http://database.51cto.com/art/201103/247849.htm
View DetailsSQL server功能的强大性,安全问题首当前冲,众所周知,安全性问题一直是DBA比较关心的问题,因为建立数据库的目的就是让相关的的客户端来进行访问,所以很难避免不出现安全隐患,例如客户端链接的权限、数据传输过程中的安全等问题,所以大家在考虑SQL Server服务器安全的问题。 客户端 客户端安全:首先需要保证客户端必须是安全的,例如需要为你的客户端安装防病毒软件,防火墙,安装升级补丁等。 数据传输 数据传输过程中的安全:数据在一般的传输过程中,很容易被监听或被捕获,所以大家应该使用加密机制来保证数据的机密性。 安全机制 需要在数据库服务器上实现安全机制以此来保证安全性。SQL Server数据库是一个非常安全的数据库,由于客户端如果想对数据库里的数据进行操作(select、insert等),就必须经历三道检查,也就是我们要经过的三道门(登录验证、数据库验证、对象权限)。 备份密码 SQL Server中的一个选项就是创建用密码创建备份。这是你在创建备份的时候可以使用的另一个选择,但是在企业版管理器或者SQL Server管理套件中,并没有提供这个选项。这里是一个使用密码选项备份的例子: backup database northwind to disk=’c:\northwind.bak' with mediapassword = 'Backup2006' 这个过程需要密码来重新存储文件,但是使用文本编辑器,这些数据仍然是可以访问的。还有,重新存储不能使用GUI来完成,所以它必须通过T-SQL重新存储命令和密码一起完成任务。 假如我们把SQL Server服务器看成一座大厦,我们首先要现有权利进入大厦,即通过大厦的大门(登录验证),然后还得拥有对某个房间的访问权限(对数据库的权限),进入房间后还得拥有打开保险柜的权利(对表的操作权限)。也就是首先我们必须先建立登录帐户,而登录帐户分为两种:windows帐户和SQL帐户(因为SQL Server有两种身份验证,即windows身份验证和混合身份验证)。 具体操作方法:打开“SSMS—SQL Server实例—安全性—登录名”,右键选择“新建登录名”,选择身份验证模式(身份验证模式不同,帐户类型也不一样,注意:建立windows登录帐户,必须先在windows操作系统上先建立该账户),输入名字,并为该用户选择一个默认数据库(比如默认为master数据库)。该账户建立好之后,我们通过“新建”—“数据库引擎查询”的方式来测试用户,我们会发现该用户只能连接数据库,并不能对数据库进行任何其他操作。所以下面是第二道门,建立数据库用户,以便用户可以访问数据库,对数据库进行操作。我们在建立数据库用户时,其实就是映射登录用户,所以在一般情况下,我们的登录名和数据库用户名是一致的。 操作方法:第一种:打开“SSMS—SQL Server实例—具体的数据库—安全性—数据库用户”;第二种:直接在以前建立过的登录用户上映射数据库就可以了。最后一道门是在具体的数据库对象(比如表)上授予具体的权限,三种权限如下:授予、回收和拒绝。 SQL server的安全性问题不可马虎忽略,为了确保SQL server中数据信息的安全,一定要做好安全工作方面的工作,比如安全审计等等。
View Details最近看到一个SQL Server的小例子,发现完全可以作为SQL server的一道入门面试题。题目如下: 例:有一合同表Contract Id Name Total buget 1 合同名称 100 102,22 2 合同名称2 300 ,102,22, 3 合同名称3 200 103,23, 要求:用SQL语句更新表的buget字段,如果前后没有","要加上","(即一个英文逗号)。(10分) 创建表数据: View Code
1 |
<ol class="dp-sql"><li class="alt"><span><span>use Testdb2 </span></span></li><li><span>go </span></li><li class="alt"><span>IF </span><span class="op"><font color="#808080">NOT</font></span><span> OBJECT_ID(</span><span class="string"><font color="#0000ff">'[Contract]'</font></span><span>) </span><span class="keyword"><strong><font color="#006699">IS</font></strong></span><span> </span><span class="op"><font color="#808080">NULL</font></span><span> </span></li><li><span class="keyword"><strong><font color="#006699">DROP</font></strong></span><span> </span><span class="keyword"><strong><font color="#006699">TABLE</font></strong></span><span> [Contract] </span></li><li class="alt"><span>GO </span></li><li><span class="keyword"><strong><font color="#006699">Create</font></strong></span><span> </span><span class="keyword"><strong><font color="#006699">table</font></strong></span><span> [Contract] </span></li><li class="alt"><span>(ID </span><span class="keyword"><strong><font color="#006699">int</font></strong></span><span> </span><span class="keyword"><strong><font color="#006699">primary</font></strong></span><span> </span><span class="keyword"><strong><font color="#006699">key</font></strong></span><span> identity(1,1) </span></li><li><span>,[</span><span class="keyword"><strong><font color="#006699">Name</font></strong></span><span>] nvarchar(50) </span><span class="op"><font color="#808080">null</font></span><span> </span></li><li class="alt"><span>,Total </span><span class="keyword"><strong><font color="#006699">float</font></strong></span><span> </span><span class="op"><font color="#808080">null</font></span><span> </span></li><li><span>,buget Nvarchar(500) </span><span class="op"><font color="#808080">null</font></span><span> ) </span></li><li class="alt"><span>go </span></li><li><span class="keyword"><strong><font color="#006699">insert</font></strong></span><span> </span><span class="keyword"><strong><font color="#006699">into</font></strong></span><span> [Contract] </span></li><li class="alt"><span class="keyword"><strong><font color="#006699">select</font></strong></span><span> </span><span class="string"><font color="#0000ff">'合同名称'</font></span><span>, 100,</span><span class="string"><font color="#0000ff">'102,22'</font></span><span> </span></li><li><span class="keyword"><strong><font color="#006699">union</font></strong></span><span> </span><span class="op"><font color="#808080">all</font></span><span> </span></li><li class="alt"><span class="keyword"><strong><font color="#006699">select</font></strong></span><span> </span><span class="string"><font color="#0000ff">'合同名称2'</font></span><span>, 300,</span><span class="string"><font color="#0000ff">',102,22,'</font></span><span> </span></li><li><span class="keyword"><strong><font color="#006699">union</font></strong></span><span> </span><span class="op"><font color="#808080">all</font></span><span> </span></li><li class="alt"><span class="keyword"><strong><font color="#006699">select</font></strong></span><span> </span><span class="string"><font color="#0000ff">'合同名称3'</font></span><span>, 300,</span><span class="string"><font color="#0000ff">'101,23,'</font></span><span> </span></li></ol> |
分析:这道题乍看很简单,由于肯定用到Replace,所以很自然的结合left,right,从而得到以下语句 方法一:
1 |
<ol class="dp-sql"><li class="alt"><span><span class="keyword"><strong><font color="#006699">update</font></strong></span><span> [Contract] </span><span class="keyword"><strong><font color="#006699">set</font></strong></span><span> buget=</span><span class="string"><font color="#0000ff">','</font></span><span>+buget </span><span class="keyword"><strong><font color="#006699">where</font></strong></span><span> </span><span class="func"><font color="#ff1493">left</font></span><span>(buget,1)=</span><span class="string"><font color="#0000ff">','</font></span><span> </span></span></li><li><span class="keyword"><strong><font color="#006699">update</font></strong></span><span> [Contract] </span><span class="keyword"><strong><font color="#006699">set</font></strong></span><span> buget=buget+</span><span class="string"><font color="#0000ff">','</font></span><span> </span><span class="keyword"><strong><font color="#006699">where</font></strong></span><span> </span><span class="func"><font color="#ff1493">right</font></span><span>(buget,1)=</span><span class="string"><font color="#0000ff">','</font></span><span> </span></li></ol> |
如果能写成一个 SQL语句,可以加1分。
1 |
<ol class="dp-sql"><li class="alt"><span><span class="keyword"><strong><font color="#006699">update</font></strong></span><span> [Contract] </span></span></li><li><span class="keyword"><strong><font color="#006699">set</font></strong></span><span> buget=(</span><span class="func"><font color="#ff1493">case</font></span><span> </span><span class="keyword"><strong><font color="#006699">when</font></strong></span><span> (</span><span class="func"><font color="#ff1493">left</font></span><span>(buget,1)!=</span><span class="string"><font color="#0000ff">','</font></span><span> </span><span class="op"><font color="#808080">and</font></span><span> </span><span class="func"><font color="#ff1493">right</font></span><span> (buget,1)!=</span><span class="string"><font color="#0000ff">','</font></span><span>) </span><span class="keyword"><strong><font color="#006699">then</font></strong></span><span> </span><span class="string"><font color="#0000ff">','</font></span><span>+buget+</span><span class="string"><font color="#0000ff">','</font></span><span> </span></li><li class="alt"><span class="keyword"><strong><font color="#006699">when</font></strong></span><span> </span><span class="func"><font color="#ff1493">left</font></span><span>(buget,1)!=</span><span class="string"><font color="#0000ff">','</font></span><span> </span><span class="keyword"><strong><font color="#006699">then</font></strong></span><span> </span><span class="string"><font color="#0000ff">','</font></span><span>+buget </span></li><li><span class="keyword"><strong><font color="#006699">when</font></strong></span><span> </span><span class="func"><font color="#ff1493">right</font></span><span>(buget,1)!=</span><span class="string"><font color="#0000ff">','</font></span><span> </span><span class="keyword"><strong><font color="#006699">then</font></strong></span><span> buget+</span><span class="string"><font color="#0000ff">','</font></span><span> </span></li><li class="alt"><span class="keyword"><strong><font color="#006699">else</font></strong></span><span> buget </span></li><li><span class="keyword"><strong><font color="#006699">end</font></strong></span><span>) </span></li></ol> |
如果能从字符串的开关和结尾这个思路出发,结合Reverse,可以提到如下方法: 方法二:
1 |
<ol class="dp-sql"><li class="alt"><span><span class="keyword"><strong><font color="#006699">update</font></strong></span><span> [Contract] </span><span class="keyword"><strong><font color="#006699">set</font></strong></span><span> buget=</span><span class="string"><font color="#0000ff">','</font></span><span>+buget </span><span class="keyword"><strong><font color="#006699">where</font></strong></span><span> charindex(</span><span class="string"><font color="#0000ff">','</font></span><span>,buget)<>1 </span></span></li><li><span class="keyword"><strong><font color="#006699">update</font></strong></span><span> [Contract] </span><span class="keyword"><strong><font color="#006699">set</font></strong></span><span> buget=buget+</span><span class="string"><font color="#0000ff">','</font></span><span> </span><span class="keyword"><strong><font color="#006699">where</font></strong></span><span> charindex(</span><span class="string"><font color="#0000ff">','</font></span><span>,reverse(buget))<>1 </span></li></ol> |
该方法,主要涉及charindex函数和reverse函数。 说实话,我当时就这两种思路,这也是SQL中常见的基本用法。但出人意料的第三种方法出现了。 方法三:
1 |
<ol class="dp-sql"><li class="alt"><span><span class="keyword"><strong><font color="#006699">UPDATE</font></strong></span><span> [contract] </span><span class="keyword"><strong><font color="#006699">SET</font></strong></span><span> Buget = </span><span class="string"><font color="#0000ff">','</font></span><span>+Buget+</span><span class="string"><font color="#0000ff">','</font></span><span> </span></span></li><li><span class="keyword"><strong><font color="#006699">UPDATE</font></strong></span><span> [contract] </span><span class="keyword"><strong><font color="#006699">SET</font></strong></span><span> Buget = </span><span class="func"><font color="#ff1493">REPLACE</font></span><span>(Buget,</span><span class="string"><font color="#0000ff">',,'</font></span><span>,</span><span class="string"><font color="#0000ff">','</font></span><span>) </span></li></ol> |
解析:该方法最主要的亮点不在于语法的精妙,而在于其思路的异于常规。先给两边补上逗号,再替换双逗号为单逗号。这在实际编程中确实难能可贵。换句话说,如果没有事先思考过的话,这反映了解题者反应敏捷,思路开放。因此,至少可以再加3分。 当然,此语句其实还是有bug,比如如果原bug字段中间有两个逗号,那么在Replace时就会更新掉不应该更新的内容。不过,稍加修正,限定replace的范围即可, 受此思路启发,可以引申得到以下类似方法: 方法四:
1 |
<ol class="dp-sql"><li class="alt"><span><span class="keyword"><strong><font color="#006699">UPDATE</font></strong></span><span> [contract] </span><span class="keyword"><strong><font color="#006699">SET</font></strong></span><span> Buget = </span><span class="func"><font color="#ff1493">substring</font></span><span>(BuGet,2,len(BuGet)-1) wherecharindex(</span><span class="string"><font color="#0000ff">','</font></span><span>,buget)=1 </span></span></li><li><span class="keyword"><strong><font color="#006699">UPDATE</font></strong></span><span> [contract] </span><span class="keyword"><strong><font color="#006699">SET</font></strong></span><span> Buget = </span><span class="func"><font color="#ff1493">substring</font></span><span>(BuGet,1,len(BuGet)-1) wherecharindex(</span><span class="string"><font color="#0000ff">','</font></span><span>,reverse(buget))=1 </span></li><li class="alt"><span class="keyword"><strong><font color="#006699">UPDATE</font></strong></span><span> [contract] </span><span class="keyword"><strong><font color="#006699">SET</font></strong></span><span> BuGet = </span><span class="string"><font color="#0000ff">','</font></span><span>+BuGet+</span><span class="string"><font color="#0000ff">','</font></span><span> </span></li></ol> |
该方法是先去掉两边的逗号,再给每条记录加上逗号,比起方法三来,稍显繁琐,这也反衬了方法三的巧妙。 当然,也可以结合前面的思路稍作修正,这里就不再赘述,请读者自己思考。 感悟:释迦牟尼说过“人生需要经过六项修炼:布施、持戒、忍辱、精进、禅定、智慧。”,SQL编程,或C#、Java,甚至Javascrip的某个领域也是如此。技术是死的,思路是鲜活的,有时候,思路能轻易地突破技术很难实现的死角。到了一定程度时,会发现潜意识里已经被惯性思维塞满,而无法接受新鲜思维方式或思路,如果一段时间内持续如此,那么,我们应该警醒,把自己的头脑放空,把自己置于一个初学者的地位,重新开始“精进”的修炼! 原文链接:http://www.cnblogs.com/downmoon/archive/2011/03/02/1968615.html
View Details1、 首先要搞明白什么叫执行计划? 执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的,比如一条SQL语句如果用来从一个10万条记录的表中查1条记录,那查询优化器会选择“索引查找”方式,如果该表进行了归档,当前只剩下5000条记录了,那查询优化器就会改变方案,采用“全表扫描”方式。 可见,执行计划并不是固定的,它是“个性化的”。产生一个正确的“执行计划”有两点很重要: (1) SQL语句是否清晰地告诉查询优化器它想干什么? (2) 查询优化器得到的数据库统计信息是否是最新的、正确的? 2、 统一SQL语句的写法 对于以下两句SQL语句,程序员认为是相同的,数据库查询优化器认为是不同的。
1 |
<ol class="dp-sql"><li class="alt"><span><span class="keyword"><strong><font color="#006699">select</font></strong></span><span> * </span><span class="keyword"><strong><font color="#006699">from</font></strong></span><span> dual </span></span></li><li><span class="keyword"><strong><font color="#006699">select</font></strong></span><span> * </span><span class="keyword"><strong><font color="#006699">From</font></strong></span><span> dual </span></li></ol> |
其实就是大小写不同,查询分析器就认为是两句不同的SQL语句,必须进行两次解析。生成2个执行计划。所以作为程序员,应该保证相同的查询语句在任何地方都一致,多一个空格都不行! 3、 不要把SQL语句写得太复杂 我经常看到,从数据库中捕捉到的一条SQL语句打印出来有2张A4纸这么长。一般来说这么复杂的语句通常都是有问题的。我拿着这2页长的SQL语句去请教原作者,结果他说时间太长,他一时也看不懂了。可想而知,连原作者都有可能看糊涂的SQL语句,数据库也一样会看糊涂。 一般,将一个Select语句的结果作为子集,然后从该子集中再进行查询,这种一层嵌套语句还是比较常见的,但是根据经验,超过3层嵌套,查询优化器就很容易给出错误的执行计划。因为它被绕晕了。像这种类似人工智能的东西,终究比人的分辨力要差些,如果人都看晕了,我可以保证数据库也会晕的。 另外,执行计划是可以被重用的,越简单的SQL语句被重用的可能性越高。而复杂的SQL语句只要有一个字符发生变化就必须重新解析,然后再把这一大堆垃圾塞在内存里。可想而知,数据库的效率会何等低下。 4、 使用“临时表”暂存中间结果 简化SQL语句的重要方法就是采用临时表暂存中间结果,但是,临时表的好处远远不止这些,将临时结果暂存在临时表,后面的查询就在tempdb中了,这可以避免程序中多次扫描主表,也大大减少了程序执行中“共享锁”阻塞“更新锁”,减少了阻塞,提高了并发性能。 5、 OLTP系统SQL语句必须采用绑定变量
1 |
<ol class="dp-sql"><li class="alt"><span><span class="keyword"><strong><font color="#006699">select</font></strong></span><span> * </span><span class="keyword"><strong><font color="#006699">from</font></strong></span><span> orderheader </span><span class="keyword"><strong><font color="#006699">where</font></strong></span><span> changetime > ‘2010-10-20 00:00:01’ </span></span></li><li><span class="keyword"><strong><font color="#006699">select</font></strong></span><span> * </span><span class="keyword"><strong><font color="#006699">from</font></strong></span><span> orderheader </span><span class="keyword"><strong><font color="#006699">where</font></strong></span><span> changetime > ‘2010-09-22 00:00:01’ </span></li></ol> |
以上两句语句,查询优化器认为是不同的SQL语句,需要解析两次。如果采用绑定变量
1 |
<ol class="dp-sql"><li class="alt"><span><span class="keyword"><strong><font color="#006699">select</font></strong></span><span> * </span><span class="keyword"><strong><font color="#006699">from</font></strong></span><span> orderheader </span><span class="keyword"><strong><font color="#006699">where</font></strong></span><span> changetime > @chgtime </span></span></li></ol> |
@chgtime变量可以传入任何值,这样大量的类似查询可以重用该执行计划了,这可以大大降低数据库解析SQL语句的负担。一次解析,多次重用,是提高数据库效率的原则。 6、 绑定变量窥测 事物都存在两面性,绑定变量对大多数OLTP处理是适用的,但是也有例外。比如在where条件中的字段是“倾斜字段”的时候。 “倾斜字段”指该列中的绝大多数的值都是相同的,比如一张人口调查表,其中“民族”这列,90%以上都是汉族。那么如果一个SQL语句要查询30岁的汉族人口有多少,那“民族”这列必然要被放在where条件中。这个时候如果采用绑定变量@nation会存在很大问题。 试想如果@nation传入的第一个值是“汉族”,那整个执行计划必然会选择表扫描。然后,第二个值传入的是“布依族”,按理说“布依族”占的比例可能只有万分之一,应该采用索引查找。但是,由于重用了第一次解析的“汉族”的那个执行计划,那么第二次也将采用表扫描方式。这个问题就是著名的“绑定变量窥测”,建议对于“倾斜字段”不要采用绑定变量。 7、 只在必要的情况下才使用begin tran SQL Server中一句SQL语句默认就是一个事务,在该语句执行完成后也是默认commit的。其实,这就是begin tran的一个最小化的形式,好比在每句语句开头隐含了一个begin tran,结束时隐含了一个commit。 有些情况下,我们需要显式声明begin tran,比如做“插、删、改”操作需要同时修改几个表,要求要么几个表都修改成功,要么都不成功。begin tran 可以起到这样的作用,它可以把若干SQL语句套在一起执行,最后再一起commit。好处是保证了数据的一致性,但任何事情都不是完美无缺的。Begin tran付出的代价是在提交之前,所有SQL语句锁住的资源都不能释放,直到commit掉。 可见,如果Begin tran套住的SQL语句太多,那数据库的性能就糟糕了。在该大事务提交之前,必然会阻塞别的语句,造成block很多。 Begin tran使用的原则是,在保证数据一致性的前提下,begin tran 套住的SQL语句越少越好!有些情况下可以采用触发器同步数据,不一定要用begin tran。 8、 一些SQL查询语句应加上nolock 在SQL语句中加nolock是提高SQL Server并发性能的重要手段,在oracle中并不需要这样做,因为oracle的结构更为合理,有undo表空间保存“数据前影”,该数据如果在修改中还未commit,那么你读到的是它修改之前的副本,该副本放在undo表空间中。这样,oracle的读、写可以做到互不影响,这也是oracle广受称赞的地方。SQL Server 的读、写是会相互阻塞的,为了提高并发性能,对于一些查询,可以加上nolock,这样读的时候可以允许写,但缺点是可能读到未提交的脏数据。使用nolock有3条原则。 (1) 查询的结果用于“插、删、改”的不能加nolock ! (2) 查询的表属于频繁发生页分裂的,慎用nolock ! (3) 使用临时表一样可以保存“数据前影”,起到类似oracle的undo表空间的功能, 能采用临时表提高并发性能的,不要用nolock 。 9、 聚集索引没有建在表的顺序字段上,该表容易发生页分裂 比如订单表,有订单编号orderid,也有客户编号contactid,那么聚集索引应该加在哪个字段上呢?对于该表,订单编号是顺序添加的,如果在orderid上加聚集索引,新增的行都是添加在末尾,这样不容易经常产生页分裂。然而,由于大多数查询都是根据客户编号来查的,因此,将聚集索引加在contactid上才有意义。而contactid对于订单表而言,并非顺序字段。 比如“张三”的“contactid”是001,那么“张三”的订单信息必须都放在这张表的第一个数据页上,如果今天“张三”新下了一个订单,那该订单信息不能放在表的最后一页,而是第一页!如果第一页放满了呢?很抱歉,该表所有数据都要往后移动为这条记录腾地方。 SQL Server的索引和Oracle的索引是不同的,SQL Server的聚集索引实际上是对表按照聚集索引字段的顺序进行了排序,相当于oracle的索引组织表。SQL Server的聚集索引就是表本身的一种组织形式,所以它的效率是非常高的。也正因为此,插入一条记录,它的位置不是随便放的,而是要按照顺序放在该放的数据页,如果那个数据页没有空间了,就引起了页分裂。所以很显然,聚集索引没有建在表的顺序字段上,该表容易发生页分裂。 曾经碰到过一个情况,一位哥们的某张表重建索引后,插入的效率大幅下降了。估计情况大概是这样的。该表的聚集索引可能没有建在表的顺序字段上,该表经常被归档,所以该表的数据是以一种稀疏状态存在的。比如张三下过20张订单,而最近3个月的订单只有5张,归档策略是保留3个月数据,那么张三过去的15张订单已经被归档,留下15个空位,可以在insert发生时重新被利用。在这种情况下由于有空位可以利用,就不会发生页分裂。但是查询性能会比较低,因为查询时必须扫描那些没有数据的空位。 重建聚集索引后情况改变了,因为重建聚集索引就是把表中的数据重新排列一遍,原来的空位没有了,而页的填充率又很高,插入数据经常要发生页分裂,所以性能大幅下降。 对于聚集索引没有建在顺序字段上的表,是否要给与比较低的页填充率?是否要避免重建聚集索引?是一个值得考虑的问题! 10、加nolock后查询经常发生页分裂的表,容易产生跳读或重复读 加nolock后可以在“插、删、改”的同时进行查询,但是由于同时发生“插、删、改”,在某些情况下,一旦该数据页满了,那么页分裂不可避免,而此时nolock的查询正在发生,比如在第100页已经读过的记录,可能会因为页分裂而分到第101页,这有可能使得nolock查询在读101页时重复读到该条数据,产生“重复读”。同理,如果在100页上的数据还没被读到就分到99页去了,那nolock查询有可能会漏过该记录,产生“跳读”。 上面提到的哥们,在加了nolock后一些操作出现报错,估计有可能因为nolock查询产生了重复读,2条相同的记录去插入别的表,当然会发生主键冲突。 11、使用like进行模糊查询时应注意 有的时候会需要进行一些模糊查询比如
1 |
<ol class="dp-sql"><li class="alt"><span><span class="keyword"><strong><font color="#006699">select</font></strong></span><span> * </span><span class="keyword"><strong><font color="#006699">from</font></strong></span><span> contact </span><span class="keyword"><strong><font color="#006699">where</font></strong></span><span> username </span><span class="op"><font color="#808080">like</font></span><span> ‘%yue%’ </span></span></li></ol> |
关键词%yue%,由于yue前面用到了“%”,因此该查询必然走全表扫描,除非必要,否则不要在关键词前加%, 12、数据类型的隐式转换对查询效率的影响 sql server2000的数据库,我们的程序在提交sql语句的时候,没有使用强类型提交这个字段的值,由sql […]
View DetailsCase具有两种格式。简单Case函数和Case搜索函数。 —简单Case函数 CASE sex WHEN '1' THEN '男' WHEN '2' THEN '女' ELSE '其他' END --Case搜索函数 CASE WHEN sex = '1' THEN '男' WHEN sex = '2' THEN '女' ELSE '其他' END 这两种方式,可以实现相同的功能。简单Case函数的写法相对比较简洁,但是和Case搜索函数相比,功能方面会有些限制,比如写判断式。 还有一个需要注意的问题,Case函数只返回第一个符合条件的值,剩下的Case部分将会被自动忽略。 —比如说,下面这段SQL,你永远无法得到“第二类”这个结果 CASE WHEN col_1 IN ( 'a', 'b') THEN '第一类' WHEN col_1 IN ('a') THEN '第二类' ELSE'其他' END 下面我们来看一下,使用Case函数都能做些什么事情。 一,已知数据按照另外一种方式进行分组,分析。 有如下数据:(为了看得更清楚,我并没有使用国家代码,而是直接用国家名作为Primary Key) 国家(country)人口(population) 中国600 美国100 加拿大100 英国200 法国300 日本250 德国200 墨西哥50 印度250 根据这个国家人口数据,统计亚洲和北美洲的人口数量。应该得到下面这个结果。 洲人口 亚洲1100 北美洲250 其他700 想要解决这个问题,你会怎么做?生成一个带有洲Code的View,是一个解决方法,但是这样很难动态的改变统计的方式。 如果使用Case函数,SQL代码如下: SELECT SUM(population), CASE […]
View Detailsusing System;using System.Data;using System.Xml; //***************************************// 作者: yangtang_newton//其实用DataSet操作XML,归根到底就是对DataSet里的表格,行,列等进行操作,//然后用DataSet里的东西重新写到XML中,从而实现编辑XML的目的。如果再配合上.xsd文件的话,那效果更佳。//xsd生成方法:xsd file.xml [/outputdir:directory] [/parameters:file.xml] //*************************************** namespace USTC{/// <summary>/// OperateXmlByDataSet 的摘要说明。/// </summary>public class XmlDataSet{private string strXmlPath;//这个是相对路径public XmlDataSet(string strXmlPath){ // // TODO: 在此处添加构造函数逻辑 // this.strXmlPath=strXmlPath;} #region GetDataSetByXml/// <summary>/// 读取xml直接返回DataSet/// </summary>/// <param name="strXmlPath">xml文件相对路径</param>/// <returns></returns>public DataSet GetDataSetByXml(){ try { DataSet ds = new DataSet(); ds.ReadXml(GetXmlFullPath(strXmlPath)); if(ds.Tables.Count > 0) { return ds; } return null; } catch(Exception) { return null; }}#endregion /* * 以下代码读取到一个没有排序和筛选的DataSet。 USTC.XmlDataSet XML=new XmlDataSet(@"XML/PortalCfg.xml");DataGrid1.DataSource = XML.GetDataSetByXml();DataGrid1.DataBind();//以下代码读到的数据是经过筛选和排序的:DataGrid1.DataSource =XML.GetDataViewByXml("name = 'Asp.net’", //条件:name列值为Asp.net"peopleNum desc"); //按peopleNum列降序排列DataGrid1.DataBind(); */ #region GetDataViewByXml/// <summary>/// 读取Xml返回一个经排序或筛选后的DataView/// </summary>/// <param name="strXmlPath"></param>/// <param name="strWhere">筛选条件,如:"name = […]
View DetailsDataSet和XML文件互操作的例子 //读取XML文件到DataSetDataSet ds = new DataSet();MyXmlPath = "C:\\Message.xml";FileStream fs = new FileStream(MyXmlPath, FileMode.Open);ds.ReadXml(fs, XmlReadMode.ReadSchema);fs.Close();//筛选行存入DataSetDataRow[] FoundRows;string Expression = "AREA IN ('" + Area + "',")";string SortOrder = "DAY DESC";FoundRows = ds.Tables[0].Select(Expression, SortOrder);DataTable Mydt = new DataTable();Mydt.Columns.Add("FKEY");Mydt.Columns.Add("IIIII");Mydt.Columns.Add("NAME");Mydt.Columns.Add("AREA");Mydt.Columns.Add("X");Mydt.Columns.Add("Y");Mydt.Columns.Add("BKIND");Mydt.Columns.Add("SKIND");Mydt.Columns.Add("DAY");Mydt.Columns.Add("ETIME");Mydt.Columns.Add("TYPE");Mydt.Columns.Add("MAIN");Mydt.Columns.Add("TTIME");foreach (DataRow dr in FoundRows){Mydt.Rows.Add(dr.ItemArray);}DataSet Myds = new DataSet();Myds.Tables.Add(Mydt);//保存到XML文件Myds.WriteXml(MyXmlPath, XmlWriteMode.WriteSchema); //ListView的一个例子XmlDocument doc = new XmlDocument();XmlDeclaration Declaration = doc.CreateXmlDeclaration("1.0","GB2312","");doc.AppendChild(Declaration);XmlElement xe = doc.CreateElement("Infos");doc.AppendChild(xe);for(int i = 0;i < listview.Items.Count;i++){XmlNode parent = doc.CreateElement("Info" + i.ToString());ListViewItem temp = listview.Items[i];for(int j = 0;j < temp.SubItems.Count;j++){string s = temp.SubItems[j].Text;XmlNode node = doc.CreateElement(s);node.InnerText = s;parent.AppendChild(node);}doc.AppendChild(parent);}doc.Save(FilePath);
View Details以前在使用VB来实现多线程的时候,发现有一定的难度。虽然也有这样那样的方法,但都不尽人意,但在C#中,要编写多线程应用程序却相当的简单。这篇文章将作简要的介绍,以起到抛砖引玉的作用! .NET将关于多线程的功能定义在System.Threading名字空间中。因此,要使用多线程,必须先声明引用此名字空间(using System.Threading;)。 即使你没有编写多线程应用程序的经验,也可能听说过“启动线程”“杀死线程”这些词,其实除了这两个外,涉及多线程方面的还有诸如“暂停线程”“优先级”“挂起线程”“恢复线程”等等。下面将一个一个的解释。 1.启动线程 顾名思义,“启动线程”就是新建并启动一个线程的意思,如下代码可实现: Thread thread1 = new Thread(new ThreadStart( Count)); 其中的 Count 是将要被新线程执行的函数。 2.杀死线程 “杀死线程”就是将一线程斩草除根,为了不白费力气,在杀死一个线程前最好先判断它是否还活着(通过 IsAlive 属性),然后就可以调用 Abort 方法来杀死此线程。 3.暂停线程 它的意思就是让一个正在运行的线程休眠一段时间。如 thread.Sleep(1000); 就是让线程休眠1秒钟。 4.优先级 这个用不着解释了。Thread类中有一个ThreadPriority属性,它用来设置优先级,但不能保证操作系统会接受该优先级。一个线程的优先级可分为5种:Normal, AboveNormal, BelowNormal, Highest, Lowest。具体实现例子如下: thread.Priority = ThreadPriority.Highest; 5.挂起线程 Thread类的Suspend方法用来挂起线程,直到调用Resume,此线程才可以继续执行。如果线程已经挂起,那就不会起作用。 if (thread.ThreadState = ThreadState.Running) { thread.Suspend(); } 6.恢复线程 用来恢复已经挂起的线程,以让它继续执行,如果线程没挂起,也不会起作用。 if (thread.ThreadState = ThreadState.Suspended) { thread.Resume(); } 下面将列出一个例子,以说明简单的线程处理功能。此例子来自于帮助文档。 using System; using System.Threading; // Simple threading scenario: Start a static method running // on a second thread. public class ThreadExample { // The ThreadProc method is called when the thread starts. // It […]
View Details我现在正在使用vs2010英文版,平常也很少看那些有的没的文字,而且在写程序的时候很”明智”的异常捕获并且写进日志里去,所以没有出现那个可爱的调试状态下出现异常的那个小框框. 我在上网查了一会儿,有人说直接设置控件的CheckForIllegalCrossThreadCalls属性为false,但是因为可能的不安全因素所以还是不用的好.当然还有写一个委托来同步调用.但是我觉得它有点麻烦,就放弃用委托同步了. 后来我搞了好多种写法,都摆脱不了这个怨念,最终也摆脱了异常捕获并抛出了这个小框框. 框框上明确的说道:”线程间操作无效: 从不是创建控件 [控件名称] 的线程访问它。”但是在框框的Suggestion列表中显示出了一条项目,大意是:”如何:对 Windows 窗体控件进行线程安全调用.”,我点开它,并且跳进了MSDN. 原来在MSDN上就有教咱如果夸线程操作控件啊.亏我还Google半天呢. MSDN上的办法就是使用委托来同步调用控件的.具体方法我也就不卖关子了.因为MSDN上的代码比较繁多,所以我稍微改进了一下. //首先声明一个委托. delegate void SetValueDelegate(int val); //模仿程序中的跨线程调用 public void Progress() { Thread t = new Thread(() => { //跨线程由此开始 SetValue(100); }); t.Start(); } //关键部分在这里 public void SetValue(int val) { //InvokeRequired属性当非创建本控件线程操作的时候 //它返回true,此时使用窗体(该控件的创建者)的Invoke方法创建同步调用. //妙哉,妙哉! if (ProgressBar1.InvokeRequired) { var svd = new SetValueDelegate(SetValue); this.Invoke(svd, val); } else { ProgressBar1.Value = val; } } 好了,就是这样,非讨论文章就越精炼越好.希望能帮到你. 最后附上MSDN的地址:这里
View Details在ecshop2.7.0开始,底部版权Powered by ecshop如果删除了之后,就会发现Powered by ecshop到处乱跑,如何彻底删除Powered by ecshop版权呢。首先,需要打开js/common.js删除以下js函数. onload = function(){ var link_arr = document.getElementsByTagName(String.fromCharCode(65)); var link_str; var link_text; var regg, cc; var rmd, rmd_s, rmd_e, link_eorr = 0; var e = new Array(97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122 ); try { for(var i = 0; i < link_arr.length; i++) { link_str = link_arr[i].href; if (link_str.indexOf(String.fromCharCode(e[22], 119, 119, 46, e[4], 99, e[18], e[7], e[14], e[15], […]
View Details