表結構是系統演進的骨架。糟糕的字段設計導致:審計無據、業務邏輯硬編碼、DDL 變更頻發引發鎖爭用、查詢性能退化。本文提煉六大通用字段模式,覆蓋審計、生命週期、軟刪除、主鍵策略、擴展性、大表優化,助力構建可演進的企業級數據模型。
| 字段名 | 類型 | 含義 | 賦值策略 |
|---|---|---|---|
| CREATED_BY | NUMBER(18) | 創建人 ID | 應用層注入當前登錄用戶 ID (非 DB USER) |
| CREATION_DATE | TIMESTAMP(6) WITH TIME ZONE | 創建時間 | DB 端默認值 `SYSTIMESTAMP`,避免客戶端時間不準 |
| LAST_UPDATED_BY | NUMBER(18) | 最後修改人 ID | 應用層每次 UPDATE 注入 |
| LAST_UPDATE_DATE | TIMESTAMP(6) WITH TIME ZONE | 最後修改時間 | DB 端默認值 `SYSTIMESTAMP` + 觸發器/應用層雙重保險 |
CREATE OR REPLACE TRIGGER trg_table_who
BEFORE INSERT OR UPDATE ON my_table
FOR EACH ROW
BEGIN
IF INSERTING THEN
:NEW.creation_date := SYSTIMESTAMP;
:NEW.created_by := NVL(SYS_CONTEXT('APP_CTX','USER_ID'), -1); -- 從上下文獲取
ELSIF UPDATING THEN
:NEW.last_update_date := SYSTIMESTAMP;
:NEW.last_updated_by := NVL(SYS_CONTEXT('APP_CTX','USER_ID'), -1);
END IF;
END;
| 字段名 | 類型 | 含義 | 取值規範 |
|---|---|---|---|
| STATUS | NUMBER(2) | 啟用狀態 | 0=失效/下架, 1=生效/上架, 2=草稿, 3=審核中... (擴展用常數表/Check 约束) |
| START_DATE | DATE / TIMESTAMP | 生效開始 | NULL=即時生效;查詢條件 `START_DATE IS NULL OR START_DATE <= SYSDATE` |
| END_DATE | DATE / TIMESTAMP | 生效結束 | NULL=永久生效;查詢條件 `END_DATE IS NULL OR END_DATE >= SYSDATE` |
CREATE OR REPLACE VIEW v_active_articles AS
SELECT * FROM articles
WHERE status = 1
AND (start_date IS NULL OR start_date <= SYSDATE)
AND (end_date IS NULL OR end_date >= SYSDATE);
-- 所有業務查詢走視圖,保證一致性,避免 WHERE 子句漏寫
| 字段名 | 類型 | 含義 | 策略 |
|---|---|---|---|
| DELETED | NUMBER(1) | 刪除標誌 | 0=正常, 1=已刪除。**所有查詢強制帶 `DELETED=0`** (視圖/框架攔截器強制) |
| DELETE_DATE | TIMESTAMP | 刪除時間 | 刪除操作時賦值 `SYSTIMESTAMP` |
| DELETED_BY | NUMBER(18) | 刪除人 | 應用層注入當前用戶 ID |
邏輯刪除後,業務鍵 (如 `USER_NAME`, `EMAIL`) 可能需重用。唯一索引會阻擋。
-- 方案:部分索引 (11g+ 不直接支援,變通)
-- 1. 組合唯一索引包含 DELETED
CREATE UNIQUE INDEX uk_user_email ON users (email, deleted);
-- 正常數據 (DELETED=0) 唯一;刪除數據 (DELETED=1) 允許重複 email
-- 注意:NULL 值在唯一索引中不參與唯一性檢查,若 DELETED 可為 NULL 需調整
-- 2. 虛擬列 + 唯一索引 (12c+ 推薦)
ALTER TABLE users ADD (email_active VARCHAR2(255) GENERATED ALWAYS AS
(CASE WHEN deleted=0 THEN email END) VIRTUAL);
CREATE UNIQUE INDEX uk_user_email_active ON users (email_active);
-- 僅正常數據的 email 參與唯一性
Oracle 無 MySQL `AUTO_INCREMENT` / SQL Server `IDENTITY` (12c 前),**Sequence 是唯一標準方案**。
-- 1. 原生 Identity (首選)
CREATE TABLE orders (
order_id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 CACHE 1000 ORDER),
...
);
-- 2. 11g/12c 兼容:Sequence + 默認值 (無觸發器開銷)
CREATE SEQUENCE order_seq CACHE 1000 ORDER;
CREATE TABLE orders (
order_id NUMBER DEFAULT order_seq.NEXTVAL PRIMARY KEY,
...
);
-- 12c+ DEFAULT ON NULL 允許顯式插入 NULL 觸發序列
大型系統上線後,**加列 (DDL) 風險極高**:排他鎖阻塞寫入、導致視圖/包/觸發器失效需重編譯、統計信息丟失。
ALTER TABLE core_business ADD (
attr_number_01 NUMBER, attr_number_02 NUMBER, ..., attr_number_10 NUMBER,
attr_string_01 VARCHAR2(4000), ..., attr_string_10 VARCHAR2(4000),
attr_date_01 TIMESTAMP, ..., attr_date_05 TIMESTAMP,
attr_clob_01 CLOB, attr_clob_02 CLOB
);
Oracle 行存儲格式:**尾部連續 NULL 列零字節存儲**;中間 NULL 僅佔 1 字節長度標誌。大量預留 NULL 列**幾乎零存儲成本**。
-- 主表:高頻、小長度、核心索引
CREATE TABLE users_core (
user_id NUMBER PRIMARY KEY,
login_name VARCHAR2(64) UNIQUE,
email VARCHAR2(128),
status NUMBER(1),
created_date TIMESTAMP,
-- 10-15 核心列
);
-- 擴展表:低頻、大字段、無索引或少索引
CREATE TABLE users_ext (
user_id NUMBER PRIMARY KEY, -- 同主鍵,外鍵關聯核心表
profile_json CLOB,
avatar_blob BLOB,
preferences VARCHAR2(4000),
-- 50+ 低頻列
);
-- 查詢聯合 (主鍵關聯 = 高效 Nested Loops)
SELECT c.login_name, e.profile_json
FROM users_core c JOIN users_ext e ON c.user_id = e.user_id
WHERE c.login_name = 'john';
| 指標 | 拆分前 (寬表) | 拆分後 (核心表) | 提升 |
|---|---|---|---|
| 平均行長度 | 2500 bytes | 200 bytes | 12.5x |
| 單塊行數 | 3 | 40 | 13x |
| 全表掃描 100萬行 I/O | ~300k 塊 | ~2.5k 塊 | 120x |
| 核心查詢緩存命中率 | 低 (大行擠占 Buffer Cache) | 高 | 顯著 |