数据库存储过程详解
/* 存储过程可以看作是在数据库中的存储t-sql脚本 为什么使用存储过程 1、增加性能 本地存储发送的内容少、调用快、预编译、高速缓存 一般语句的执行:检查权限、检查语法,建立执行计划处理语句的要求 存储过程:创建时已经检查了语法;第一次执行的时候执行计划被创建,被编译; 再次执行时不需要重检查语法、不需要重编译、根据已经缓存的计划来决定是否需要重创建执行计划 2、增强安全 加密、分离(权限设置,用户只需要有执行存储过程的权限,不需要有访问存储过程所使用的对象的权限) 3、在transact-sql中使用非数据库技术 dll 4、编程模式——使用外部编程语言调用 1)input 2)output 3)feedback 状态代码或描述性的文本 4)模块化、可重用、可调用其他存储过程 5)隐藏程序逻辑,便于编程 6)可以调用动态连接库(外接的程序) 基本原则:越简单越好 单一任务 */ /* 分类 1、系统存储过程 存在于master数据库,一般以sp_开头 提供对系统表格数据调用、数据库管理功能、安全管理功能的支持 --表格授权 use pubs go execute sp_table_privileges stores --显示kylin\administrator的所有进程 execute sp_who @loginame='W2K3SERVER\Administrator' --报告有关孤立的 microsoft windows nt 用户和组的信息,这些用户和组已不在 windows nt 环境中,但仍在 microsoft sql server系统表中拥有项。 execute sp_validatelogins 2、本地存储过程 用户创建的解决特定问题的 3、临时存储过程 存储于tempdb 创建、调用时的数据库 使用范围 生存周期 #local 不限数据库 创建时的连接有效 从创建时开始,当创建的连接中断时消失 ##global 不限数据库 所有连接 从创建时开始,当创建的连接中断时消失 直接创建在tempdb的存储过程 tempdb 所有连接 从创建时开始,当数据库服务器服务停止时消失 create proc #local as select '#local' go exec #local go create proc ##global as select '##global' go exec ##global go use tempdb go create procedure directtemp as select * from [pubs].[dbo].[authors] go use northwind go exec tempdb.dbo.directtemp 4、扩展存储过程 c++ xp xp_sendmail既是系统存储过程,也是扩展存储过程 使用objectproperty来判断是否是扩展存储过程 use master --扩展存储过程 select objectproperty(object_id('sp_prepare'), 'isextendedproc') --非扩展存储过程 select objectproperty(object_id('xp_logininfo'), 'isextendedproc') 5、远程存储过程 目前版本中只是为了向后兼容,已被分布式查询替代 */ /* 存储过程在数据库中如何存储 名字 sysobjects 文本 syscomments */ /* 练习1:通过查询分析器中的对象查看器查看存储过程 */ /* 练习2:查看存储过程的内容 图形 语句 */ select * from sysobjects select * from syscomments go select * from syscomments where id = object_id('custorderhist') go select name,text from sysobjects inner join syscomments on sysobjects.id = syscomments.id where sysobjects.name = 'custorderhist' go sp_helptext sp_helptext go use northwind go exec sp_help custorderhist exec sp_helptext custorderhist exec sp_depends custorderhist exec sp_stored_procedures 'custorderhist' /* 系统存储过程 以使用为主 */ /* 本地存储过程的创建、修改、删除 1、t-sql语句 create procedure alter procedure drop procedure create procedure 存储过程名字 as 存储过程文本 go alter procedure 存储过程名字 as 存储过程文本 go drop procedure 存储过程名字 2、企业管理器 右键 向导 */ /* 简单 */ -- -- -- select top 1 * from products -- -- -- select top 1 * from orders -- -- -- select top 1 * from [order details] /*1、和视图比较*/ alter proc sp_qry_salesdetails as select a.productid as 商品编号,a.productname as 商品名称,b.unitprice as 数量,b.quantity as 价格, b.unitprice*b.quantity as 金额,c.requireddate as 销售时间 from [order details] as b join products as a on b.productid=a.productid join orders as c on b.orderid=c.orderid go print '测试' execute sp_qry_salesdetails --递归算法 --视图 存储过程 函数 alter view v_qry_salesdetails as select a.productid as 商品编号,a.productname as 商品名称,b.unitprice as 数量,b.quantity as 价格, b.unitprice*b.quantity as 金额,c.requireddate as 销售时间 from [order details] as b join products as a on b.productid=a.productid join orders as c on b.orderid=c.orderid print '测试' select * from v_qry_salesdetails /* 默认情况下第一次执行时的执行计划被保存,以后执行时都是用这个执行计划,直到服务器重启或存储过程使用的表格变化时 当存储过程变化时,如:参数变化,需要重新编译、制定新的执行计划 当每次调用存储过程时强制重新编译的方法: 1、创建时指定 with recompile 2、sp_recompile */ create procedure sp1 as select * from customers exec sp1 alter procedure sp1 as select * from customers alter procedure sp1 with recompile as select * from customers sp_recompile sp1 --加密存储过程 with encryption select objectproperty(object_id('sp_qry_salesdetails'), 'isencrypted') /* 删除存储过程 drop proc */ use northwind go create proc dbo.sp_dropproc as select 'northwind.dbo.sp_dropproc' go exec northwind.dbo.sp_dropproc go use master go create proc dbo.sp_dropproc as select 'master.dbo.sp_dropproc' go exec master.dbo.sp_dropproc go use northwind go drop proc sp_dropproc go exec sp_dropproc exec master.dbo.sp_dropproc /* 提供输入参数 input */ create proc qry_salesdetails @y int,@m int --varchar(10) as select a.productid as 商品编号,a.productname as 商品名称,b.unitprice as 数量,b.quantity as 价格,b.unitprice*b.quantity as 金额,c.requireddate as 销售时间 from [order details] as b join products as a on b.productid=a.productid join orders as c on b.orderid=c.orderid --where convert(varchar(2),month(c.requireddate)) = @m where year(c.requireddate) = @y and month(c.requireddate) = @m go exec qry_salesdetails 1996,9 exec qry_salesdetails 9,1996 exec qry_salesdetails @m=9,@y=1996 exec qry_salesdetails @y=1996,@m=9 go /* northwind 数据库 orders order details 表格 * 根据指定用户ID显示此用户在1996-07-01到1997-07-01之间的订货记录 要求存储过程文本加密 */ use northwind go --创建存储过程 -- drop proc qry_showorders create proc qry_showorders @custid nchar(5) with encryption --加密 as if @custid is null -- begin -- print '提供了不正确的参数' -- return -- end select * from orders od inner join [order details] oddt on od.orderid = oddt.orderid where shippeddate >='1996-07-01' and shippeddate <='1997-07-01' and od.customerid = @custid go --调用、检验刚刚创建的存储过程 exec qry_showorders @custid = 'vinet' exec qry_showorders null go --检查是否已经被加密 exec sp_helptext qry_showorders /* 返回值 output ,一个返回值变量一次只能有一个返回的值 */ create proc testoutput @a varchar(10) output as select @a = 100 go declare @b varchar(10) --exec testoutput @b output exec testoutput @a=@b output select @b --error create proc sum_money @count money, @unitprice money as select @count*@unitprice go declare @sum_temp money ,@sum_temp2 money set @sum_temp2 = exec sum_money @count= 1.1,@unitprice = 2.2 create proc sum_money @count money, @unitprice money ,@sum money output as set @sum = @count*@unitprice go declare @sum_temp money ,@sum_temp2 money exec sum_money @count= 1.1,@unitprice = 2.2,@sum = @sum_temp output set @sum_temp2= @sum_temp*100 select @sum_temp2 create proc test_output @in nvarchar(100),@out nvarchar(100) output as print 'i''m @in ' + @in set @out = @in print 'i''m @out '+@out go declare @i nvarchar(100),@o nvarchar(100) set @i = '让我们一起来测试' exec test_output @in = @i,@out = @o output select @o /* return 语句和错误处理 */ --return 主要用来进行错误处理 create proc testreturn @a int as if @a<0 begin return(-1) end else if @a = 0 begin return(0) end else begin return(1) end go declare @rtn int exec @rtn = testreturn @a=-100 select @rtn go /* @@error */ select @@error go select 'a'+1 go select @@error select error, description from master.dbo.sysmessages where error = 245 create proc testerror as select 'a'+1 go exec testerror go create proc testerror as declare @e int,@a int ,@b int set @e = 0 set @a = 1 set @b = 0 select @a/@b if @@error<>0 begin print '有错误' set @e = @@error end return @e go declare @er int exec @er = testerror select @er /* @@rowcount */ select @@rowcount select * from customers select @@rowcount /* null 值 */ create proc testreturn @a int as if @a is null begin return(100) end else if @a<0 begin return(-1) end else if @a = 0 begin return(0) end else begin return(1) end /*************************************************************************************************************************** 特殊问题 ***************************************************************************************************************************/ /* 关于sp_的命名 */ use master go create sp_test as select '现在是master数据库' go use northwind go create sp_test as select '现在是northwind数据库' go exec sp_test exec master.dbo.sp_test drop sp_test create proc sp1_test as select '这是master' go use northwind go create proc sp1_test as select '这是northwind' exec sp1_test drop proc sp1_test /* 命名延迟解决方案: 创建存储过程时,应用的对象可以不存在,建议存储过程及引用的对象所有者都设置为dbo */ --按契约编程 use northwind go create proc testdelay as select * from tbldelay go exec testdelay /* 在创建存储过程时可以逻辑上形成组,以便作为同一个管理单元并在一个程序中使用 */ create proc groupedproc;1 as select 'groupedproc;1 ' go create proc groupedproc;2 as select 'groupedproc;2 ' go sp_helptext groupedproc go exec groupedproc;1 go exec groupedproc;2 go exec groupedproc go drop proc groupedproc /* 存储过程嵌套,最多32层 */ create proc a as select 'a' go create proc b as select 'b' exec a go exec b /* 使用默认值 */ -- -- drop proc testdefault create proc testdefault @a int,@b int=2 as select @a,@b go exec testdefault 1 go exec testdefault @a=1 exec testdefault 1,100 /* 在服务器启动时自动运行的存储过程 要求:所有者是dbo,在master数据库中 */ use northwind go create table start ( dt datetime ) go use master go create proc autostart as insert into northwind.dbo.start values(getdate()) go --设置为自动运行 execute sp_procoption @procname = autostart, @optionname = startup, @optionvalue = true go use master --判断是否自动运行 select objectproperty(object_id('autostart'), 'execisstartup') go select * from northwind.dbo.start --停止自动运行 execute sp_procoption @procname = autostart, @optionname = startup, @optionvalue = false execute sp_configure @configname = 'scan for startup procs', @configvalue = 0 reconfigure go /* 扩展存储过程 使用sp_addextendedproc 注册 或使用企业管理器 在master 扩展存储过程 */ -- -- -- -- exec xp_dirtree "D:\" -- -- -- -- -- -- ------msg 15281, level 16, state 1, procedure xp_cmdshell, line 1 -- -- -- ------sql server blocked access to procedure 'sys.xp_cmdshell' of component 'xp_cmdshell' because this component is turned off as part of the security configuration for this server. a system administrator can enable the use of 'xp_cmdshell' by using sp_configure. for more information about enabling 'xp_cmdshell', see "surface area configuration" in sql server books online. ---exec xp_cmdshell "dir *.exe" -- -- -- -- -- -- exec xp_cmdshell tree -- -- -- /* 练习:向northwind数据库中的customers 表格插入记录的存储过程 名字insertcust */ select insert update delete create proc insertcust @custid nchar(5), @cmpnm nvarchar(40), @cntnm nvarchar(30), @cntttl nvarchar(30), @addr nvarchar(60), @city nvarchar(15), @rg nvarchar(15), @pscd nvarchar(10), @cntry nvarchar(15), @phone nvarchar(24), @fax nvarchar(24) as --业务逻辑 insert into customers(customerid,companyname,contactname,contacttitle, address,city,region,postalcode,country,phone,fax) values(@custid,@cmpnm,@cntnm,@cntttl, @addr,@city,@rg,@pscd,@cntry,@phone,@fax) go exec insertcust @custid='abcd',@cmpnm='abc company',@cntnm='anyone',@cntttl='mr.',@addr='anywhere', @city='shanghai',@rg='huangpu',@pscd='200000',@cntry='chian',@phone='021-88888888',@fax='021-66666666' go --简单实现 create proc createcustid @id nchar(5) output as --自动产生客户ID create proc insertcust @cmpnm nvarchar(40), @cntnm nvarchar(30), @cntttl nvarchar(30), @addr nvarchar(60), @city nvarchar(15), @rg nvarchar(15), @pscd nvarchar(10), @cntry nvarchar(15), @phone nvarchar(24), @fax nvarchar(24) as declare @id nchar(t5) exec createcustid @id output insert into customers(customerid,companyname,contactname,contacttitle,address,city,region,postalcode,country,phone,fax) values(@id,@cmpnm,@cntnm,@cntttl,@addr,@city,@rg,@pscd,@cntry,@phone,@fax) go /* 其他要考虑的因素: customerid 自动生成 如果重复怎么处理? 生成新id? 电话号码格式不正确如何处理? return */ ------------------------------------------------------------------------------------------------------------------------ set nocount off select 'a' go -- -- -- set nocount on select 'a' /* 动态语句的使用——动态条件 */ create proc qry_salesdetails @no int = -1,@start char(10),@end char(10) as select a.productid as 商品编号,a.productname as 商品名称,b.unitprice as 数量,b.quantity as 价格,b.unitprice*b.quantity as 金额,c.requireddate as 销售时间 from [order details] as b join products as a on b.productid=a.productid join orders as c on b.orderid=c.orderid where a.productid= @no and c.requireddate<=@end and c.requireddate>=@start go exec qry_salesdetails 6,'1996-01-01','1997-01-01' alter proc qry_salesdetails @no int = -1,@start char(10),@end char(10) as declare @sql varchar(4000) set @sql = 'select a.productid as 商品编号,a.productname as 商品名称, b.unitprice as 数量,b.quantity as 价格,b.unitprice*b.quantity as 金额, c.requireddate as 销售时间 from [order details] as b join products as a on b.productid=a.productid join orders as c on b.orderid=c.orderid where 1=1 ' if @no is not null set @sql = @sql + ' and a.productid = '+convert(varchar(10),@no) if @start is not null and @end is not null set @sql = @sql + ' and c.requireddate >= '''+ @start+'''' + ' and c.requireddate <= '''+ @end+'''' --print @sql exec(@sql) print '''' go exec qry_salesdetails @end=null,@start=null exec qry_salesdetails @no=35,@end=null,@start=null exec qry_salesdetails @no=null,@end='1997-07-01',@start='1996-07-01' exec qry_salesdetails @no=38,@end='1997-07-01',@start='1996-07-01' sp_stored_procedures qry_salesdetails /* 临时表的使用 年度销售汇总表 月汇总 年汇总 */ drop table tempdb..#temp go create table #temp ( 商品编号 varchar(100), 商品名称 varchar(100), 金额 money, 销售时间 datetime, 排序 int ) insert into #temp select a.productid as 商品编号,a.productname as 商品名称, b.unitprice*b.quantity as 金额,c.requireddate as 销售时间, month(c.requireddate) from [order details] as b join products as a on b.productid=a.productid join orders as c on b.orderid=c.orderid where year(c.requireddate) = 1996 insert into #temp(商品编号,金额,排序) select '月汇总',sum(金额),month(销售时间) from #temp group by year(销售时间),month(销售时间) insert into #temp(商品编号,金额,排序) select '年汇总',sum(金额),12 from #temp where 销售时间 is not null select * from #temp order by 排序 ,商品名称 desc select * from #temp drop table tempdb..#temp
更多SQL的相关技术文章,请访问SQL教程栏目进行学习!
以上就是数据库存储过程详解的详细内容,更多请关注其它相关文章!