UPSERT into table with dynamic table name

前端 未结 3 725
北荒
北荒 2020-12-11 14:27

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
相关标签:
3条回答
  • 2020-12-11 14:46

    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?

    0 讨论(0)
  • 2020-12-11 14:50

    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

    0 讨论(0)
  • 2020-12-11 14:55

    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.

    0 讨论(0)
提交回复
热议问题