UPSERT进入具有动态表名的表

时间:2011-07-20 13:02:17

标签: c# sql oracle dynamic upsert

任何更好的UPSERT进入表格的方法,提供:

  • 以~1行/秒
  • 进行数据upsert
  • 表名是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天,尝试各种有趣的& 绝望的解决方案......即使是一个非常相关的链接,如果你找到了一个 老实说,我会很感激。

3 个答案:

答案 0 :(得分:4)

MERGE与Native动态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; 

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

答案 1 :(得分: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语句。

答案 2 :(得分:2)

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

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

CREATE TABLE "testOne" (ID NUMBER);

然后表名将存储为testOne。但是,如果您执行了UPDATE testOne,则会将其视为UPDATE TESTONE,并且您将收到“无此表”错误。

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