核心語法差異速查表
| 特性 | SQL Server (T-SQL) | Oracle (PL/SQL) |
| 返回單值 | 存儲過程 + OUTPUT / RETURN | **函數 (FUNCTION)** 更適合 |
| 大量處理無返回 | 存儲過程 | 存儲過程 (PROCEDURE) |
| 參數定義位置 | `CREATE PROC @p1 INT, @p2 INT AS` (無括號) | `CREATE PROCEDURE (P1 IN INT, P2 OUT INT) AS` (**有括號**) |
| 區域變數宣告 | `DECLARE @v INT` (**@ 前綴**) | `V INT;` (**無 @、在 DECLARE 區或 BEGIN 前**) |
| DDL 在過程中 | 直接執行 | **禁止**,需 `EXECUTE IMMEDIATE` 動態 SQL |
| 臨時表 | `#temp` (連線級)、`##temp` (全域) | **全域暫存表** (結構永久、資料會話隔離),建表需在過程外 |
| IF EXISTS 用法 | `IF EXISTS (SELECT ...) BEGIN ... END` | `EXISTS` **只能用在 WHERE**,需 `SELECT COUNT(*) INTO v ... IF v>0 THEN` |
| IF 語法 | `IF @x=1 BEGIN ... END ELSE BEGIN ... END` | `IF x=1 THEN ... ELSIF x=2 THEN ... ELSE ... END IF;` |
| 空字串 vs NULL | `''` ≠ `NULL` | `''` **等同於** `NULL` |
| 事務提交 | 隱式自動提交 (可關閉) | **預設需顯式 COMMIT** |
| 調用過程 | `EXEC proc` / `EXECUTE proc` | 直接 `proc(...);` 或 `CALL proc(...);` (**無 EXEC**) |
| UPDATE/DELETE ... FROM | 支援 `UPDATE T SET ... FROM T JOIN ...` | **不支援**,需子查詢或 MERGE |
| 隱式日期轉換 | 靈活 | 受 `NLS_DATE_FORMAT` 參數控制,較嚴格 |
遷移實戰範例:跨系統對賬存儲過程
原 T-SQL (簡化)
CREATE PROCEDURE P_GLBalanceWithOtherSys
@FEntityNo VARCHAR(12), @FYear INT, @FPeriod INT
AS
DECLARE @ret INT, @sret CHAR(1)
IF (SELECT FValue FROM T_SysParam WHERE FEntityNo=@FEntityNo AND FSysNo='Csh' AND FParamNo='CshStartFlag') = '1'
BEGIN
CREATE TABLE #Temp1 (FAmount DECIMAL(18,2))
INSERT INTO #Temp1
SELECT ABS(SUM(A.FBeginAmountOrg)-SUM(B.FBeginAmount)) + ...
FROM T_GLBal A, T_CshBal B
WHERE A.FEntityNo=B.FEntityNo AND A.FYear=B.FYear AND A.FPeriod=B.FPeriod
AND A.FAcctNo=B.FAcctNo AND A.FCurrency=B.FCurrency AND A.FStatus='0'
AND A.FEntityNo=@FEntityNo
GROUP BY A.FEntityNo, A.FYear, A.FPeriod, A.FAcctNo, A.FCurrency
IF EXISTS (SELECT * FROM #Temp1 WHERE FAmount <> 0)
BEGIN
RAISERROR('對賬不平!',18,1)
RETURN -1
END
DROP TABLE #Temp1
END
目標 PL/SQL (Oracle 10g+)
CREATE OR REPLACE PROCEDURE P_GLBalanceWithOtherSys (
P_FENTITYNO IN VARCHAR2,
P_FYEAR IN NUMBER,
P_FPERIOD IN NUMBER
) AS
V_FLAG CHAR(1);
V_CNT NUMBER;
BEGIN
-- 1. 參數查詢
SELECT FVALUE INTO V_FLAG
FROM T_SYSPARAM
WHERE FENTITYNO = P_FENTITYNO AND FSYSNO = 'Csh' AND FPARAMNO = 'CshStartFlag';
IF V_FLAG = '1' THEN
-- 2. 全域暫存表 (需預先建立結構: CREATE GLOBAL TEMPORARY TABLE GTT_TEMP1 (FAMOUNT NUMBER) ON COMMIT DELETE ROWS;)
INSERT INTO GTT_TEMP1
SELECT ABS(SUM(A.FBEGINAMOUNTORG)-SUM(B.FBEGINAMOUNT)) + ...
FROM T_GLBAL A, T_CSHBAL B
WHERE A.FENTITYNO = B.FENTITYNO
AND A.FYEAR = B.FYEAR
AND A.FPERIOD = B.FPERIOD
AND A.FACCTNO = B.FACCTNO
AND A.FCURRENCY = B.FCURRENCY
AND A.FSTATUS = '0'
AND A.FENTITYNO = P_FENTITYNO
GROUP BY A.FENTITYNO, A.FYEAR, A.FPERIOD, A.FACCTNO, A.FCURRENCY;
-- 3. 替代 IF EXISTS
SELECT COUNT(*) INTO V_CNT FROM GTT_TEMP1 WHERE FAMOUNT <> 0;
IF V_CNT > 0 THEN
RAISE_APPLICATION_ERROR(-20001, '對賬不平!');
RETURN; -- 過程無返回值用 RETURN
END IF;
-- 4. ON COMMIT DELETE ROWS 自動清理,無需 DROP
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20002, '系統參數未配置');
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20999, '對賬異常: ' || SQLERRM);
END P_GLBalanceWithOtherSys;
/
遷移關鍵點總結
- 單值返回改用 **FUNCTION**。
- 臨時表 → **全域暫存表 (GTT)**,結構永久、資料隔離、提交自動清理。
- `IF EXISTS` → `SELECT COUNT(*) INTO v ... IF v>0`。
- `RAISERROR` → `RAISE_APPLICATION_ERROR(-20xxx, 'msg')` (自定義錯誤碼 -20000~-20999)。
- 動態 SQL (`EXECUTE IMMEDIATE`) 處理 DDL/動態表名。
- 日期處理統一用 `TO_DATE`/`TO_CHAR` 配合 `NLS_DATE_FORMAT`。
- 事務邊界明確:`COMMIT`/`ROLLBACK` 由應用或過程控制。