I wonder why there is a difference in execution plan cost for the 2 queries :
create table test_insert(id int not null primary key, value varchar2(10));
--1
insert into test_insert(id,value)
values (2,'111');
--2
insert into test_insert(id,value)
select 3,'111' from dual where not exists (select null from test_insert where id =3);
Cost for query 2 is always higher (how much depends on number of rows in the table).
In my understanding not exits ...
should not add any overhead – in order to enforce PK constraint the engine must check the corresponding unique index anyway, so subquery adds extra parsing time, but not execution time or extra step to the plan.
The example is for Oracle, but I checked Postgres as well, results are similar.
For example,
--1
| Id | Operation | Name | Cost |
--------------------------------------------------------
| 0 | INSERT STATEMENT | | 1 |
| 1 | LOAD TABLE CONVENTIONAL | TEST_INSERT | |
--------------------------------------------------------
--2
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | | | 3 (100)| |
| 1 | LOAD TABLE CONVENTIONAL | TEST_INSERT | | | | |
|* 2 | FILTER | | | | | |
| 3 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
|* 4 | INDEX UNIQUE SCAN | SYS_C0012345 | 1 | 13 | 1 (0)| 00:00:01 |
Best Answer
I'm not exactly sure what you're trying to demonstrate, in the bottom you have a
NOT EXISTS
. That's entails either anindex only scan
PostgreSQL plan,
So of course it's slower. It's checking the index before it inserts, and then the btree-insertion for the PRIMARY KEY is checking again (as it always must).
If you want to roll that into a try-or-skip, use
ON CONFLICT DO NOTHING
Here is an example of your problem.. Sample schema,
Sample transactions,
What happens in the above?
EXISTS
tests operating in the defaultREAD COMMITTED ISOLATION
level gets a snapshot. And then it holds up and goes to sleep.The first transaction throws an error,
Switch the order of those transactions though and the first transaction sees that the key is there and never tries to insert into the btree.
Hopefully that helps you understand what's happening.