TYPE array_name IS VARRAY(size) OF element_type [NOT NULL];
范例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
DECLARE --定义一个最多保存5个VARCHAR(25)的数据类型成员的VARRAY数据类型 TYPE reg_varray_type IS VARRAY(5) OFVARCHAR(25); --声明一个该VARRAY的数据类型的变量 v_reg_varray_type REG_VARRAY_TYPE;
DECLARE v_empno emp.empno%TYPE :=$no; Type t_record is record ( v_name emp.ename%TYPE, v_sal emp.sal%TYPE, v_date emp.hiredate%TYPE ); Rec t_record; BEGIN SELECT ename,sal,hiredate INTO Rec FROM emp WHERE empno = v_empno; DBMS_PUTOUT.PUT_LINE(Rec.v_name||'---'||Rec.v_sal||'--'||Rec.v_date)
1.6 %ROWTYPE
使用%ROWTYPE操作符,返回一个记录类型,其数据类型和数据库表的数据结构一致。
1 2 3 4 5 6 7 8
DEcLARE v_empno emp.empno%TYPE; rec emp%ROWTYPE; BEGIN SELECT*INTO rec FROM emp WHERE emp,empno = v_empno; DBMS_OUTPUT.PUT_LINE('姓名:'||rec.ename||'工资:'||rec.sal||'工作时间:'||rec.hiredate); END;
DECLARE TYPE dept_table_type ISTABLEOF dept%TYPE INDEX BY BINARY_INTEGER; my_dname_table dept_table_type; v_count NUMBER(2) :=4; v_i NUMBER(2); BEGIN FOR i in1 .. v_count LOOP SELECT*INTO my_dname_table(i) FROM dept WHERE deptno = i ; END LOOP; FORintin my_dname_table.FIRST .. my_dname_table.LAST LOOP DBMS_OUTPUT.PUT_LINE('Department number: '||my_dname_table(int).deptno); DBMS_OUTPUT.PUT_LINE('Department name: '|| my_dname_table(int).dname); END LOOP; END;
DECLARE v_empno employees.employee_id%TYPE :=&empno; V_salary employees.salary%TYPE; V_comment VARCHAR2(35); BEGIN SELECT salary INTO v_salary FROM employees WHERE employee_id = v_empno; IF v_salary <1500THEN V_comment:='太少了,加点吧~!'; ELSIF v_salary <3000THEN V_comment:='多了点,少点吧~!'; ELSE V_comment:='没有薪水~!'; END IF; DBMS_OUTPUT.PUT_LINE(V_comment); exception when no_data_found then DBMS_OUTPUT.PUT_LINE('没有数据~!'); when others then DBMS_OUTPUT.PUT_LINE(sqlcode ||'---'|| sqlerrm); END;
--ex1 DECLARE CURSOR c_cursor ISSELECT first_name || last_name, Salary FROM EMPLOYEES WHERE rownum<11; v_ename EMPLOYEES.first_name%TYPE; v_sa- EMPLOYEES.Salary%TYPE; BEGIN OPEN c_cursor; FETCH c_cursor INTO v_ename, v_sal; WHILE c_cursor%FOUND LOOP DBMS_OUTPUT.PUT_LINE(v_ename||'---'||to_char(v_sal) ); FETCH c_cursor INTO v_ename, v_sal; END LOOP; CLOSE c_cursor; END;
-- ex2 DECLARE DeptRec DEPARTMENTS%ROWTYPE; Dept_name DEPARTMENTS.DEPARTMENT_NAME%TYPE; Dept_loc DEPARTMENTS.LOCATION_ID%TYPE; CURSOR c1 IS SELECT DEPARTMENT_NAME, LOCATION_ID FROM DEPARTMENTS WHERE DEPARTMENT_ID <=30; CURSOR c2(dept_no NUMBER DEFAULT10) IS SELECT DEPARTMENT_NAME, LOCATION_ID FROM DEPARTMENTS WHERE DEPARTMENT_ID <= dept_no; CURSOR c3(dept_no NUMBER DEFAULT10) IS SELECT*FROM DEPARTMENTS WHERE DEPARTMENTS.DEPARTMENT_ID <=dept_no; BEGIN --打开游标c1 OPEN c1; --开始循环,这里是为了循环游标c1里的所有列 LOOP --提取游标c1的成员的值放入变量dept_name和dept_loc中 FETCH c1 INTO dept_name, dept_loc; --判断最近一次FETCT的c1是否存在,不存咋退出,结束循环。 EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE(dept_name||'---'||dept_loc); END LOOP; --关闭游标 CLOSE c1;
OPEN c2; LOOP FETCH c2 INTO dept_name, dept_loc; EXIT WHEN c2%NOTFOUND; DBMS_OUTPUT.PUT_LINE(dept_name||'---'||dept_loc); END LOOP; CLOSE c2;
--游标c3可设置自定义变量,来控制游标的值 OPEN c3(dept_no =>20); LOOP FETCH c3 INTO deptrec; EXIT WHEN c3%NOTFOUND; DBMS_OUTPUT.PUT_LINE(deptrec.DEPARTMENT_ID||'---'||deptrec.DEPARTMENT_NAME||'---'||deptrec.LOCATION_ID); END LOOP; CLOSE c3; END;
-- 给工资低于1200 的员工增加工资50 DECLARE v_empno EMPLOYEES.EMPLOYEE_ID%TYPE; v_sal EMPLOYEES.Salary%TYPE; CURSOR c_cursor ISSELECT EMPLOYEE_ID, Salary FROM EMPLOYEES; BEGIN OPEN c_cursor; LOOP FETCH c_cursor INTO v_empno, v_sal; EXIT WHEN c_cursor%NOTFOUND; IF v_sal<=1200THEN UPDATE EMPLOYEES SET Salary=Salary+50WHERE EMPLOYEE_ID=v_empno; DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'工资已更新!'); END IF; DBMS_OUTPUT.PUT_LINE('记录数:'|| c_cursor %ROWCOUNT); END LOOP; CLOSE c_cursor; END;
-- 没有参数且没有返回值的游标 DECLARE v_f_name employees.first_name%TYPE; v_j_id employees.job_id%TYPE; CURSOR c1 --声明游标,没有参数没有返回值 IS SELECT first_name, job_id FROM employees WHERE department_id =20; BEGIN OPEN c1; --打开游标 LOOP FETCH c1 INTO v_f_name, v_j_id; --提取游标 IF c1%FOUND THEN DBMS_OUTPUT.PUT_LINE(v_f_name||'的岗位是'||v_j_id); ELSE DBMS_OUTPUT.PUT_LINE('已经处理完结果集了'); EXIT; END IF; END LOOP; CLOSE c1; --关闭游标 END;
-- 有参数且没有返回值的游标 DECLARE v_f_name employees.first_name%TYPE; v_h_date employees.hire_date%TYPE; CURSOR c2(dept_id NUMBER, j_id VARCHAR2) --声明游标,有参数没有返回值 IS SELECT first_name, hire_date FROM employees WHERE department_id = dept_id AND job_id = j_id; BEGIN OPEN c2(90, 'AD_VP'); --打开游标,传递参数值 LOOP FETCH c2 INTO v_f_name, v_h_date; --提取游标 IF c2%FOUND THEN DBMS_OUTPUT.PUT_LINE(v_f_name||'的雇佣日期是'||v_h_date); ELSE DBMS_OUTPUT.PUT_LINE('已经处理完结果集了'); EXIT; END IF; END LOOP; CLOSE c2; --关闭游标 END;
-- 有参数且有返回值的游标 DECLARE TYPE emp_record_type IS RECORD( f_name employees.first_name%TYPE, h_date employees.hire_date%TYPE); v_emp_record EMP_RECORD_TYPE;
CURSOR c3(dept_id NUMBER, j_id VARCHAR2) --声明游标,有参数有返回值 RETURN EMP_RECORD_TYPE IS SELECT first_name, hire_date FROM employees WHERE department_id = dept_id AND job_id = j_id; BEGIN OPEN c3(j_id =>'AD_VP', dept_id =>90); --打开游标,传递参数值 LOOP FETCH c3 INTO v_emp_record; --提取游标 IF c3%FOUND THEN DBMS_OUTPUT.PUT_LINE(v_emp_record.f_name||'的雇佣日期是' ||v_emp_record.h_date); ELSE DBMS_OUTPUT.PUT_LINE('已经处理完结果集了'); EXIT; END IF; END LOOP; CLOSE c3; --关闭游标 END;
-- 基于游标定义记录变量 DECLARE CURSOR c4(dept_id NUMBER, j_id VARCHAR2) --声明游标,有参数没有返回值 IS SELECT first_name f_name, hire_date FROM employees WHERE department_id = dept_id AND job_id = j_id; --基于游标定义记录变量,比声明记录类型变量要方便,不容易出错 v_emp_record c4%ROWTYPE; BEGIN OPEN c4(90, 'AD_VP'); --打开游标,传递参数值 LOOP FETCH c4 INTO v_emp_record; --提取游标 IF c4%FOUND THEN DBMS_OUTPUT.PUT_LINE(v_emp_record.f_name||'的雇佣日期是' ||v_emp_record.hire_date); ELSE DBMS_OUTPUT.PUT_LINE('已经处理完结果集了'); EXIT; END IF; END LOOP; CLOSE c4; --关闭游标 END;
-- ex1 DECLARE CURSOR c_sal IS SELECT employee_id, first_name || last_name ename, salary FROM employees ; BEGIN --隐含打开游标 FOR v_sal IN c_sal LOOP --隐含执行一个FETCH语句 DBMS_OUTPUT.PUT_LINE(to_char(v_sal.employee_id)||'---'|| v_sal.ename||'---'||to_char(v_sal.salary)) ; --隐含监测c_sal%NOTFOUND END LOOP; --隐含关闭游标 END;
-- ex2 当所声明的游标带有参数时,通过游标FOR 循环语句为游标传递参数 DECLARE CURSOR c_cursor(dept_no NUMBER DEFAULT10) IS SELECT department_name, location_id FROM departments WHERE department_id <= dept_no; BEGIN DBMS_OUTPUT.PUT_LINE('当dept_no参数值为30:'); FOR c1_rec IN c_cursor(30) LOOP DBMS_OUTPUT.PUT_LINE(c1_rec.department_name||'---'||c1_rec.location_id); END LOOP; DBMS_OUTPUT.PUT_LINE(CHR(10)||'使用默认的dept_no参数值10:'); FOR c1_rec IN c_cursor LOOP DBMS_OUTPUT.PUT_LINE(c1_rec.department_name||'---'||c1_rec.location_id); END LOOP; END;
-- 删除EMPLOYEES表中某部门的所有员工,如果该部门中已没有员工,则在DEPARTMENT表中删除该部门 DECLARE V_deptno department_id%TYPE :=&p_deptno; BEGIN DELETEFROM employees WHERE department_id=v_deptno; IF SQL%NOTFOUND THEN DELETEFROM departments WHERE department_id=v_deptno; END IF; END;
-- 通过隐式游标SQL的%ROWCOUNT属性来了解修改了多少行 DECLARE v_rows NUMBER; BEGIN --更新数据 UPDATE employees SET salary =30000 WHERE department_id =90AND job_id ='AD_VP'; --获取默认游标的属性值 v_rows :=SQL%ROWCOUNT; DBMS_OUTPUT.PUT_LINE('更新了'||v_rows||'个雇员的工资'); --回退更新,以便使数据库的数据保持原样 ROLLBACK; END;
-- 从EMPLOYEES表中查询某部门的员工情况,将其工资最低定为 1500; DECLARE V_deptno employees.department_id%TYPE :=&p_deptno; CURSOR emp_cursor IS SELECT employees.employee_id, employees.salary FROM employees WHERE employees.department_id=v_deptno FORUPDATE NOWAIT; BEGIN FOR emp_record IN emp_cursor LOOP IF emp_record.salary <1500THEN UPDATE employees SET salary=1500 WHERECURRENTOF emp_cursor; END IF; END LOOP; -- COMMIT; END; -- 将EMPLOYEES表中部门编码为90、岗位为AD_VP的雇员的工资都更新为2000元; DECLARE v_emp_record employees%ROWTYPE; CURSOR c1 IS SELECT*FROM employees FORUPDATE; BEGIN OPEN c1; LOOP FETCH c1 INTO v_emp_record; EXIT WHEN c1%NOTFOUND; IF v_emp_record.department_id =90AND v_emp_record.job_id ='AD_VP' THEN UPDATE employees SET salary =20000 WHERECURRENTOF c1; --更新当前游标行对应的数据行 END IF; END LOOP; COMMIT; --提交已经修改的数据 CLOSE c1; END;
-- 强类型参照游标变量类型 DECLARE TYPE emp_job_rec IS RECORD( Employee_id employees.employee_id%TYPE, Employee_name employees.first_name%TYPE, Job_title employees.job_id%TYPE ); TYPE emp_job_refcur_type ISREFCURSORRETURN emp_job_rec; Emp_refcur emp_job_refcur_type ; Emp_job emp_job_rec; BEGIN OPEN emp_refcur FOR SELECT employees.employee_id, employees.first_name||employees.last_name, employees.job_id FROM employees ORDERBY employees.department_id; FETCH emp_refcur INTO emp_job; WHILE emp_refcur%FOUND LOOP DBMS_OUTPUT.PUT_LINE(emp_job.employee_id||': '||emp_job.employee_name||' is a '||emp_job.job_title); FETCH emp_refcur INTO emp_job; END LOOP; END;
-- 弱类型参照游标变量类型 DECLARE Type refcur_t ISREFCURSOR; Refcur refcur_t; TYPE sample_rec_type IS RECORD ( Id number, Description VARCHAR2 (30) ); sample sample_rec_type; selection varchar2(1) :=UPPER (SUBSTR ('&tab', 1, 1)); BEGIN IF selection='D'THEN OPEN refcur FOR SELECT departments.department_id, departments.department_name FROM departments; DBMS_OUTPUT.PUT_LINE('Department data'); ELSIF selection='E'THEN OPEN refcur FOR SELECT employees.employee_id, employees.first_name||' is a '||employees.job_id FROM employees; DBMS_OUTPUT.PUT_LINE('Employee data'); ELSE DBMS_OUTPUT.PUT_LINE('Please enter ''D'' or ''E'''); RETURN; END IF; DBMS_OUTPUT.PUT_LINE('----------------------'); FETCH refcur INTO sample; WHILE refcur%FOUND LOOP DBMS_OUTPUT.PUT_LINE(sample.id||': '||sample.description); FETCH refcur INTO sample; END LOOP; CLOSE refcur; END;
BEGIN OPEN c1 FORSELECT*FROM employees WHERE department_id =20; LOOP FETCH c1 INTO v_emp_record; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE(v_emp_record.first_name||'的雇佣日期是' ||v_emp_record.hire_date); END LOOP; --将同一个游标变量对应到另一个SELECT语句 OPEN c1 FORSELECT*FROM regions WHERE region_id IN(1,2); LOOP FETCH c1 INTO v_reg_record; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE(v_reg_record.region_id||'表示' ||v_reg_record.region_name); END LOOP; CLOSE c1; END;
-- 使用游标变量(有RETURN子句) DECLARE --定义一个与employees表中的这几个列相同的记录数据类型 TYPE emp_record_type IS RECORD( f_name employees.first_name%TYPE, h_date employees.hire_date%TYPE, j_id employees.job_id%TYPE); --声明一个该记录数据类型的记录变量 v_emp_record EMP_RECORD_TYPE; --定义一个游标数据类型 TYPE emp_cursor_type ISREFCURSOR RETURN EMP_RECORD_TYPE; --声明一个游标变量 c1 EMP_CURSOR_TYPE; BEGIN OPEN c1 FOR SELECT first_name, hire_date, job_id FROM employees WHERE department_id =20; LOOP FETCH c1 INTO v_emp_record; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE('雇员名称:'||v_emp_record.f_name ||' 雇佣日期:'||v_emp_record.h_date ||' 岗位:'||v_emp_record.j_id); END LOOP; CLOSE c1; END;
EXCEPTION WHEN first_exception THEN<code to handle first exception > WHEN second_exception THEN<code to handle second exception > WHEN OTHERS THEN<code to handle others exception > END;
-- 更新指定员工工资,如工资小于1500,则加100; DECLARE v_empno employees.employee_id%TYPE :=&empno; v_sa- employees.salary%TYPE; BEGIN SELECT salary INTO v_sal FROM employees WHERE employee_id = v_empno; IF v_sal<=1500THEN UPDATE employees SET salary = salary +100WHERE employee_id=v_empno; DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'员工工资已更新!'); ELSE DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'员工工资已经超过规定值!'); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('数据库中没有编码为'||v_empno||'的员工'); WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE('程序运行错误!请使用游标'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END;
DECLARE v_deptno departments.department_id%TYPE :=&deptno; deptno_remaining EXCEPTION; PRAGMA EXCEPTION_INIT(deptno_remaining, -2292); /* -2292 是违反一致性约束的错误代码 */ BEGIN DELETEFROM departments WHERE department_id = v_deptno; EXCEPTION WHEN deptno_remaining THEN DBMS_OUTPUT.PUT_LINE('违反数据完整性约束!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END;
-- 更新指定员工工资,增加100; DECLARE v_empno employees.employee_id%TYPE :=&empno; no_result EXCEPTION; BEGIN UPDATE employees SET salary = salary+100WHERE employee_id = v_empno; IF SQL%NOTFOUND THEN RAISE no_result; END IF; EXCEPTION WHEN no_result THEN DBMS_OUTPUT.PUT_LINE('你的数据更新语句失败了!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END;
CREATEOR REPLACE FUNCTION get_salary(p_deptno NUMBER) RETURN NUMBER AS v_sal NUMBER; BEGIN IF p_deptno ISNULLTHEN RAISE_APPLICATION_ERROR(-20991, ’部门代码为空’); ELSIF p_deptno<0THEN RAISE_APPLICATION_ERROR(-20992, ’无效的部门代码’); ELSE SELECTSUM(employees.salary) INTO v_sal FROM employees WHERE employees.department_id=p_deptno; RETURN v_sal; END IF; END;
BEGIN V_salary :=get_salary(-10); EXCEPTION WHEN invalid_deptno THEN V_sqlcode :=SQLCODE; V_sqlerr :=SQLERRM; INSERTINTO errlog(errcode, errtext) VALUES(v_sqlcode, v_sqlerr); COMMIT; END inner1;
EXCEPTION WHEN null_deptno THEN V_sqlcode :=SQLCODE; V_sqlerr :=SQLERRM; INSERTINTO errlog(errcode, errtext) VALUES(v_sqlcode, v_sqlerr); COMMIT; WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); ENDouter;
-- 定义触发器,使用RAISE_APPLICATION_ERROR阻止没有员工姓名的新员式记录插入: CREATEOR REPLACE TRIGGER tr_insert_emp BEFORE INSERTON employees FOREACHROW BEGIN IF :new.first_name ISNULLOR :new.last_name isnullTHEN RAISE_APPLICATION_ERROR(-20000,'Employee must have a name.'); END IF; END;
IN, OUT, IN OUT是形参的模式。若省略,则为 IN 模式。 IN 模式的形参只能将实参传递给形参,进入函数内部,但只能读不能写,函数返回时实参的值不变。 OUT 模式的形参会忽略调用时的实参值(或说该形参的初始值总是 NULL),但在函数内部可以被读或写,函数返回时形参的值会赋予给实参。 IN OUT 具有前两种模式的特性,即调用时,实参的值总是传递给形参,结束时,形参的值传递给实参。调用时,对于 IN 模式的实参可以是常量或变量,但对于 OUT 和 IN OUT模式的实参必须是变量。只有在确认function_name函数是新函数或是要更新的函数时,才使用OR REPALCE关键字,否则容易删除有用的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
--获取某部门的工资总和 CREATEOR REPLACE FUNCTION get_salary( Dept_no NUMBER, Emp_count OUT NUMBER) RETURN NUMBER IS V_sum NUMBER; BEGIN SELECTSUM(SALARY), count(*) INTO V_sum, emp_count FROM EMPLOYEES WHERE DEPARTMENT_ID=dept_no; RETURN v_sum; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('你需要的数据不存在!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END get_salary;
-- 组合传递 CREATEOR REPLACE FUNCTION demo_fun( Name VARCHAR2,--注意VARCHAR2不能给精度,如:VARCHAR2(10),其它类似 Age INTEGER, Sex VARCHAR2) RETURN VARCHAR2 AS V_var VARCHAR2(32); BEGIN V_var := name||':'||TO_CHAR(age)||'岁.'||sex; RETURN v_var; END; DECLARE Var VARCHAR(32); BEGIN Var := demo_fun('user1', 30, sex =>'男'); DBMS_OUTPUT.PUT_LINE(var); Var := demo_fun('user2', age =>40, sex =>'男'); DBMS_OUTPUT.PUT_LINE(var); Var := demo_fun('user3', sex =>'女', age =>20); DBMS_OUTPUT.PUT_LINE(var); END;
5.4 参数默认值
在 CREATE OR REPLACE FUNCTION 语句中声明函数参数时可以使用DEFAULT关键字为输入的参数指定默认值。
1 2 3 4 5 6 7 8 9 10 11
CREATEOR REPLACE FUNCTION demo_fun( Name VARCHAR2, Age INTEGER, Sex VARCHAR2 DEFAULT'男') RETURN VARCHAR2 AS V_var VARCHAR2(32); BEGIN V_var := name||':'||TO_CHAR(age)||'岁.'||sex; RETURN v_var; END;
DECLARE var VARCHAR(32); BEGIN var := demo_fun('user1', 30); DBMS_OUTPUT.PUT_LINE(var); var := demo_fun('user2', age =>40); DBMS_OUTPUT.PUT_LINE(var); var := demo_fun('user3', sex =>'女', age =>20); DBMS_OUTPUT.PUT_LINE(var); END;
-- 删除指定员工记录; CREATEOR REPLACE PROCEDURE DelEmp (v_empno IN employees.employee_id%TYPE) AS No_result EXCEPTION; BEGIN DELETEFROM employees WHERE employee_id = v_empno; IF SQL%NOTFOUND THEN RAISE no_result; END IF; DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'的员工已被删除!'); EXCEPTION WHEN no_result THEN DBMS_OUTPUT.PUT_LINE('温馨提示:你需要的数据不存在!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END DelEmp;
-- 插入员工记录: CREATEOR REPLACE PROCEDURE InsertEmp( v_empno in employees.employee_id%TYPE, v_firstname in employees.first_name%TYPE, v_lastname in employees.last_name%TYPE, v_deptno in employees.department_id%TYPE ) AS empno_remaining EXCEPTION; PRAGMA EXCEPTION_INIT(empno_remaining, -1); /* -1 是违反唯一约束条件的错误代码 */ BEGIN INSERTINTO EMPLOYEES(EMPLOYEE_ID, FIRST_NAME, LAST_NAME, HIRE_DATE,DEPARTMENT_ID) VALUES(v_empno, v_firstname,v_lastname, sysdate, v_deptno); DBMS_OUTPUT.PUT_LINE('温馨提示:插入数据记录成功!'); EXCEPTION WHEN empno_remaining THEN DBMS_OUTPUT.PUT_LINE('温馨提示:违反数据完整性约束!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END InsertEmp;
-- 使用存储过程向departments表中插入数据。 CREATEOR REPLACE PROCEDURE insert_dept (v_dept_id IN departments.department_id%TYPE, v_dept_name IN departments.department_name%TYPE, v_mgr_id IN departments.manager_id%TYPE, v_loc_id IN departments.location_id%TYPE) IS ept_null_error EXCEPTION; PRAGMA EXCEPTION_INIT(ept_null_error, -1400); ept_no_loc_id EXCEPTION; PRAGMA EXCEPTION_INIT(ept_no_loc_id, -2291); BEGIN INSERTINTO departments (department_id, department_name, manager_id, location_id) VALUES (v_dept_id, v_dept_name, v_mgr_id, v_loc_id); DBMS_OUTPUT.PUT_LINE('插入部门'||v_dept_id||'成功'); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN RAISE_APPLICATION_ERROR(-20000, '部门编码不能重复'); WHEN ept_null_error THEN RAISE_APPLICATION_ERROR(-20001, '部门编码、部门名称不能为空'); WHEN ept_no_loc_id THEN RAISE_APPLICATION_ERROR(-20002, '没有该地点'); END insert_dept; -- 调用实例1 DECLARE ept_20000 EXCEPTION; PRAGMA EXCEPTION_INIT(ept_20000, -20000); ept_20001 EXCEPTION; PRAGMA EXCEPTION_INIT(ept_20001, -20001); ept_20002 EXCEPTION; PRAGMA EXCEPTION_INIT(ept_20002, -20002); BEGIN insert_dept(300, '部门300', 100, 2400); insert_dept(310, NULL, 100, 2400); insert_dept(310, '部门310', 100, 900); EXCEPTION WHEN ept_20000 THEN DBMS_OUTPUT.PUT_LINE('ept_20000部门编码不能重复'); WHEN ept_20001 THEN DBMS_OUTPUT.PUT_LINE('ept_20001部门编码、部门名称不能为空'); WHEN ept_20002 THEN DBMS_OUTPUT.PUT_LINE('ept_20002没有该地点'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('others出现了其他异常错误'); END; DECLARE ept_20000 EXCEPTION; PRAGMA EXCEPTION_INIT(ept_20000, -20000); ept_20001 EXCEPTION; PRAGMA EXCEPTION_INIT(ept_20001, -20001); ept_20002 EXCEPTION; PRAGMA EXCEPTION_INIT(ept_20002, -20002); BEGIN insert_dept(v_dept_name =>'部门310', v_dept_id =>310, v_mgr_id =>100, v_loc_id =>2400); insert_dept(320, '部门320', v_mgr_id =>100, v_loc_id =>900); EXCEPTION WHEN ept_20000 THEN DBMS_OUTPUT.PUT_LINE('ept_20000部门编码不能重复'); WHEN ept_20001 THEN DBMS_OUTPUT.PUT_LINE('ept_20001部门编码、部门名称不能为空'); WHEN ept_20002 THEN DBMS_OUTPUT.PUT_LINE('ept_20002没有该地点'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('others出现了其他异常错误'); END;
-- 查询指定员工记录; CREATEOR REPLACE PROCEDURE QueryEmp (v_empno IN employees.employee_id%TYPE, v_ename OUT employees.first_name%TYPE, v_sa-OUT employees.salary%TYPE) AS BEGIN SELECT last_name || last_name, salary INTO v_ename, v_sal FROM employees WHERE employee_id = v_empno; DBMS_OUTPUT.PUT_LINE('温馨提示:编码为'||v_empno||'的员工已经查到!'); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('温馨提示:你需要的数据不存在!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END QueryEmp; --调用 DECLARE v1 employees.first_name%TYPE; v2 employees.salary%TYPE; BEGIN QueryEmp(100, v1, v2); DBMS_OUTPUT.PUT_LINE('姓名:'||v1); DBMS_OUTPUT.PUT_LINE('工资:'||v2); QueryEmp(103, v1, v2); DBMS_OUTPUT.PUT_LINE('姓名:'||v1); DBMS_OUTPUT.PUT_LINE('工资:'||v2); QueryEmp(104, v1, v2); DBMS_OUTPUT.PUT_LINE('姓名:'||v1); DBMS_OUTPUT.PUT_LINE('工资:'||v2); END;
-- 计算指定部门的工资总和,并统计其中的职工数量。 CREATEOR REPLACE PROCEDURE proc_demo ( dept_no NUMBER DEFAULT10, sal_sum OUT NUMBER, emp_count OUT NUMBER ) IS BEGIN SELECTSUM(salary), COUNT(*) INTO sal_sum, emp_count FROM employees WHERE department_id = dept_no; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('温馨提示:你需要的数据不存在!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END proc_demo; DECLARE V_num NUMBER; V_sum NUMBER(8, 2); BEGIN Proc_demo(30, v_sum, v_num); DBMS_OUTPUT.PUT_LINE('温馨提示:30号部门工资总和:'||v_sum||',人数:'||v_num); Proc_demo(sal_sum => v_sum, emp_count => v_num); DBMS_OUTPUT.PUT_LINE('温馨提示:10号部门工资总和:'||v_sum||',人数:'||v_num); END;
-- GRANT语法 GRANT system_privilege | role TOuser| role | PUBLIC [WITH ADMIN OPTION] GRANT object_privilege |ALLON schema.object TOuser| role | PUBLIC [WITHGRANT OPTION] -- 例子 CREATEOR REPLACE PUBLIC SYNONYM dbms_job FOR dbms_job
GRANTEXECUTEON dbms_job TO PUBLIC WITHGRANT OPTION
BEFORE INSERT BEFORE INSERTFOREACHROW AFTER INSERT AFTER INSERTFOREACHROW BEFORE UPDATE BEFORE UPDATEFOREACHROW AFTER UPDATE AFTER UPDATEFOREACHROW BEFORE DELETE BEFORE DELETEFOREACHROW AFTER DELETE AFTER DELETEFOREACHROW
-- 建立一个触发器, 当职工表 emp 表被删除一条记录时,把被删除记录写到职工表删除日志表中去 CREATETABLE emp_his ASSELECT*FROM EMP WHERE1=2; CREATEOR REPLACE TRIGGER tr_del_emp BEFORE DELETE--指定触发时机为删除操作前触发 ON scott.emp FOREACHROW--说明创建的是行级触发器 BEGIN --将修改前数据插入到日志记录表 del_emp ,以供监督使用。 INSERTINTO emp_his(deptno , empno, ename , job ,mgr , sal , comm , hiredate ) VALUES( :old.deptno, :old.empno, :old.ename , :old.job,:old.mgr, :old.sal, :old.comm, :old.hiredate ); END; DELETE emp WHERE empno=7788; DROPTABLE emp_his; DROPTRIGGER del_emp;
-- 限制对Departments表修改(包括INSERT,DELETE,UPDATE)的时间范围, -- 即不允许在非工作时间修改departments表。 CREATEOR REPLACE TRIGGER tr_dept_time BEFORE INSERTORDELETEORUPDATE ON departments BEGIN IF (TO_CHAR(sysdate,'DAY') IN ('星期六', '星期日')) OR (TO_CHAR(sysdate, 'HH24:MI') NOTBETWEEN'08:30'AND'18:00') THEN RAISE_APPLICATION_ERROR(-20001, '不是上班时间,不能修改departments表'); END IF; END;
-- 限定只对部门号为80的记录进行行触发器操作。 CREATEOR REPLACE TRIGGER tr_emp_sal_comm BEFORE UPDATEOF salary, commission_pct ORDELETE ON HR.employees FOREACHROW WHEN (old.department_id =80) BEGIN CASE WHEN UPDATING ('salary') THEN IF :NEW.salary < :old.salary THEN RAISE_APPLICATION_ERROR(-20001, '部门80的人员的工资不能降'); END IF; WHEN UPDATING ('commission_pct') THEN IF :NEW.commission_pct < :old.commission_pct THEN RAISE_APPLICATION_ERROR(-20002, '部门80的人员的奖金不能降'); END IF; WHEN DELETING THEN RAISE_APPLICATION_ERROR(-20003, '不能删除部门80的人员记录'); ENDCASE; END;
-- 利用行触发器实现级联更新。在修改了主表regions中的region_id之后(AFTER), -- 级联的、自动的更新子表countries表中原来在该地区的国家的region_id。 CREATEOR REPLACE TRIGGER tr_reg_cou AFTER updateOF region_id ON regions FOREACHROW BEGIN DBMS_OUTPUT.PUT_LINE('旧的region_id值是'||:old.region_id ||'、新的region_id值是'||:new.region_id); UPDATE countries SET region_id = :new.region_id WHERE region_id = :old.region_id; END;
-- 在触发器中调用过程。 CREATEOR REPLACE PROCEDURE add_job_history ( p_emp_id job_history.employee_id%type , p_start_date job_history.start_date%type , p_end_date job_history.end_date%type , p_job_id job_history.job_id%type , p_department_id job_history.department_id%type ) IS BEGIN INSERTINTO job_history (employee_id, start_date, end_date, job_id, department_id) VALUES(p_emp_id, p_start_date, p_end_date, p_job_id, p_department_id); END add_job_history; --创建触发器调用存储过程... CREATEOR REPLACE TRIGGER update_job_history AFTER UPDATEOF job_id, department_id ON employees FOREACHROW BEGIN add_job_history(:old.employee_id, :old.hire_date, sysdate, :old.job_id, :old.department_id); END;
INSTEAD OF 选项使ORACLE激活触发器,而不执行触发事件。只能对视图和对象视图建立INSTEAD OF触发器,而不能对表、模式和数据库建立INSTEAD OF 触发器。
FOR EACH ROW选项说明触发器为行触发器。行触发器和语句触发器的区别表现在:行触发器要求当一个DML语句操走影响数据库中的多行数据时,对于其中的每个数据行,只要它们符合触发约束条件,均激活一次触发器;而语句触发器将整个语句操作作为触发事件,当它符合约束条件时,激活一次触发器。当省略 FOR EACH ROW 选项时,BEFORE 和AFTER 触发器为语句触发器,而INSTEAD OF 触发器则为行触发器。
REFERENCING 子句说明相关名称,在行触发器的PL/SQL块和 WHEN 子句中可以使用相关名称参照当前的新、旧列值,默认的相关名称分别为 OLD 和 NEW 。触发器的PL/SQL块中应用相关名称时,必须在它们之前加冒号:,但在WHEN子句中则不能加冒号: