本文章主要是针对WEB开发人员或想用SQL存储过程分页等初级人员。
相信你也有搜过关于ASP用存储过程分页的。网络上类似这个的文章多的跟米似的。但好象千篇一律。有测试的人相信你会知道什么结果。
但有点很重要就是存储过程写了后怎么去调用它或参数怎么传递却没几个写得清楚。这对存储过程不熟悉的人或初学者来说看了满脑子都是雾水!
所以学东西还是从基础学起,最根本的知识还是不能丢的。希望看完本文章能给你个“速成”!呵呵~ ^_- 图文并茂的教程是最好不过了。
然后你再去看看你之前看过的文章要是你能看明白及能应用了,那说明你已经入门了,同时本文的目的也达到了!
看完本文章你应该可以(本文目的):
▲创建、修改、删除存储过程。
▲理解存储过程类型及编写。
▲掌握ASP调用SQL存储过程。
▲了解SQL语句的优化。
在说存储过程前,我们先来了解下存储过程的优点或为什么要用存储过程:
一、存储过程的优点
1、SQL语句已经预编绎过了,因此执行效率、性能大大增加。
2、可以接受参数、输出参数、返回单个或多个结果集以及返回值。可以向程序返回错误原因。
3、减少网络流量。如:执行插入记录这个功能时只要传输存储过程名、参数和这些参数的数值即可。免去写一大串操纵的SQL语句。
我们将会发现要是网络速度慢时这个是多么的有用,即减少对客户机的压力,又可以简化一系列复杂语句。
4、减少注入式攻击。存储过程可以包含程序流、逻辑以及对数据库的查询。同时可以实体封装和隐藏了数据逻辑。
5、存储过程可以在单个存储过程中执行一系列SQL语句,存储过程内还可以嵌套其它存储过程。
6、有利于SQL语句的重用。
二、准备工作:
运行环境:为了实现本文的例子必须安装SQL Server 2000/2005和IIS5.0以上。建议再安装下Dreamweaver 8软件,这个绝对是Web开发的利器!接下来就是:
了解基本的SQL语句并创建库和表为下面的操作做准备。
这里先说下,创建、修改和删除数据库、表和存储过程等等一般都有2种方法:
1在“企业管理器”里进行创建、修改和删除数据库、表和存储过程。这个对初学者来说也许比较直观易懂。
2在“查询分析器”里进行创建、修改和删除数据库、表和存储过程。作为开发人员来说用这个最方便不过了,同时多写写SQL语句记记练练语法以熟练操作。
本文全部用第2种方法在“查询分析器”里进行。好了,开始“劳动”了。。。。
依次是创建Johnny数据库--建testTable表---插入信息。
CREATE database johnny --创建数据库
go
USE johnny --使用数据库
go
CREATE TABLE testTable( --创建表
tID int identity(1,1), --创建字段,下同
tUserName varchar(50),
tTel varchar(30)
)
Go
CREATE TABLE testUser --再创建用户表
(
uID int primary key identity(1,1), --创建主键并设置递增1。
uName varchar(20) not null, --用户名,非空
uEmail varchar(50) not null, --用户名电子邮件,非空
uQQ int default 0 --用户QQ,默认0
)
go
/*先给用户表插入几条记录*/
insert into testUser(uName,uEmail,uQQ) values('johnny','chenzxmail@163.com',40623660)
insert into testUser(uName,uEmail,uQQ) values('张三','12345@163.com',123456)
insert into testUser(uName,uEmail,uQQ) values('李四','43435@163.com',4354534)
go
/*现在给testTable表插入记录*/
set nocount on --当 SET NOCOUNT 为 ON 时,存储过程中包含的一些语句并不返回许多实际的数据,则该设置由于大量减少了网络流量,因此可显著提高性能。
declare @i int --DECLARE 语句声明变量@i,并用 SET 或 SELECT 语句给其指派值。
set @i=1
while @i<=200000 --只要指定的条件为真,就重复执行语句。即插入200000的数据,数据的值随便你任意取,我这里也是随便插入做测试,呵呵发了1分14秒的时间。
begin
insert into testTable(tUserName,tTel) values(CAST(round(rand()*100000,0) AS varchar),CAST(round(rand()*100000,0) AS varchar)+CAST(CEILING(round(rand()*100000,0)/3) AS VARCHAR))
SELECT @i=@i+1
end
go
/*呵呵,这样准备工作就完成了。看看刚才插入的部分数据吧:*/
select * from testUser
go
SELECT TOP 50 * FROM testable
三、创建、修改、删除存储过程
有了刚才的准备工作,现在把重点放在说存储过程。
1、创建存储过程
创建存储过程,存储过程是保存起来的可以接受和返回用户提供的参数的 Transact-SQL 语句的集合。
可以创建一个过程供永久使用,或在一个会话中临时使用(局部临时过程),或在所有会话中临时使用(全局临时过程)。
语法:(注:更详细的说明请参考有关书籍或联机从书,按F1)
CREATE PROC [ EDURE ] procedure_name
[ { @parameter data_type }
[ VARYING ] [ = default ] [ OUTPUT ]
] [ ,...n ]
[ WITH { RECOMPILE | ENCRYPTION | RECOMPILE , ENCRYPTION } ]
[ FOR REPLICATION ]
AS
sql_statement [ ...n ]
我们以前学过知道中括号[]的是可选的,所以最简单的可以简化为:
CREATE PROC procName
AS
sql_statement
其中:procName 就是存储过程名,sql_statement 也就是写Transact-SQL 语句用的。
注意:如果用户创建的存储过程与系统存储过程同名,则永远不执行用户创建的存储过程。
语法总是抽象难理解的,我们来个例子看看就容易理解。
①此存储过程的名字是testPROC1,他的功能是从testTable表中查询前50条记录的信息,也就是返回一个记录集。
Use Johnny
go
CREATE PROC testPROC1
AS
SELECT top 50 * FROM testable
这样存储过程就创建好了。
2、修改存储过程。
对已创建的存储过程觉得要修改时只要把CREATE改为ALTER就可以了。如修改①存储过程(其功能是删除testUser表的所以记录):
Use Johnny
go
ALTER PROC testPROC1
AS
DELETE FROM testUser
3、删除存储过程。
删除存储过程用命令DROP,即:
DROP PROC[DURE]{ procedure }
如删除①存储过程:
DROP PROC testPROC1
这样①存储过程就删除了。
到此,第一个知识点结束。
四、存储过程的类型及ASP调用实例说明
1、 只返回一个记录集的存储过程
此类型就比如①,现在我们创建个如:
①存储过程testPROC1 代码:
USE johnny
GO
CREATE PROC testPROC1
AS
DELETE FROM testUser
执行下。现在我们在ASP中对这个简单的存储过程进行调用:
②ASP代码:新建一个文件(推荐使用Dreamweaver),然后输入如下代码:
<%@LANGUAGE="VBSCRIPT" CODEPAGE="936"%>
<%
dim conn,connstr
on error resume next
set conn=server.CreateObject("adodb.connection")
connstr="Provider=SQLOLEDB;Server=.;Database=johnny;UID=sa;PWD=123"
conn.open connstr
if err then
err.clear
Set conn = Nothing
response.Write("数据库连接错误")
response.End()
end if
dim cmd,rs
set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = connstr '数据库连接字串
cmd.CommandText = "dbo.testPROC1" '存储过程名
cmd.CommandType = 4 '使用的是存储过程
cmd.CommandTimeout = 0 '超时
cmd.Prepared = true '将SQL命令先行编译
'cmd.Execute() '如果是没有返回记录集recordset的(如删除等)直接用cmd.Execute()。
set rs=cmd.Execute() '有返回记录集并赋给rs
set cmd=nothing
'显示数据信息
while not rs.eof
response.Write(rs(0)&rs(1)&rs(2)&rs(3)&"<hr />")
rs.movenext
wend
%>
说明:
注意:红色为连接数据库及创建对象,绿色为存储过程调用,以后存储过程调用时ASP页面都只写出调用的部分绿色代码,红色部分自己再加上去。
connstr="Provider=SQLOLEDB;Server=.;Database=johnny;UID=sa;PWD=123"
是假设你的SQL Server 2000/2005,在本地,数据库为johnny,用户sa,密码123
在以上代码中,CommandType属性表明请求的类型,取值及说明如下:
-1 表明CommandText参数的类型无法确定
1 表明CommandText是一般的命令类型
2 表明CommandText参数是一个存在的表名称
4 表明CommandText参数是一个存储过程的名称
另:还可以通过Connection对象或Recordset对象调用存储过程,方法分别如下:
将以上代码从
dim cmd,rs
……
到
set cmd=nothing
替换为:
(用Connection时)
Dim rs
set rs=conn.execute("dbo.testPROC1",0,4) '最后一个参断含义同CommandType
(用Recordset时)
set rs=server.CreateObject("adodb.recordset")
rs.open "dbo.testPROC1",connstr,0,1,4 '最后一个参断含义含义同CommandType
注意:这Connection对象或Recordset对象调用存储过程的不建议用,特别是在有参数传递的情况下。
以上是简单的存储过程及其在ASP中调用的详细操作。是引导大家入门,下面我们来看看稍微复杂点的也是实际应用中常用的存储过程。
2、有返回值、输入参数和输出参数的储存过程。
我们再去留意下存储过程的语法中有怎么一个参数:[OUTPUT],它表明参数是返回参数。该选项的值可以返回给 EXEC[UTE]。使用 OUTPUT 参数可将信息返回给调用过程。在实际应用中,通常我们是同时有返回值、输入参数和输出参数的存储过程。比如我们想取得班级表里某位同学的家庭地址,这时我们就应该输入该同学的“学号”(输入参数)及要显示的“家庭地址”(输出参数)和要验证该学号是否存在(返回值)。
下面就拿我们创建的用户表testUser来做例子。其功能是输入一个用户名查询他的电子邮件,同时验证输入的用户是否存在(比如存在返回1,否则返回0之类)并返回相应的值。
①存储过程testPROC2代码:
/*==============
第2个存储过程:即有返回值,输入参数和输出参数。
●RETURN 语句无条件终止查询、存储过程或批处理。存储过程或批处理中 RETURN 语句后面的语句都不执行。
●return:除非特别指明,所有系统存储过程返回 0 值表示成功,返回非零值则表示失败。
●本存储过程的return都是特别的说明,所以其返回值都是有相应的意义。
●下面的返回值0、1、2假如去掉的话,即如果 RETURN 未指定值,则存储过程返回 0。在程序调用且正常运行的情况下获取“返回值”的值都是返回 0 。
==============*/
Use Johnny
Go
CREATE PROC testPROC2
@userName varchar(20), --声明变量@userName
@userEmail varchar(50) OUTPUT --声明变量@userEmail,且是输出参数。
AS
set nocount on
begin
IF @userName is null OR @userName='' --当输入参数为空或is null时
begin
set @userEmail='请输入要查询的用户名!' --符合条件给输出参数赋值,以便提示
return 0 --输入参数为空时,“返回值”我们这里设置为 0(当然可以设置其他的数字)。
end
ELSE
IF exists(SELECT uEmail FROM testUser WHERE uName=@userName) --如果输入参数存在时
begin
SELECT @userEmail=uEmail FROM testUser WHERE uName=@userName --输出参数赋值
return 1 --验证输入的参数(用户名),存在时“返回值”我们这里设置为 1。
end
ELSE
begin
set @userEmail='不存在此用户名!' ----如果输入参数 不 存在时,提示
return 2 --输入的参数(用户名)不 存在时“返回值”我们这里设置为 2。
end
end
执行下。
②ASP代码:现在我们在ASP中对testPROC2这个的存储过程进行调用代码(绿色部分):
dim cmd,userName,userEmail
userName=Trim(request.Form("uName")) '输入参数
set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = connstr
cmd.CommandText = "dbo.testPROC2"
cmd.CommandType = 4
cmd.CommandTimeout = 0
cmd.Prepared = true
cmd.Parameters.Append cmd.CreateParameter("@RETURN_VALUE", 3, 4)
cmd.Parameters.Append cmd.CreateParameter("@userName", 200, 1,20,userName)
cmd.Parameters.Append cmd.CreateParameter("@userEmail", 200, 2,50)
cmd.Execute()
'返回值
flag=cmd(0)
'输出参数 类
userEmail=cmd(2) '或userEmail=cmd("@userEmail")或userEmail=cmd.Parameters(1) 其读取顺序时从0开始。
set cmd=nothing
response.Write("<form id=""form1"" name=""form1"" method=""post"" action=""test2.asp"">")
response.Write("<input name=""uName"" type=""text"" id=""uName"" />")
response.Write("<input type=""submit"" name=""Submit"" value=""查询"" /></form>")
if request.Form("Submit")<>"" then
response.Write("你的“输入参数”是:【"&userName&"】<br />查询结果:<br />“输出参数”是:【"&userEmail&"】<br />“返回值”是:【"&flag&"】")response.End()
end if
说明:
上面图中的3种结果正好与我们的存储过程里预设的功能相同,当然第3种情况我们一般会在客户端设置让它一定要输入信息,而不是没有任何输入(或空格)的情况就点查询按钮。
这个也常用在验证登陆系统中,比如登陆的帐号无效(不存在时)其返回值为0,有(存在)其返回值为1等等。好了,现在开始讲解上面的代码的意思了。
在ASP中要取得返回值,需要利用Parameters集合来声明参数(具体的请参考ADO):
cmd.Parameters.Append cmd.CreateParameter("@RETURN_VALUE", 3, 4)
cmd.Parameters.Append cmd.CreateParameter("@userName", 200, 1,20,userName)
cmd.Parameters.Append cmd.CreateParameter("@userEmail", 200, 2,50)
代码中有这3行(返回值,必须最先出现)。括号里的参数什么意思呢?含义如下:
cmd.Parameters.Append cmd.CreateParameter("@RETURN_VALUE", 3, 4)
第一个参数@RETURN_VALUE为参数名,这个名字可以任意取(如取:JOHNNY),但一般取跟存储过程中的参数一样容易理解,而且参数多时对应起来容易。从字意上看它就是返回值。
第二个参数3表示:参数的数据类型。既然是数据类型那肯定有好几种了,是的,这里列了个对应表:
adBigInt: 20 ;
adBinary : 128 ;
adBoolean: 11 ;
adChar: 129 ;
adDBTimeStamp: 135 ;
adEmpty: 0 ;
adInteger: 3 ;
adSmallInt: 2 ;
adTinyInt: 16 ;
adVarChar: 200 ;
根据这个表我们就知道这个3是什么意思了。它就是整型数据类型。对于返回值,它只能取且-1到-99为保留值。
第三个参数4表示参数的类型。它的取值范围是0 ~ 4分别是:
0 : 类型无法确定;1: 输入参数;2: 输入参数;3:输入或输出参数;4: 返回值
所以我们可以看出这里的4表示它是返回值。
现在说
cmd.Parameters.Append cmd.CreateParameter("@userName", 200, 1,20,userName)
cmd.Parameters.Append cmd.CreateParameter("@userEmail", 200, 2,50)
这个,我们看到这2行正是我们testPROC2存储过程中对应的参数,可以知道输入参数@userName是需要5个参数的。
分别是:参数名称、参数数据类型、参数的类型、数据长度、参数值
而输出参数@userEmail只要4个参数,少了最后的参数值。这样就明白了这些参数的含义了吧,呵呵~~,不明白?!不会吧,我那尽量说得通俗易懂些:)
注意:这里参数的顺序一定要跟创建存储过程中声明参数顺序一致,同时如果存储过程同时具有返回值以及输入、输出参数,则返回值要最先声明。
下面是错误的:
cmd.Parameters.Append cmd.CreateParameter("@userEmail", 200, 2,50)
cmd.Parameters.Append cmd.CreateParameter("@userName", 200, 1,20,userName)
cmd.Parameters.Append cmd.CreateParameter("@RETURN_VALUE", 3, 4)
下面我们来说下有返回输出参数和记录集的存储过程
3、有返回输出参数和记录集的存储过程
经过前面2点的讲解相信你现在对存储过程又有进一步的理解了。这里我就拿我们最常使用的存储过程分页来说这个有返回输出参数和记录集的存储过程。
首先打开SQL 查询分析器,(下面的注解我就没有每条都写了,不清楚可以选中它然后按:Shift+F1或直接按F1 然后再索引里输入你要查询的关键字。)
①存储过程代码:输入:
Use Johnny
Go
/*=======第3个存储过程====
有返回输出参数和记录集的存储过程
有任何问题:QQ:40623660
========================*/
CREATE PROC testPROC3
@tPageCount int OUTPUT, --输出参数,总页数
@tPageSize int, --每页显示多少条记录
@tPageNo int --当前页码
AS
SET NOCOUNT ON
BEGIN
/*取得总记录条数*/
DECLARE @tRecordCount int --总记录条数
SELECT @tRecordCount=COUNT(tID) FROM testTable
/*确定总的页数*/
IF (@tRecordCount % @tPageSize) = 0
SET @tPageCount=CEILING(@tRecordCount/@tPageSize) --CEILING函数:返回大于或等于所给数字表达式的最小整数。
ELSE
SET @tPageCount=CEILING(@tRecordCount/@tPageSize)+1
/*如果输入的页码大于总的页数*/
IF @tPageNo > @tPageCount
SET @tPageNo = @tPageCount
/*确定当前页记录的起止标记*/
DECLARE @tStart int , @tEnd int --设置起止标志
SET @tStart=(@tPageNo-1) * @tPageSize
SET @tEnd =@tStart+@tPageSize+1
/*由其止标记得到当前页的记录*/
SELECT * FROM testTable WHERE (tID>@tStart) AND (tID<@tEnd)
/*我们把总记录做为返回值,这样存储过程就变了有返回值,输出参数和记录集了*/
RETURN @tRecordCount --做为返回值
END
选中运行下。
②ASP代码:然后我们就打开Dreamweaver 在你的测试文件夹下新建test3.asp然后输入我们的数据库连接字符串(存储过程1调用的红色部分),再输入下面代码:
url="?" '或:文件名.asp?
StartTime = Timer
dim cmd,PageNo,tPageCount,tPageSize,tRecordCount
PageNo=Cint(Trim(request("PageNo"))) '输入参数
if PageNo="" or PageNo<=0 then PageNo=1
tPageSize=10
set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = connstr
cmd.CommandText = "dbo.testPROC3"
cmd.CommandType = 4
cmd.CommandTimeout = 0
cmd.Prepared = true
cmd.Parameters.Append cmd.CreateParameter("@tRecordCount", 3, 4)
cmd.Parameters.Append cmd.CreateParameter("@tPageCount", 3, 2)
cmd.Parameters.Append cmd.CreateParameter("@tPageSize", 3, 1,10,tPageSize)
cmd.Parameters.Append cmd.CreateParameter("@PageNo", 3, 1,10,PageNo)
set rs=cmd.Execute()
rs.close() '注意:若要取得参数值,需先关闭记录集对象,要使用的时候再打开
'返回值
tRecordCount=cmd(0)
'输出参数
tPageCount=cmd(1) '或tPageCount =cmd("@tPageCount ")或tPageCount =cmd.Parameters(1) 其参数顺序时从0开始。
IF PageNo>=tPageCount THEN PageNo=tPageCount
set cmd=nothing
'显示记录
IF tRecordCount=0 THEN
response.Write("没有记录!")
response.End()
else
rs.open()
while not rs.eof
response.Write("<li>"&rs("tID")&"|"&rs("tUserName")&"|"&rs("tTel")&"</li><hr />"&VbCrlf)
rs.movenext
wend
'========下面是分页======
if PageNo<=1 then
Response.Write "<font color='#FF0000'>[首页] [上一页]</font>"
else
Response.Write"[<a href='"&url&"PageNo=1' target='_self' title='首页'><font color='#FF0000'>首页</font></a>] "
Response.Write" [<a href='"&url&"PageNo="&(PageNo-1)&"' target='_self' title='上一页 '><font color='#FF0000'>上一页</font></a>] "
end if
if PageNo>=tPageCount then
Response.Write "<font color='#FF0000'>[下一页] [尾页]</font>"
else
Response.Write" [<a href='"&url&"PageNo="&(PageNo+1)&"' target='_self' title='下一页 '><font color='#FF0000'>下一页</font></a>] "
Response.Write" [<a href='"&url&"PageNo="&tPageCount&"' target='_self' title='尾页 '><font color='#FF0000'>尾页</font></a>]"
end if
Response.Write("[页次 第<font color=red>"&PageNo&"</font>页/共<font color=red>" & tPageCount &"</font>页]")
Response.Write("[共<font color=red >"&tRecordCount&"</font>条 <font color=red>"& tPageSize & "</font>条/页]")
Response.Write(" 转到" & "<input id=goto name=goto size=""6"" maxsize=""10"" value="& PageNo&" onKeyDown=""if(event.keyCode==13) {window.location.href='"&url&"pageNo='+this.value; return false;}"" onkeyup=""value=value.replace(/[^\d]/g,'') "" onbeforepaste=""clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"" />" & "页<input type=submit value=""Go"" onclick= ""gotoPage()"" />")
Response.Write("</td></tr></table>")
Response.Write("<script>function gotoPage(){var pg=document.getElementById('goto'). value;if(isNaN(pg)||pg>"&tPageCount&"||pg<0){window.alert('请正确输入页码,只能为正整数!且不能大于"&tPageCount&"');return false;}else {window.location.href='"&url&"PageNo='+document.getElementById('goto').value+'';return true;}}</script>")
rs.close()
EndTime = Timer
response.Write(" 所用时间:【"&FormatNumber((EndTime - StartTime),5,true)&"秒】<br />")
end if
这个页面的代码有点多,直接COPY下,保存下。
另:关于ASP调用存储过程参数简写的问题:ASP调用存储过程可以使用with 命令来简写代码。比如本例中:
……
set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = connstr
cmd.CommandText = "dbo.testPROC3"
cmd.CommandType = 4
cmd.CommandTimeout = 0
cmd.Prepared = true
cmd.Parameters.Append cmd.CreateParameter("@tRecordCount", 3, 4)
cmd.Parameters.Append cmd.CreateParameter("@tPageCount", 3, 2)
cmd.Parameters.Append cmd.CreateParameter("@tPageSize", 3, 1,10,tPageSize)
cmd.Parameters.Append cmd.CreateParameter("@PageNo", 3, 1,10,PageNo)
set rs=cmd.Execute()
rs.close()
……
这些代码可以用with命令来简化为:
……
set cmd = Server.CreateObject("ADODB.Command")
with cmd
.ActiveConnection = connstr
.CommandText = "dbo.testPROC3"
.CommandType = 4
.CommandTimeout = 0
.Prepared = true
.Parameters.Append .CreateParameter("@tRecordCount", 3, 4)
.Parameters.Append .CreateParameter("@tPageCount", 3, 2)
.Parameters.Append .CreateParameter("@tPageSize", 3, 1,10,tPageSize)
.Parameters.Append .CreateParameter("@PageNo", 3, 1,10,PageNo)
set rs=.Execute()end with
rs.close()
…..
运行的结果一样。
4、返回多个记录集合的存储过程
在应用中有时候存储过程要用到多个表或返回多个记录集的情况下,在不知道如何获取
①/*=======第4个存储过程=====
这个存储过程比较简单主要是说明怎么读取多个记录集的问题。
注意:记录集的要按顺序来读取。不能跳着读取。
======================*/
USE johnny
go
CREATE PROC testPROC4
AS
SET NOCOUNT ON
BEGIN
SELECT uName FROM testUser
SELECT uEmail FROM testUser
SELECT uQQ FROM testUser
END
②ASP页面:在输入连接数据库字符串后再加入下面的代码
dim cmd
set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = connstr ' connstr是连接字符串
cmd.CommandText = "dbo.testPROC4"
cmd.CommandType = 4
cmd.CommandTimeout = 0
cmd.Prepared = true
set rs=cmd.Execute()
set cmd=nothing
'显示记录
response.Write("下面是第1个显示用户名记录集的信息:<br />")
while not rs.eof
response.Write("<li>"&rs("uName")&"</li><hr />"&VbCrlf)
rs.movenext
wend
response.Write("下面是第2个显示EMAIL记录集的信息:<br />")
set rs=rs.Nextrecordset '当你要显示第2个
while not rs.eof
response.Write("<li>"&rs("uEmail")&"</li><hr />"&VbCrlf)
rs.movenext
wend
response.Write("下面是第3个显示QQ记录集的信息:<br />")
set rs=rs.Nextrecordset '当你要显示第2个
while not rs.eof
response.Write("<li>"&rs("uQQ")&"</li><hr />"&VbCrlf)
rs.movenext
wend
set rs=nothing
保存下。
当你学习到这里的话,且上面的存储过程都是手工的敲到SQL查询分析器里的话,相信你现在肚子里应该有点东西了。呵呵~~,能自己动手编写自己需要的存储过程了。相信你再去看其他的存储过程应该应该是没问题了。另外要是你有动手输入的话你应该会发现我的每个存储过程最前面都有加:
use Johnny
go
这两行代码,为什么呢,提示我们用的是哪个数据库和是否存在还有就是因为有时候我们SQL查询分析器关了。再次打开时候当前的数据库不是johnny,而是master 。但我们运行这个切没在johnny数据库上运行搞得你一直找不出问题的所在,一直以为是存储过程哪里出错了。呵呵。~
说到查看存储过程,系统有这么个关键字可以查看存储过程的代码:sp_helptext 如查看 testPROC1存储过程的代码可以这样:sp_helptext testPROC1 就可以了。
5、现在来讲下当同时调用多个存储过程的情况:
这里介绍两种方法:
① 创建多个Command对象
Dim cmd
Set cmd=Server.CreateObject("ADODB.Command")
'调用存储过程1
'…..
Set cmd=nothing
Set cmd=Server.CreateObject("ADODB.Command")
'调用存储过程2
'…..
Set cmd=nothing
……
② 只创建一个Command 对象,在调用第下一个存储过程时将当前的参数倒序清楚。
Dim cmd
Set cmd=Server.CreateObject("ADODB.Command")
'调用存储过程1
'…..
'倒序清楚参数(假设3个)
cmd.Parameters.delete 2
cmd.Parameters.delete 1
cmd.Parameters.delete 0
'调用存储过程2
'…..
……
Set cmd=nothing
以上两种效率差不多(第②个略高点点)看个人喜欢,自行选择。
至此,本文的主要内容也到了一个段落了。相信对于一般的应用应该是No problem!
最后我们来说下本文目的的第四点:SQL语句的优化
五、SQL语句的优化(有兴趣的可以了解)
1、合理的索引
索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。索引的使用要恰到好处,其使用原则如下:
●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
另外,当数据库表更新大 量数据后,删除并重建索引可以提高查询速度。
(1)在下面两条select语句中:
select * from table1 where field1<=10000 and field1>=0;
select * from table1 where field1>=0 and field1<=10000;
如果数据表中的数据field1都>=0,则第一条select语句要比第二条select语句效率高的多,因为第二条select语句的第一个条件耗费了大量的系统资源。
第一个原则:在where子句中应把最具限制性的条件放在最前面。
(2)在下面的select语句中:
select * from tab where a=… and b=… and c=…;
若有索引index(a,b,c),则where子句中字段的顺序应和索引中字段顺序一致。
第二个原则:where子句中字段的顺序应和索引中字段顺序一致。
以下假设在field1上有唯一索引I1,在field2上有非唯一索引I2。
(3) select field3,field4 from tb where field1='sdf' 快
select * from tb where field1='sdf' 慢,
因为后者在索引扫描后要多一步ROWID表访问。
(4) select field3,field4 from tb where field1>='sdf' 快
select field3,field4 from tb where field1>'sdf' 慢
因为前者可以迅速定位索引。
(5) select field3,field4 from tb where field2 like 'R%' 快
select field3,field4 from tb where field2 like '%R' 慢,
因为后者不使用索引。
(6) 使用函数如:
select field3,field4 from tb where upper(field2)='RMN'不使用索引。
如果一个表有两万条记录,建议不使用函数;如果一个表有五万条以上记录,严格禁止使用函数!两万条记录以下没有限制。
(7) 空值不在索引中存储,所以
select field3,field4 from tb where field2 is[not] null不使用索引。
(8) 不等式如
select field3,field4 from tb where field2!='TOM'不使用索引。
相似地,
select field3,field4 from tb where field2 not in('M','P')不使用索引。
(9) 多列索引,只有当查询中索引首列被用于条件时,索引才能被使用。
(10) MAX,MIN等函数,如
Select max(field2) from tb使用索引。所以,如果需要对字段取max,min,sum等,应该加索引。
一次只使用一个聚集函数,如:
select “min”=min(field1), “max”=max(field1) from tb
不如:select “min”=(select min(field1) from tb) , “max”=(select max(field1) from tb)
(11) 重复值过多的索引不会被查询优化器使用。而且因为建了索引,修改该字段值时还要修改索引,所以更新该字段的操作比没有索引更慢。
(12) 索引值过大(如在一个char(40)的字段上建索引),会造成大量的I/O开销(甚至会超过表扫描的I/O开销)。因此,尽量使用整数索引。 Sp_estspace可以计算表和索引的开销。
(13) 对于多列索引,order by的顺序必须和索引的字段顺序一致。
(14) 多表联结(具体查询方案需要通过测试得到)
where子句中限定条件尽量使用相关联的字段,且尽量把相关联的字段放在前面。
select a.field1,b.field2 from a,b where a.field3=b.field3
1. field3上没有索引的情况下:
对a作全表扫描,结果排序
对b作全表扫描,结果排序
结果合并。
对于很小的表或巨大的表比较合适。
2. field3上有索引
按照表联结的次序,b为驱动表,a为被驱动表
对b作全表扫描
对a作索引范围扫描
如果匹配,通过a的rowid访问
(15) 避免一对多的join。如:
select tb1.field3,tb1.field4,tb2.field2 from tb1,tb2 where tb1.field2=tb2.field2 and tb1.field2=‘BU1032’ and tb2.field2= ‘aaa’
不如:
declare @a varchar(80)
select @a=field2 from tb2 where field2=‘aaa’
select tb1.field3,tb1.field4,@a from tb1 where field2= ‘aaa’
(16) 子查询
用exists/not exists代替in/not in操作
比较:
select a.field1 from a where a.field2 in(select b.field1 from b where b.field2=100)
select a.field1 from a where exists( select 1 from b where a.field2=b.field1 and b.field2=100)
select field1 from a where field1 not in( select field2 from b)
select field1 from a where not exists( select 1 from b where b.field2=a.field1)
(17) char类型的字段不建索引比int类型的字段不建索引更糟糕。建索引后性能只稍差一点。
(18) 使用count(*)而不要使用count(column_name),避免使用count(distinct column_name)。
(19) 等号右边尽量不要使用字段名,如:
select * from tb where field1 = field3
(20) 避免使用or条件,因为or不使用索引。
简单的例子:
SQL语句索引的利用
采用函数处理的字段不能利用索引,如:
substr(hbs_bh,1,4)=’5400’,优化处理:hbs_bh like ‘5400%’
trunc(sk_rq)=trunc(sysdate), 优化处理:
sk_rq>=trunc(sysdate) and sk_rq<trunc(sysdate+1)
进行了显式或隐式的运算的字段不能进行索引,如:
ss_df+20>50,优化处理:ss_df>30
‘X’||hbs_bh>’X5400021452’,优化处理:hbs_bh>’5400021542’
sk_rq+5=sysdate,优化处理:sk_rq=sysdate-5
hbs_bh=5401002554,优化处理:hbs_bh=’ 5401002554’,注:此条件对hbs_bh 进行隐式的to_number转换,因为hbs_bh字段是字符型。
条件内包括了多个本表的字段运算时不能进行索引,如:
ys_df>cx_df,无法进行优化
qc_bh||kh_bh=’5400250000’,优化处理:qc_bh=’5400’ and kh_bh=’250000’
以上的SQL优化只是给大家做个简单的介绍。希望你能看懂些,及在以后的应用中能想到的话就用上吧。这也是本文的目的。
为了方便大家参考我把所有的T-SQL语句写如下:
这里是所有的T-SQL语句,可以做为学习参考,里面我都注释,再不懂就在查询分析器里F1呵呵~:):
/*=======开始=========*/
CREATE database johnny --创建数据库
go
USE johnny --使用数据库
go
CREATE TABLE testTable( --创建表
tID int identity(1,1), --创建字段,下同
tUserName varchar(50),
tTel varchar(30)
)
go
CREATE TABLE testUser
(
uID int primary key identity(1,1), --创建主键
uName varchar(20) not null, --用户名,非空
uEmail varchar(50) not null, --用户名电子邮件,非空
uQQ int default 0 --用户QQ,默认0
)
go
/*先给用户表插入几条记录*/
insert into testUser(uName,uEmail,uQQ) values('johnny','chenzxmail@163.com',40623660)
insert into testUser(uName,uEmail,uQQ) values('张三','12345@163.com',123456)
insert into testUser(uName,uEmail,uQQ) values('李四','43435@163.com',4354534)
go
/*查看刚才插入的数据*/
select * from testuser
go
/*插入数据到测试的表*/
set nocount on --当 SET NOCOUNT 为 ON 时,存储过程中包含的一些语句并不返回许多实际的数据,则该设置由于大量减少了网络流量,因此可显著提高性能。
declare @i int --DECLARE 语句声明变量@i,并用 SET 或 SELECT 语句给其指派值。
set @i=1 --用 SET 或 SELECT 语句给其指派值
while @i<=200000 --只要指定的条件为真,就重复执行语句。即插入200000的数据,呵呵发了1分14秒的时间。
begin
insert into testTable(tUserName,tTel) values(CAST(round(rand()*100000,0) AS varchar),CAST(round(rand()*100000,0) AS varchar)+CAST(CEILING(round(rand()*100000,0)/3) AS VARCHAR))
SELECT @i=@i+1
end
go
/*查看刚才插入的前50条记录的信息*/
SELECT TOP 50 * FROM testTable
go
/*第一个简单的存储过程*/
CREATE PROC testPROC1
AS
SELECT * FROM testUser
go
/*==============
第2个存储过程:即有返回值,输入参数和输出参数。
●RETURN 语句无条件终止查询、存储过程或批处理。存储过程或批处理中 RETURN 语句后面的语句都不执行。
●return的说明:除非特别指明,所有系统存储过程返回 0 值表示成功,返回非零值则表示失败。
●本存储过程的return都是特别的说明,所以其返回值都是有相应的意义。
●下面的返回值0、1、2假如去掉的话,即如果 RETURN 未指定值,则存储过程返回 0。在程序调用且正常运行的情况下获取“返回值”的值都是返回 0 。
==============*/
CREATE PROC testPROC2
@userName varchar(20), --声明变量@userName
@userEmail varchar(50) OUTPUT --声明变量@userEmail,且是输出参数。
AS
set nocount on
begin
IF @userName is null OR @userName='' --当输入参数为空或is null时
begin
set @userEmail='请输入要查询的用户名!' --符合条件给输出参数赋值,以便提示
return 0 --输入参数为空时,“返回值”我们这里设置为 0(当然可以设置其他的数字)。
end
ELSE
IF exists(SELECT uEmail FROM testUser WHERE uName=@userName) --如果输入参数存在时
begin
SELECT @userEmail=uEmail FROM testUser WHERE uName=@userName --输出参数赋值
return 1 --验证输入的参数(用户名),存在时“返回值”我们这里设置为 1。
end
ELSE
begin
set @userEmail='不存在此用户名!' ----如果输入参数 不 存在时,提示
return 2 --输入的参数(用户名)不 存在时“返回值”我们这里设置为 2。
end
end
go
/*=======第3个存储过程====
有返回输出参数和记录集的存储过程
有任何问题:QQ:40623660
========================*/
CREATE PROC testPROC3
@tPageCount int OUTPUT, --输出参数,总页数
@tPageSize int, --每页显示多少条记录
@tPageNo int --当前页码
AS
SET NOCOUNT ON
BEGIN
/*取得总记录条数*/
DECLARE @tRecordCount int --总记录条数
SELECT @tRecordCount=COUNT(tID) FROM testTable
--PRINT @tRecordCount
/*确定总的页数*/
IF (@tRecordCount % @tPageSize) = 0
SET @tPageCount=CEILING(@tRecordCount/@tPageSize) --CEILING函数:返回大于或等于所给数字表达式的最小整数。
ELSE
SET @tPageCount=CEILING(@tRecordCount/@tPageSize)+1
/*如果输入的页码大于总的页数*/
IF @tPageNo > @tPageCount
SET @tPageNo = @tPageCount
/*确定当前页记录的起止标记*/
DECLARE @tStart int , @tEnd int --设置起止标志
SET @tStart=(@tPageNo-1) * @tPageSize
SET @tEnd =@tStart+@tPageSize+1
/*由其止标记得到当前页的记录*/
SELECT * FROM testTable WHERE (tID>@tStart) AND (tID<@tEnd)
--SELECT * FROM testTable WHERE (tID<@tEnd) AND (tID>@tStart)
--SELECT * FROM testTable WHERE tID between @tStart+1 AND @tEnd
/*我们把总记录做为返回值,这样存储过程就变了有返回值,输出参数和记录集了*/
RETURN @tRecordCount --做为返回值
END
GO
/*=======第4个存储过程=====
这个存储过程比较简单主要是说明怎么读取多个记录集的问题。
注意:记录集的要按顺序来读取。不能跳着读。
======================*/
USE johnny
go
CREATE PROC testPROC4
AS
SET NOCOUNT ON
BEGIN
SELECT uName FROM testUser
SELECT uEmail FROM testUser
SELECT uQQ FROM testUser
END
go
--sp_helptext testPROC1 --用于查看存储过程
Word教程网 | Excel教程网 | Dreamweaver教程网 | Fireworks教程网 | PPT教程网 | FLASH教程网 | PS教程网 |
HTML教程网 | DIV CSS教程网 | FLASH AS教程网 | ACCESS教程网 | SQL SERVER教程网 | C语言教程网 | JAVASCRIPT教程网 |
ASP教程网 | ASP.NET教程网 | CorelDraw教程网 |