PL/SQL 程式設計的基石在於對數據類型、運算符、變量生命周期的精準掌握。本文系統整理 Oracle 常用運算符、變量聲明賦值規則、NULL 參與運算的特殊語義、顯式/隱式類型轉換函數、變量作用域與可見性規則,並附帶可直接運行的代碼示例。
| 分類 | 運算符 | 含義 | 示例/備註 |
|---|---|---|---|
| 比較 | = | 等於 | |
| <> / != / ~= / ^= | 不等於 | 四種寫法等價 | |
| < / > / <= / >= | 大小比較 | ||
| IS NULL / IS NOT NULL | 空值判斷 | 不能用 = NULL | |
| BETWEEN ... AND ... | 範圍包含 | 邊界包含 | |
| IN (list) / NOT IN | 集合成員 | 列表可含子查詢 | |
| 算術 | + - * / | 加減乘除 | |
| := | 賦值 | PL/SQL 專用,SQL 用 = | |
| => | 關聯 | 命名參數傳遞、記錄構造 | |
| .. | 範圍 | FOR i IN 1..10 LOOP | |
| || | 字符串連接 | 'Hello' || 'World' → 'HelloWorld' | |
| 邏輯 | AND / OR / NOT | 邏輯與/或/非 | 短路求值 |
| LIKE / NOT LIKE | 模式匹配 | % _ 萬用字符 |
-- 單純聲明 v_name VARCHAR2(30); v_sal NUMBER(8,2); v_hiredate DATE; -- 聲明並初始化 (推薦) v_count NUMBER := 0; v_flag BOOLEAN := TRUE; v_deptno NUMBER(2) NOT NULL := 10; -- NOT NULL 約束需初始化 -- 使用 %TYPE 錨定類型 (最佳實踐) v_ename emp.ename%TYPE; v_sal emp.sal%TYPE; -- 使用 %ROWTYPE 錨定記錄 v_emp_rec emp%ROWTYPE;
-- 直接賦值 v_name := 'SCOTT'; -- SELECT INTO 賦值 (單行結果) SELECT ename, sal INTO v_name, v_sal FROM emp WHERE empno = 7788; -- 多行結果需用游標或 BULK COLLECT -- 錯誤示範: SELECT ename INTO v_name FROM emp; -- TOO_MANY_ROWS
NULL 代表「未知」,而非「零」或「空字符串」。任何與 NULL 運算結果多為 NULL,例外:字符串連接 ||。
| 運算 | 結果 | 說明 |
|---|---|---|
| NULL + 5 | NULL | 算術運算傳播 NULL |
| NULL - 5 | NULL | |
| NULL * 5 | NULL | |
| NULL / 5 | NULL | |
| NULL || 'ABC' | 'ABC' | Oracle 特有:NULL 視為空串參與連接 |
| 'ABC' || NULL | 'ABC' | |
| NULL || NULL | NULL | |
| NVL(NULL, 0) + 5 | 5 | 使用 NVL/COALESCE 處理 NULL |
雖然 Oracle 支援隱式轉換,但生產代碼強制要求顯式轉換,避免格式模型不匹配 (ORA-01861)、性能損耗、計劃不穩定。
| 轉換方向 | 函數 | 語法 | 示例 |
|---|---|---|---|
| 字符 → 數字 | TO_NUMBER | TO_NUMBER(char [, fmt [, nlsparam]]) | TO_NUMBER('1,234.56', '9,999.99') → 1234.56 |
| 數字 → 字符 | TO_CHAR | TO_CHAR(num [, fmt [, nlsparam]]) | TO_CHAR(1234.56, 'FM9,999.00') → '1,234.56' |
| 字符 → 日期 | TO_DATE | TO_DATE(char [, fmt [, nlsparam]]) | TO_DATE('2024-01-15 14:30:00', 'YYYY-MM-DD HH24:MI:SS') |
| 日期 → 字符 | TO_CHAR | TO_CHAR(date [, fmt [, nlsparam]]) | TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') |
| 字符 → 時間戳 | TO_TIMESTAMP | TO_TIMESTAMP(char [, fmt [, nlsparam]]) | TO_TIMESTAMP('2024-01-15 14:30:00.123', 'YYYY-MM-DD HH24:MI:SS.FF3') |
| 字符 → 間隔 | TO_DSINTERVAL / TO_YMINTERVAL | 標準 ISO 格式 | TO_DSINTERVAL('10 12:30:00') |
YYYY 四位年 MM 月(01-12) DD 日(01-31) HH24 24制時 MI 分(00-59) SS 秒(00-59) DAY 星期名 MON 月份縮寫 DY 星期縮寫 FM 填充模式關閉 (去除前導零/空格) FX 精確匹配模式 (格式字符串需完全匹配)
PL/SQL 採用**靜態詞法作用域**,塊結構決定變量生命周期。
DECLARE
v_msg VARCHAR2(20) := 'Outer Block'; -- 外層變量
BEGIN
DBMS_OUTPUT.PUT_LINE('1. ' || v_msg); -- 輸出: Outer Block
DECLARE
v_msg VARCHAR2(20) := 'Inner Block'; -- 內層同名變量 (遮蔽外層)
BEGIN
DBMS_OUTPUT.PUT_LINE('2. ' || v_msg); -- 輸出: Inner Block
END;
DBMS_OUTPUT.PUT_LINE('3. ' || v_msg); -- 輸出: Outer Block (內層結束,外層恢復可見)
END;
/
-- 輸出結果:
-- 1. Outer Block
-- 2. Inner Block
-- 3. Outer Block
<> DECLARE v_num NUMBER := 100; BEGIN < > DECLARE v_num NUMBER := 200; BEGIN DBMS_OUTPUT.PUT_LINE('Inner v_num: ' < >.v_num); -- 200 DBMS_OUTPUT.PUT_LINE('Outer v_num: ' < >.v_num); -- 100 (標籤限定訪問外層) END; END outer_block; /
DECLARE
-- 錨定類型聲明
v_empno emp.empno%TYPE := 7788;
v_ename emp.ename%TYPE;
v_sal emp.sal%TYPE;
v_comm emp.comm%TYPE;
v_annual NUMBER(12,2);
c_tax_rate CONSTANT NUMBER(3,2) := 0.03; -- 常量聲明
BEGIN
-- 查詢賦值,處理 NULL 佣金
SELECT ename, sal, NVL(comm, 0)
INTO v_ename, v_sal, v_comm
FROM emp
WHERE empno = v_empno;
-- 計算年薪: (月薪 + 佣金) * 12
v_annual := (v_sal + v_comm) * 12;
-- 輸出格式化
DBMS_OUTPUT.PUT_LINE('員工: ' || v_ename);
DBMS_OUTPUT.PUT_LINE('月薪: ' || TO_CHAR(v_sal, 'FM99,999.00'));
DBMS_OUTPUT.PUT_LINE('佣金: ' || TO_CHAR(v_comm, 'FM99,999.00'));
DBMS_OUTPUT.PUT_LINE('年薪: ' || TO_CHAR(v_annual, 'FM999,999.00'));
DBMS_OUTPUT.PUT_LINE('稅後: ' || TO_CHAR(v_annual * (1 - c_tax_rate), 'FM999,999.00'));
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('員工編號 ' || v_empno || ' 不存在');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('錯誤: ' || SQLERRM);
END;
/
扎實掌握類型系統、運算符語義、NULL 邏輯、轉換函數與作用域規則,是編寫健壯、可移植、高性能 PL/SQL 代碼的前提。建議建立團隊編碼規範,強制顯式轉換、NULL 防禦、錨定類型、標準命名,通過代碼審查與靜態分析工具 (如 PMD、SonarQube PL/SQL 插件) 落地執行。