0

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.
APC
  • 144,005
  • 19
  • 170
  • 281
Sunny
  • 119
  • 1
  • 2
  • 13
  • **@Frank Schmitt, Rajesh Chamarthi & Gary Myers** ==================================================== **THANK YOU SO SO MUCH FOR YOUR REPLIES** ==================================================== It was really funny cause I actually not only did I have to re write the procedure based entirely on _Frank Schmitt's_ script & then came across just a few issues which were totally resolved by both _Rajesh's_ & _Gary's_ answers, it was like the answers had some sort of foresight. Once again thank you guys. – Sunny Jul 22 '11 at 13:38

3 Answers3

4

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?

Frank Schmitt
  • 30,195
  • 12
  • 73
  • 107
  • I used your script, as is & started making changes except for a few double quotes at the table/column identifier names, cause a few were keywords(i got that from ***Gary's*** answer).. & it turned out perfect, thank you. – Sunny Jul 22 '11 at 13:37
2

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.

Rajesh Chamarthi
  • 18,568
  • 4
  • 40
  • 67
  • I couldn't make changes to the function which generated the table name as the tables were created by another procedure dynamically & this procedures job was to insert data into the table just generated... Thanks for the DBMS_OUTPUT.PUT_LINE idea, although i didn't use it directly, instead made an REF OUT cursor & passed the query generated & passed it to code behind from where i made the call & checked it & found an issue with one of the param i was passing... Thanks a lot for that idea, gonna use it from now onwards, really helped me debug. – Sunny Jul 22 '11 at 13:43
  • No problem. In fact you should look at Tom Kyte's Debug.f package for instrumenting your code. dbms_output is ok.. for one-off testing and seeing at results in development, but you need to set serveroutput on for this, which you cannot do in Production. – Rajesh Chamarthi Jul 22 '11 at 13:53
2

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

Gary Myers
  • 34,963
  • 3
  • 49
  • 74
  • thank you for the answer,absence of double quotes had me getting Missing = sign ERROR, didn't understand it at first & then a few hours later saw your reply & tries wrapping all identifiers in double quotes & viola that problem got solved, . – Sunny Jul 22 '11 at 13:47