How to do an update + join in PostgreSQL? - postgresql
Basically, I want to do this:
update vehicles_vehicle v
join shipments_shipment s on v.shipment_id=s.id
set v.price=s.price_per_vehicle;
I'm pretty sure that would work in MySQL (my background), but it doesn't seem to work in postgres. The error I get is:
ERROR: syntax error at or near "join"
LINE 1: update vehicles_vehicle v join shipments_shipment s on v.shi...
^
Surely there's an easy way to do this, but I can't find the proper syntax. So, how would I write this In PostgreSQL?
The UPDATE syntax is:
[ WITH [ RECURSIVE ] with_query [, ...] ]
UPDATE [ ONLY ] table [ [ AS ] alias ]
SET { column = { expression | DEFAULT } |
( column [, ...] ) = ( { expression | DEFAULT } [, ...] ) } [, ...]
[ FROM from_list ]
[ WHERE condition | WHERE CURRENT OF cursor_name ]
[ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ]
In your case I think you want this:
UPDATE vehicles_vehicle AS v
SET price = s.price_per_vehicle
FROM shipments_shipment AS s
WHERE v.shipment_id = s.id
Or if you need to join on two or more tables:
UPDATE table_1 t1
SET foo = 'new_value'
FROM table_2 t2
JOIN table_3 t3 ON t3.id = t2.t3_id
WHERE
t2.id = t1.t2_id
AND t3.bar = True;
The answer of Mark Byers is the optimal in this situation.
Though in more complex situations you can take the select query that returns rowids and calculated values and attach it to the update query like this:
with t as (
-- Any generic query which returns rowid and corresponding calculated values
select t1.id as rowid, f(t2, t2) as calculatedvalue
from table1 as t1
join table2 as t2 on t2.referenceid = t1.id
)
update table1
set value = t.calculatedvalue
from t
where id = t.rowid
This approach lets you develop and test your select query and in two steps convert it to the update query.
So in your case the result query will be:
with t as (
select v.id as rowid, s.price_per_vehicle as calculatedvalue
from vehicles_vehicle v
join shipments_shipment s on v.shipment_id = s.id
)
update vehicles_vehicle
set price = t.calculatedvalue
from t
where id = t.rowid
Note that column aliases are mandatory otherwise PostgreSQL will complain about the ambiguity of the column names.
Let me explain a little more by my example.
Task: correct info, where abiturients (students about to leave secondary school) have submitted applications to university earlier, than they got school certificates (yes, they got certificates earlier, than they were issued (by certificate date specified). So, we will increase application submit date to fit certificate issue date.
Thus. next MySQL-like statement:
UPDATE applications a
JOIN (
SELECT ap.id, ab.certificate_issued_at
FROM abiturients ab
JOIN applications ap
ON ab.id = ap.abiturient_id
WHERE ap.documents_taken_at::date < ab.certificate_issued_at
) b
ON a.id = b.id
SET a.documents_taken_at = b.certificate_issued_at;
Becomes PostgreSQL-like in such a way
UPDATE applications a
SET documents_taken_at = b.certificate_issued_at -- we can reference joined table here
FROM abiturients b -- joined table
WHERE
a.abiturient_id = b.id AND -- JOIN ON clause
a.documents_taken_at::date < b.certificate_issued_at -- Subquery WHERE
As you can see, original subquery JOIN's ON clause have become one of WHERE conditions, which is conjucted by AND with others, which have been moved from subquery with no changes. And there is no more need to JOIN table with itself (as it was in subquery).
For those actually wanting to do a JOIN you can also use:
UPDATE a
SET price = b_alias.unit_price
FROM a AS a_alias
LEFT JOIN b AS b_alias ON a_alias.b_fk = b_alias.id
WHERE a_alias.unit_name LIKE 'some_value'
AND a.id = a_alias.id;
You can use the a_alias in the SET section on the right of the equals sign if needed.
The fields on the left of the equals sign don't require a table reference as they are deemed to be from the original "a" table.
For those wanting to do a JOIN that updates ONLY the rows your join returns use:
UPDATE a
SET price = b_alias.unit_price
FROM a AS a_alias
LEFT JOIN b AS b_alias ON a_alias.b_fk = b_alias.id
WHERE a_alias.unit_name LIKE 'some_value'
AND a.id = a_alias.id
--the below line is critical for updating ONLY joined rows
AND a.pk_id = a_alias.pk_id;
This was mentioned above but only through a comment..Since it's critical to getting the correct result posting NEW answer that Works
Here we go:
update vehicles_vehicle v
set price=s.price_per_vehicle
from shipments_shipment s
where v.shipment_id=s.id;
Simple as I could make it.
To add something quite important to all the great answers above, when you want to update a join-table, you may have 2 problems:
you cannot use the table you want to update to JOIN another one
Postgres wants a ON clause after the JOIN so you cannot only use where clauses.
This means that basically, the following queries are not valid:
UPDATE join_a_b
SET count = 10
FROM a
JOIN b on b.id = join_a_b.b_id -- Not valid since join_a_b is used here
WHERE a.id = join_a_b.a_id
AND a.name = 'A'
AND b.name = 'B'
UPDATE join_a_b
SET count = 10
FROM a
JOIN b -- Not valid since there is no ON clause
WHERE a.id = join_a_b.a_id
AND b.id = join_a_b.b_id
a.name = 'A'
AND b.name = 'B'
Instead, you must use all the tables in the FROM clause like this:
UPDATE join_a_b
SET count = 10
FROM a, b
WHERE a.id = join_a_b.a_id
AND b.id = join_a_b.b_id
AND a.name = 'A'
AND b.name = 'B'
It might be straightforward for some but I got stuck on this problem wondering what's going on so hopefully, it will help others.
Here's a simple SQL that updates Mid_Name on the Name3 table using the Middle_Name field from Name:
update name3
set mid_name = name.middle_name
from name
where name3.person_id = name.person_id;
The link below has a example that resolve and helps understant better how use update and join with postgres.
UPDATE product
SET net_price = price - price * discount
FROM
product_segment
WHERE
product.segment_id = product_segment.id;
See: http://www.postgresqltutorial.com/postgresql-update-join/
First Table Name: tbl_table1 (tab1).
Second Table Name: tbl_table2 (tab2).
Set the tbl_table1's ac_status column to "INACTIVE"
update common.tbl_table1 as tab1
set ac_status= 'INACTIVE' --tbl_table1's "ac_status"
from common.tbl_table2 as tab2
where tab1.ref_id= '1111111'
and tab2.rel_type= 'CUSTOMER';
To UPDATE one Table using another, in PostGRE SQL / AWS (SQL workbench).
In PostGRE SQL, this is how you need to use joins in UPDATE Query:
UPDATE TABLEA set COLUMN_FROM_TABLEA = COLUMN_FROM_TABLEB FROM TABLEA,TABLEB WHERE FILTER_FROM_TABLEA = FILTER_FROM_TABLEB;
Example:
Update Employees Set Date_Of_Exit = Exit_Date_Recorded , Exit_Flg = 1 From Employees, Employee_Exit_Clearance Where Emp_ID = Exit_Emp_ID
Table A - Employees Columns in Table A - Date_Of_Exit,Emp_ID,Exit_Flg Table B is - Employee_Exit_Clearance Columns in Table B - Exit_Date_Recorded,Exit_Emp_ID
1760 rows affected
Execution time: 29.18s
--goal: update selected columns with join (postgres)--
UPDATE table1 t1
SET column1 = 'data'
FROM table1
RIGHT JOIN table2
ON table2.id = table1.id
WHERE t1.id IN
(SELECT table2.id FROM table2 WHERE table2.column2 = 12345)
The first way is slower than the second way.
First:
DO $$
DECLARE
page int := 10000;
min_id bigint; max_id bigint;
BEGIN
SELECT max(id),min(id) INTO max_id,min_id FROM opportunities;
FOR j IN min_id..max_id BY page LOOP
UPDATE opportunities SET sec_type = 'Unsec'
FROM opportunities AS opp
INNER JOIN accounts AS acc
ON opp.account_id = acc.id
WHERE acc.borrower = true
AND opp.sec_type IS NULL
AND opp.id >= j AND opp.id < j+page;
COMMIT;
END LOOP;
END; $$;
Second:
DO $$
DECLARE
page int := 10000;
min_id bigint; max_id bigint;
BEGIN
SELECT max(id),min(id) INTO max_id,min_id FROM opportunities;
FOR j IN min_id..max_id BY page LOOP
UPDATE opportunities AS opp
SET sec_type = 'Unsec'
FROM accounts AS acc
WHERE opp.account_id = acc.id
AND opp.sec_type IS NULL
AND acc.borrower = true
AND opp.id >= j AND opp.id < j+page;
COMMIT;
END LOOP;
END; $$;
WORKS PERFECT!!!
POSTGRE SQL - UPDATE With a JOIN
BELOW CODE - Check the positioning of columns and IDs as below:
If you place it exactly as below, then only it will work!
---IF you want to update FIRST table
UPDATE table1
SET attribute1 = table2.attribute1
FROM table2
WHERE table2.product_ID = table1.product_ID;
OR
---IF you want to update SECOND table
UPDATE table2
SET attribute1 = table1.attribute1
FROM table1
WHERE table1.product_ID = table2.product_ID;
Related
Updating a table by joining an array of FKs of another table [duplicate]
Basically, I want to do this: update vehicles_vehicle v join shipments_shipment s on v.shipment_id=s.id set v.price=s.price_per_vehicle; I'm pretty sure that would work in MySQL (my background), but it doesn't seem to work in postgres. The error I get is: ERROR: syntax error at or near "join" LINE 1: update vehicles_vehicle v join shipments_shipment s on v.shi... ^ Surely there's an easy way to do this, but I can't find the proper syntax. So, how would I write this In PostgreSQL?
The UPDATE syntax is: [ WITH [ RECURSIVE ] with_query [, ...] ] UPDATE [ ONLY ] table [ [ AS ] alias ] SET { column = { expression | DEFAULT } | ( column [, ...] ) = ( { expression | DEFAULT } [, ...] ) } [, ...] [ FROM from_list ] [ WHERE condition | WHERE CURRENT OF cursor_name ] [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ] In your case I think you want this: UPDATE vehicles_vehicle AS v SET price = s.price_per_vehicle FROM shipments_shipment AS s WHERE v.shipment_id = s.id Or if you need to join on two or more tables: UPDATE table_1 t1 SET foo = 'new_value' FROM table_2 t2 JOIN table_3 t3 ON t3.id = t2.t3_id WHERE t2.id = t1.t2_id AND t3.bar = True;
The answer of Mark Byers is the optimal in this situation. Though in more complex situations you can take the select query that returns rowids and calculated values and attach it to the update query like this: with t as ( -- Any generic query which returns rowid and corresponding calculated values select t1.id as rowid, f(t2, t2) as calculatedvalue from table1 as t1 join table2 as t2 on t2.referenceid = t1.id ) update table1 set value = t.calculatedvalue from t where id = t.rowid This approach lets you develop and test your select query and in two steps convert it to the update query. So in your case the result query will be: with t as ( select v.id as rowid, s.price_per_vehicle as calculatedvalue from vehicles_vehicle v join shipments_shipment s on v.shipment_id = s.id ) update vehicles_vehicle set price = t.calculatedvalue from t where id = t.rowid Note that column aliases are mandatory otherwise PostgreSQL will complain about the ambiguity of the column names.
Let me explain a little more by my example. Task: correct info, where abiturients (students about to leave secondary school) have submitted applications to university earlier, than they got school certificates (yes, they got certificates earlier, than they were issued (by certificate date specified). So, we will increase application submit date to fit certificate issue date. Thus. next MySQL-like statement: UPDATE applications a JOIN ( SELECT ap.id, ab.certificate_issued_at FROM abiturients ab JOIN applications ap ON ab.id = ap.abiturient_id WHERE ap.documents_taken_at::date < ab.certificate_issued_at ) b ON a.id = b.id SET a.documents_taken_at = b.certificate_issued_at; Becomes PostgreSQL-like in such a way UPDATE applications a SET documents_taken_at = b.certificate_issued_at -- we can reference joined table here FROM abiturients b -- joined table WHERE a.abiturient_id = b.id AND -- JOIN ON clause a.documents_taken_at::date < b.certificate_issued_at -- Subquery WHERE As you can see, original subquery JOIN's ON clause have become one of WHERE conditions, which is conjucted by AND with others, which have been moved from subquery with no changes. And there is no more need to JOIN table with itself (as it was in subquery).
For those actually wanting to do a JOIN you can also use: UPDATE a SET price = b_alias.unit_price FROM a AS a_alias LEFT JOIN b AS b_alias ON a_alias.b_fk = b_alias.id WHERE a_alias.unit_name LIKE 'some_value' AND a.id = a_alias.id; You can use the a_alias in the SET section on the right of the equals sign if needed. The fields on the left of the equals sign don't require a table reference as they are deemed to be from the original "a" table.
For those wanting to do a JOIN that updates ONLY the rows your join returns use: UPDATE a SET price = b_alias.unit_price FROM a AS a_alias LEFT JOIN b AS b_alias ON a_alias.b_fk = b_alias.id WHERE a_alias.unit_name LIKE 'some_value' AND a.id = a_alias.id --the below line is critical for updating ONLY joined rows AND a.pk_id = a_alias.pk_id; This was mentioned above but only through a comment..Since it's critical to getting the correct result posting NEW answer that Works
Here we go: update vehicles_vehicle v set price=s.price_per_vehicle from shipments_shipment s where v.shipment_id=s.id; Simple as I could make it.
To add something quite important to all the great answers above, when you want to update a join-table, you may have 2 problems: you cannot use the table you want to update to JOIN another one Postgres wants a ON clause after the JOIN so you cannot only use where clauses. This means that basically, the following queries are not valid: UPDATE join_a_b SET count = 10 FROM a JOIN b on b.id = join_a_b.b_id -- Not valid since join_a_b is used here WHERE a.id = join_a_b.a_id AND a.name = 'A' AND b.name = 'B' UPDATE join_a_b SET count = 10 FROM a JOIN b -- Not valid since there is no ON clause WHERE a.id = join_a_b.a_id AND b.id = join_a_b.b_id a.name = 'A' AND b.name = 'B' Instead, you must use all the tables in the FROM clause like this: UPDATE join_a_b SET count = 10 FROM a, b WHERE a.id = join_a_b.a_id AND b.id = join_a_b.b_id AND a.name = 'A' AND b.name = 'B' It might be straightforward for some but I got stuck on this problem wondering what's going on so hopefully, it will help others.
Here's a simple SQL that updates Mid_Name on the Name3 table using the Middle_Name field from Name: update name3 set mid_name = name.middle_name from name where name3.person_id = name.person_id;
The link below has a example that resolve and helps understant better how use update and join with postgres. UPDATE product SET net_price = price - price * discount FROM product_segment WHERE product.segment_id = product_segment.id; See: http://www.postgresqltutorial.com/postgresql-update-join/
First Table Name: tbl_table1 (tab1). Second Table Name: tbl_table2 (tab2). Set the tbl_table1's ac_status column to "INACTIVE" update common.tbl_table1 as tab1 set ac_status= 'INACTIVE' --tbl_table1's "ac_status" from common.tbl_table2 as tab2 where tab1.ref_id= '1111111' and tab2.rel_type= 'CUSTOMER';
To UPDATE one Table using another, in PostGRE SQL / AWS (SQL workbench). In PostGRE SQL, this is how you need to use joins in UPDATE Query: UPDATE TABLEA set COLUMN_FROM_TABLEA = COLUMN_FROM_TABLEB FROM TABLEA,TABLEB WHERE FILTER_FROM_TABLEA = FILTER_FROM_TABLEB; Example: Update Employees Set Date_Of_Exit = Exit_Date_Recorded , Exit_Flg = 1 From Employees, Employee_Exit_Clearance Where Emp_ID = Exit_Emp_ID Table A - Employees Columns in Table A - Date_Of_Exit,Emp_ID,Exit_Flg Table B is - Employee_Exit_Clearance Columns in Table B - Exit_Date_Recorded,Exit_Emp_ID 1760 rows affected Execution time: 29.18s
--goal: update selected columns with join (postgres)-- UPDATE table1 t1 SET column1 = 'data' FROM table1 RIGHT JOIN table2 ON table2.id = table1.id WHERE t1.id IN (SELECT table2.id FROM table2 WHERE table2.column2 = 12345)
The first way is slower than the second way. First: DO $$ DECLARE page int := 10000; min_id bigint; max_id bigint; BEGIN SELECT max(id),min(id) INTO max_id,min_id FROM opportunities; FOR j IN min_id..max_id BY page LOOP UPDATE opportunities SET sec_type = 'Unsec' FROM opportunities AS opp INNER JOIN accounts AS acc ON opp.account_id = acc.id WHERE acc.borrower = true AND opp.sec_type IS NULL AND opp.id >= j AND opp.id < j+page; COMMIT; END LOOP; END; $$; Second: DO $$ DECLARE page int := 10000; min_id bigint; max_id bigint; BEGIN SELECT max(id),min(id) INTO max_id,min_id FROM opportunities; FOR j IN min_id..max_id BY page LOOP UPDATE opportunities AS opp SET sec_type = 'Unsec' FROM accounts AS acc WHERE opp.account_id = acc.id AND opp.sec_type IS NULL AND acc.borrower = true AND opp.id >= j AND opp.id < j+page; COMMIT; END LOOP; END; $$;
WORKS PERFECT!!! POSTGRE SQL - UPDATE With a JOIN BELOW CODE - Check the positioning of columns and IDs as below: If you place it exactly as below, then only it will work! ---IF you want to update FIRST table UPDATE table1 SET attribute1 = table2.attribute1 FROM table2 WHERE table2.product_ID = table1.product_ID; OR ---IF you want to update SECOND table UPDATE table2 SET attribute1 = table1.attribute1 FROM table1 WHERE table1.product_ID = table2.product_ID;
Update table with from sub select
I have two table a and b. I want to update the row in table a that is the most recent insert for each id from the earliest insert in table b where a.id = b.id I've been trying to use an update statement with a sub select in the from. If I execute the sub query on its own it returns x number of rows, however when I execute the whole update statement it updated y number of rows. update a set title = b.title created_at = b.created_at from ( select e.id,e.title,e.created_at from ( select l.id, l.title, l.created_at l.t_insert from b l left join b r l.id = r.id and l.t_insert > r.t_insert ) e join ( select l.id, l.title, l.created_at, l.t_insert from a l left join a r on l.report_id = r.report_id and l.t_insert < r.t_insert ) f ) where a.id=b.id I want the same number of rows to be updated as returned in the sub select query in the from.
In this case, having fewer rows updated than returned by the subquery could be because one row id is returned more than once in the subquery. If that happens, the update statement will still only update the row once. I'm assuming the statement you've provided is not exactly what you're running, but you should check that the subquery is not providing duplicates in the id field of the subquery (either using DISTINCT or GROUP BY or by double checking your JOIN conditions.
Updating a CTE table fail cause of derived or constant field
I'm using MS-SQL 2012 WITH C1 ( SELECT ID, 0 as Match, Field2, Count(*) FROM TableX GROUP BY ID, Fields2 ) UPDATE C1 SET Match = 1 WHERE ID = (SELECT MATCHING_ID FROM AnotherTable WHERE ID = C1.ID) This TSQL statement gives me the following error: Update or insert of view or function 'C1' failed because it contains a derived or constant field. Ideally I would like to create a "fake field" named Match and set its default value to 0. Then with the update I would like to Update ONLY the records that have an existing entry on the "AnotherTable". Any thoughts what am I doing wrong? Thanks in advanced.
Try doing a Left Outer Join like SELECT x.ID, ISNULL(a.Matching_ID, 0) as Match, x.Field2, Count(*) FROM TableX x LEFT OUTER JOIN AnotherTable a on x.ID = a.ID GROUP BY x.ID, ISNULL(a.Matching_ID, 0), x.Fields2 without the need of a C1
If I am understanding correctly, the problem is that you are trying to update the CTE table. If you update the table directly you should be fine. Does this modified version help? SELECT t.ID , CASE WHEN (EXISTS (SELECT MATCHING_ID FROM AnotherTable WHERE ID = t.ID)) THEN 1 ELSE 0 END ,t.Field2 ,Count(*) FROM TableX t GROUP BY ID, Fields2
Postgres join not respecting outer where clause
In SQL Server, I know for sure that the following query; SELECT things.* FROM things LEFT OUTER JOIN ( SELECT thingreadings.thingid, reading FROM thingreadings INNER JOIN things on thingreadings.thingid = things.id ORDER BY reading DESC LIMIT 1) AS readings ON things.id = readings.thingid WHERE things.id = '1' Would join against thingreadings only once the WHERE id = 1 had restricted the record set down. It left joins against just one row. However in order for performance to be acceptable in postgres, I have to add the WHERE id= 1 to the INNER JOIN things on thingreadings.thingid = things.id line too. This isn't ideal; is it possible to force postgres to know that what I am joining against is only one row without explicitly adding the WHERE clauses everywhere? An example of this problem can be seen here; I am trying to recreate the following query in a more efficient way; SELECT things.id, things.name, (SELECT thingreadings.id FROM thingreadings WHERE thingid = things.id ORDER BY id DESC LIMIT 1), (SELECT thingreadings.reading FROM thingreadings WHERE thingid = things.id ORDER BY id DESC LIMIT 1) FROM things WHERE id IN (1,2) http://sqlfiddle.com/#!15/a172c/2
Not really sure why you did all that work. Isn't the inner query enough? SELECT t.* FROM thingreadings tr INNER JOIN things t on tr.thingid = t.id AND t.id = '1' ORDER BY tr.reading DESC LIMIT 1; sqlfiddle demo When you want to select the latest value for each thingID, you can do: SELECT t.*,a.reading FROM things t INNER JOIN ( SELECT t1.* FROM thingreadings t1 LEFT JOIN thingreadings t2 ON (t1.thingid = t2.thingid AND t1.reading < t2.reading) WHERE t2.thingid IS NULL ) a ON a.thingid = t.id sqlfiddle demo The derived table gets you the record with the most recent reading, then the JOIN gets you the information from things table for that record.
The where clause in SQL applies to the result set you're requesting, NOT to the join. What your code is NOT saying: "do this join only for the ID of 1"... What your code IS saying: "do this join, then pull records out of it where the ID is 1"... This is why you need the inner where clause. Incidentally, I also think Filipe is right about the unnecessary code.
Bulk update a column in Oracle 11G
I have two tables say Table1 and Table2 that contains the following column with which I should join and perform an update a column of Table1 with the value of the same column present in Table2. Columns for Join condition: Table1.mem_ssn and Table2.ins_ssn Table1.sys_id and Table2.sys_id Table1.grp_id and Table2.grp_id Column to update: Table1.dtofhire=Table2.dtofhire I need a way to bulk update (using single update query without looping) the above mentioned column in Oracle 11G. Table1 does not contain any key constraint specified since it will be used as a staging table for Data upload. Please help me out to update the same.
You can use the MERGE statement. It should look something like this: MERGE INTO table1 D USING (SELECT * FROM table2 ) S ON (D.mem_ssn = S.ins_ssn and D.sys_id = S.sys_id and D.grp_id=S.grp_id) WHEN MATCHED THEN UPDATE SET D.dtofhire=S.dtofhire; UPDATE: Since you have more than one row in table2 with the same (ins_ssn,sys_id,grp_id) and you want the max dtofhire, you should change the query in the using clause: MERGE INTO table1 D USING (SELECT ins_ssn, sys_id, grp_id, max(dtofhire) m_dtofhire FROM table2 GROUP BY ins_ssn,sys_id,grp_id) S ON (D.mem_ssn = S.ins_ssn and D.sys_id = S.sys_id and D.grp_id=S.grp_id) WHEN MATCHED THEN UPDATE SET D.dtofhire=S.m_dtofhire;
The query that I used to arrive the functionality is seen below UPDATE table1 T2 SET dtofhire = (SELECT Max(dtofhire) AS dtofhire FROM table2 T1 WHERE T2.mem_ssn = T1.ins_ssn AND T2.sys_id = T1.sys_id AND T2.grp_id = T1.grp_id GROUP BY ins_ssn, sys_id, grp_id) WHERE ( mem_ssn, sys_id, grp_id ) IN (SELECT ins_ssn, sys_id, grp_id FROM table2 );