命令行窗口运行plsql之前需要运行
SET SERVEROUTPUT ON
才会将 dbms_output.put_line
的打印结果打印出
申明变量
DECLARE
--申明变量 %TYPE:获得对应字段的类型
v_sal employees.employee_id%TYPE;
v_email VARCHAR2(256);
v_hire_date DATE;
BEGIN
SELECT salary, email, hire_date
INTO v_sal, v_email, v_hire_date
FROM employees
WHERE employee_id = 100;
DBMS_OUTPUT.PUT_LINE('v_sal: ' || v_sal || ', ' || 'v_email: ' ||
v_email || ', ' || 'v_hire_date: ' ||
TO_CHAR(v_hire_date, 'YYYY-MM-DD'));
END;
申明记录
DECLARE
--申明一个记录类型
TYPE emp_record_type IS RECORD(
v_sal employees.employee_id%TYPE,
v_email VARCHAR2(256),
v_hire_date DATE);
emp_record emp_record_type;
BEGIN
SELECT salary, email, hire_date
INTO emp_record.v_sal, emp_record.v_email, emp_record.v_hire_date
FROM employees
WHERE employee_id = 100;
DBMS_OUTPUT.PUT_LINE(emp_record.v_sal || ', ' || emp_record.v_email || ', ' ||
emp_record.v_hire_date);
END;
DECLARE
--%ROWTYPE:使记录和表结构保持一致;&:运行时会在窗口中输入g_emp_id的值再赋给v_emp_id
v_emp_id NUMBER := &g_emp_id;
v_emp_record employees%ROWTYPE;
BEGIN
SELECT * INTO v_emp_record FROM employees WHERE employee_id = v_emp_id;
DBMS_OUTPUT.PUT_LINE(v_emp_record.employee_id || ', ' || v_emp_record.salary || ', ' || v_emp_record.email);
END;
IF ... THEN ...
ELSIF ... THEN ...
ELSE
...
END IF;
CASE
WHEN ... THEN ...
WHEN ... THEN ...
END;
/*
查询出 150号 员工的工资,
若其工资大于或等于 10000 则打印 'salary >= 10000'
若在 5000 到 10000 之间, 则打印 '5000<= salary < 10000'
否则打印 'salary < 5000'
*/
DECLARE
v_emp_sal employees.salary%TYPE;
v_msg VARCHAR2(256);
BEGIN
SELECT salary INTO v_emp_sal FROM employees WHERE employee_id = 100;
--方法一
IF v_emp_sal < 5000 THEN
DBMS_OUTPUT.PUT_LINE('salary < 5000');
ELSIF v_emp_sal >= 5000 AND v_emp_sal < 10000 THEN
DBMS_OUTPUT.PUT_LINE('5000<= salary < 10000');
ELSE
DBMS_OUTPUT.PUT_LINE('salary >= 10000');
END IF;
--方法二
v_msg := CASE
WHEN v_emp_sal < 5000 THEN 'salary < 5000'
WHEN v_emp_sal >= 5000 AND v_emp_sal < 10000 THEN '5000<= salary < 10000'
ELSE 'salary >= 10000'
END;
DBMS_OUTPUT.PUT_LINE(v_msg);
END;
LOOP ...
EXIT WHEN ...
END LOOP;
WHILE ... LOOP
END LOOP;
FOR i IN ... LOOP
END LOOP;
--循环方式打印1-100
DECLARE
v_count NUMBER := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(v_count);
EXIT WHEN(v_count >= 100);
v_count := v_count + 1;
END LOOP;
v_count := 1;
WHILE v_count <= 100 LOOP
DBMS_OUTPUT.PUT_LINE(v_count);
v_count := v_count + 1;
END LOOP;
FOR i IN 1..100 LOOP
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;
--打印1-100,遇到50就结束,并打印结束
--经量少用 goto
BEGIN
FOR i IN 1..100 LOOP
IF i = 50 THEN
GOTO label;
END IF;
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
<<label>>
DBMS_OUTPUT.PUT_LINE('结束');
END;
--推荐这种
BEGIN
FOR i IN 1..100 LOOP
IF i = 50 THEN
DBMS_OUTPUT.PUT_LINE('结束');
EXIT;
END IF;
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;
游标属性 | |
---|---|
%FOUND | 布尔类型,如果游标最近一次数据读取成功则返回TRUE |
%NOTFOUND | 尔类型,与%FOUND相反 |
%ISOPEN | 尔类型,如果游标是打开状态,则返回TRUE |
%ROWCOUNT | 数字类型,返回记录数 |
--打印80号部门的员工工资
DECLARE
--申明变量
v_sal employees.salary%TYPE;
v_id employees.employee_id%TYPE;
--定义游标
CURSOR emp_cursor IS SELECT salary, employee_id FROM employees WHERE department_id = 80;
BEGIN
--打开游标
OPEN emp_cursor;
--提取数据
FETCH emp_cursor INTO v_sal, v_id;
WHILE emp_cursor%FOUND LOOP
DBMS_OUTPUT.PUT_LINE('id: ' || v_id || ', salary: ' || v_sal);
FETCH emp_cursor INTO v_sal, v_id;
END LOOP;
--关闭游标
CLOSE emp_cursor;
END;
DECLARE
--申明一个记录
TYPE emp_record_type IS RECORD(
v_sal employees.salary%TYPE,
v_id employees.employee_id%TYPE
);
--申明一个记录类型的变量
emp_record emp_record_type;
--定义游标
CURSOR emp_cursor IS SELECT salary, employee_id FROM employees WHERE department_id = 80;
BEGIN
--打开游标
OPEN emp_cursor;
--提取数据
FETCH emp_cursor INTO emp_record;
WHILE emp_cursor%FOUND LOOP
DBMS_OUTPUT.PUT_LINE('id: ' || emp_record.v_id || ', salary: ' || emp_record.v_sal);
FETCH emp_cursor INTO emp_record;
END LOOP;
--关闭游标
CLOSE emp_cursor;
END;
WHILE循环读取游标数据会有很多步骤,FOR循环可以简化
--打印80号部门的员工工资
DECLARE
--申明一个游标
CURSOR emp_cur IS SELECT employee_id, salary FROM employees WHERE department_id = 80;
BEGIN
FOR c IN emp_cur LOOP
DBMS_OUTPUT.PUT_LINE('employee_id: ' || c.employee_id || ', sal: ' || c.salary);
END LOOP;
END;
示例题1
/*
利用游标, 调整公司中员工的工资:
工资范围 调整基数
0 - 5000 5%
5000 - 10000 3%
10000 - 15000 2%
15000 - 1%
*/
DECLARE
--申明变量
v_empid employees.employee_id%TYPE;
v_empsal employees.salary%TYPE;
v_factory NUMBER(3, 2);
--申明游标
CURSOR emp_sal_cur IS
SELECT employee_id, salary FROM employees;
BEGIN
/*--打开游标
OPEN emp_sal_cur;
--提取数据
FETCH emp_sal_cur INTO v_empid, v_empsal;
WHILE emp_sal_cur%FOUND LOOP
IF v_empsal >= 0 AND v_empsal < 5000 THEN v_factory := 0.05;
ELSIF v_empsal >= 5000 AND v_empsal < 10000 THEN v_factory := 0.03;
ELSIF v_empsal >= 10000 AND v_empsal < 15000 THEN v_factory := 0.02;
ELSIF v_empsal >= 15000 THEN v_factory := 0.01;
END IF;
UPDATE employees SET salary = salary * (1+v_factory) WHERE employee_id = v_empid;
FETCH emp_sal_cur INTO v_empid, v_empsal;
END LOOP;*/
FOR c IN emp_sal_cur LOOP
IF c.salary >= 0 AND c.salary < 5000 THEN
v_factory := 0.05;
ELSIF c.salary >= 5000 AND c.salary < 10000 THEN
v_factory := 0.03;
ELSIF c.salary >= 10000 AND c.salary < 15000 THEN
v_factory := 0.02;
ELSIF c.salary >= 15000 THEN
v_factory := 0.01;
END IF;
UPDATE employees
SET salary = salary * (1 + v_factory)
WHERE employee_id = c.employee_id;
END LOOP;
END;
带参数的游标
--处理带参数的游标
DECLARE
v_factory NUMBER(3, 2);
v_id employees.employee_id%TYPE;
v_sal employees.salary%TYPE;
CURSOR emp_sal_cur(emp_id NUMBER, emp_sal NUMBER) IS
SELECT employee_id, salary
FROM employees
WHERE employee_id = emp_id
AND salary > emp_sal;
BEGIN
--while参数应该在open打开的时候添加【for循环直接添加】
OPEN emp_sal_cur(emp_id => 102, emp_sal => 4000);
FETCH emp_sal_cur
INTO v_id, v_sal;
WHILE emp_sal_cur%FOUND LOOP
IF v_sal >= 0 AND v_sal < 5000 THEN
v_factory := 0.05;
ELSIF v_sal >= 5000 AND v_sal < 10000 THEN
v_factory := 0.03;
ELSIF v_sal >= 10000 AND v_sal < 15000 THEN
v_factory := 0.02;
ELSIF v_sal >= 15000 THEN
v_factory := 0.01;
END IF;
UPDATE employees
SET salary = salary * (1 + v_factory)
WHERE employee_id = v_id;
FETCH emp_sal_cur
INTO v_id, v_sal;
END LOOP;
END;
隐式游标
--隐式游标: 更新指定员工 salary(涨工资 10),如果该员工没有找到,则打印”查无此人” 信息
BEGIN
UPDATE employees SET salry = salary + 10 WHERE employee_id = 99999;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('查无此人');
END IF;
END;
预定义异常处理
--预定义异常处理
DECLARE
v_sal employees.salary%TYPE;
BEGIN
SELECT salary INTO v_sal FROM employees WHERE employee_id > 80;
DBMS_OUTPUT.PUT_LINE(v_sal);
EXCEPTION
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('输出行数太多');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('出现其它类型异常');
END;
非预定义异常处理
--非预定义异常处理
DECLARE
e_delid_exception EXCEPTION;
PRAGMA EXCEPTION_INIT(e_delid_exception, -2292);
BEGIN
DELETE FROM employees WHERE employee_id =100;
WHEN e_delid_exception THEN
DBMS_OUTPUT.PUT_LINE('违反完整性约束');
END;
用户自定义异常
DECLARE
e_too_high_sal EXCEPTION;
v_sal employees.salary%TYPE;
BEGIN
SELECT salary INTO v_sal FROM employees WHERE employee_id = 100;
IF v_sal > 10000 THEN
RAISE e_too_high_sal;
END IF;
EXCEPTION
WHEN e_too_high_sal THEN
DBMS_OUTPUT.PUT_LINE('工资太高了');
END;
示例题
--通过 select ... into ... 查询某人的工资, 若没有查询到, 则输出 "未找到数据"
DECLARE
v_sal employees.salary%TYPE;
BEGIN
SELECT salary INTO v_sal FROM employees WHERE employee_id = 1001;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT._PUT_LINE('未找到数据');
END;
--更新指定员工工资,如工资小于300,则加100;对 NO_DATA_FOUND 异常, TOO_MANY_ROWS 进行处理.
DECLARE
v_sal employees.salary%TYPE;
BEGIN
SELECT salary INTO v_sal FROM employees WHERE employee_id = 1001;
IF v_sal > 300 THEN
UPDATE employees SET salary = salary + 100 WHERE employee_id = 1001;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT._PUT_LINE('未找到数据');
WHEN TOO_MANG_ROWS THEN
DBMS_OUTPUT._PUT_LINE('输出数据太多');
END;
--自定义异常: 更新指定员工工资,增加100;若该员工不存在则抛出用户自定义异常: no_result
DECLARE
no_result EXCEPTION;
BEGIN
UPDATE employees SET salary = salary + 100 WHERE employee_id = 1001;
IF SQL%NOTFOUND THEN
RAISE no_result;
END IF;
EXCEPTION
WHEN no_result THEN
DBMS_OUTPUT._PUT_LINE('更新失败');
END;
存储函数
--存储函数
CREATE OR REPLACE FUNCTION func_name(dpt_id NUMBER, salary NUMBER)
RETURN NUMBER
IS
--函数需要申明的变量,记录类型,CURSOR【DECLARE】
BEGIN
--函数执行体
EXCEPTION
--函数执行遇到的异常
END;
存储函数的使用
--函数的 helloworld: 返回一个 "helloworld" 的字符串
--创建函数
CREATE OR REPLACE FUNCTION say_world(v_word)
RETURN VARCHAR2
IS
BEGIN
RETURN 'word is:' || v_word;
END;
--调用函数
BEGIN
DBMS_OUTPUT.PUT_LINE(say_hello('你好, Word'));
END;
--SQL执行
SELECT say_hello('你好, Word') FROM dual;
存储函数的题目
--创建一个存储函数,打印当前的系统时间
CREATE OR REPLACE FUNCTION get_sysdate
RETURN DATE
IS
v_date DATE;
BEGIN
v_date := SYSDATE;
RETURN v_date;
END;
--执行SQL
SELECT get_sysdate FROM dual;
--计算两数和
CREATE OR REPLACE FUNCTION add_param(v_x NUMBER, v_y NUMBER)
RETURN NUMBER
IS
BEGIN
v_sum NUMBER := v_x+v_y;
RETURN v_sum;
END;
--执行SQL
SELECT add_param(1,2) FROM dual;
--定义一个函数: 获取给定部门的工资总和, 要求:部门号定义为参数, 工资总额定义为返回值.
CREATE OR REPLACE FUNCTION get_dpt_sal_func1(dpt_id NUMBER)
RETURN NUMBER
IS
v_sumsal NUMBER;
CURSOR dpt_sal_cur IS SELECT SUM(salary) FROM employees WHERE department_id = dpt_id;
BEGIN
FOR c IN dpt_sal_cur LOOP
v_sumsal: = v_sumal + c.salary;
END LOOP;
RETURN v_dpt_sal;
END;
DECLARE
v_dptid NUMBER := 80;
BEGIN
DBMS_OUTPUT.PUT_LINE(get_dpt_sal_func2(v_dptid));
END;
/*
要求: 定义一个函数: 获取给定部门的工资总和 和 该部门的员工总数(定义为 OUT 类型的参数).
要求: 部门号定义为参数, 工资总额定义为返回值.
*/
CREATE OR REPLACE FUNCTION get_dpt_sal2(dpt_id IN NUMBER, emp_count OUT NUMBER)
RETURN NUMBER
IS
v_empcount NUMBER := 0;
v_dpt_sal NUMBER :=0;
CURSOR dpt_sal_cur IS SELECT salary FROM employees WHERE department_id = dpt_id;
BEGIN
FOR c IN dpt_sal_cur LOOP
v_dpt_sal := c.salary + v_dpt_sal;
v_empcount := v_empcount + 1;
END LOOP;
RETURN v_dpt_sal;
END;
DECLARE
v_num NUMBER := 0;
BEGIN
DBMS_OUTPUT.PUT_LINE(get_dpt_sal2(80, v_num));
DBMS_OUTPUT.PUT_LINE(v_num);
END;
存储过程
--定义一个存储过程: 获取给定部门的工资总和(通过 out 参数), 要求:部门号和工资总额定义为参数
CREATE OR REPLACE PROCEDURE get_sal_proc(dpt_id IN NUMBER, OUT sumsal NUMBER)
IS
CURSOR emp_sal_cur IS SELECT salary FROM employees WHERE department_id dpt_id;
BEGIN
sumsal := 0;
FOR c IN emp_sal_cur IN LOOP
sumsal := sumsal + c.salary;
END LOOP;
DBMS_OUTPUT.PUT_LINE(sumsal);
END;
BEGIN
get_sal_proc(80, 0);
END;
/*
自定义一个存储过程完成以下操作:
对给定部门(作为输入参数)的员工进行加薪操作, 若其到公司的时间在 (? , 95) 期间, 为其加薪 %5
[95 , 98) %3
[98, ?) %1
得到以下返回结果: 为此次加薪公司每月需要额外付出多少成本(定义一个 OUT 型的输出参数).
*/
CREATE OR REPLACE PROCEDURE add_sal_proc(dpt_id IN NUMBER, cost OUT NUMBER)
IS
CURSOR emp_sal_cur IS SELECT employee_id, salary, hire_date FROM employees WHERE department_id = dpt_id;
v_factory NUMBER(4,2);
BEGIN
cost += 0;
FOR c IN emp_sal_cur LOOP
IF c.hire_date < TO_DATE('1995', 'YYYY') THEN
v_factory := 0.05;
ELSIF c.hire_date >= TO_DATE('1995', 'YYYY') AND c.hire_date < TO_DATE('1998', 'YYYY') THEN
v_factory := 0.03;
ELSE
v_factory := 0.01;
END IF;
cost := cost + salary * v_factory;
END LOOP;
DBMS_OUTPUT.PUT_LINE(cost);
END;
BEGIN
add_sal_proc(80, 0);
END;
CREATE OR REPLACE TRIGGER update_emp_trigger
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
dbms_output.put_line('更新前old salary: ' || :old.salary || ', 更新后new salary: ' || :new.salary);
END;
SELECT * FROM employees;
UPDATE employees SET salary = salary - 100 WHERE employee_id = 100;
--编写一个触发器, 在对 employees 记录进行删除的时候, 在 my_emp_bak 表中备份对应的记录
CREATE TABLE my_emp_bak AS SELECT employee_id, salary FROM employees WHERE 1=2;
CREATE OR REPLACE TRIGGER my_emp_bak
BEFORE
DELETE ON employees
FOR EACH ROW
BEGIN
INSERT INTO my_emp_bak(employee_id, salary) VALUES(:old.employee_id, :old.salary);
END;
SELECT * FROM employees;
SELECT * FROM my_emp_bak;
DELETE FROM employees;
ROLLBACK;