a1ex07
a1ex07

Reputation: 37382

Oracle 10 MERGE performance

I had a very strange performance related problem with MERGE in Oracle 10. In a few words, I have a stored procedure that calculates and stores user rank based on her activity in the system and contains just one MERGE statement:

MERGE INTO user_ranks target USING 
([complex query that returns rank_id and user_id])src ON 
(src.user_id = target.user_id)
WHEN MATCHED THEN UPDATE SET target.rank_id = src.rank_id
WHEN NOT MATCHED THEN INSERT (target.user_id, target.rank_id)
  VALUES (src.user_id, src.rank_id);

// user_ranks table structure:
CREATE TABLE user_ranks (user_id INT NOT NULL 
PRIMARY KEY USING INDEX (CREATE UNIQUE INDEX UQ_uid_uranks ON user_ranks(user_id)),
rank_id INT NOT NULL,
CONSTRAINT FK_uid_uranks FOREIGN KEY (user_id) REFERENCES users(id),
CONSTRAINT FK_rid_uranks FOREIGN KEY(rank_id) REFERENCES ranks(id));
// no index on rank_id - intentionally, ranks table is a lookup with 
// a very few records and no delete/update allowed

The subquery which is used as a source for MERGE returns at most 1 record (user_id is passed as parameter to the procedure). It's quite expensive but execution time is acceptable (1-1.2 sec). The problem is that MERGE execution time hikes to more than 40 seconds, and I have no clue why. I tried using LEADING hint with no success. But if I split the statement into 2 parts, first one - run SELECT subquery and store result(rank_id) into variable and then merge - MERGE ... USING (SELECT user_id, rank_id FROM DUAL)src ... everything works just fine. From what I read, there are known issues with Oracle's MERGE, but they are mostly related to triggers (no triggers in my case). It also says that MERGE works slower than combination of INSERT and UPDATE, but I believe the "normal" difference is around 5-10%, not 30 times...

I'm trying to understand what I did wrong... Thanks for your suggestions.

Update Execution plan is quite long to post it here, in short : subquery cost by itself is 12737, with merge - 76305. Stats output for merge :

> Statistics

         108  recursive calls
           4  db block gets
    45630447  consistent gets
       24905  physical reads
           0  redo size
         620  bytes sent via SQL*Net to client
        1183  bytes received via SQL*Net from client
           2  SQL*Net roundtrips to/from client
           2  sorts (memory)
           0  sorts (disk)

subquery alone :

> Statistics

           8  recursive calls
           0  db block gets
          34  consistent gets
           0  physical reads
           0  redo size
         558  bytes sent via SQL*Net to client
         234  bytes received via SQL*Net from client
           1  SQL*Net roundtrips to/from client
           1  sorts (memory)
           0  sorts (disk)

Upvotes: 4

Views: 3245

Answers (1)

HAL 9000
HAL 9000

Reputation: 3985

If you've set up SQL*Plus autotrace it would be a matter of seconds to see which part of the actual execution plan has caused the most physical and logical I/O and used the most memory.

Note that the information you get using this method is much more precise than a simple explain plan.

Upvotes: 2

Related Questions