MySQL 適合中小型應用,Oracle 承擔超大規模核心業務。遷移不僅是語法轉換,更是**架構模式、數據類型、並發模型、運維體系**的全維度重構。本文梳理 8 大必踩坑點與標準化轉換方案。
AUTO_INCREMENT → SEQUENCE + 觸發器CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50));
INSERT INTO t (name) VALUES ('test'); -- id 自動賦值
-- 1. 創建序列
CREATE SEQUENCE seq_t_id
INCREMENT BY 1 START WITH 1
MAXVALUE 9999999999 CYCLE NOCACHE;
-- 2. 創建表(無自增屬性)
CREATE TABLE t (id NUMBER(10) PRIMARY KEY, name VARCHAR2(50));
-- 3. 創建觸發器自動填充
CREATE OR REPLACE TRIGGER trg_t_bir
BEFORE INSERT ON t
FOR EACH ROW
BEGIN
SELECT seq_t_id.NEXTVAL INTO :NEW.id FROM dual;
END;
/
-- 4. 插入時無需指定 id
INSERT INTO t (name) VALUES ('test');
遷移工具提示:自動化工具(如 AWS SCT、Ora2Pg)可自動生成 Sequence+Trigger 組合。
| 場景 | MySQL | Oracle | 轉換規則 |
|---|---|---|---|
| 字符串字面量 | 'abc' 或 "abc" | 僅 'abc' | 統一改單引號 |
| 字符串包含單引號 | 'don\'t' 或 "don't" | 'don''t' | 單引號轉雙單引號 |
| 標識符(表/列名) | `table` 或 "table" | "TABLE"(大寫) | Oracle 無引號默認大寫,雙引號保持大小寫 |
LIMIT → ROWNUM/OFFSET...FETCHSELECT * FROM t ORDER BY id LIMIT 20 OFFSET 40; -- 第 3 頁,每頁 20 條
SELECT * FROM t ORDER BY id
OFFSET 40 ROWS FETCH NEXT 20 ROWS ONLY;
-- 寫法一:雙層 ROWNUM
SELECT * FROM (
SELECT a.*, ROWNUM rn FROM (
SELECT * FROM t ORDER BY id
) a WHERE ROWNUM <= 60
) WHERE rn > 40;
-- 寫法二:ROW_NUMBER() 分析函數(性能更好)
SELECT * FROM (
SELECT t.*, ROW_NUMBER() OVER (ORDER BY id) rn FROM t
) WHERE rn BETWEEN 41 AND 60;
關鍵點:Oracle ROWNUM 是**結果集生成時**賦值,**不能用 ROWNUM > 40 直接過濾**(永無結果),必須先賦值再外層過濾。
TEXT/BLOB → CLOB/BLOB + DBMS_LOB| MySQL | Oracle | 注意事項 |
|---|---|---|
TEXT (64KB) | CLOB (4GB) | |
MEDIUMTEXT (16MB) | CLOB | |
LONGTEXT (4GB) | CLOB | |
BLOB | BLOB |
DBMS_LOBDECLARE
l_clob CLOB;
BEGIN
INSERT INTO t (id, content) VALUES (1, EMPTY_CLOB()) RETURNING content INTO l_clob;
DBMS_LOB.WRITEAPPEND(l_clob, LENGTH(:long_text), :long_text);
COMMIT;
END;
/
應用層(Java/JDBC)可用 PreparedStatement.setCharacterStream() 或 setClob() 直接流式寫入。
| 功能 | MySQL | Oracle |
|---|---|---|
| 當前時間 | NOW(), SYSDATE() | SYSDATE (精確到秒), SYSTIMESTAMP (精確到毫秒) |
| 字符串轉日期 | STR_TO_DATE('2024-01-01','%Y-%m-%d') | TO_DATE('2024-01-01','YYYY-MM-DD') |
| 日期轉字符串 | DATE_FORMAT(dt,'%Y-%m-%d %H:%i:%s') | TO_CHAR(dt,'YYYY-MM-DD HH24:MI:SS') |
| 日期運算(+7天) | DATE_ADD(NOW(), INTERVAL 7 DAY) | SYSDATE + 7 (數字代表天) |
| 日期運算(+3小時) | DATE_ADD(NOW(), INTERVAL 3 HOUR) | SYSDATE + 3/24 |
| 截取日期 | DATE(dt) | TRUNC(dt) |
-- MySQL
INSERT INTO t (col) VALUES (''); -- 合法,長度 0
INSERT INTO t (col) VALUES (NULL); -- 合法(除非 NOT NULL)
-- Oracle
INSERT INTO t (col) VALUES (''); -- 等同於 INSERT NULL,若 NOT NULL 會報錯
INSERT INTO t (col) VALUES (NULL); -- 同樣
'' 替換為單空格 ' ' 或特定標記 '[EMPTY]'**LIKE vs INSTR| 寫法 | MySQL | Oracle | 索引利用 |
|---|---|---|---|
col LIKE '%abc%' | ✅ 支持 | ✅ 支持 | ❌ 全表掃描(前導通配符) |
col LIKE 'abc%' | ✅ 走索引 | ✅ 走索引 | ✅ 索引範圍掃描 |
INSTR(col, 'abc') > 0 | ✅ 支持 | ✅ 推薦 | ❌ 通常不走索引(除非函數索引) |
建議:Oracle 中優先用 LIKE 'prefix%' 走索引;全文搜索場景使用 **Oracle Text** 或 **Elasticsearch** 外部引擎。
Oracle PL/SQL 中:
CURSOR c_data IS SELECT * FROM t;
v_rec c_data%ROWTYPE;
OPEN c_data;
LOOP
FETCH c_data INTO v_rec;
EXIT WHEN c_data%NOTFOUND;
-- 處理
END LOOP;
CLOSE c_data; -- 必須顯式關閉!
Java/JDBC:ResultSet、Statement、Connection 必須在 finally 塊中關閉,建議使用 try-with-resources (JDK 7+)。
MySQL → Oracle 遷移,**語法轉換只佔 30% 工作量**,70% 在於**架構適配、性能調優、運維體系重建、業務驗證**。建立標準化轉換規範、引入自動化工具、分階段灰度驗證,才能將風險控制在可接受範圍內,平滑完成核心系統上雲/遷移。