PLSQL编程基础
作者:互联网
PLSQL编程
针对于数据库的数据批量操作所使用的一类语言
-
强类型语言:定义变量的时候必须同时定义变量的数据类型
-
弱类型语言:定义变量时不必定义变量的数据类型,变量的数据类型由变量的值决定,无需进行数据的类型转换(shell、javascript)
变量
- 代码格式
declare //声明区域
begin
//书写PLSQL语句
end;
- 代码示例
set serveroutput on; //将信息打印在Developer窗口中
declare //变量定义赋值区
sname VARCHAR2(20) :='lss'; //赋值符号为 :=
begin
-- 书写PLSQL语句
dbms_output.put_line(sname||'like 111');
end;
- 变量赋值
set serveroutput on;
DECLARE
sname varchar2(20) DEFAULT 'jerry'; //变量和值的关系必须一对一,变量和返回值的类型也要注意匹配
BEGIN
SELECT ename INTO sname FROM scott.emp WHERE empno=7934;
dbms_output.put_line(sname);
END;
- 复制scott用户的表emp,如果当先用户下没有emp表,则读取时需要加其所属用户前缀或者直接复制该表到当前用户。
// 不加scott前缀,可以复制表emp。
create table emp as select * from scott.emp
//只复制表的结构,不复制表内容(1=2返回一个布尔值,永远都是false,即表中所有内容都不会满足1=2,就能过滤表中的所有内容只复制表结构)
create table emp as select * from scott.emp where 1=2
- 常量声明(常量的值不能在后续语句中更改)
set serveroutput on; //将此代码的结果在Developer窗口中显示
DECLARE
pi CONSTANT number :=3.14; //常量声明和赋值
r number DEFAULT 3; //default的作用等同于 :=
area number;
BEGIN
area := pi * r * r;
dbms_output.put_line(area);
END;
- 打印emp表中empno=7934的所有数据
set serveroutput on;
DECLARE
myemp EMP%ROWTYPE //myemp用于存放emp表的一行数据,即将一行数据导入myemp中
BEGIN
SELECT * INTO myemp FROM emp WHERE empno=7934;
dbms_output.put_line(myemp.ename);////打印数据要声明该行数据的字段值,即前缀
END;
- 打印姓名为SMITH的用户的薪水和部门编号
set serveroutput on;
DECLARE
myemp EMP%ROWTYPE; //直接使用emp表中ROW的类型
BEGIN
SELECT * INTO myemp FROM emp WHERE ename='SMITH';
dbms_output.put_line(||'薪水为:'||myemp.sal||',部门为:'||myemp.deptno);
END;
- 将emp中empno=7934的薪水全部加3000后打印
set serveroutput on;
DECLARE
sal emp.sal%TYPE;
mysal number(4):=3000;
totalsal mysal%TYPE; //totalsal和已定义变量mysal 使用相同的数据类型
BEGIN
SELECT sal INTO sal FROM emp WHERE empno=7934;
totalsal:=sal+mysal;
dbms_output.put_line(totalsal);
END;
select * from emp
控制语句
- 代码示例 查看miller的薪水,当薪水大于1500时,加2000,否则扣12000
set serveroutput on;
DECLARE
msal emp.sal%TYPE;
BEGIN
SELECT sal INTO msal FROM emp WHERE ename ='MILLER';
IF msal>1500 THEN
update emp set sal=sal+2000 where ename='MILLER';//要更新数据到表中
commit; //每次都要提交到数据库
ELSE
update emp set sal=sal-1200 where ename='MILLER';
commit;
END IF;
END;
- if else 语句
DECLARE
jsal emp.sal%TYPE;
davg jsal%TYPE;
BEGIN
SELECT sal INTO jsal FROM emp WHERE ename ='JAMES';
selsct avg(sal) into davg from emp where deptno =
(select deptno from emp where ename='JAMES');
IF jsal<davg THEN
update emp set sal=sal-100 where ename='MILLER';
commit;
ELSE jsal>davg then
update emp set sal=sal-50 where ename='KING';
commit;
else
update emp set sal=sal-500;
commit;
END IF;
END;
- case 语句
set serveroutput on;
declare
v_grade char(1) :='A';
begin
case v_grade
when 'A' then
dbms_output.put_line('E');
when 'B' then
dbms_output.put_line('G');
when 'C' then
dbms_output.put_line('f');
when 'D' then
dbms_output.put_line('N');
end case;
end;
- 循环语句
declare
counter number(3) := 0;
sum number := 0;
begin
loop
counter := counter +1;
sum := sum + counter;
if counter >= 100 then
exit;
end if;
end loop;
dems_outbut.put_line('result is:' || sum);
end;
- while语句
set serveroutput on;
DECLARE
counter number(3) := 0;
sumResult number := 0;
BEGIN
WHILE counter < 100 LOOP
counter := counter + 1;
sumResult := sumResult + counter;
END LOOP;
dbms_output.put_line('result is :' || sumResult);
END;
- 借助循环打印emp表中所有人的ename、和sal
set serveroutput on;
declare
emp_row EMP%ROWTYPE;
v_count number(2);
v number(2):=0;
begin
--总行数决定循环的次数
select count(*) into v_count from emp;
LOOP
v:=v+1;
select empno,ename,job,sal into emp_row from
(select rownum r,e.* from emp e)a where a.r=v;
dbms_output.put_line(emp_row.ename||'--'||emp_row.sal);
exit when v=v_count;
END LOOP;
END;
触发器
触发器是一种在事件发生时隐式地自动执行的PL/SQL块,不能接受参数,不能被显式调用。
根据触发器所创建的语句及所影响的对象的不同,可以分为三类。
1、DML触发器
对数据进行DML语句操作(insert、update、delete)时会触发的触发器,分为以下几种。
语句级触发器(只触发一次,与语句)
语句级触发器则只触发一次,与语句所影响到的行数无关。
行级触发器
行级触发器会对数据库表中的受影响的每一行触发一次触发器代码。
before触发器或after触发器
before触发器在触发事件发生之前执行触发器代码, after触发器则在触发事件发生之后执行。
- 语法与格式
create [or replace] trigger trigger_name --触发器名称
{before | after}
trigger_event -- 触发条件
on table_name -- 该触发器针对于哪一张表
[for each row]
[when trigger_condition]
trigger_body
- trigger_name:触发器名称
- before | after:指定触发器是在触发事件发生之前触发还暗示发生之后触发
- trigger_event:触发事件,在DML触发器中主要为insert、update、delete等
- table_name:表名,表示发生触发器作用的对象
- for each row:指定创建的是行级触发器,若没有该子句则创建的是语句级触发器
- when trigger_condition:添加的触发条件
- trigger_body:触发体,是标准的PL/SQL语句块
2、替代触发器(instead of触发器)
对视图进行操作时定义的触发器,替代触发器只能定义在视图上
- 语法与格式
create [or replace] trigger trigger_name --触发器名称
instead of trigger_event --触发事件
on view_name --视图名称
for each row --替代触发器必须指定为行级的触发器
[when trigger_condition] --触发条件
trigger_body --触发体,PL/SQL块
3、系统事件触发器
对数据库实例或某个用户模式进行操作时定义的触发器,可以分为:数据库系统触发器和用户触发器
- DML触发器代码示例
//触发器基于student和stu_log表,所以先创表
create table STUDENT
(
id NUMBER(19), --id
stu_no VARCHAR2(20), --学号
stu_name VARCHAR2(32), --姓名
stu_age NUMBER, --年龄
stu_major VARCHAR2(32) --专业
)
tablespace lssspace;
create table STU_LOG
(
log_id NUMBER, --日志id
log_action VARCHAR2(100), --操作名称
log_date DATE, --操作时间
log_message VARCHAR2(32) --
)
tablespace lssspace;
-- 创建序列
create sequence seq_test
start with 1
nomaxvalue
nominvalue
nocycle
nocache
- before触发器(行级触发器)
create or replace trigger modify_stu before insert on student for each row //触发条件
declare
next_id number;
begin
select seq_test.nextval into next_id from dual;
:new.id :=next_id;
-- :new是即将插入的语句。将插入语句中的主键值替换为从序列中取出的值next_id;
end;
- 创建成功后,按照要求插入一条数据,但不插入ID,查询表数据时表内会自动生成id
insert into student(stu_no,stu_name,stu_age,stu_major)
values('NO1','张三',20,'中文系');
创建触发器时当前用户如果是普通用户,则需要管理员用户赋值:grant create trigger to 用户名
- after触发器(行级触发器) 将对student表的操作都记录到stu_log表中(update of 用于指定一个或多个字段,指定字段被更新时才会触发触发器)
create or replace trigger modify_stu1
after insert or delete or update of stu_name
on student
for each row --触发条件
begin
if inserting then --操作为插入数据时
insert into stu_log values(1,'insert',sysdate,:new.stu_name);
elsif deleting then --操作为删除数据时
insert into stu_log values(2,'delete',sysdate,:old.stu_name);
elsif updating then
insert into stu_log values(3,'update_old',sysdate,:old.stu_name);
insert into stu_log values(4,'update_new',sysdate,:new.stu_name);
end if;
end;
--操作表数据查看触发器效果
insert into student values(3,'NO3','李四',23,'java系');
-- 查看修改记录
select * from stu_log
- 创建语句触发器
控制对表的修改
create or replace trigger modify_stu2
before insert or update or delete on student
begin
if deleting then
raise_application_error(-20001,'该表不允许删除数据');
-- 自定义错误提示 前者是错误编号,后者是错误信息
-- 当提示错误时,DML操作会被拦截,不会被执行
elsif updating then
raise_application_error(-20002,'该表不允许修改数据');
elsif inserting then
raise_application_error(-20003,'该表不允许插入数据');
end if;
end;
当插入、修改和删除数据时,会出现表中的自定义错误提示,保证表的安全性。
游标
游标属性
- Cursor_name%FOUND:布尔型属性,当最近一次提取游标操作FETCH,成功则为 TRUE,否则为FALSE;
- Cursor_name%NOTFOUND:布尔型属性,与%FOUND相反;
- Cursor_name%ISOPEN:布尔型属性,当游标已打开时返回 TRUE;
- Cursor_name%ROWCOUNT:数字型属性,返回已从游标中读取的记录数。
显式游标
- 查询前10名员工的信息。
DECLARE
CURSOR c_cursor
IS
SELECT ename, sal
FROM emp
WHERE rownum<11;
v_ename emp.ename%TYPE;
v_sal emp.sal%TYPE;
BEGIN
-- 打开游标
OPEN c_cursor;
FETCH c_cursor INTO v_ename, v_sal;
-- 只要指针在移动的过程中 指向的数据不为空
-- 循环就继续
WHILE c_cursor%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(v_ename||'---'||v_sal );
FETCH c_cursor INTO v_ename, v_sal;
END LOOP;
CLOSE c_cursor;
END;
游标的for循环(隐式游标)
DECLARE
CURSOR c_sal IS SELECT empno, ename , sal
FROM emp ;
BEGIN
--隐含打开游标
FOR v_sal IN c_sal LOOP
--隐含执行一个FETCH语句
DBMS_OUTPUT.PUT_LINE(to_char(v_sal.empno)||'---'|| v_sal.ename||'---'||to_char(v_sal.sal)) ;
--隐含监测c_sal%NOTFOUND
END LOOP;
--隐含关闭游标
END;
-- 查询所有员工的数据,将所有比平均工资大于等于的员工的sal-500 将所有比平均工资低的员工+50
declare
cursor c_cur
is
select empno,sal from emp;
avg_sal emp.sal%type;
begin
select avg(sal) into avg_sal from emp;
-- 这里的v_sal是在for循环中所定义的临时变量
-- 该变量的类型是rowtype类型,存储的是游标读取到的
-- 一行记录
for v_sal in c_cur loop
if v_sal.sal>=avg_sal then
update emp set sal=sal-500 where empno=v_sal.empno;
commit;
else
update emp set sal=sal+50 where enmpno=v_sal.empno;
commit;
end if;
end loop;
end;
- 给工资低于1200 的员工增加工资50
declare
cursor c_cur is select empno,sal from emp;
v_empno emp.empno%type;
v_sal emp.sal%type;
begin
open c_cur;
loop
fetch c_cur into v_empno,v_sal;
EXIT WHEN c_cur%NOTFOUND;
if v_sal<1200 then
update emp set sal=sal+50 where empno=v_empno;
commit;
close c_cur;
end loop;
end;
DECLARE
v_empno emp.empno%TYPE;
v_sal emp.sal%TYPE;
CURSOR c_cursor IS SELECT empno, sal FROM emp;
BEGIN
OPEN c_cursor;
LOOP
FETCH c_cursor INTO v_empno, v_sal;
EXIT WHEN c_cursor%NOTFOUND;
IF v_sal<=1200 THEN
UPDATE emp SET sal=sal+50 WHERE empno=v_empno;
DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'工资已更新!');
END IF;
DBMS_OUTPUT.PUT_LINE('记录数:'|| c_cursor %ROWCOUNT);
END LOOP;
CLOSE c_cursor;
END;
存储过程
对PL/SQL代码的封装 存储过程可以存储在服务器端
GRANT CREATE PROCEDURE to scott;
//赋权限
- 统计emp表的雇员总人数
CREATE OR REPLACE PROCEDURE EMP_COUNT
AS
V_TOTAL NUMBER(10);
BEGIN
SELECT COUNT(*) INTO V_TOTAL FROM EMP;
DBMS_OUTPUT.PUT_LINE('雇员总人数为:'||V_TOTAL);
END;
SET SERVEROUTPUT ON;
execute EMP_COUNT;
SET SERVEROUTPUT ON;
BEGIN
EMP_COUNT;
END;
- 编写显示雇员信息的存储过程EMP_LIST,并引用EMP_COUNT存储过程。 存储过程可以直接调用 也可以在plsql 或者其他存储过程中进行调用
CREATE OR REPLACE PROCEDURE EMP_LIST
AS
CURSOR
emp_cursor
IS
SELECT empno,ename FROM emp;
BEGIN
FOR Emp_record IN emp_cursor
LOOP
DBMS_OUTPUT.PUT_LINE
(Emp_record.empno||'-'||Emp_record.ename);
END LOOP;
EMP_COUNT;
END;
参数传递
- IN:定义一个输入参数变量,用于传递参数给存储过程
- OUT:定义一个输出参数变量,用于从存储过程获取数据
- IN OUT:定义一个输入、输出参数变量,兼有以上两者的功能
-编写给雇员增加工资的存储过程CHANGE_SALARY,通过IN类型的参数传递要增加工资的雇员编号和增加的工资额。
-- 存储过程的参数 输入参数,如果在存储过程调用的时候没有提供参数值,则参数值默认为7788
CREATE OR REPLACE PROCEDURE
CHANGE_SALARY(P_EMPNO IN NUMBER DEFAULT 7788,
P_RAISE IN NUMBER DEFAULT 10)
-- p_empno:=7900; p_raise:100;
AS
V_ENAME VARCHAR2(10);
V_SAL NUMBER(5);
BEGIN
SELECT ENAME,SAL INTO V_ENAME,V_SAL FROM EMP
WHERE EMPNO=P_EMPNO;
UPDATE EMP SET SAL=SAL+P_RAISE WHERE EMPNO=P_EMPNO;
DBMS_OUTPUT.PUT_LINE
('雇员'||V_ENAME||'的工资被改为'||
TO_CHAR(V_SAL+P_RAISE));
COMMIT;
EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE
('发生错误,修改失败!');
ROLLBACK;
END;
set serveroutput on;
execute change_salary(7900,100);
- 使用OUT类型的参数返回存储过程的结果.
CREATE OR REPLACE PROCEDURE
EMP_COUNT(P_TOTAL OUT NUMBER)
AS
BEGIN
-- 由存储过程给参数赋值
SELECT COUNT(*) INTO P_TOTAL FROM EMP;
END;
- IN 和 OUT的区别
DECLARE
V_EMPCOUNT NUMBER;
BEGIN
EMP_COUNT(V_EMPCOUNT);
-- 参数类型为out的参数 用来接受存储过程的运算结果,调用时 会把运算结果赋予给调用参数
-- V_EMPCOUNT:=P_TOTAL;
DBMS_OUTPUT.PUT_LINE('雇员总人数为:'||V_EMPCOUNT);
END;
- 使用IN OUT类型的参数,给电话号码增加区码.
CREATE OR REPLACE PROCEDURE
ADD_REGION(P_PHONE_NUM IN OUT VARCHAR2)
AS
BEGIN
P_PHONE_NUM:='0755-'||P_PHONE_NUM;
END;
set serveroutput on;
DECLARE
V_PHONE_NUM VARCHAR2(15);
BEGIN
V_PHONE_NUM:='26731092';
ADD_REGION(V_PHONE_NUM);
-- 将v_phone_num的值传递给存储过程
-- P_PHONE_NUM值为'0755-26731092'
-- V_PHONE_NUM=P_PHONE_NUM
DBMS_OUTPUT.PUT_LINE('新的电话号码:'||V_PHONE_NUM);
END;
函数
- 格式
CREATE [OR REPLACE] FUNCTION
RETURN 数据类型
[说明部分]
BEGIN
可执行部分
RETURN (表达式) -- 当运行到return表达式的时候,函数会立即停止运行
END [函数名];
- 参数是可选的,但只能是IN类型
- 在定义部分的RETURN数据类型,用来表示函数的数据类型,也就是返回值的类型,此部分不可省略。
- 在可执行部分的RETURN(表达式),用来生成函数的返回值,其表达式的类型应该和定义部分说明的函数返回值的数据类型一致。
- 在函数的执行部分可以有多个RETURN语句,但只有一个RETURN语句会被执行,一 旦执行了RETURN语句,则函数结束并返回调用环境。
- 一个存储函数在不需要时可以删除,但删除的人应是函数的创建者或者是拥有DROP ANY PROCEDURE系统权限的人。其语法如下:
DROP FUNCTION 函数名;
- 重新编译一个存储函数时,编译的人应是函数的创建者或者拥有ALTER ANY PROCEDURE系统权限的人。重新编译一个存储函数的语法如下:
ALTER PROCEDURE 函数名 COMPILE;
- 函数的调用者应是函数的创建者或拥有EXECUTE ANY PROCEDURE系统权限的人,或是被函数的拥有者授予了函数执行权限的账户。
- 函数的引用和存储过程不同,函数要出现在程序体中,可以参加表达式的运算或单独出现在表达式中,其形式为:变量名:=函数名(...)
- 创建一个通过雇员编号返回雇员名称的函数GET_EMP_NAME。
CREATE OR REPLACE FUNCTION GET_EMP_NAME
(P_EMPNO NUMBER DEFAULT 7788) -- 参数只能是in类型
RETURN VARCHAR2 -- 声明返回值的数据类型
AS
V_ENAME VARCHAR2(10);
BEGIN
SELECT ENAME INTO V_ENAME FROM EMP WHERE EMPNO=P_EMPNO;
RETURN(V_ENAME);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('没有该编号雇员!');
RETURN (NULL);
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('有重复雇员编号!');
RETURN (NULL);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('发生其他错误!');
RETURN (NULL);
END;
set serveroutput on;
declare
v_ename emp.ename%type;
begin
-- 用v_ename去接受存储函数通过return返回的运算结果
v_ename:=GET_EMP_NAME(7900);
dbms_output.put_line(v_ename);
end;
- 创建一个通过部门编号返回部门名称的存储函数GET_DEPT_NAME。
CREATE OR REPLACE FUNCTION GET_DEPT_NAME
(P_DEPTNO NUMBER DEFAULT 10)
RETURN VARCHAR2
AS
V_DNAME VARCHAR2(10);
BEGIN
SELECT DNAME INTO V_DNAME FROM DEPT WHERE DEPTNO=P_DEPTNO;
RETURN(V_ENAME);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('没有该编号部门!');
RETURN (NULL);
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('有重复部门编号!');
RETURN (NULL);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('发生其他错误!');
RETURN (NULL);
END;
分页
截取部分查询结果显示在页面中
定义一个存储过程 提供page 和pagecount这两个参数,在页面中显示指定页数的表记录。 确定每一页有多少行 pagecount 数据从哪一页开始显示
页数 | 起始行 | 从第几行到第几行 |
---|---|---|
1 | 1 | (0-5] |
2 | 6 | (5-10] |
3 | 11 | (10-15] |
rowstart=(page-1)*pagecount+1
rowcount 最后一页第十页 46-50
select empno,ename,job,mgr,hiredate,sal,comm,deptno
from (select rownum r,e.* from emp e) where r>rowstart and r<=rowstart+5;
首页 上一页 下一页 尾页
首页 rowstart=0;
下一页 rowstart=rowstart+5;
if rowstart>rowcount rowstart=rowcount
上一页 rowstart=rowstart-5;
if rowstart<0 then rowstart = 0;
select empno,ename,job,mgr,hiredate,sal,comm,deptno
into v_emp_row from
(select rownum r,e.* from emp e)
where r=2;
create or replace procedure fenye
(page in number default 1,pagecount in number default 5)
as
counter number(2):=0;--计数器 控制生成几个查询语句
rowstart number(3):=0; -- 起始行
v_emp_row emp%rowtype;-- 只能存储一行
begin
-- 算出了起始行前一行
rowstart:=(page-1)*pagecount;
loop
-- 控制循环运行pagecount次
counter:=counter+1;
exit when counter>pagecount;
rowstart:=rowstart+1;
select empno,ename,job,mgr,hiredate,sal,comm,deptno
into v_emp_row from
(select rownum r,e.* from emp e)
where r=rowstart;
dbms_output.put_line(v_emp_row.empno||'-'||v_emp_row.ename);
end loop;
end;
标签:ename,触发器,END,PLSQL,sal,--,编程,基础,emp 来源: https://www.cnblogs.com/arica/p/16490576.html