PostgreSQL update tbl1 from tbl2 one-to-many considerations (exactly which one matches)

Keywords: PostgreSQL github Database

Label

PostgreSQL, update from, one-to-many

background

First of all, table A and table B need related columns. After association, table A and table B should have a one-to-one or one-to-one relationship. If one-to-one, it is easy to understand.

What if it's one-to-many? Any database will give you an uncertain answer (related to the scanning method of execution plan data).

The tests are as follows:

sar=> create table a (id int primary key, info text);  
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "a_pkey" for table "a"  
CREATE TABLE  
sar=> create table b (id int, info text);  
CREATE TABLE  
sar=> insert into a select generate_series(1,10),'digoal';  
INSERT 0 10  
sar=> insert into b select generate_series(1,10),'Digoal';  
INSERT 0 10  
sar=> insert into b select generate_series(1,10),'DIGOAL';  
INSERT 0 10  
sar=> select * from a where id=1;  
 id |  info    
----+--------  
  1 | digoal  
(1 row)  
  
sar=> select * from b where id=1;  
 id |  info    
----+--------  
  1 | Digoal  
  1 | DIGOAL  
(2 rows)  

What does a.id mean after the following updates? Is it Digoal or DIGOAL?

Look at the results of the first implementation plan

b The table has not yet been indexed and is used nestloop+Full table scan  
  
postgres=# explain update a set info=b.info from b where a.id=b.id and a.id=1;  
                                 QUERY PLAN                                    
-----------------------------------------------------------------------------  
 Update on a  (cost=0.15..28.70 rows=6 width=48)  
   ->  Nested Loop  (cost=0.15..28.70 rows=6 width=48)  
         ->  Index Scan using a_pkey on a  (cost=0.15..2.77 rows=1 width=10)  
               Index Cond: (id = 1)  
         ->  Seq Scan on b  (cost=0.00..25.88 rows=6 width=42)  
               Filter: (id = 1)  
(6 rows)  
  
//Digoal hits ahead in full table scan  
  
postgres=# select * from b where id=1 limit 1;  
 id |  info    
----+--------  
  1 | Digoal  
(1 row)  
  
//Update got the first hit b.info  
  
sar=> update a set info=b.info from b where a.id=b.id and a.id=1;  
UPDATE 1  
sar=> select * from a where id=1;  
 id |  info    
----+--------  
  1 | Digoal  
(1 row)  

Look at the second execution plan, using nestloop + index scan

Create a composite index so that when the index is scanned, DIGOAL This record goes to the front.  
postgres=# create index idx_b_id on b(id, info);  
CREATE INDEX  
  
  
postgres=# set enable_seqscan=off;  
SET  
  
postgres=# select * from b where id=1 limit 1;  
 id |  info    
----+--------  
  1 | DIGOAL  
(1 row)  
  
//Now execute the plan. Table B uses the index.  
postgres=# explain update a set info=b.info from b where a.id=b.id and a.id=1;  
                                  QUERY PLAN                                     
-------------------------------------------------------------------------------  
 Update on a  (cost=0.29..5.53 rows=1 width=48)  
   ->  Nested Loop  (cost=0.29..5.53 rows=1 width=48)  
         ->  Index Scan using a_pkey on a  (cost=0.15..2.77 rows=1 width=10)  
               Index Cond: (id = 1)  
         ->  Index Scan using idx_b_id on b  (cost=0.14..2.75 rows=1 width=42)  
               Index Cond: (id = 1)  
(6 rows)  
  
//Now update, it will become DIGOAL.  
postgres=# update a set info=b.info from b where a.id=b.id and a.id=1 returning a.ctid,*;  
  ctid  | id |  info  | id |  info    
--------+----+--------+----+--------  
 (0,11) |  1 | DIGOAL |  1 | DIGOAL  
(1 row)  
  
UPDATE 1  

Posted by littleelmo on Tue, 12 Feb 2019 19:00:18 -0800