mysql 5.0存储过程学习总结

来源:互联网 发布:freeradius mysql 编辑:程序博客网 时间:2024/05/04 13:02

一.创建存储过程
1.基本语法:
create procedure sp_name()
begin
end
2.参数传递

二.调用存储过程
1.基本语法:call sp_name()
注意:存储过程名称后面必须加括号,哪怕该存储过程没有参数传递

三.删除存储过程
1.基本语法:
drop procedure sp_name//
2.注意事项
(1)不能在一个存储过程中删除另一个存储过程,只能调用另一个存储过程

四.区块,条件,循环
1.区块定义,常用
begin
end;
也可以给区块起别名,如:
lable:begin
end lable;
可以用leave lable;跳出区块,执行区块以后的代码
2.条件语句 3.循环语句 :while循环 loop循环 repeat until循环 repeat until循环

五.其他常用命令
1.show procedure status
显示数据库中所有存储的存储过程基本信息,包括所属数据库,存储过程名称,创建时间等
2.show create procedure sp_name
显示某一个存储过程的详细信息

mysql存储过程基本函数
一.字符串类
CHARSET(str) //返回字串字符集
CONCAT (string2 [,... ]) //连接字串
INSTR (string ,substring ) //返回substring首次在string中出现的位置,不存在返回0
LCASE (string2 ) //转换成小写
LEFT (string2 ,length ) //从string2中的左边起取length个字符
LENGTH (string ) //string长度
LOAD_FILE (file_name ) //从文件读取内容
LOCATE (substring , string [,start_position ] ) 同INSTR,但可指定开始位置
LPAD (string2 ,length ,pad ) //重复用pad加在string开头,直到字串长度为length
LTRIM (string2 ) //去除前端空格
REPEAT (string2 ,count ) //重复count次
REPLACE (str ,search_str ,replace_str ) //在str中用replace_str替换search_str
RPAD (string2 ,length ,pad) //在str后用pad补充,直到长度为length
RTRIM (string2 ) //去除后端空格
STRCMP (string1 ,string2 ) //逐字符比较两字串大小,
SUBSTRING (str , position [,length ]) //从str的position开始,取length个字符,
即参数position必须大于等于1
TRIM([[BOTH|LEADING|TRAILING] [padding] FROM]string2) //去除指定位置的指定字符
UCASE (string2 ) //转换成大写
RIGHT(string2,length) //取string2最后length个字符
SPACE(count) //生成count个空格

二.数学类
ABS (number2 ) //绝对值 www.52mvc.com
BIN (decimal_number ) //十进制转二进制
CEILING (number2 ) //向上取整
CONV(number2,from_base,to_base) //进制转换
FLOOR (number2 ) //向下取整
FORMAT (number,decimal_places ) //保留小数位数
HEX (DecimalNumber ) //转十六进制
注:HEX()中可传入字符串,则返回其ASC-11码,如HEX(’DEF’)返回4142143
也可以传入十进制整数,返回其十六进制编码,如HEX(25)返回19
LEAST (number , number2 [,..]) //求最小值
MOD (numerator ,denominator ) //求余
POWER (number ,power ) //求指数
RAND([seed]) //随机数
ROUND (number [,decimals ]) //四舍五入,decimals为小数位数]
三.日期时间类
ADDTIME (date2 ,time_interval ) //将time_interval加到date2
CONVERT_TZ (datetime2 ,fromTZ ,toTZ ) //转换时区
CURRENT_DATE ( ) //当前日期
CURRENT_TIME ( ) //当前时间
CURRENT_TIMESTAMP ( ) //当前时间戳
DATE (datetime ) //返回datetime的日期部分
DATE_ADD (date2 , INTERVAL d_value d_type ) //在date2中加上日期或时间
DATE_FORMAT (datetime ,FormatCodes ) //使用formatcodes格式显示datetime
DATE_SUB (date2 , INTERVAL d_value d_type ) //在date2上减去一个时间
DATEDIFF (date1 ,date2 ) //两个日期差
DAY (date ) //返回日期的天
DAYNAME (date ) //英文星期
DAYOFWEEK (date ) //星期(1-7) ,1为星期天
DAYOFYEAR (date ) //一年中的第几天
EXTRACT (interval_name FROM date ) //从date中提取日期的指定部分
MAKEDATE (year ,day ) //给出年及年中的第几天,生成日期串
MAKETIME (hour ,minute ,second ) //生成时间串
MONTHNAME (date ) //英文月份名
NOW ( ) //当前时间
SEC_TO_TIME (seconds ) //秒数转成时间
STR_TO_DATE (string ,format ) //字串转成时间,以format格式显示
TIMEDIFF (datetime1 ,datetime2 ) //两个时间差
TIME_TO_SEC (time ) //时间转秒数]
WEEK (date_time [,start_of_week ]) //第几周
YEAR (datetime ) //年份
DAYOFMONTH(datetime) //月的第几天
HOUR(datetime) //小时
LAST_DAY(date) //date的月的最后日期
MICROSECOND(datetime) //微秒
MONTH(datetime) //月
MINUTE(datetime) //分


## **********first test,procedure**********
#<1>
use testprocedure;
delimiter //
create procedure simpleproce1 (out par1 int)
begin
select count(*) into par1 from proce;
end
//

delimiter ;
call simpleproce1(@a);
select @a;
#<2>,每次只有单一的行可以被取回select id,name into par1,par2 from proce LIMIT 1;中的LIMIT 1;

use testprocedure;

delimiter //
DROP procedure IF EXISTS simpleproce2
create procedure simpleproce2 (out par1 int,out par2 char(30))
begin
select id,name into par1,par2 from proce LIMIT 1;
end
//

delimiter ;
call simpleproce2(@a,@b);
select @a,@b;


## *********second test,function************
#<3>
delimiter //

DROP FUNCTION IF EXISTS hello
//
create function hello(s char(20)) returns char(50)
return concat('Hello, ',s,'!');
//
delimiter ;
select hello('world');
show create function testprocedure.hello\G
#它返回子程序的特征,如数据库,名字,类型,创建者及创建和修改日期
show function status like 'hello'\G

#<4>
#注意name不能和字段名相同
delimiter //
DROP procedure IF EXISTS test //

CREATE PROCEDURE test ()
BEGIN
DECLARE name VARCHAR(5) DEFAULT 'bob';
DECLARE newname VARCHAR(5);
DECLARE xid INT;

SELECT name,id INTO newname,xid
FROM proce WHERE name = name;
SELECT newname;
END;
//
call test1() //
#***
delimiter //
DROP procedure IF EXISTS test2 //

CREATE PROCEDURE test2 ()
BEGIN

DECLARE newname VARCHAR(5);
DECLARE xid INT;

SELECT name,id INTO newname,xid
FROM proce limit 1;
SELECT newname,xid;
END;
//

call test2() //
#<5>
use testprocedure;
CREATE PROCEDURE p1 () SELECT * FROM proce;

call p1();

#<6>注意此处的handler是设置SQLSTATE值,SQLWARNING是对所有以01开头的SQLSTATE代码的速记
#NOT FOUND是对所有以02开头的SQLSTATE代码的速记
#SQLEXCEPTION是对所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE代码的速记
#DECLARE CONTINUE HANDLER声明CONTINUE异常处理
#事实上这里的23000SQLSTATE是更常用的,当外键约束出错或主键约束出错就被调用了。
#当没有发生该23000异常时, select @x2的值将是null,而不是1,
#并且后面的第2个语句执行时将会报主键约束错误,此时@x2=1,@x=4,虽然第2句有了异常,但是后面的语句继续执行
#保存到数据的数据是3,test3和5,test5

use testprocedure;
delimiter //
DROP procedure IF EXISTS handlerdemo
//

create procedure handlerdemo()
begin
declare continue handler for sqlstate '23000' set @x2=1;
set @x=1;
insert into proce values(3,'test3');
set @x=2;
insert into proce values(3,'test4');
set @x=3;
insert into proce values(5,'test5');
set @x=4;
end;
//

call handlerdemo()//

select @x //
select @x2 //

## ************光标****************
#<7>光标必须在声明处理程序之前被声明,并且变量和条件必须在声明光标或处理程序之前被声明
#在这里先声明变量a,b,c,后声明cursor
create procedure curdemo()
begin
declare done int default 0;
declare a char(16);
declare b,c int;
declare cur1 cursor for select id,name from proce;
declare cur2 cursor for select id from proce2;
declare continue handler for sqlstate '02000' set done=1;

open cur1;
open cur2;

repeat
fetch cur1 into b,a;
fetch cur2 into c;
if not done then
if b
insert into proce3 values(b,a);
else
insert into proce3 values(c,a);
end if;
end if;
until done end repeat;

close cur1;
close cur2;
end

## **************** Case *******************
#<8>when ... then ;case ... end case;
delimiter //
DROP procedure IF EXISTS p13
//
create procedure p13(in par1 int)
begin
declare var1 int;
set var1=par1+1;

case var1
when 0 then insert into casetest values(17);
when 1 then insert into casetest values(18);
else insert into casetest values(19);
end case;
end;
//

call p13(-1)//
call p13(0)//
call p13(1)//
call p13(null)//


## **************** while ****************
#<9>while ... do ... end while;为了防止null的错误,set v=0是必须的
delimiter //
DROP procedure IF EXISTS p14
//

create procedure p14()
begin
declare v int;
set v=0;
while v < 5 do
insert into casetest values (v);
set v=v+1;
end while;
end;//

call p14()//

## ***************** repeat *****************
#<10>repeat ...until ... end repeat; 是执行后检查(until v>=5),而while是执行前检查(while v<5)
delimiter //
DROP procedure IF EXISTS p15
//

create procedure p15()
begin
declare v int;
set v=0;
repeat
insert into casetest values(v);
set v=v+1;
until v >=5

end repeat;

end;
//

call p15()//

## ***************** loops *****************
#<11> loop 和while一样不需要初始条件,同时和repeat一样不需要结束条件
# loop_label: loop
# ...
# if .. then
# leave loop_label
# end if
# end loop

delimiter //
DROP procedure IF EXISTS p16
//

create procedure p16()
begin
declare v int;
set v=0;
loop_label: loop
insert into casetest values(v);
set v=v+1;
if v >=5 then
leave loop_label;
end if;
end loop;
end;//

call p16()//

## ***************** Labels *****************
# <12>labels标号; 注意此处的until 0=0后面没有分号“;”
delimiter //
DROP procedure IF EXISTS p17//

create procedure p17()
label_1:begin

label_2:while 0=1 do leave label_2; end while;

label_3:repeat leave label_3;until 0=0 end repeat;

label_4:loop leave label_4; end loop;

end;//

call p17()//

#<13>labels 标号结束符;
delimiter //
DROP procedure IF EXISTS p18//

create procedure p18()
label_1:begin

label_2:while 0=1 do leave label_2; end while label_2;

label_3:repeat leave label_3;until 0=0 end repeat label_3;

label_4:loop leave label_4; end loop label_4;

end label_1;//

call p18()//

#<14>leave和labels 跳出和标号;leave 使程序跳出复杂的语句
delimiter //
DROP procedure IF EXISTS p19//

create procedure p19(par char)

label_1:begin
label_2:begin
label_3:begin

if par is not null then
if par='a' then leave label_1;
else
begin
if par='b' then
leave label_2;
else
leave label_3;
end if;
end;
end if;
end if;

end label_3;
end label_2;
end label_1;

//

call p19('a')//


#<15>iterate迭代,必须用leave;iterate意思是重新开始复合语句,相当于 continue
#该结果中3将不被保存到数据库表中
delimiter //
DROP procedure IF EXISTS p20//

create procedure p20()
begin
declare v int;
set v=0;
loop_label:loop

if v=3 then
set v=v+1;
iterate loop_label;
end if;
insert into casetest values(v);
set v=v+1;

if v>=5 then
leave loop_label;
end if;

end loop loop_label;
end;//

call p20()//


#<16>Grand combination大组合

delimiter //
DROP procedure IF EXISTS p21//

create procedure p21(in par1 int,out par2 int)
language sql deterministic sql security invoker
begin
declare v int;

label goto_label;

start_label:loop
if v=v then
leave start_label;
else
iterate start_label;
end if;
end loop start_label;

repeat
while 1=0 do begin end;
end while;
until v=v
end repeat;

goto goto_label;


end;
//

call p21()//

## **************** trigger ***************************
#<17>
use testprocedure;

CREATE TABLE trig1(a1 int);
CREATE TABLE trig2(a2 int);
CREATE TABLE trig3(a3 int not null AUTO_INCREMENT PRIMARY KEY);

CREATE TABLE trig4(
a4 INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
b4 INT DEFAULT 0
);

insert into trig3(a3) values(null),(null),(null),(null),(null),(null),(null),(null),(null),(null);
insert into trig4(a4) values(0),(0),(0),(0),(0),(0),(0),(0),(0),(0);

delimiter //
DROP trigger trigtest//

create trigger trigtest before insert on trig1
for each row begin
insert into trig2 set a2=NEW.a1;
delete from trig3 where a3=NEW.a1;
update trig4 set b4=b4+1 where a4=NEW.a1;
end;


ALTER PROCEDURE PROC_INSERT_DATA_DETAIL
        @DealerID varchar(50), 
        @FieldName varchar(2000),
        @FieldValue varchar(2000)
    AS
    BEGIN
        DECLARE @Count INT
        DECLARE @StrSQL VARCHAR(2000)
        SET @Count = (SELECT COUNT(*) FROM myDATA_Details WHERE DealerID = @DealerID)

        IF (@COUNT>0)
            BEGIN
                SET @StrSQL = 'UPDATE myDATA_Details SET '+ @FieldName + ' = ''' +@FieldValue + ''' WHERE DealerID = '+ @DealerID
                EXEC(@StrSQL)
            END
        ELSE
            BEGIN
                INSERT INTO myDATA_Details (DealerID) VALUES (@DealerID)
                SET @StrSQL = 'UPDATE myDATA_Details SET '+ @FieldName + ' = ''' +@FieldValue + ''' WHERE DealerID = '+ @DealerID
                EXEC(@StrSQL)
            END
    END



SQL Server存储过程使用剖析
一、SQL Server的存储过程概述
  存储过程(Stored Procedure)是一组已被编辑好的,存储在服务器上的能够执行某种功能的预编译的Transact-SQL代码。它是一种封装重复任务操作的方法,支持用户提供的参数变量,具有强大的编程能力。存储过程通过参数传递、 进行判断、声明变量,以及返回信息来扩充标准SQL 语言的功能。可以把存储过程看成是以数据库
对象形式存储在 SQL Server 中的一段程序或函数。当执行存储过程时,该存储过程是在 SQL Server上运行,而不是在客户端发送请求。存储过程可以是一个简单的 SQL 语句,如 select * from gz。存储过程也可以是由一系列用来对数据库表实现复杂商务规则的SQL 语句和控制流语言语句所组成。存储过程非常类似于DOS系统中批处理文件(*.bat)。在批处理文件中,可以包含一组经常执行的命令,这一组命令可以通过批处理文件的执行而执行。同样的,存储过程也是把要完成某项任务的许多SQL语句写在一起,组成命令集合的形式,然后,通过执行存储过程执行相应的任务。
  存储过程与客户端SQL命令操作的应用程序相比,具有下列优点:
  1.可以大大提高系统效率。对于客户端来说,只要调用已在服务器中存放的存储过程,并通过网络发送该过程名和少量入口参数,数据库服务器就可以执行该过程,在执行完成后只返回结果集数据给客户端应用程序,而无须再在网上传送大量的命令和中间结果数据,这样可以充分利用服务器的高性能来提高运算速度,并减轻网络负担。
  2.提高系统的可维护性。由于存储过程是以代码形式存在的一种数据库对象,所以它的创建和删除都很简单,并且不会影响到库中的其他数据对象。例如在银行报表管理系统中,有些计算规则往往会随着时间和客户要求的改变而改变,如果将这些业务规则的处理直接放到客户应用程序中去做,在规则发生改变时需要修改大量的客户端源程序代码,然后重新进行编译和链接,这样不仅增加了客户程序的维护难度,而且降低了系统的效率。但如果将这些规则放在服务器的存储过程中,而由客户端应用程序来调用此过程,情况将大为改观。当某个业务的计算规则发生变化时,只需要修改或重写对应的服务器存储过程就可以了,从而提高了系统的可维护性。
  3.增强系统的安全性。将存储过程用在安全性上就是利用其“授权”的特性,因为存储过程就像数据库中的其他对象,如“表”和“字段”那样可以被操作,所以只有当一个用户被授予某个权限后,才可以做与自身的权限相符合的动作。
  4.增强SQL语言的功能和灵活性。由于SQL语言自身的限制,它不能声明变量,不能使用if/else结构,不能使用循环语句。存储过程利用流控制语句和内部函数实现了SQL语言本身所不能做到的事,等于从另一个方面提高了SQL语言的功能和灵活性。
  二、存储过程在数据库编程中的应用 
  存储过程的应用具有很大的灵活性,在具体的开发过程中往往可以根据实际需要的不同而采用相应的技术与方法。
  1.存储过程的嵌套调用。有时候可能要对一批数据进行重复的处理,或者在一个存储过程中要用到其他存储过程的处理结果,这时就可以使用存储过程的嵌套调用来实现。下面结合一个用于进行数据统计和分析的具体实例来讲述存储过程嵌套调用的实现方法。首先创建一个用于数据统计的子存储过程,把调用它的父存储过程要传给它的统计条件定义为输入参数,而把父存储过程想要得到的统计结果定义为输出参数,并在子存储过程中计算出它们的值作为返回结果。当然,这个子存储过程也可以作为一个独立的存储过程来使用。建立子存储过程如下:
  CREATE PROCEDURE up_jsszl
    @z**mnoin char(3), // 定义输入参数
    @tjyfin int,
    @ylfszl money output // 定义输出参数
   AS
   BEGIN
  DECLARE @ylfsz money,
    @ylfyz money //声明局部变量
   ... ...
  SELECT @ylfszl = @ylfsz / @ylfyz, // 得到并返回结果
   RETURN
  END
  然后创建一个父存储过程,在父存储过程中通过使用特定的参数值去调用子存储过程来得到相应的统计结果。
  CREATE PROCEDURE up_bjszl
    @z**mnoin char(3), // 定义输入参数
    @tjyfin int
   AS
   BEGIN
  DECLARE 
    @z**mno char(3), // 声明局部变量
    @tjyf int,
    @ylfszl money
  ... ...
  EXECUTE up_jsszl 
    @z**mno, // 调用子存储过程
    @tjyf,
    @ylfszl 
    output
   ... ...
   RETURN
  END
  由此可见,可以将那些实现不同功能的代码模块化,然后通过使用存储过程的嵌套调用将它们组合起来去实现一些更复杂的功能。这样既可以增强代码的可重用性,又可以使整个应用程序的结构更加清晰,而且能够有效地提高应用程序的开发效率和系统的可维护性。
  2.在存储过程中使用游标。游标提供了一种处理结果集中记录的灵活手段,通过它可以根据需要对结果集中的各条记录进行一些相应的处理。在存储过程中需要对数据库中的表或表中满足一定条件的记录集中的各条记录进行处理时就可以利用游标来进行。游标的使用要经过声明游标、打开游标、读取游标、关闭游标和释放游标这五个步骤。下面的存储过程就是通过在满足特定条件的记录集上定义一个游标来逐条处理记录集中的每一条记录的。
  CREATE PROCEDURE up_fxszl
    @z**mnoin char(3), // 定义输入参数
    @tjyfin int
   AS
   BEGIN
  DECLARE @bmno char(3), // 声明局部变量
    @ylfszl money
   ... ...
  DECLARE cur_bmno CURSOR FOR // 声明游标
   SELECT JZSNO FROM JZS WHERE FJNO = @z**mnoin
   OPEN cur_bmno // 打开游标
  FETCH cur_bmno INTO @bmno //用游标取一条记录
  WHILE @@sqlstatus = 0 // 处理记录集中各记录
   BEGIN
  EXECUTE up_jsszl @bmno, // 调用子存储过程
    @tjyfin,
    @ylfszl output
   ... ...
  FETCH cur_bmno INTO @bmno //用游标取记录
   END
  CLOSE cur_bmno // 关闭游标
  DEALLOCATE CURSOR cur_bmno // 释放游标
   ... ...
  END
  在上面的存储过程中声明了一个游标来逐条取得从数据库表中得到的结果集中的记录,并在循环控制语句中将该记录作为入口参数去调用子存储过程来对每一条记录进行处理,然后再对处理结果作进一步的分析。可以看到通过将游标和存储过程调用结合起来,可以很方便地对结果集中的各条记录进行处理。
  3.在存储过程中使用临时表。在使用存储过程时,一般是通过定义输出参数来得到它的计算结果。但是当进行一些复杂的数据处理要求返回的数据量比较大时,如果还是通过输出参数来返回计算结果,则会因为定义的参数过多而使存储过程显得很杂乱,而且在存储过程中对可以定义的参数个数也是有一定限制的。另外,有时可能有多个存储过程要用到一些相同的中间结果,而且连这些中间结果甚至也是由几个存储过程共同产生的。这时使用临时表将会带来很大的便利。在Sybase存储过程中,可以用CREATETABLE命令来创建临时表,并在表名前加上符号“#”作为临时表的标识,然后就可以像使用数据库中其他的表一样使用它。例如:
  CREATE PROCEDURE up_cxszl
    @z**mnoin char(3), // 定义输入参数
    @tjyfin int
   AS
   BEGIN
  DECLARE @bmno char(3), // 声明局部变量
    @ylfszl money
   ... ...
  CREATE TABLE #SFSZLCX (BMNO char(3) null, //创建临时表
    YLFSZL money null)
   ... ...
  DECLARE cur_bmno CURSOR FOR // 声明游标
  SELECT JZSNO FROM JZS WHERE FJNO = @z**mnoin
   OPEN cur_bmno // 打开游标
  FETCH cur_bmno INTO @bmno //用游标取一条记录
  WHILE @@sqlstatus = 0 // 处理记录集中各记录
   BEGIN
  EXECUTE up_jsszl @bmno, // 调用子存储过程
    @tjyfin
   INSERT #SFSZLPX(BMNO, YLFSZL) // 结果存入临时表
   VALUES(@bmno, @ylfszl)
  FETCH cur_bmno INTO @bmno // 用游标取记录
   END
  CLOSE cur_bmno // 关闭游标
  DEALLOCATE CURSOR cur_bmno // 释放游标
   RETURN
  END
  由于临时表的生命周期与创建它的存储过程相同,随着存储过程执行的结束,临时表也就不复存在了。所以如果想要在一个存储过程中利用其他存储过程产生的中间结果,则应该在父过程中创建临时表,在子过程中直接引用它,并把子过程所得的中间结果存入临时表中。
  三、存储过程的应用策略 
  使用存储过程的目的是为了提高应用系统的运行效率,增强系统的可维护性,保证数据的完整性与一致性。下面给出了采用存储过程的一些基本策略:
  1.重复调用的、需要一定运行效率的逻辑与运算处理宜采用存储过程实现。虽然客户端应用程序也能进行这样的逻辑与运算处理,但存储过程的运行效率高。因为它是编译与优化好的过程程序,而客户端应用程序的每个SQL语句都要临时送入数据库服务器进编译和优化执行。如果客户端应用程序包含多条SQL语句,客户端应用程序则要通过网络与数据库服务器多次通信才能完成任务,运行效率进一步降低。
  2.易于变化的业务规则应放入存储过程中。例如,要编写一段计算奖金的处理程序,而奖金的发放办法会经常根据具体情况进行调整,此时应把奖金业务程序写成存储过程,让客户端应用程序调用此过程来得到奖金数据。当计算奖金的办法发生变化时,只需修改存储过程即可,而应用程序不用任何改动,这样增强了应用程序的可维护性。
  3.需要集中管理和控制的逻辑与运算处理应放入存储过程中。存储过程只需在数据库服务器中保存一份拷贝,所有的应用子系统均可调用执行该存储过程,而无须每个应用子系统编写相同的处理逻辑程序,这样也便于应用程序的维护与版本的管理。
  4.存储过程可作为保证系统数据安全性和数据完整性的一种实现机制。例如,一个用户可以被授予权限去调用存储过程执行修改某特定表的行列子集,即使他对该表没有任何其他权限,这样可保证系统数据的安全性。同样,通过特殊类型的存储过程——触发器还可使相关的表数据操作在一起发生,从而维护数据的完整性。
  5.需要对基本表的数据进行较复杂的逻辑处理才能返回所需的结果数据集,应采用存储过程完成。在应用程序开发中,经常会遇到这样的情况,应用程序报表数据、统计分析数据等很难直接从基本数据表处理得到,而需要对基本表进行较复杂的逻辑操作处理或者需要建立若干过渡临时表,才能得到最终的结果数据。如果这种处理操作均在客户端应用程序完成,效率是低下的。相反若采用存储过程实现,诸多问题都可迎刃而解。
  四、结语
  存储过程的功能很强大,它可以根据用户的设置完成从简单的查询到应用程序逻辑控制的任何事情。存储过程在信息管理系统中的应用非常广泛,它为实现复杂的数据应用提供了很好的解决方法。它不仅能提高应用系统的运行效率增强系统的可维护性,还可以保证数据的完整性与可靠性。


SQL Server中“函数”的两种用法
本文主要主要讲解了SQL Server数据库中函数的两种用法,具体内容请参考下文: 

1. 由于update里不能用存储过程,并且由于根据更新表的某些字段还要进行计算。所以很多人采用的是游标的方法,在这里我们可以用函数的方法实现。 

函数部分: 


  CREATE FUNCTION [DBO].[FUN_GETTIME] (@TASKPHASEID INT) 
  RETURNS FLOAT AS 
  BEGIN 
  DECLARE @TASKID INT, 

  @HOUR FLOAT, 
  @PERCENT FLOAT, 
  @RETURN FLOAT 
  IF @TASKPHASEID IS NULL 
  BEGIN 
  RETURN(0.0) 
  END 
  SELECT @TASKID=TASKID,@PERCENT=ISNULL(WORKPERCENT,0)/100 
  FROM TABLETASKPHASE 
  WHERE ID=@TASKPHASEID 
  SELECT @HOUR=ISNULL(TASKTIME,0) FROM TABLETASK 
  WHERE ID=@TASKID 
  SET @RETURN=@HOUR*@PERCENT 
  RETURN (@RETURN) 
  END 


调用函数的存储过程部分: 


  CREATE PROCEDURE [DBO].[PROC_CALCCA] 
  @ROID INT 
  AS 
  BEGIN 
  DECLARE @CA FLOAT 
  UPDATE TABLEFMECA 
  SET 
  Cvalue_M= ISNULL(MODERATE,0)*ISNULL
(FMERATE,0)*ISNULL(B.BASFAILURERATE,0)*[DBO].[FUN_GETTIME](C.ID) 
  FROM TABLEFMECA ,TABLERELATION B,TABLETASKPHASE C 
  WHERE ROID=@ROID AND TASKPHASEID=C.ID AND B.ID=@ROID 
  SELECT @CA=SUM(ISNULL(Cvalue_M,0)) FROM TABLEFMECA WHERE ROID=@ROID 
  UPDATE TABLERELATION 
  SET CRITICALITY=@CA 
  WHERE ID=@ROID 
  END 
  GO 

2. 我们要根据某表的某些记录,先计算后求和,因为无法存储中间值,平时我们也用游标的方法进行计算。但SQL Server 2000里支持。 


SUM ( [ ALL | DISTINCT ] expression )
expression  

是常量、列或函数,或者是算术、按位与字符串等运算符的任意组合。因此我们可以利用这一功能。 


函数部分:
  


CREATE FUNCTION [DBO].[FUN_RATE] (@PARTID INT,
@ENID INT,@SOURCEID INT, @QUALITYID INT,@COUNT INT) 
  
RETURNS FLOAT AS 
  BEGIN 
  DECLARE @QXS FLOAT, @G FLOAT, @RATE FLOAT 
  IF (@ENID=NULL) OR (@PARTID=NULL) OR (@SOURCEID=NULL) OR (@QUALITYID=NULL) 
  BEGIN 
  RETURN(0.0) 
  END 
  SELECT @QXS= ISNULL(XS,0) FROM TABLEQUALITY WHERE ID=@QUALITYID 
  SELECT @G=ISNULL(FRATE_G,0) FROM TABLEFAILURERATE 
  WHERE (SUBKINDID=@PARTID) 
AND( ENID=@ENID) 
AND ( DATASOURCEID=@SOURCEID) 
AND( ( (ISNULL(MINCOUNT,0)<=ISNULL(@COUNT,0)) 
AND ( ISNULL(MAXCOUNT,0)>=ISNULL(@COUNT,0))) 
  OR(ISNULL(@COUNT,0)>ISNULL(MAXCOUNT,0))) 
  SET @RATE=ISNULL(@QXS*@G,0) 
  RETURN (@RATE) 
  END
调用函数的存储过程部分: 


 CREATE PROC PROC_FAULTRATE 
  @PARTID INTEGER, @QUALITYID INTEGER,@SOURCEID INTEGER,
@COUNT INTEGER, @ROID INT, @GRADE INT,@RATE FLOAT=0 OUTPUTAS 
  BEGIN 
  DECLARE 
  @TASKID INT 
  SET @RATE=0.0 
  SELECT @TASKID=ISNULL(TASKPROID,-1) FROM TABLERELATION 
WHERE ID=(SELECT PID FROM TABLERELATION WHERE ID=@ROID) 
  IF (@TASKID=-1) OR(@GRADE=1) BEGIN 
  SET @RATE=0 
  RETURN 
  END 
  SELECT @RATE=SUM([DBO].[FUN_RATE] 
(@PARTID,ENID,@SOURCEID, @QUALITYID,@COUNT) *ISNULL(WORKPERCENT,0)/100.0) 
  FROM TABLETASKPHASE 
  WHERE TASKID=@TASKID 
  END 
  GO 


LEFT JOIN操作用于在任何的 FROM 子句中,组合来源表的记录。使用 LEFT JOIN 运算来创建一个左边外部联接。左边外部联接将包含了从第一个(左边)开始的两个表中的全部记录,即使在第二个(右边)表中并没有相符值的记录。 

语法:
FROM table1 LEFT JOIN table2 ON table1.field1 compopr table2.field2 

说明:
① table1, table2参数用于指定要将记录组合的表的名称。
② field1, field2参数指定被联接的字段的名称。且这些字段必须有相同的数据类型及包含相同类型的数据,但它们不需要有相同的名称。
③ compopr参数指定关系比较运算符:"=", "<", ">", "<=", ">=" 或 "<>"。
④ 如果在INNER JOIN操作中要联接包含Memo 数据类型或 OLE Object 数据类型数据的字段,将会发生错误。
三.相关的复杂的解释和实例
简介: 外部连接和自联接 inner join(等值连接) 只返回两个表中联结字段相等的行 left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录 on 指定表间联结字段及其关系的等号 "=" 表达式, 返回 true 或 false. 当表达式返回 true 时, 则查询中包含该记录. ! 外部连接只能操作已存在于数据库中的数据
update (ctarticle as a left join ctclass as c on a.classid = c.classid) left join cttag as b on a.articleid = b.articleid 
set tag=tag+' ', b.articleid=a.articleid, b.classid=a.classid, b.nclassid=a.nclassid 
where a.classid=23 and a.nclassid=0 and tagid is not null 

update (ctarticle as a left join (ctnclass as c left join ctclass as d on c.classid = d.classid) on a.nclassid = c.nclassid and a.classid = c.classid) left join cttag as b on a.articleid = b.articleid set tag=d.class+' '+c.nclass, b.articleid=a.articleid, b.classid=a.classid, b.nclassid=a.nclassid where a.classid=23 and a.nclassid=197; 

更新操作 
左连接中数据的筛选 
insert into cttag(articleid,classid,nclassid) select a.articleid,a.classid,a.nclassid from ctarticle a left join cttag b on a.articleid=b.articleid where b.articleid is null 

//本语句功能为, 显示主表的全部内容, 插入数据到副表中没有的数据 
//主要作用为: 让数据减少冗余 

上例中的延续 
select a.*, b.*, c.*, d.* from cttag as d left join ((ctarticle as a left join ctclass as b on a.classid=b.classid) left join ctnclass as c on a.nclassid=c.nclassid) on d.articleid=a.articleid; 

显示文章表中的全部, 调用类别表中的栏目 
select a.*, b.*, c.* from (ctarticle a left join ctclass b on a.classid=b.classid) left join ctnclass c on a.nclassid=c.nclassid 

//作用, 有时在文章表中包含了在个别类别表中没有的数据, 用这个语法可以读出文章表的全部数据 
//a 为 文章表, b 为主类别, c 为子类别 

同上例, 选择追加数据时加上空格 
insert into cttag(articleid,classid,nclassid,tag) select a.articleid,a.classid,a.nclassid,d.class+' '+c.nclass 
from (ctarticle as a left join (ctnclass c left join ctclass d on c.classid=d.classid) on a.classid=c.classid and a.nclassid=c.nclassid) left join cttag as b on a.articleid = b.articleid where a.classid=4 and a.nclassid=154; 

连接n个表, 并追加数据到其中一个表, n=4 
insert into cttag(articleid,classid,nclassid,tag) select a.articleid,a.classid,a.nclassid,d.class+c.nclass 
from (ctarticle as a left join (ctnclass c left join ctclass d on c.classid=d.classid) on a.classid=c.classid and a.nclassid=c.nclassid) left join cttag as b on a.articleid = b.articleid where a.classid=1 and a.nclassid=1; 

//解读 
插入到 表2(栏1,栏2,栏3,栏4) 
选择 别名a.栏1, 别名a.栏2, 别名a.栏3, 别名d.栏4 加上 别名c.栏5 
从 (表1 别名a 左连接 (表3 别名c 左连接 表4 别名d 在 别名c.栏2 等于 别名d.栏2) 在 别名a.栏2 等于 别名c.栏2 和 别名a.栏3=别名c.栏3) 左连接 表2 别名b 在 别名a.栏1 等于 别名b.栏1 在那里 别名a.栏2=1 和 别名a.栏3=1 

连接两个表, 并追加数据到其中一个表 
insert into cttag(articleid,classid,nclassid) 
select a.articleid,a.classid,a.nclassid 
from ctarticle as a left join cttag as b on a.articleid = b.articleid where a.classid=1 and a.nclassid=1; 

//解读 
插入到 表2(栏1,栏2,栏3) 
选择 别名a.栏1, 别名a.栏2, 别名a.栏3 
从 表1 别名a 左连接 表2 别名b 在 别名a.栏1 等于 别名b.栏1 在那里 别名a.栏4=1 和 别名a.栏5=1 

左连接 

同步两表的数据 
update ctarticle a inner join cttag b on a.articleid = b.articleid set b.classid=a.classid, b.nclassid=a.nclassid; 

//解读 
更新 表1 别名a 联接 表2 别名2 在 别名a.栏1 等于 别名b.栏1 设置 别名b.栏2 更新为 别名a.栏2, 别名b.栏3 更新为 别名a.栏3 

右外连接 
select a.*, b.* from bunclass a right join ctclass b on a.classid=b.classid where a.nclassid=20 

查询别名 a,b 表, 只匹配 b 表中的内容. 

添加数据到连接表之一 
insert into cttag ( tag, articleid ) select top 1 b.tag, a.articleid from ctarticle as a left join cttag as b on a.articleid = b.articleid where a.articleid order by a.articleid desc; 

变通中的用法二 
insert into bureply 
select b.*, a.classid, a.nclassid 
from article as a inner join reply as b on a.articleid = b.articleid 
where classid=50; 

实际应用中的变通 
insert into butag ( tag, articleid, classid, nclassid) 
select b.tag, a.articleid, a.classid, a.nclassid 
from article as a inner join tag as b on a.articleid = b.articleid 
where classid=24; 


添加数据到其他表 
insert into butag ( tag, articleid ) 
select b.tag, a.articleid 
from article as a inner join tag as b on a.articleid = b.articleid 
where a.articleid<>false; 

//解读 
添加到 接收表(列1,列2) 
选择 别名b.列1, 别名a.列2 
从 表1 表名a 联接 表2 表名b 在 别名a.列c 等于 别名b.列c 
在哪里 别名a.列c 不等于 没有 

实际应用中的变通 
select b.tag, a.articleid, a.classid, a.nclassid 
from article as a inner join tag as b on a.articleid = b.articleid 
where a.classid=24; 

查询 
select b.tag, a.articleid 
from article as a inner join tag as b on a.articleid = b.articleid 
where a.articleid<>false; 

//解读 
选择 别名b.列, 别名a.列 
从 表1 别名a 联接 表2 别名b 在 别名a.列c = 别名b.列c 
在哪里 别名a.列c 不等于 没有 
注: as 不是必要
 
原创粉丝点击