You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:将含游标与循环的Oracle函数转换为Snowflake函数

Oracle函数GET_SN_CONCAT转Snowflake函数报错修复

需要将Oracle的GET_SN_CONCAT函数转换为Snowflake版本,该函数根据传入参数通过游标循环拼接所有序列号并返回结果。改写后出现语法错误,附上相关代码及报错信息,寻求解决方法。


原Oracle函数代码

CREATE OR REPLACE FUNCTION C3BL_HADOOP.GET_SN_CONCAT (
   P_ID1_I          NUMBER,
   P_ID2_I          NUMBER,
   P_SN_TYPE_I      VARCHAR2,
   P_DELIMITER_I    VARCHAR2)
   RETURN VARCHAR2
AS
   CURSOR C_SHIP_SN
   IS
      SELECT DISTINCT NVL ( (SELECT ACTUAL_SERIAL_NO
                               FROM XXCCS_BOP_SN_CROSS_REF
                              WHERE DUM_ID = SERIAL.FM_SERIAL_NUMBER),
                           TRIM (SERIAL.FM_SERIAL_NUMBER))
                         FM_SERIAL_NUMBER
        FROM XXCCS_BOP_OE_SHIP_LINES_SN  SERIAL,
             XXCCS_BOP_OE_SHIPMENT_LINES SHIP
       WHERE     SHIP.DELIVERY_DETAIL_ID = SERIAL.DELIVERY_DETAIL_ID
             AND SHIP.LINE_ID = P_ID1_I
      UNION
      SELECT DISTINCT NVL ( (SELECT ACTUAL_SERIAL_NO
                               FROM XXCCS_BOP_SN_CROSS_REF
                              WHERE DUM_ID = XOSL.SERIAL_NUMBER),
                           TRIM (XOSL.SERIAL_NUMBER))
                         FM_SERIAL_NUMBER
        FROM XXCCS_BOP_OE_SHIPMENT_LINES XOSL
       WHERE XOSL.LINE_ID = P_ID1_I;

   CURSOR C_RECEIPT_SN
   IS
      SELECT DISTINCT NVL ( (SELECT ACTUAL_SERIAL_NO
                               FROM XXCCS_BOP_SN_CROSS_REF
                              WHERE DUM_ID = SERIAL_NUM),
                           TRIM (SERIAL_NUM))
                         SERIAL_NUM
        FROM XXCCS_BOP_OE_RCPT_LINES_SN
       WHERE SHIPMENT_LINE_ID = P_ID1_I AND TRANSACTION_ID = P_ID2_I;

   CURSOR C_CUST_SN
   IS
      SELECT DISTINCT
             NVL (TRIM (ATTRIBUTE9), TRIM (FROM_SERIAL_NUMBER))
                FROM_SERIAL_NUMBER
        FROM XXCCS_BOP_OE_LOT_SERIAL_NUM 
       WHERE LINE_ID = P_ID1_I;

   CURSOR C_SPARE_SN
   IS
      SELECT NVL (TRIM (ATTRIBUTE9), TRIM (FROM_SERIAL_NUMBER))
                FROM_SERIAL_NUMBER
        FROM XXCCS_BOP_OE_LOT_SERIAL_NUM
       WHERE LINE_ID = P_ID1_I;

   L_START       NUMBER := 0;
   L_SN_CONCAT   VARCHAR2 (4000);
BEGIN
   IF P_SN_TYPE_I = 'SHIP'
   THEN
      FOR C_SN_CUR IN C_SHIP_SN
      LOOP
         IF L_START = 0
         THEN
            IF C_SN_CUR.FM_SERIAL_NUMBER IS NOT NULL
            THEN
               L_SN_CONCAT := C_SN_CUR.FM_SERIAL_NUMBER;
            END IF;
         ELSE
            IF C_SN_CUR.FM_SERIAL_NUMBER IS NOT NULL
            THEN
               L_SN_CONCAT :=
                  L_SN_CONCAT || P_DELIMITER_I || C_SN_CUR.FM_SERIAL_NUMBER;
            END IF;
         END IF;

         L_START := L_START + 1;
      END LOOP;
   ELSIF P_SN_TYPE_I = 'RECEIPT'
   THEN
      FOR C_SN_CUR IN C_RECEIPT_SN
      LOOP
         IF L_START = 0
         THEN
            IF C_SN_CUR.SERIAL_NUM IS NOT NULL
            THEN
               L_SN_CONCAT := C_SN_CUR.SERIAL_NUM;
            END IF;
         ELSE
            IF C_SN_CUR.SERIAL_NUM IS NOT NULL
            THEN
               L_SN_CONCAT :=
                  L_SN_CONCAT || P_DELIMITER_I || C_SN_CUR.SERIAL_NUM;
            END IF;
         END IF;

         L_START := L_START + 1;
      END LOOP;
   ELSIF P_SN_TYPE_I = 'CUST'
   THEN
      FOR C_SN_CUR IN C_CUST_SN
      LOOP
         IF L_START = 0
         THEN
            IF C_SN_CUR.FROM_SERIAL_NUMBER IS NOT NULL
            THEN
               L_SN_CONCAT := C_SN_CUR.FROM_SERIAL_NUMBER;
            END IF;
         ELSE
            IF C_SN_CUR.FROM_SERIAL_NUMBER IS NOT NULL
            THEN
               L_SN_CONCAT :=
                  L_SN_CONCAT || P_DELIMITER_I || C_SN_CUR.FROM_SERIAL_NUMBER;
            END IF;
         END IF;

         L_START := L_START + 1;
      END LOOP;
END IF;

   RETURN (L_SN_CONCAT);
EXCEPTION
   WHEN OTHERS
   THEN
      RETURN (L_SN_CONCAT);
END GET_SN_CONCAT;
/

尝试改写的Snowflake代码

CREATE OR REPLACE FUNCTION CX_DB.CX_GSLOBAC_STG.TEMP_DEVA_GET_SN_CONCAT 
(  "P_ID1_I"          NUMBER,
   "P_ID2_I"          NUMBER,
   --P_SN_TYPE_I      VARCHAR,
   "P_DELIMITER_I"    VARCHAR)
   RETURNS VARCHAR
   LANGUAGE SQL
as
$$
DECLARE

C_RECEIPT_SN CURSOR FOR 
                SELECT DISTINCT NVL (a.ACTUAL_SERIAL_NO,
                TRIM (b.SERIAL_NUM))
                SERIAL_NUM
                FROM EDW_SERVICE_ETL_DB.SS.CSF_RCV_SERIAL_TRANSACTIONS b,
                left join (SELECT DUM_ID,ACTUAL_SERIAL_NO
                FROM EDW_SERVICE_ETL_DB.SS.CSF_XXCTS_INV_SNO_CROSS_REF) a
                a.DUM_ID = b.SERIAL_NUM
                WHERE SHIPMENT_LINE_ID = P_ID1_I AND TRANSACTION_ID = P_ID2_I;

L_SN_CONCAT VARCHAR;

BEGIN
LET L_START       NUMBER := 0;
FOR C_SN_CUR in C_RECEIPT_SN DO
    IF L_START = 0
     THEN
        IF C_SN_CUR.SERIAL_NUM IS NOT NULL
        THEN
           L_SN_CONCAT := C_SN_CUR.SERIAL_NUM;
        END IF;
     ELSE
        IF C_SN_CUR.SERIAL_NUM IS NULL
        THEN
           L_SN_CONCAT :=
              L_SN_CONCAT || P_DELIMITER_I || C_SN_CUR.SERIAL_NUM;
        END IF;
     END IF;
    L_START := L_START + 1;
END FOR;
RETURN L_SN_CONCAT;
END;
$$
;

报错信息

syntax error line 4 at position 0 unexpected 'C_RECEIPT_SN'. syntax
error line 5 at position 37 unexpected 'a'. syntax error line 12 at
position 16 unexpected 'WHERE'. (line 4)


错误原因及修复方案

1. SQL语言UDF不支持游标和循环

Snowflake的LANGUAGE SQL类型UDF仅支持单条SQL语句逻辑,无法使用游标、DECLARE变量、FOR循环等PL/SQL风格语法。要实现循环逻辑,需改用LANGUAGE JAVASCRIPT类型UDF,或者用Snowflake原生的LISTAGG聚合函数直接拼接,无需循环。

2. 游标SQL语句语法错误

原游标中的JOIN语法存在两处错误:

  • FROM后多余的逗号需删除,LEFT JOIN前不需要逗号分隔表
  • 表关联条件前必须添加ON关键字

修正后的游标SQL:

SELECT DISTINCT NVL(a.ACTUAL_SERIAL_NO, TRIM(b.SERIAL_NUM)) AS SERIAL_NUM
FROM EDW_SERVICE_ETL_DB.SS.CSF_RCV_SERIAL_TRANSACTIONS b
LEFT JOIN (SELECT DUM_ID, ACTUAL_SERIAL_NO FROM EDW_SERVICE_ETL_DB.SS.CSF_XXCTS_INV_SNO_CROSS_REF) a
ON a.DUM_ID = b.SERIAL_NUM
WHERE SHIPMENT_LINE_ID = P_ID1_I AND TRANSACTION_ID = P_ID2_I

3. 循环条件逻辑错误

原代码中ELSE分支判断C_SN_CUR.SERIAL_NUM IS NULL,会导致空值被错误拼接,应改为IS NOT NULL,与第一个分支逻辑保持一致。

4. 完整修复后的JavaScript版本UDF(还原所有分支逻辑)

以下是匹配原Oracle函数所有分支逻辑的Snowflake JavaScript UDF:

CREATE OR REPLACE FUNCTION CX_DB.CX_GSLOBAC_STG.GET_SN_CONCAT(
    P_ID1_I NUMBER,
    P_ID2_I NUMBER,
    P_SN_TYPE_I VARCHAR,
    P_DELIMITER_I VARCHAR
) RETURNS VARCHAR
LANGUAGE JAVASCRIPT
AS
$$
var sn_concat = "";
var start = 0;

// 处理SHIP类型
if (P_SN_TYPE_I === 'SHIP') {
    var ship_sql = `
        SELECT DISTINCT NVL((SELECT ACTUAL_SERIAL_NO FROM XXCCS_BOP_SN_CROSS_REF WHERE DUM_ID = SERIAL.FM_SERIAL_NUMBER), TRIM(SERIAL.FM_SERIAL_NUMBER)) AS FM_SERIAL_NUMBER
        FROM XXCCS_BOP_OE_SHIP_LINES_SN SERIAL
        JOIN XXCCS_BOP_OE_SHIPMENT_LINES SHIP ON SHIP.DELIVERY_DETAIL_ID = SERIAL.DELIVERY_DETAIL_ID
        WHERE SHIP.LINE_ID = ?
        UNION
        SELECT DISTINCT NVL((SELECT ACTUAL_SERIAL_NO FROM XXCCS_BOP_SN_CROSS_REF WHERE DUM_ID = XOSL.SERIAL_NUMBER), TRIM(XOSL.SERIAL_NUMBER)) AS FM_SERIAL_NUMBER
        FROM XXCCS_BOP_OE_SHIPMENT_LINES XOSL
        WHERE XOSL.LINE_ID = ?
    `;
    var stmt = snowflake.createStatement({sqlText: ship_sql, binds: [P_ID1_I, P_ID1_I]});
    var rs = stmt.execute();
    while (rs.next()) {
        var sn = rs.getColumnValue(1);
        if (sn !== null) {
            if (start === 0) {
                sn_concat = sn;
            } else {
                sn_concat += P_DELIMITER_I + sn;
            }
            start++;
        }
    }
}
// 处理RECEIPT类型
else if (P_SN_TYPE_I === 'RECEIPT') {
    var receipt_sql = `
        SELECT DISTINCT NVL(a.ACTUAL_SERIAL_NO, TRIM(b.SERIAL_NUM)) AS SERIAL_NUM
        FROM EDW_SERVICE_ETL_DB.SS.CSF_RCV_SERIAL_TRANSACTIONS b
        LEFT JOIN (SELECT DUM_ID, ACTUAL_SERIAL_NO FROM EDW_SERVICE_ETL_DB.SS.CSF_XXCTS_INV_SNO_CROSS_REF) a
        ON a.DUM_ID = b.SERIAL_NUM
        WHERE SHIPMENT_LINE_ID = ? AND TRANSACTION_ID = ?
    `;
    var stmt = snowflake.createStatement({sqlText: receipt_sql, binds: [P_ID1_I, P_ID2_I]});
    var rs = stmt.execute();
    while (rs.next()) {
        var sn = rs.getColumnValue(1);
        if (sn !== null) {
            if (start === 0) {
                sn_concat = sn;
            } else {
                sn_concat += P_DELIMITER_I + sn;
            }
            start++;
        }
    }
}
// 处理CUST类型
else if (P_SN_TYPE_I === 'CUST') {
    var cust_sql = `
        SELECT DISTINCT NVL(TRIM(ATTRIBUTE9), TRIM(FROM_SERIAL_NUMBER)) AS FROM_SERIAL_NUMBER
        FROM XXCCS_BOP_OE_LOT_SERIAL_NUM
        WHERE LINE_ID = ?
    `;
    var stmt = snowflake.createStatement({sqlText: cust_sql, binds: [P_ID1_I]});
    var rs = stmt.execute();
    while (rs.next()) {
        var sn = rs.getColumnValue(1);
        if (sn !== null) {
            if (start === 0) {
                sn_concat = sn;
            } else {
                sn_concat += P_DELIMITER_I + sn;
            }
            start++;
        }
    }
}

return sn_concat;
$$;

5. 简化版:使用LISTAGG聚合函数(无需循环)

若无需复杂的空值判断逻辑,可直接用Snowflake的LISTAGG函数实现拼接,性能更优:

CREATE OR REPLACE FUNCTION CX_DB.CX_GSLOBAC_STG.GET_SN_CONCAT(
    P_ID1_I NUMBER,
    P_ID2_I NUMBER,
    P_SN_TYPE_I VARCHAR,
    P_DELIMITER_I VARCHAR
) RETURNS VARCHAR
LANGUAGE SQL
AS
$$
CASE P_SN_TYPE_I
    WHEN 'SHIP' THEN (
        SELECT LISTAGG(DISTINCT NVL((SELECT ACTUAL_SERIAL_NO FROM XXCCS_BOP_SN_CROSS_REF WHERE DUM_ID = SERIAL.FM_SERIAL_NUMBER), TRIM(SERIAL.FM_SERIAL_NUMBER)), P_DELIMITER_I)
        FROM XXCCS_BOP_OE_SHIP_LINES_SN SERIAL
        JOIN XXCCS_BOP_OE_SHIPMENT_LINES SHIP ON SHIP.DELIVERY_DETAIL_ID = SERIAL.DELIVERY_DETAIL_ID
        WHERE SHIP.LINE_ID = P_ID1_I
        UNION ALL
        SELECT LISTAGG(DISTINCT NVL((SELECT ACTUAL_SERIAL_NO FROM XXCCS_BOP_SN_CROSS_REF WHERE DUM_ID = XOSL.SERIAL_NUMBER), TRIM(XOSL.SERIAL_NUMBER)), P_DELIMITER_I)
        FROM XXCCS_BOP_OE_SHIPMENT_LINES XOSL
        WHERE XOSL.LINE_ID = P_ID1_I
    )
    WHEN 'RECEIPT' THEN (
        SELECT LISTAGG(DISTINCT NVL(a.ACTUAL_SERIAL_NO, TRIM(b.SERIAL_NUM)), P_DELIMITER_I)
        FROM EDW_SERVICE_ETL_DB.SS.CSF_RCV_SERIAL_TRANSACTIONS b
        LEFT JOIN (SELECT DUM_ID, ACTUAL_SERIAL_NO FROM EDW_SERVICE_ETL_DB.SS.CSF_XXCTS_INV_SNO_CROSS_REF) a
        ON a.DUM_ID = b.SERIAL_NUM
        WHERE SHIPMENT_LINE_ID = P_ID1_I AND TRANSACTION_ID = P_ID2_I
    )
    WHEN 'CUST' THEN (
        SELECT LISTAGG(DISTINCT NVL(TRIM(ATTRIBUTE9), TRIM(FROM_SERIAL_NUMBER)), P_DELIMITER_I)
        FROM XXCCS_BOP_OE_LOT_SERIAL_NUM
        WHERE LINE_ID = P_ID1_I
    )
END
$$;

内容的提问来源于stack exchange,提问作者Shiva Shylaja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 08:25:55