Reputation: 40491
I've encountered something weird which I can't explain .
I'm using the following query:
MERGE INTO Main_Table t
USING Stg_Table s
ON(s.site_id = t.site_id)
WHEN MATCHED THEN
UPDATE SET t.arpu_prev_period = s.arpu_prev_period
.... --50 more columns
where t.period_code = 201612
Stg_Table : Indexed (Site_Id)
Main_Table:
- Indexed (Period_code,Site_id)
- Partitioned by period_code
- Note - I tried adding an index on Site_Id
alone , same execution plan .
I would expect an execution plan that uses single partition scan, but instead I'm getting Partition list all
.
This is the execution plan:
6 | 0 | MERGE STATEMENT | |
7 | 1 | MERGE | Main_Table |
8 | 2 | VIEW | |
9 | 3 | HASH JOIN | |
10 | 4 | TABLE ACCESS FULL | Stg_Table |
11 | 5 | PARTITION LIST ALL| |
12 | 6 | TABLE ACCESS FULL| Main_Table |
EDIT: For clarification , if it wasn't clear, I'm not looking for an answer on how to make Oracle to scan only a single partition, I already know that placing the t.period_code = 201612
in the ON
clause will be fine. My question is - Why doesn't oracle evaluates the WHERE
clause which should filter only the specific partition ?
Upvotes: 8
Views: 121
Reputation: 21115
A distinction should be made between a WHERE clause
of merge insert / update clause and a WHERE clause
of SELECT statement.
The MERGE clauses are in general saing - do not UPDATE or do not INSERT. A generation of ACCESS predicate in general case is not trivial.
As other pointed out in the simplest case with only one UPDATE WHERE clause, there is no ACCESS predicate generation performed as of 11g.
It is also documentes as such
Specify the where_clause if you want the database to execute the update operation only if the specified condition is true. The condition can refer to either the data source or the target table. If the condition is not true, then the database skips the update operation when merging the row into the table.
i.e. the predicate skipps the update not a record in row source.
Upvotes: 0
Reputation: 44991
It seems like there is no optimization on the UPDATE's WHERE clause, at all.
create table t (n,x) as select level n,-1 x from dual connect by level <= 1000000;
create table s (n,x) as select level n,-1 x from dual connect by level <= 1000000;
merge into t
using s
on (s.n = t.n)
when matched then update set t.x = s.x where 1=2
;
Upvotes: 2
Reputation: 4818
Please consider moving t.period_code = 201612
to on condition: on (t.period_code = 201612 and s.site_id = t.site_id)
. I think your query is trying tyo access all partitions PARTITION LIST ALL
and that's the problem.
If you add condition that access single partition to on it should go better.
Another option is:
MERGE INTO (select * from Main_Table where period_code = 201612) t
USING Stg_Table s
ON(t.period_code = 201612 and s.site_id = t.site_id)
WHEN MATCHED THEN
UPDATE SET t.arpu_prev_period = s.arpu_prev_period
.... --50 more columns
where t.period_code = 201612
To directly point that update is only for one partition.
EDIT
Ok, so trying to answer question why. Oracle can't combine two conditions into one. where
contains information about partition but to apply where
on joined data it needs to apply ON
condition first. But at that point of time it has no information about partition just site_id
so it decides to do scan over all partitions. You need to inform Oracle at first step (which is joining on
) what partition you want to use.
In other words to apply where
which contains information about partition first it needs to resolve:
MERGE INTO Main_Table t
USING Stg_Table s
ON(s.site_id = t.site_id)
and here you have to access all partitions. where
is part of when matched
so first we need to determine if there is any match or not without knowledge about partitions.
Upvotes: 0