Oracle PL/SQL - 循环值作为没有动态 SQL 的动态列名

Posted

技术标签:

【中文标题】Oracle PL/SQL - 循环值作为没有动态 SQL 的动态列名【英文标题】:Oracle PL/SQL - Loop value as dynamic column name without dynamic SQL 【发布时间】:2020-11-25 17:33:04 【问题描述】:

我想用相同的值更新具有相同列名的多个表,而不是对每个表进行更新,并且由于 Oracle 不提供一次更新多个表的方法,我虽然使用为此循环。

我试过了,但它没有按预期工作:

begin
  for i in (select column_value from table(sys.dbms_debug_vc2coll('tab1', 'tab2'))) loop
    update i set my_col = 'my value';
  end loop;
end;

我知道我可以通过 execute immediate 使用动态 SQL,但有没有办法避免它?

【问题讨论】:

简单回答:否。 这看起来很奇怪,为什么要在多个表中为所有行存储相同的值?似乎这是解决您真正遇到的任何问题的错误方法。 @AndrewSayer 这不是针对所有行,我提供的示例代码是高度简化的。在实际情况中,有一个 where 子句说明要更新哪一行。 好的,你可以使用你的 SQL 来生成 SQL,你不必使用 execute immediate 或 dbms_sql 来运行它——你只需要: spool 一个脚本,用眼睛检查它,运行它手动。这是您所追求的还是您实际上希望它自己执行(在这种情况下您必须使用动态 SQL)。 @user5507535 避免过多的动态 SQL 很好,但同时避免会导致其他问题。学习一些我在回答 here 中解释过的处理动态 SQL 的技巧可能会有所帮助。 【参考方案1】:

问题(以及问题)看起来很简单:

用相同的值更新具有相同列名的多个表

如何根据这些表和一个instead of 触发器创建一个视图来完成这项工作?方法如下:

示例表:

SQL> select * from tab_a;

        ID M
---------- -
         1 x
         2 y

SQL> select * from tab_b;

NA MY_
-- ---
LF www
JW zzz
MC

查看:

SQL> create or replace view v_tabs as
  2    select to_char(id) idn, my_col from tab_a
  3    union all
  4    select name           , my_col from tab_b;

View created.

SQL> select * from v_tabs;

IDN                                      MY_
---------------------------------------- ---
1                                        x
2                                        y
LF                                       www
JW                                       zzz
MC

而不是触发器:

SQL> create or replace trigger trg_tabs
  2    instead of update on v_tabs
  3    for each row
  4  begin
  5    update tab_a set my_col = :new.my_col;
  6    update tab_b set my_col = :new.my_col;
  7  end;
  8  /

Trigger created.

测试:

SQL> update v_tabs set my_col = 'e';

5 rows updated.

SQL> select * from tab_a;

        ID M
---------- -
         1 e
         2 e

SQL> select * from tab_b;

NA MY_
-- ---
LF e
JW e
MC e

SQL>

所有涉及的表中的所有MY_COL 值都设置为e。这就是你要求的,对吧?

【讨论】:

【参考方案2】:

如果您不能使用execute immediate,请尝试使用DBMS_SQL 包: https://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_sql.htm#i996963

这是一个从文档中获取的关于如何使用这个包的语法示例:

    CREATE OR REPLACE PROCEDURE demo(salary IN NUMBER) AS
        cursor_name INTEGER;
        rows_processed INTEGER;
    BEGIN
        cursor_name := dbms_sql.open_cursor;
        DBMS_SQL.PARSE(cursor_name, 'DELETE FROM emp WHERE sal > :x', DBMS_SQL.NATIVE);
        DBMS_SQL.BIND_VARIABLE(cursor_name, ':x', salary);
        rows_processed := DBMS_SQL.EXECUTE(cursor_name);
        DBMS_SQL.CLOSE_CURSOR(cursor_name);
    EXCEPTION
    WHEN OTHERS THEN
        DBMS_SQL.CLOSE_CURSOR(cursor_name);
    END;
    /

【讨论】:

以上是关于Oracle PL/SQL - 循环值作为没有动态 SQL 的动态列名的主要内容,如果未能解决你的问题,请参考以下文章

Oracle PL/SQL 中的 for 循环后变量丢失值

PL/SQL Oracle :- 在传递值时动态 UNPIVOT ORACLE TABLE

oracle ORA-06502:PL/SQL:数字或值错误:批量绑定:截断绑定

执行动态 PL/SQL 块以执行求和并返回值

关于 Oracle PL SQL 循环

如何在 oracle pl/sql 查询中动态获取字段名称?