本文系統整理 Oracle 進階資料庫物件,涵蓋視圖、索引、序列、同義詞、函數、觸發器、存儲過程與 Package,提供建立語法、核心參數、使用場景、最佳實踐與常見陷阱,適合開發與 DBA 參考。
-- 基本建立
CREATE OR REPLACE VIEW emp_v20 (empno, ename, sal)
AS SELECT empno, ename, sal FROM emp WHERE deptno = 20;
-- WITH CHECK OPTION:防止更新違背視圖定義條件
CREATE OR REPLACE VIEW emp_v20_chk
AS SELECT empno, ename, sal, deptno FROM emp WHERE deptno = 20
WITH CHECK OPTION CONSTRAINT emp_v20_ck;
-- WITH READ ONLY:唯讀視圖
CREATE OR REPLACE VIEW emp_v20_ro
AS SELECT empno, ename, sal, deptno FROM emp WHERE deptno = 20
WITH READ ONLY;
GROUP BY、DISTINCT、集合運算、多表連接(部分情況)、衍生欄位等。索引儲存 (key_value, ROWID) 對,透過 B-Tree 結構快速定位 ROWID,再回表取資料。獨立於表儲存,Oracle 自動維護。
| 優點 | 缺點 |
|---|---|
| 加速查詢、唯一性約束、加速 JOIN、減少排序分組成本 | 佔用空間、DML 維護開銷大、降低寫入效能 |
-- 一般索引
CREATE INDEX idx_emp_dept ON emp(deptno);
-- 複合索引(欄位順序關鍵)
CREATE INDEX idx_emp_dept_job ON emp(deptno, job);
-- 唯一索引
CREATE UNIQUE INDEX uk_emp_ename ON emp(ename);
-- 位圖索引(低基數欄位,如性別、狀態)
CREATE BITMAP INDEX bmp_emp_sex ON emp(sex);
-- 函數索引
CREATE INDEX idx_emp_upper_name ON emp(UPPER(ename));
UPPER(col)、col+0、TO_CHAR 等)。IS NULL、NOT IN、!=、LIKE '%...' 等。REBUILD 減少碎片;刪除無用索引;指定 TABLESPACE 分離 I/O。CREATE SEQUENCE seq_emp
INCREMENT BY 1 START WITH 1 MAXVALUE 999999 MINVALUE 1
CYCLE CACHE 20 NOORDER;
-- 使用
SELECT seq_emp.NEXTVAL FROM dual; -- 取得下一值
SELECT seq_emp.CURRVAL FROM dual; -- 取得當前值(需先 NEXTVAL)
INSERT INTO emp(empno) VALUES(seq_emp.NEXTVAL);
注意:CACHE 可能導致跳號(異常關機丟失緩存值),關鍵業務建議 NOCACHE。
CREATE SYNONYM emp FOR scott.emp; 僅建立者可用。CREATE PUBLIC SYNONYM emp FOR scott.emp; 所有用戶可用(仍需權限)。CREATE OR REPLACE FUNCTION get_annual_sal(p_empno emp.empno%TYPE)
RETURN NUMBER
AS
v_sal NUMBER;
BEGIN
SELECT (sal + NVL(comm,0)) * 12 INTO v_sal FROM emp WHERE empno = p_empno;
RETURN v_sal;
END;
/
-- 呼叫
SELECT get_annual_sal(7369) FROM dual;
參數模式:IN(預設,唯讀)、OUT(唯寫)、IN OUT(可讀寫)。
| 類型 | 基礎物件 | 觸發事件 | FOR EACH ROW |
|---|---|---|---|
| DML 觸發器 | 表 | INSERT/UPDATE/DELETE | 可選(行級/語句級) |
| INSTEAD OF | 視圖 | INSERT/UPDATE/DELETE | 強制行級 |
| 系統觸發器 | Database/Schema | LOGON/STARTUP/DDL 等 | 不適用 |
CREATE OR REPLACE TRIGGER trg_emp_sal_chk
BEFORE UPDATE OF sal ON emp
FOR EACH ROW
BEGIN
IF :NEW.sal < :OLD.sal THEN
RAISE_APPLICATION_ERROR(-20010, '薪資不可降低');
END IF;
END;
/
-- :NEW 為新值,:OLD 為舊值;INSERT 時 :OLD 為 NULL,DELETE 時 :NEW 為 NULL
CREATE OR REPLACE TRIGGER trg_v_reg_cou_ins
INSTEAD OF INSERT ON v_reg_cou
FOR EACH ROW
DECLARE v_cnt NUMBER;
BEGIN
SELECT COUNT(*) INTO v_cnt FROM regions WHERE region_id = :NEW.r_id;
IF v_cnt = 0 THEN INSERT INTO regions VALUES(:NEW.r_id, :NEW.r_name); END IF;
SELECT COUNT(*) INTO v_cnt FROM countries WHERE country_id = :NEW.c_id;
IF v_cnt = 0 THEN INSERT INTO countries VALUES(:NEW.c_id, :NEW.c_name, :NEW.r_id); END IF;
END;
COMMIT/ROLLBACK/SAVEPOINT。LONG/LOB 操作受限。CREATE OR REPLACE PROCEDURE add_dept(
p_deptno IN dept.deptno%TYPE,
p_dname IN dept.dname%TYPE,
p_loc IN dept.loc%TYPE
) AS
v_cnt NUMBER;
BEGIN
SELECT COUNT(*) INTO v_cnt FROM dept WHERE deptno = p_deptno;
IF v_cnt = 0 THEN
INSERT INTO dept VALUES(p_deptno, p_dname, p_loc);
DBMS_OUTPUT.PUT_LINE('新增成功');
ELSE
DBMS_OUTPUT.PUT_LINE('部門已存在');
END IF;
END;
/
-- 呼叫
EXEC add_dept(50, 'AI', 'TAIPEI');
參數模式同函數;OUT/IN OUT 需用變數接收回傳值。
-- 規格(介面)
CREATE OR REPLACE PACKAGE pkg_dept AS
FUNCTION add_dept(p_no NUMBER, p_name VARCHAR2, p_loc VARCHAR2) RETURN NUMBER;
FUNCTION del_dept(p_no NUMBER) RETURN NUMBER;
PROCEDURE query_dept(p_no IN NUMBER);
END pkg_dept;
/
-- 主體(實作)
CREATE OR REPLACE PACKAGE BODY pkg_dept AS
FUNCTION add_dept(...) RETURN NUMBER IS ... BEGIN ... END;
FUNCTION del_dept(...) RETURN NUMBER IS ... BEGIN ... END;
PROCEDURE query_dept(...) IS ... BEGIN ... END;
BEGIN NULL; END pkg_dept;
/
-- 呼叫
EXEC pkg_dept.add_dept(60, 'DATA', 'TAICHUNG');