Thread: Postgres update with self join

Postgres update with self join

From
"Igor Kryltsov"
Date:
Hi,


If you can help me to correct my mistake.
To simplify my question:

I have table:

create table test (
name varchar(10),
code integer,
master varchar(10));

I have values:
insert into test values ('ABC', 15074, null);
insert into test values ('ABC1', 0, 'ABC');
insert into test values ('ABC2', 0, 'ABC');
insert into test values ('EKL', 15075, null);
insert into test values ('EKL1', 0, 'EKL');


Table looks like:

select * from test;
 name | code  | master
------+-------+--------
 ABC  | 15074 |
 ABC1 |     0 | ABC
 ABC2 |     0 | ABC
 EKL  | 15075 |
 EKL1 |     0 | EKL
(5 rows)


Now I need to replace "0" values in "code" column by corresponding "code"
values by following link between "master" field in a record where code=0 and
"name" field where it is not.
By the other words first two 0's have to be replaced with 15074 and last 0
with 15075.

This update works in MSSQL but in Postgres it replaces code values as shown
below.

update test
set code = i1.code
from test i1
join test i2 on i1.name = i2.master
where i2.code = 0;


 select * from test;
 name | code  | master
------+-------+--------
 ABC  | 15074 |
 ABC1 | 15074 | ABC
 ABC2 | 15074 | ABC
 EKL  | 15074 |
 EKL1 | 15074 | EKL
(5 rows)

... all values 15074.


Thank you,


Igor



Re: Postgres update with self join

From
Stephan Szabo
Date:
On Tue, 10 Aug 2004, Igor Kryltsov wrote:

> Table looks like:
>
> select * from test;
>  name | code  | master
> ------+-------+--------
>  ABC  | 15074 |
>  ABC1 |     0 | ABC
>  ABC2 |     0 | ABC
>  EKL  | 15075 |
>  EKL1 |     0 | EKL
> (5 rows)
>
>
> Now I need to replace "0" values in "code" column by corresponding "code"
> values by following link between "master" field in a record where code=0 and
> "name" field where it is not.
> By the other words first two 0's have to be replaced with 15074 and last 0
> with 15075.
>
> This update works in MSSQL but in Postgres it replaces code values as shown
> below.
>
> update test
> set code = i1.code
> from test i1
> join test i2 on i1.name = i2.master
> where i2.code = 0;

You probably need to be constraining the join between test and (i1 join
i2).

Maybe an additional where clause like "and test.name=i2.name" or something
like that would work.

Or, I think you can do this with a subselect which would have the
advantage of not requiring extensions to the standard.  Perhaps something
like
 update test set code=(select code from test i2 where test.master=i2.name)
 where code=0;
would do it.

Re: Postgres update with self join

From
Tom Lane
Date:
"Igor Kryltsov" <kryltsov@yahoo.com> writes:
> Now I need to replace "0" values in "code" column by corresponding "code"
> values by following link between "master" field in a record where code=0 and
> "name" field where it is not.

> This update works in MSSQL but in Postgres it replaces code values as shown
> below.

> update test
> set code = i1.code
> from test i1
> join test i2 on i1.name = i2.master
> where i2.code = 0;

That query has no join condition to the target table, so it's hardly
surprising that it updates everything in sight.  If it "works" in MSSQL
it must be because they are making some weird decision to pretend that
one or the other of the mentions of test in the FROM clause ought to be
identified with the target table.  (If it acts as you want then they
must be identifying "test i2" with "test", which is *really* weird ---
you would think the first occurrence of test in the FROM would be the
one they'd pick.  I suppose this is another instance of an
implementation bug becoming enshrined as a feature.)

In Postgres you want to do something like this:

update test
set code = mst.code
from test mst
where test.master = mst.name
and test.code = 0;

To act exactly as you stated in words you'd probably also want to add
"and mst.code <> 0".

            regards, tom lane

Re: Postgres update with self join

From
Franco Bruno Borghesi
Date:
This is the way you do it in postgreSQL:

UPDATE
   test
SET
   code=T2.code
FROM
   test T2
WHERE
   test.code=0 AND
   test.master=T2.name;

you need to specify the join condition in the WHERE clause.

On Tue, 2004-08-10 at 00:34, Igor Kryltsov wrote:
Hi,


If you can help me to correct my mistake.
To simplify my question:

I have table:

create table test (
name varchar(10),
code integer,
master varchar(10));

I have values:
insert into test values ('ABC', 15074, null);
insert into test values ('ABC1', 0, 'ABC');
insert into test values ('ABC2', 0, 'ABC');
insert into test values ('EKL', 15075, null);
insert into test values ('EKL1', 0, 'EKL');


Table looks like:

select * from test;name | code  | master
------+-------+--------ABC  | 15074 |ABC1 |     0 | ABCABC2 |     0 | ABCEKL  | 15075 |EKL1 |     0 | EKL
(5 rows)


Now I need to replace "0" values in "code" column by corresponding "code"
values by following link between "master" field in a record where code=0 and
"name" field where it is not.
By the other words first two 0's have to be replaced with 15074 and last 0
with 15075.

This update works in MSSQL but in Postgres it replaces code values as shown
below.

update test
set code = i1.code
from test i1
join test i2 on i1.name = i2.master
where i2.code = 0;

select * from test;name | code  | master
------+-------+--------ABC  | 15074 |ABC1 | 15074 | ABCABC2 | 15074 | ABCEKL  | 15074 |EKL1 | 15074 | EKL
(5 rows)

... all values 15074.


Thank you,


Igor



---------------------------(end of broadcast)---------------------------
TIP 9: the planner will ignore your desire to choose an index scan if your     joining column's datatypes do not match
Attachment

Re: Postgres update with self join

From
"Igor Kryltsov"
Date:
Thank you very much.

This works:
UPDATE

test

SET

code=T2.code

FROM

test T2

WHERE

test.code=0 AND

test.master=T2.name;



Igor