数据库
首页 > 数据库> > PLSQL编程基础

PLSQL编程基础

作者:互联网

PLSQL编程

针对于数据库的数据批量操作所使用的一类语言

变量
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。
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;

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;

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;

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

控制语句
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;
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;
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;
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;

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、系统事件触发器

对数据库实例或某个用户模式进行操作时定义的触发器,可以分为:数据库系统触发器和用户触发器


//触发器基于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
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;
insert into student(stu_no,stu_name,stu_age,stu_major)
 values('NO1','张三',20,'中文系'); 

创建触发器时当前用户如果是普通用户,则需要管理员用户赋值:grant create trigger to 用户名


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:数字型属性,返回已从游标中读取的记录数。

显式游标
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;
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;  
//赋权限
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;
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; 

参数传递

-编写给雇员增加工资的存储过程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);

CREATE OR REPLACE PROCEDURE
EMP_COUNT(P_TOTAL OUT NUMBER)
AS
BEGIN
-- 由存储过程给参数赋值
SELECT COUNT(*) INTO P_TOTAL FROM EMP;
END;
DECLARE 
V_EMPCOUNT NUMBER;
BEGIN 
EMP_COUNT(V_EMPCOUNT);
-- 参数类型为out的参数 用来接受存储过程的运算结果,调用时 会把运算结果赋予给调用参数
-- V_EMPCOUNT:=P_TOTAL;
DBMS_OUTPUT.PUT_LINE('雇员总人数为:'||V_EMPCOUNT);
END;  

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 [函数名]; 
DROP FUNCTION 函数名; 
ALTER PROCEDURE 函数名 COMPILE;

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;

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