使用forall立即使用for for Update语句

发布于 2025-01-31 12:09:58 字数 2710 浏览 2 评论 0原文

我需要根据查询获取的数据更新表。 但是,在查询中,我使用ListAgg生成列名称及其相应的值。 我能够通过使用简单的循环使用行处理来更新表,但是我正在探索使用即时执行forall的方法,因为我正在更改更新语句运行时的设置子句。

set serveroutput on;

DECLARE 
l_sql VARCHAR2(4000);
cursor c1 is SELECT 
    OFFER_MIGRATION_ID,
    listagg(column_name||' = nvl('||column_name||','''||DEFAULT_CHAR_VALUE||''')', ','||chr(13)) within group (order by column_name) as set_clause
FROM
(
SELECT  distinct och.OFFER_MIGRATION_ID,
        och.ATTR_ID,
        ch_map.COLUMN_NAME,
        coalesce(och_list.value_name,och.DEFAULT_CHAR_VALUE) as DEFAULT_CHAR_VALUE
 FROM OFFERING_CHARS och,
      CHAR_MAP ch_map,
      OFFERING_CHARS_list och_list
Where ch_map.transformation = 'CONSTANT_DICT'
  and ch_map.attr_id = och.ATTR_ID
  and och.DEFAULT_CHAR_VALUE is not null
  and och_list.offer_migration_id(+) = och.offer_migration_id
  and och_list.characteristic_id(+) = och.characteristic_id
  and och_list.value(+) = och.DEFAULT_CHAR_VALUE
)
GROUP BY OFFER_MIGRATION_ID;  

TYPE char_list IS TABLE OF c1%rowtype index by pls_integer;
l_char_list char_list;
BEGIN 

/******** CONSTANT_FROM_DICT *********/
open c1;
fetch c1 bulk collect into l_char_list;
close c1;

FORALL i IN l_char_list.first .. l_char_list.last

    EXECUTE IMMEDIATE 'UPDATE product_char SET :1 WHERE offer_migration_id = '||l_char_list(i).offer_migration_id
    using l_char_list(i).set_clause;
 
 COMMIT;
 
END;

此块给出错误 -

Error report -
ORA-06550: line 35, column 92:
PLS-00440: FORALL bulk IN-bind variables cannot be used here
ORA-06550: line 35, column 5:
PL/SQL: Statement ignored
ORA-06550: line 35, column 5:
PLS-00435: DML statement without BULK In-BIND cannot be used inside FORALL
06550. 00000 -  "line %s, column %s:\n%s"
*Cause:    Usually a PL/SQL compilation error.
*Action:

光标的set_clause列的值将就像 -

OFFER_MIGRATION_ID, SET_CLAUSE

10001   "CCA_CATEGORY = nvl(CCA_CATEGORY,'PD'),CCA_LIFECYCLE_STATUS = nvl(CCA_LIFECYCLE_STATUS,'A'),EQUIPMENT_TYPE = nvl(EQUIPMENT_TYPE,'Devices')"
20001   DATA_ALLOWANCE_SO = nvl(DATA_ALLOWANCE_SO,'9225')
20023   "DURATION = nvl(DURATION,'6'),DATA_ALLOWANCE_SUP_OFFER = nvl(DATA_ALLOWANCE_SUP_OFFER,'9203')"
20024   DATA_ALLOWANCE_SUP_OFFER = nvl(DATA_ALLOWANCE_SUP_OFFER,'9203')

我的db是 -

Oracle Database 19c Standard Edition 2 Release 19.0.0.0.0 - Production

在plsql块中使用的循环,我正在寻找更好执行需要很长时间。

FOR i IN 1..l_char_list.COUNT
 LOOP
    l_sql := 'UPDATE stgdm_product_char SET '||chr(13)||l_char_list(i).set_clause||chr(13)||' WHERE offer_migration_id = '||l_char_list(i).offer_migration_id;
--    dbms_output.put_line(l_sql);
    EXECUTE IMMEDIATE l_sql;
 END LOOP;

I Require to update a table based on data fetched from a query.
however in query I am generating Column Name and its respective values using LISTAGG.
I am able to update the table using the row by row processing with simple loop however I am exploring ways to utilize the FORALL with EXECUTE IMMEDIATE since I am changing the SET clause of UPDATE statement runtime.

set serveroutput on;

DECLARE 
l_sql VARCHAR2(4000);
cursor c1 is SELECT 
    OFFER_MIGRATION_ID,
    listagg(column_name||' = nvl('||column_name||','''||DEFAULT_CHAR_VALUE||''')', ','||chr(13)) within group (order by column_name) as set_clause
FROM
(
SELECT  distinct och.OFFER_MIGRATION_ID,
        och.ATTR_ID,
        ch_map.COLUMN_NAME,
        coalesce(och_list.value_name,och.DEFAULT_CHAR_VALUE) as DEFAULT_CHAR_VALUE
 FROM OFFERING_CHARS och,
      CHAR_MAP ch_map,
      OFFERING_CHARS_list och_list
Where ch_map.transformation = 'CONSTANT_DICT'
  and ch_map.attr_id = och.ATTR_ID
  and och.DEFAULT_CHAR_VALUE is not null
  and och_list.offer_migration_id(+) = och.offer_migration_id
  and och_list.characteristic_id(+) = och.characteristic_id
  and och_list.value(+) = och.DEFAULT_CHAR_VALUE
)
GROUP BY OFFER_MIGRATION_ID;  

TYPE char_list IS TABLE OF c1%rowtype index by pls_integer;
l_char_list char_list;
BEGIN 

/******** CONSTANT_FROM_DICT *********/
open c1;
fetch c1 bulk collect into l_char_list;
close c1;

FORALL i IN l_char_list.first .. l_char_list.last

    EXECUTE IMMEDIATE 'UPDATE product_char SET :1 WHERE offer_migration_id = '||l_char_list(i).offer_migration_id
    using l_char_list(i).set_clause;
 
 COMMIT;
 
END;

This block gives error --

Error report -
ORA-06550: line 35, column 92:
PLS-00440: FORALL bulk IN-bind variables cannot be used here
ORA-06550: line 35, column 5:
PL/SQL: Statement ignored
ORA-06550: line 35, column 5:
PLS-00435: DML statement without BULK In-BIND cannot be used inside FORALL
06550. 00000 -  "line %s, column %s:\n%s"
*Cause:    Usually a PL/SQL compilation error.
*Action:

The values for set_clause column from the cursor will be like for example --

OFFER_MIGRATION_ID, SET_CLAUSE

10001   "CCA_CATEGORY = nvl(CCA_CATEGORY,'PD'),CCA_LIFECYCLE_STATUS = nvl(CCA_LIFECYCLE_STATUS,'A'),EQUIPMENT_TYPE = nvl(EQUIPMENT_TYPE,'Devices')"
20001   DATA_ALLOWANCE_SO = nvl(DATA_ALLOWANCE_SO,'9225')
20023   "DURATION = nvl(DURATION,'6'),DATA_ALLOWANCE_SUP_OFFER = nvl(DATA_ALLOWANCE_SUP_OFFER,'9203')"
20024   DATA_ALLOWANCE_SUP_OFFER = nvl(DATA_ALLOWANCE_SUP_OFFER,'9203')

My DB is -

Oracle Database 19c Standard Edition 2 Release 19.0.0.0.0 - Production

The loop which is working inside the PLSQL block I am looking for ways to better implement it instead of going row by row since it takes long time to execute.

FOR i IN 1..l_char_list.COUNT
 LOOP
    l_sql := 'UPDATE stgdm_product_char SET '||chr(13)||l_char_list(i).set_clause||chr(13)||' WHERE offer_migration_id = '||l_char_list(i).offer_migration_id;
--    dbms_output.put_line(l_sql);
    EXECUTE IMMEDIATE l_sql;
 END LOOP;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

浅笑轻吟梦一曲 2025-02-07 12:09:58

您不需要这么复杂。由于目标表具有已知的固定列集,因此您只能在pivot子句中白色列表这些列,然后使用Merge语句(那么您只需要单个SQL语句,不需要动态SQL)。

由于您尚未提供适当的最小可重复的示例,char_mapoffering_chars_list进入单个表your_combined_tables ,它可以简单地进行反向工程师:

CREATE TABLE your_combined_tables(
  offer_migration_id, column_name, default_char_value
) AS
SELECT 10001, 'CCA_CATEGORY',             'PD'      FROM DUAL UNION ALL
SELECT 10001, 'CCA_LIFECYCLE_STATUS',     'A'       FROM DUAL UNION ALL
SELECT 10001, 'EQUIPMENT_TYPE',           'Devices' FROM DUAL UNION ALL
SELECT 20001, 'DATA_ALLOWANCE',           '9225'    FROM DUAL UNION ALL
SELECT 20023, 'DURATION',                 '6'       FROM DUAL UNION ALL
SELECT 20023, 'DATA_ALLOWANCE_SUP_OFFER', '9203'    FROM DUAL UNION ALL
SELECT 20024, 'DATA_ALLOWANCE_SUP_OFFER', '9203'    FROM DUAL;

CREATE TABLE product_char(
  offer_migration_id       NUMBER PRIMARY KEY,
  cca_category             VARCHAR2(20),
  cca_lifecycle_status     VARCHAR2(20),
  equipment_type           VARCHAR2(20),
  data_allowance           VARCHAR2(20),
  duration                 VARCHAR2(20),
  data_allowance_sup_offer VARCHAR2(20)
);

INSERT INTO product_char (offer_migration_id)
SELECT 10001 FROM DUAL UNION ALL
SELECT 20001 FROM DUAL UNION ALL
SELECT 20023 FROM DUAL UNION ALL
SELECT 20024 FROM DUAL;

然后您可以使用:

MERGE INTO product_char dst
USING (
  SELECT *
  FROM   your_combined_tables
  PIVOT  (
    MAX(default_char_value)
    FOR column_name IN (
      'CCA_CATEGORY'             AS cca_category,
      'CCA_LIFECYCLE_STATUS'     AS cca_lifecycle_status,
      'EQUIPMENT_TYPE'           AS equipment_type,
      'DATA_ALLOWANCE'           AS data_allowance,
      'DURATION'                 AS duration,
      'DATA_ALLOWANCE_SUP_OFFER' AS data_allowance_sup_offer
    )
  )
) src
ON (src.offer_migration_id = dst.offer_migration_id)
WHEN MATCHED THEN
  UPDATE
  SET cca_category             = NVL(dst.cca_category, src.cca_category),
      cca_lifecycle_status     = NVL(dst.cca_lifecycle_status, src.cca_lifecycle_status),
      equipment_type           = NVL(dst.equipment_type, src.equipment_type),
      data_allowance           = NVL(dst.data_allowance, src.data_allowance),
      duration                 = NVL(dst.duration, src.duration),
      data_allowance_sup_offer = NVL(dst.data_allowance_sup_offer, src.data_allowance_sup_offer);

然后在之后 使用: 合并,该表包含:

effer_migration_idcca_categorycca_lifecycle_status设备_TYPEdata_allowance持续时间data_allowance_sup_offer
10001pda设备nullnullnull
20001nullnullnull9225nullnull
20023nullnullnullnull69203
20024nullnullnullnullnull9203

db<> fiddle

You do not need it to be so complicated. Since the destination table has a known, fixed set of columns then you can just white-list those columns in a PIVOT clause and then use a MERGE statement (then you only need a single SQL statement and do not need dynamic SQL).

Since you have not provided a proper minimal reproducible example, I have combined all three of your input tables OFFERING_CHARS, CHAR_MAP and OFFERING_CHARS_list into a single table your_combined_tables which is much simpler to reverse engineer:

CREATE TABLE your_combined_tables(
  offer_migration_id, column_name, default_char_value
) AS
SELECT 10001, 'CCA_CATEGORY',             'PD'      FROM DUAL UNION ALL
SELECT 10001, 'CCA_LIFECYCLE_STATUS',     'A'       FROM DUAL UNION ALL
SELECT 10001, 'EQUIPMENT_TYPE',           'Devices' FROM DUAL UNION ALL
SELECT 20001, 'DATA_ALLOWANCE',           '9225'    FROM DUAL UNION ALL
SELECT 20023, 'DURATION',                 '6'       FROM DUAL UNION ALL
SELECT 20023, 'DATA_ALLOWANCE_SUP_OFFER', '9203'    FROM DUAL UNION ALL
SELECT 20024, 'DATA_ALLOWANCE_SUP_OFFER', '9203'    FROM DUAL;

CREATE TABLE product_char(
  offer_migration_id       NUMBER PRIMARY KEY,
  cca_category             VARCHAR2(20),
  cca_lifecycle_status     VARCHAR2(20),
  equipment_type           VARCHAR2(20),
  data_allowance           VARCHAR2(20),
  duration                 VARCHAR2(20),
  data_allowance_sup_offer VARCHAR2(20)
);

INSERT INTO product_char (offer_migration_id)
SELECT 10001 FROM DUAL UNION ALL
SELECT 20001 FROM DUAL UNION ALL
SELECT 20023 FROM DUAL UNION ALL
SELECT 20024 FROM DUAL;

Then you can use:

MERGE INTO product_char dst
USING (
  SELECT *
  FROM   your_combined_tables
  PIVOT  (
    MAX(default_char_value)
    FOR column_name IN (
      'CCA_CATEGORY'             AS cca_category,
      'CCA_LIFECYCLE_STATUS'     AS cca_lifecycle_status,
      'EQUIPMENT_TYPE'           AS equipment_type,
      'DATA_ALLOWANCE'           AS data_allowance,
      'DURATION'                 AS duration,
      'DATA_ALLOWANCE_SUP_OFFER' AS data_allowance_sup_offer
    )
  )
) src
ON (src.offer_migration_id = dst.offer_migration_id)
WHEN MATCHED THEN
  UPDATE
  SET cca_category             = NVL(dst.cca_category, src.cca_category),
      cca_lifecycle_status     = NVL(dst.cca_lifecycle_status, src.cca_lifecycle_status),
      equipment_type           = NVL(dst.equipment_type, src.equipment_type),
      data_allowance           = NVL(dst.data_allowance, src.data_allowance),
      duration                 = NVL(dst.duration, src.duration),
      data_allowance_sup_offer = NVL(dst.data_allowance_sup_offer, src.data_allowance_sup_offer);

Then after the MERGE, the table contains:

OFFER_MIGRATION_IDCCA_CATEGORYCCA_LIFECYCLE_STATUSEQUIPMENT_TYPEDATA_ALLOWANCEDURATIONDATA_ALLOWANCE_SUP_OFFER
10001PDADevicesnullnullnull
20001nullnullnull9225nullnull
20023nullnullnullnull69203
20024nullnullnullnullnull9203

db<>fiddle here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文