【问题标题】:UPSERT into table with dynamic table nameUPSERT 进入具有动态表名的表
【发布时间】:2011-10-09 09:28:45
【问题描述】:

提供任何更好的将 UPSERT 插入表格的方法:

  • 数据更新插入速度约为 1 行/秒
  • 表名称是 DYNAMIC,使用传递给它的 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 不能是动态的 ???
  • 我是一个新手,我编码的第三个月,我通过了 STACKOVERFLOW & Google 搜索了 3 天,尝试各种有趣的 & 绝望的解决方案......如果你找到了一个非常相关的链接 将不胜感激。

【问题讨论】:

  • @Frank Schmitt、Rajesh Chamarthi 和 Gary Myers ========================== ========================== 非常感谢您的回复 ========= ============================================ 这真的很有趣,因为我实际上我不仅必须完全基于 Frank Schmitt 的 脚本重新编写程序,然后遇到了一些问题,Rajesh'sGary's 都完全解决了这些问题 答案,就像答案有某种远见一样。再次感谢你们。

标签: c# sql oracle dynamic upsert


【解决方案1】:

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 时收到的错误消息?

【讨论】:

  • 我按原样使用了您的脚本,并开始进行更改,除了表/列标识符名称中的几个双引号,因为一些是关键字(我从 Gary's 回答).. 结果很完美,谢谢。
【解决方案2】:

您应该考虑编写此代码以使用静态 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 语句。

【讨论】:

  • 我无法更改生成表名的函数,因为表是由另一个过程动态创建的,并且此过程的工作是将数据插入刚刚生成的表中...谢谢DBMS_OUTPUT.PUT_LINE 的想法,虽然我没有直接使用它,而是制作了一个 REF OUT 游标并将生成的查询传递给后面的代码,从我进行调用并检查它并发现其中一个参数有问题 i正在通过......非常感谢这个想法,从现在开始使用它,真的帮助我调试。
  • 没问题。事实上,您应该查看 Tom Kyte 的 Debug.f 包来检测您的代码。 dbms_output 没问题.. 用于一次性测试并在开发中查看结果,但您需要为此设置 serveroutput,而在生产环境中无法做到这一点。
【解决方案3】:

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

其次,您是否使用了大小写混合的表名。如果你做一个

CREATE TABLE "testOne" (ID NUMBER);

那么表名将被存储为 testOne。但是当您执行UPDATE testOne 时,将被视为UPDATE TESTONE,您将收到“没有这样的表”错误。

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

【讨论】:

  • 谢谢你的回答,没有双引号让我得到 Missing = sign ERROR,起初不明白,然后几个小时后看到你的回复并尝试将所有标识符用双引号括起来和中提琴,这个问题得到了解决,.
猜你喜欢
  • 1970-01-01
  • 2016-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
相关资源
最近更新 更多