使用BULK COLLECT INTO遇ORA-00933错误,RETURNING子句存问题
解决ORA-00933:
FORALL+INSERT...SELECT搭配RETURNING的语法限制问题 你碰到的这个ORA-00933错误,其实是Oracle PL/SQL里一个很常见的语法限制问题——你在FORALL批量操作里用了INSERT...SELECT搭配RETURNING BULK COLLECT INTO,刚好踩了Oracle的语法禁区。
问题根源
Oracle明确规定:RETURNING BULK COLLECT INTO仅支持两种场景:
- 单条值插入语句(
INSERT INTO ... VALUES(...)) FORALL结合VALUES的批量插入场景
而你的插入逻辑是基于SELECT查询结果的INSERT...SELECT,这种情况下RETURNING子句不被语法支持,直接触发了“SQL命令未正确结束”的错误,这也是你移除RETURNING后就能正常编译的核心原因。
另外还要提一句,你代码里还有个小疏漏:你定义了t_pallet_ids作为表类型,但直接在RETURNING里引用这个类型名是不对的,得先声明一个该类型的变量(比如v_pallet_ids t_pallet_ids;),再把变量用在INTO后面。
两种可行的修正方案
方案1:用MERGE替代INSERT...SELECT
MERGE语句支持在WHEN NOT MATCHED THEN分支中使用RETURNING子句,刚好适配你的插入逻辑:
CREATE OR REPLACE PROCEDURE CIMS.QC_PALLET_HOLD_BY_HOUR_A_REL( QC_HOLD_ID_IN IN INTEGER, HOUR_STR IN VARCHAR2, DAY_CODE IN VARCHAR2, TOP_CODE_IN IN VARCHAR2, QC_RLS_DISPOSITION_ID_IN INTEGER, SUCC_PALS_OUT OUT VARCHAR2 ) IS l_count binary_integer; l_array dbms_utility.lname_array; TYPE t_pallet_ids is TABLE of pallet_hold.pallet_hold_id%type; v_pallet_ids t_pallet_ids; -- 声明类型变量 BEGIN dbms_utility.comma_to_table( list => regexp_replace(HOUR_STR, '(^|,)','\1x'), tablen => l_count, tab => l_array ); BEGIN FORALL i IN l_array.FIRST .. l_array.LAST MERGE INTO PALLET_HOLD ph USING ( SELECT V.PALLET_NO, V.TOP_CODE, V.BOTTOM_CODE, QC_HOLD_ID_IN AS QC_HOLD_ID FROM PALLET_MASTER_INQ_VIEW V WHERE PROD_HOUR = substr(l_array(i),2) AND SUBSTR(BOTTOM_CODE,0,5) = DAY_CODE AND TOP_CODE = TOP_CODE_IN ) src ON (ph.QC_HOLD_ID = src.QC_HOLD_ID AND ph.PALLET_NO = src.PALLET_NO) WHEN NOT MATCHED THEN INSERT (PALLET_NO, TOP_CODE, BOTTOM_CODE, QC_HOLD_ID) VALUES (src.PALLET_NO, src.TOP_CODE, src.BOTTOM_CODE, src.QC_HOLD_ID) RETURNING PALLET_HOLD_ID BULK COLLECT INTO v_pallet_ids; EXCEPTION WHEN OTHERS THEN NULL; END; COMMIT; EXCEPTION WHEN OTHERS THEN RAISE; END; /
方案2:先批量查询数据,再用VALUES批量插入
如果PALLET_HOLD_ID是自增序列生成的,可以先把要插入的数据查询出来存入集合,再用FORALL+VALUES批量插入,这样就能正常使用RETURNING了:
CREATE OR REPLACE PROCEDURE CIMS.QC_PALLET_HOLD_BY_HOUR_A_REL( QC_HOLD_ID_IN IN INTEGER, HOUR_STR IN VARCHAR2, DAY_CODE IN VARCHAR2, TOP_CODE_IN IN VARCHAR2, QC_RLS_DISPOSITION_ID_IN INTEGER, SUCC_PALS_OUT OUT VARCHAR2 ) IS l_count binary_integer; l_array dbms_utility.lname_array; TYPE t_insert_data IS TABLE OF PALLET_HOLD%ROWTYPE; v_insert_data t_insert_data; TYPE t_pallet_ids is TABLE of pallet_hold.pallet_hold_id%type; v_pallet_ids t_pallet_ids; -- 声明类型变量 BEGIN dbms_utility.comma_to_table( list => regexp_replace(HOUR_STR, '(^|,)','\1x'), tablen => l_count, tab => l_array ); -- 先查询出符合条件的待插入数据 SELECT V.PALLET_NO, V.TOP_CODE, V.BOTTOM_CODE, QC_HOLD_ID_IN BULK COLLECT INTO v_insert_data FROM PALLET_MASTER_INQ_VIEW V WHERE PROD_HOUR IN (SELECT substr(column_value,2) FROM TABLE(l_array)) AND SUBSTR(BOTTOM_CODE,0,5) = DAY_CODE AND TOP_CODE = TOP_CODE_IN AND NOT EXISTS ( SELECT 1 FROM PALLET_HOLD WHERE QC_HOLD_ID = QC_HOLD_ID_IN AND PALLET_NO = V.PALLET_NO ); -- 批量插入并收集返回ID FORALL i IN v_insert_data.FIRST .. v_insert_data.LAST INSERT INTO PALLET_HOLD VALUES v_insert_data(i) RETURNING PALLET_HOLD_ID BULK COLLECT INTO v_pallet_ids; COMMIT; EXCEPTION WHEN OTHERS THEN RAISE; END; /
内容的提问来源于stack exchange,提问作者elesh.j
相关产品推荐
相关产品推荐

