Question

I have a table, DASHBOARD, with a primary key of PERSON_ID.
I want to populate all PERSON_IDs for each column using a MERGE statement that queries another table for counts, ALERT_EVENTS.

I have written MERGE statements for each column, below is one of the queries -

MERGE INTO DASHBOARD D
USING
    (SELECT PERSON_ID FROM PERSON) P 
ON (D.PERSON_ID = P.PERSON_ID)
WHEN MATCHED THEN
    UPDATE SET D.ZONES = (SELECT COUNT(EVENT_ID) FROM ALERT_EVENTS WHERE PERSON_ID = P.PERSON_ID AND EMAIL_ALERT_TYPE_ID = '40') WHERE D.PERSON_ID = P.PERSON_ID
WHEN NOT MATCHED THEN
    INSERT (D.PERSON_ID)
    VALUES (P.PERSON_ID);

My problem is this query takes too long to run, usually around 50 minutes.

There are 4000 PERSON_IDs in the DASHBOARD table and there are 1.4 million EVENT_IDs in the ALERT_EVENTS table. The ALERT_EVENTS table is made up of the following columns -

"EVENT_ID"            NUMBER(*,0) NOT NULL ENABLE,
"PERSON_ID"           NUMBER(*,0) NOT NULL ENABLE,
"DEVICE_ID"              NUMBER(*,0) NOT NULL ENABLE,
"ALERT_TYPE_ID" NUMBER(*,0) NOT NULL ENABLE,
"EVENT_DATE_TIME" DATE NOT NULL ENABLE,
"TEXT"            VARCHAR2(4000 BYTE),
"STATUS"          NUMBER(*,0) DEFAULT 0 NOT NULL ENABLE,
"PROC_STATUS_ID"  NUMBER(*,0) DEFAULT 1 NOT NULL ENABLE,
"ALERT_STATUS_ID" NUMBER DEFAULT 1 NOT NULL ENABLE

and one UNIQUE index on EVENT_ID.

I have tried adding and removing indexes (tried with 1 index and 3 indexes), which doesn't seem to help performance.

Based on my EXPLAIN PLAN (below), I believe I have a table structure problem, as my database always wants to do a FULL TABLE SCAN when executing the MERGE statement.

 Operation             Name         Rows    Bytes   Cost (%CPU) Time
 MERGE STATEMENT                4127    314K    21 (5)          00:00:01
 MERGE             DASHBOARD                
 VIEW                   
 HASH JOIN OUTER                4127    120K    21 (5)          00:00:01
 INDEX FAST FULL SCAN  PK_PERSON    4127    16508   4 (0)           00:00:01
 TABLE ACCESS FULL     DASHBOARD    4215    107K    16 (0)          00:00:01
 SORT AGGREGATE                 1   7       
 TABLE ACCESS FULL     ALERT_EVENTS 27  189 5247 (2)    00:01:03

I was going to partition the table, but we only have Oracle Standard, and not Enterprise, so it's not an included feature.

How can I speed up this merge statement without using partitions?

I am considering throwing out most of the rows, which would help slightly, but the underlying problem still exists.

What am I missing here?

Thanks in advance for any ideas.

Was it helpful?

Solution

I've not used merge on Oracle, so not 100% sure this will work, but the following should be faster, especially with an index on alert_events (email_alert_type_id, person_id) or alert_events (person_id, email_alert_type_id). Ideally you would use the first and see a merge join in the query plan, but I'm not sure if Oracle's query optimizer can do this.

merge into 
    dashboard d
using (
    select
        p.person_id,
        count(e.person_id) zones
    from
        person p
            left outer join
        alert_events e
            on p.person_id = e.person_id and 
               e.email_alert_type_id = '40' 
    group by
        p.person_id
    ) x
on
    (d.person_id = x.person_id)
when matched then update
    set d.zones = x.zones
when not matched then insert (person_id, zones)
    values (x.person_id, x.zones);

I've also included zones in the insert, it's trivial to remove this to stay in line with the original query.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top