UPSERT 到具有动态表名的表中

发布于 2024-11-25 06:26:11 字数 1030 浏览 2 评论 0原文

任何更好的 UPSERT 到表中的方法,前提是:

  • 以约 1 行/秒的速度更新插入数据
  • 表名称是动态的,使用传递给它的 ObjectID 参数生成

以下过程抛出: “ORA-00942:表或视图不存在”

CREATE OR REPLACE PROCEDURE
PROCEDURE "SPINSERTDATA"
(
  pObjectID IN RAW,
  pDateTime IN TIMESTAMP,
  pValue IN BINARY_DOUBLE,
)
AS
BEGIN
  Declare
    vQueryInsert VARCHAR2(1000);
    vQueryUpdate VARCHAR2(1000);
    vTableName VARCHAR2(30);
  Begin      
      vTableName := FGETTABLENAME(POBJECTID => pObjectID);
      vQueryUpdate := 'UPDATE '      || vTableName || ' SET "VALUE" = :1';
      vQueryInsert := 'INSERT INTO ' || vTableName || ' ("DTTIME", "VALUE") VALUES (:1, :2)';

      EXECUTE IMMEDIATE vQueryInsert USING pDateTime, pValue;
        EXCEPTION
          WHEN DUP_VAL_ON_INDEX THEN 
            EXECUTE IMMEDIATE vQueryUpdate USING pValue;
  End;
END "SPINSERTDATA";
  • 显然 MERGE 不起作用,因为 TableName 不能是动态的 ???
  • 我是一个新手,我编码的第三个月,我艰难地度过了 堆栈溢出与谷歌搜索了三天了,尝试了各种有趣的和有趣的内容。 绝望的解决方案......即使是一个非常相关的链接,如果你找到了一个 会受到真诚的赞赏。

Any better method to UPSERT into a table, provided :

  • Data upsert at ~1 row/second
  • Table Name is DYNAMIC, generated using ObjectID parameter passed to it

THE FOLLOWING PROCEDURE THROWS :
"ORA-00942: table or view does not exist"

CREATE OR REPLACE PROCEDURE
PROCEDURE "SPINSERTDATA"
(
  pObjectID IN RAW,
  pDateTime IN TIMESTAMP,
  pValue IN BINARY_DOUBLE,
)
AS
BEGIN
  Declare
    vQueryInsert VARCHAR2(1000);
    vQueryUpdate VARCHAR2(1000);
    vTableName VARCHAR2(30);
  Begin      
      vTableName := FGETTABLENAME(POBJECTID => pObjectID);
      vQueryUpdate := 'UPDATE '      || vTableName || ' SET "VALUE" = :1';
      vQueryInsert := 'INSERT INTO ' || vTableName || ' ("DTTIME", "VALUE") VALUES (:1, :2)';

      EXECUTE IMMEDIATE vQueryInsert USING pDateTime, pValue;
        EXCEPTION
          WHEN DUP_VAL_ON_INDEX THEN 
            EXECUTE IMMEDIATE vQueryUpdate USING pValue;
  End;
END "SPINSERTDATA";
  • Apparently MERGE does not work as the TableName Cannot be dynamic
    ???
  • I'm a newbie, my third month of coding, I scourged through
    STACKOVERFLOW & Googled for 3 days now, trying all sorts of funny &
    desperate solutions ... Even a very relevant link if you found one
    would be honestly appreciated.

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

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

发布评论

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

评论(3

苦行僧 2024-12-02 06:26:11

MERGE 与本机动态 SQL(立即执行)完美配合:

create table so_test(pk number not null primary key, value varchar2(20));

insert into so_test(pk, value) values(1, 'one');

declare
  l_SQL varchar2(4000);
  l_tablename varchar2(4000) default 'so_test';
begin
  l_SQL := 'merge into ' || l_tablename || ' target' ||
    ' using (select 1 pk, ''eins'' value from dual union all
             select 2 pk, ''zwei'' value from dual) source
      on (target.pk = source.pk)
      when matched then 
        update set target.value = source.value
      when not matched then
        insert values(source.pk, source.value)      
  ';
  dbms_output.put_line(l_sql);
  execute immediate l_SQL;
end; 

您能否发布使用 MERGE 时收到的错误消息?

MERGE works perfectly fine with Native dynamic SQL (EXECUTE IMMEDIATE):

create table so_test(pk number not null primary key, value varchar2(20));

insert into so_test(pk, value) values(1, 'one');

declare
  l_SQL varchar2(4000);
  l_tablename varchar2(4000) default 'so_test';
begin
  l_SQL := 'merge into ' || l_tablename || ' target' ||
    ' using (select 1 pk, ''eins'' value from dual union all
             select 2 pk, ''zwei'' value from dual) source
      on (target.pk = source.pk)
      when matched then 
        update set target.value = source.value
      when not matched then
        insert values(source.pk, source.value)      
  ';
  dbms_output.put_line(l_sql);
  execute immediate l_SQL;
end; 

Could you please post the error message you get when using MERGE?

不甘平庸 2024-12-02 06:26:11

您应该考虑编写此代码以使用静态 SQL,而不是在运行时传递表名称。是否有正当理由导致您在运行时之前不知道要合并到哪个表中?

至于调试问题......

您的代码中函数 FGETTABLENAME 是如何定义的?这就是我模仿该场景的想法。我建议使用 %type (而不是数字类型的 RAW)声明并从过程名称中删除双引号。

    create or replace function FGETTABLENAME(
        POBJECTID in user_objects.object_id%type
    ) return user_objects.object_name%type
    as
      v_object_name user_objects.object_name%type;
    begin
      select object_name
        into v_object_name
        from all_objects
        where object_id = pobjectid;
       return v_object_name;
    end;
    /

SQL> select object_id, object_name from user_objects;

 OBJECT_ID OBJECT_NAME
---------- --------------------------------------------
     52641 TFIVE
     52644 SPINSERTDATA
     52643 PROCEDURE
     52645 FGETTABLENAME
     52554 GET_SAL_EMP
     52559 T1

SQL> select FGETTABLENAME(52641) from dual;

FGETTABLENAME(52641)
--------------------------------------------
TFIVE

您可以在代码中添加 DBMS_OUTPUT.PUT_LINE 语句

vTableName := FGETTABLENAME(POBJECTID => pObjectID); 

and 

vQueryUpdate := 'UPDATE '      || vTableName || ' SET "VALUE" = :1';
      vQueryInsert := 'INSERT INTO ' || vTableName || ' ("DTTIME", "VALUE") VALUES (:1, :2)';

,或者跟踪代​​码以查看向数据库触发的实际 SQL 语句。

You should consider writing this to use Static SQL instead of passing the table name at Run Time. Is there a valid reason why you don't know what table you would be merging into till run-time?

As for debugging the issue...

How is the function FGETTABLENAME defined in your code? This is what I came up with which mimics that scenario. I would suggest Using %type (instead of RAW for Number Types) declarations and removing the Double Quotes from the procedure Names.

    create or replace function FGETTABLENAME(
        POBJECTID in user_objects.object_id%type
    ) return user_objects.object_name%type
    as
      v_object_name user_objects.object_name%type;
    begin
      select object_name
        into v_object_name
        from all_objects
        where object_id = pobjectid;
       return v_object_name;
    end;
    /

SQL> select object_id, object_name from user_objects;

 OBJECT_ID OBJECT_NAME
---------- --------------------------------------------
     52641 TFIVE
     52644 SPINSERTDATA
     52643 PROCEDURE
     52645 FGETTABLENAME
     52554 GET_SAL_EMP
     52559 T1

SQL> select FGETTABLENAME(52641) from dual;

FGETTABLENAME(52641)
--------------------------------------------
TFIVE

You can add DBMS_OUTPUT.PUT_LINE statements to your code after

vTableName := FGETTABLENAME(POBJECTID => pObjectID); 

and 

vQueryUpdate := 'UPDATE '      || vTableName || ' SET "VALUE" = :1';
      vQueryInsert := 'INSERT INTO ' || vTableName || ' ("DTTIME", "VALUE") VALUES (:1, :2)';

or Trace your code to see the actual SQL statements being fired to your Database.

夜唯美灬不弃 2024-12-02 06:26:11

首先,您的 UPDATE 中没有 WHERE,因此它将更新表的每一行。

其次,您是否使用了大小写混合的表名。如果执行 a

CREATE TABLE "testOne" (ID NUMBER);

,则表名将存储为 testOne。但是,当您执行 UPDATE testOne 时,将被视为 UPDATE TESTONE 并且您将收到“no such table”错误。

避免使用大小写混合的表名。如果绝对必须,那么您需要在动态 SQL 语句中引用它们

Firstly, you don't have a WHERE in your UPDATE so it will update every row of the table.

Secondly, have you used a mixed case table name. If you do a

CREATE TABLE "testOne" (ID NUMBER);

then the table name will be stored as testOne. But when you do an UPDATE testOne is will be treated as UPDATE TESTONE and you'll get a "no such table" error.

Avoid using mixed case table names. If you absolutely must, then you'll need to quote them in the dynamic SQL statement

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