CREATE PROCEDURE Zy_Make_SortDeliverLineID
@BatchNo INT,
@ProcessType INT,
@o_err_no INT OUTPUT,
@o_err_msg VARCHAR(1024) OUTPUT
AS
BEGIN
DELETE FROM Zy_Sort_LineExitDeliverID WHERE BatchNo = @BatchNo;
IF @@ERROR <> 0 GOTO ErrHandler;
UPDATE Zy_Base_DeliverLine SET Surfix = ' ', DistributeType = 0
WHERE SUBSTRING(DeliverLineID,1,4) <> '2000';
IF @@ERROR <> 0 GOTO ErrHandler;
IF @ProcessType = 1
BEGIN
EXEC Zy_Insert_TrunkSplitData @BatchNo, @o_err_no, @o_err_msg;
IF @o_err_no <> 0 GOTO ErrHandler;
END
DELETE FROM Zy_Order_Details WHERE Quantity = 0 AND BatchNo = @BatchNo;
IF @@ERROR <> 0 GOTO ErrHandler;
DELETE FROM Zy_Order_Main WHERE BatchNo = @BatchNo
AND OrderID NOT IN (SELECT DISTINCT OrderID FROM Zy_Order_Details WHERE BatchNo = @BatchNo);
IF @@ERROR <> 0 GOTO ErrHandler;
UPDATE Zy_Base_BatchNo SET OrderQuan = (SELECT COUNT(DISTINCT OrderID)
FROM Zy_Order_Main WHERE BatchNo = @BatchNo)
WHERE BatchNo = @BatchNo;
IF @@ERROR <> 0 GOTO ErrHandler;
SET @o_err_no = 0; SET @o_err_msg = '執行成功'; RETURN;
ErrHandler:
SET @o_err_no = -1;
SET @o_err_msg = '【出錯對象】{Zy_Insert_TrunkSplitData}' + @o_err_msg;
END;
CREATE OR REPLACE FUNCTION Zy_Make_SortDeliverLineID
(BatchNo IN INTEGER,
ProcessType IN INTEGER,
o_err_no OUT INTEGER,
o_err_msg OUT VARCHAR2)
RETURN VARCHAR2
IS
BEGIN
-- 1. 刪除 (獨立 BEGIN-END 塊捕獲異常)
BEGIN
DELETE FROM Zy_Sort_LineExitDeliverID WHERE BatchNo = BatchNo;
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
o_err_no := -1;
o_err_msg := '刪除 [Zy_Sort_LineExitDeliverID] 時出錯!';
RETURN;
END;
-- 2. 更新
BEGIN
UPDATE Zy_Base_DeliverLine
SET Surfix = ' ', DistributeType = 0
WHERE SUBSTR(DeliverLineID,1,4) <> '2000'; -- SUBSTRING → SUBSTR
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
o_err_no := -1;
o_err_msg := '更新 [Zy_Base_DeliverLine] 時出錯!';
RETURN;
END;
-- 3. 條件呼叫子程序
IF ProcessType = 1 THEN
Zy_Insert_TrunkSplitData(BatchNo, o_err_no, o_err_msg); -- 無 EXEC
IF o_err_no <> 0 THEN ROLLBACK; RETURN; END IF;
END IF;
-- 4. 後續 DML 同理包裹 EXCEPTION
BEGIN
DELETE FROM Zy_Order_Details WHERE Quantity = 0 AND BatchNo = BatchNo;
EXCEPTION WHEN OTHERS THEN
ROLLBACK; o_err_no := -1; o_err_msg := '刪除 [Zy_Order_Details] 時出錯!'; RETURN;
END;
-- ... 略 ...
COMMIT; -- 顯式提交
o_err_no := 0;
o_err_msg := '執行成功';
RETURN o_err_msg;
END Zy_Make_SortDeliverLineID;
| SQL Server | Oracle | 備註 |
|---|---|---|
| CREATE PROCEDURE ... @param TYPE OUTPUT | CREATE OR REPLACE FUNCTION ... (param IN/OUT TYPE) RETURN TYPE | Oracle 常用函數返回狀態,OUT 參數攜帶詳細錯誤 |
| @@ERROR | EXCEPTION WHEN OTHERS | Oracle 無全域錯誤變數,必須用 PL/SQL 區塊捕獲 |
| GOTO Label | EXCEPTION 區塊 + RETURN/RAISE | 結構化異常處理取代 GOTO |
| EXEC proc @p1, @p2 OUT | proc(p1, p2); -- 直接呼叫 | OUT 參數自動回填 |
| SUBSTRING(str, start, len) | SUBSTR(str, start, len) | 參數順序相同 |
| IF @@ERROR <> 0 GOTO Err | BEGIN ... EXCEPTION WHEN OTHERS THEN ... END; | 每個 DML 建議獨立區塊,精準定位錯誤 |
| 隱式交易 (隱式 BEGIN TRAN) | 顯式 COMMIT/ROLLBACK | Oracle 預設不自動提交 |
CREATE OR REPLACE FUNCTION fn_getEmpName(p_str IN VARCHAR2, p_delim IN VARCHAR2)
RETURN VARCHAR2
IS
v_str VARCHAR2(4000) := '';
v_name VARCHAR2(4000);
CURSOR c IS
SELECT * FROM TABLE(CAST(fn_split(p_str, p_delim) AS ty_str_split));
r c%ROWTYPE;
BEGIN
OPEN c;
LOOP
FETCH c INTO r;
EXIT WHEN c%NOTFOUND;
BEGIN -- 內層 BEGIN-END 針對單行查詢異常
SELECT employee_name INTO v_name
FROM mocha_osm_emp a, mocha_osm_accounts b
WHERE a.id = b.emp_id AND b.user_id = r.column_value;
v_str := v_str || v_name || p_delim;
EXCEPTION WHEN NO_DATA_FOUND THEN
v_name := ''; -- 忽略無匹配行
END;
END LOOP;
CLOSE c;
IF LENGTH(v_str) > 0 THEN v_str := SUBSTR(v_str, 1, LENGTH(v_str)-1); END IF;
RETURN v_str;
END fn_getEmpName;
關鍵:游標迴圈內的單行查詢必須包裹在 BEGIN ... EXCEPTION WHEN NO_DATA_FOUND THEN ... END; 中,否則 NO_DATA_FOUND 會直接中斷整個函數。
BEGIN-END + EXCEPTION WHEN OTHERS。@@ERROR / GOTO 全部改為異常區塊。EXEC 移除,直接呼叫程序/函數。GETDATE() → SYSDATE、ISNULL → NVL、CONVERT → TO_CHAR/TO_DATE)。#temp → GLOBAL TEMPORARY TABLE 或 PL/SQL Collection。EXEC(@sql) → EXECUTE IMMEDIATE sql (注意綁定變數)。SQL Server → Oracle 移植核心在於**交易控制顯式化**與**異常處理結構化**。養成「每個 DML 一個 PL/SQL 區塊」習慣,配合對照表機械替換語法,即可高質量完成遷移。