Oracle內建單行單欄表 (SYS.DUAL,PUBLIC SYNONYM),任何用戶可讀,用於無目標表的SELECT:
SELECT USER FROM dual; -- 當前用戶
SELECT SYSDATE FROM dual; -- 當前日期時間
SELECT 1+2 FROM dual; -- 當計算機
SELECT seq.nextval FROM dual; -- 取序列值
-- 結構
CREATE TABLE sys.dual (dummy VARCHAR2(1)) TABLESPACE system ...;
-- 權限
GRANT SELECT ON sys.dual TO PUBLIC WITH GRANT OPTION;
VARCHAR2(1)而非CHAR(1):極簡設計,減少檢索開銷SYSTEM表空間:隔離用戶表空間,效能考量Oracle內核強制SYS.DUAL掃描永遠只回傳一行,無論實際資料筆數:
INSERT INTO dual VALUES ('Y'); -- 成功插入
COMMIT;
SELECT * FROM dual; -- 仍只回傳 'X' (原始行)
SELECT SYSDATE FROM dual; -- 只回傳一行日期
| 操作 | 實際結果 | 風險 |
|---|---|---|
| INSERT多行 | 內部邏輯過濾,SELECT仍只見一行 | PL/SQL SELECT ... INTO 可能拋 TOO_MANY_ROWS (ORA-01422) |
| TRUNCATE TABLE dual | 表變空 | SELECT SYSDATE FROM dual 回傳 NO DATA FOUND (ORA-01403),大量程式失效 |
| DELETE FROM dual | 每次僅刪一行,內部保護機制 | 最終仍可能刪光,同TRUNCATE後果 |
| DROP TABLE dual | 成功 | 資料庫無法啟動,報 ORA-1092 |
鐵律:DUAL表必須永遠有且僅有一行資料,嚴禁DML/DDL操作,嚴禁授予UPDATE/INSERT/DELETE權限。
-- 1. SYS用戶建立PFILE並關閉
CREATE PFILE='d:\pfile.bak' FROM SPFILE;
SHUTDOWN IMMEDIATE;
-- 2. 編輯PFILE,最後加入
replication_dependency_tracking = FALSE
-- 3. 以PFILE啟動 (允許操作系統表)
STARTUP PFILE='d:\pfile.bak';
-- 4. 重建DUAL表
CREATE TABLE "SYS"."DUAL" ("DUMMY" VARCHAR2(1)) PCTFREE 10 PCTUSED 4;
INSERT INTO dual VALUES ('X');
COMMIT;
GRANT SELECT ON dual TO PUBLIC;
-- 5. 驗證
SELECT * FROM dual; -- 應回傳 X
-- 6. 正常重啟
SHUTDOWN IMMEDIATE;
STARTUP; -- 使用SPFILE正常啟動
預防勝於治療:建立DDL觸發器攔截對SYS.DUAL的危險操作,並納入巡檢腳本定期驗證SELECT COUNT(*) FROM dual恆為1。