The problem with your query is that the subquery is not correlated. You dodn't need and you shouldn't use lessons inside the subquery.

UPDATE lessons
SET minicourse_id = subquery.minicourse_id
FROM (
  SELECT topics.minicourse_id
  FROM topics
  WHERE topics.id = lessons.topic_id   -- this refers to the "lesson"
                                       -- of the main query
) AS subquery ;

In fact, it can be written without a subquery at all:

UPDATE lessons 
SET minicourse_id = topics.minicourse_id 
FROM topics 
WHERE topics.id = lessons.topic_id ;

or with a different subquery:

UPDATE lessons 
SET minicourse_id 
    = ( SELECT minicourse_id 
        FROM topics 
        WHERE id = lessons.topic_id
      ) ;

Regarding your design, I assume you have added a lessons.minicourse_id column and this foreign key:

ALTER TABLE lessons
    ADD minicourse_id INT ;

ALTER TABLE lessons
    ADD FOREIGN KEY (minicourse_id) 
        REFERENCES minicourses (id) ;

While this achieves what you want, there is a small issue: you may end up with rows in child (lessons) that refer to a grandparent (minicourse) A and also to a parent (topics) that refers to a different grandparent B.

Of course if all your applications and users have code that is correct, this won't happen. But I suggest you enforce this in the database level and not (only) in the application level. This is quite easy to do, with the following change in the foreign keys.

It basically sets the FK to minicourses to be "through" lessons, without a direct FK. You'll still be able to use direct joins between lessons and minicourses:

ALTER TABLE lessons
    ADD minicourse_id INT ;

-- the UPDATE is the same!
UPDATE lessons 
SET minicourse_id = topics.minicourse_id 
FROM topics 
WHERE topics.id = lessons.topic_id ;

ALTER TABLE lessons
    ALTER minicourse_id SET NOT NULL ;

-- we need this for the FK below
ALTER TABLE topics 
    ADD CONSTRAINT minicourse_topic_UQ 
        UNIQUE (minicourse_id, id) ;

-- the FK is "lessons -> topics"
ALTER TABLE lessons
    ADD CONSTRAINT lessons_to_topics_FK2
        FOREIGN KEY (minicourse_id, topic_id) 
        REFERENCES topics (minicourse_id, id) ;

-- drop the previous FK to topics
ALTER TABLE lessons
    DROP CONSTRAINT lessons_to_topics_FK ;
Answer from ypercubeᵀᴹ on Stack Exchange
Top answer
1 of 1
7

The problem with your query is that the subquery is not correlated. You dodn't need and you shouldn't use lessons inside the subquery.

UPDATE lessons
SET minicourse_id = subquery.minicourse_id
FROM (
  SELECT topics.minicourse_id
  FROM topics
  WHERE topics.id = lessons.topic_id   -- this refers to the "lesson"
                                       -- of the main query
) AS subquery ;

In fact, it can be written without a subquery at all:

UPDATE lessons 
SET minicourse_id = topics.minicourse_id 
FROM topics 
WHERE topics.id = lessons.topic_id ;

or with a different subquery:

UPDATE lessons 
SET minicourse_id 
    = ( SELECT minicourse_id 
        FROM topics 
        WHERE id = lessons.topic_id
      ) ;

Regarding your design, I assume you have added a lessons.minicourse_id column and this foreign key:

ALTER TABLE lessons
    ADD minicourse_id INT ;

ALTER TABLE lessons
    ADD FOREIGN KEY (minicourse_id) 
        REFERENCES minicourses (id) ;

While this achieves what you want, there is a small issue: you may end up with rows in child (lessons) that refer to a grandparent (minicourse) A and also to a parent (topics) that refers to a different grandparent B.

Of course if all your applications and users have code that is correct, this won't happen. But I suggest you enforce this in the database level and not (only) in the application level. This is quite easy to do, with the following change in the foreign keys.

It basically sets the FK to minicourses to be "through" lessons, without a direct FK. You'll still be able to use direct joins between lessons and minicourses:

ALTER TABLE lessons
    ADD minicourse_id INT ;

-- the UPDATE is the same!
UPDATE lessons 
SET minicourse_id = topics.minicourse_id 
FROM topics 
WHERE topics.id = lessons.topic_id ;

ALTER TABLE lessons
    ALTER minicourse_id SET NOT NULL ;

-- we need this for the FK below
ALTER TABLE topics 
    ADD CONSTRAINT minicourse_topic_UQ 
        UNIQUE (minicourse_id, id) ;

-- the FK is "lessons -> topics"
ALTER TABLE lessons
    ADD CONSTRAINT lessons_to_topics_FK2
        FOREIGN KEY (minicourse_id, topic_id) 
        REFERENCES topics (minicourse_id, id) ;

-- drop the previous FK to topics
ALTER TABLE lessons
    DROP CONSTRAINT lessons_to_topics_FK ;
🌐
PostgreSQL
postgresql.org › docs › current › sql-update.html
PostgreSQL: Documentation: 18: UPDATE
May 14, 2026 - While there is no LIMIT clause for UPDATE, it is possible to get a similar effect through the use of a Common Table Expression and a self-join. With the standard PostgreSQL table access method, a self-join on the system column ctid is very efficient: WITH exceeded_max_retries AS ( SELECT w.ctid FROM work_item AS w WHERE w.status = 'active' AND w.num_retries > 10 ORDER BY w.retry_timestamp FOR UPDATE LIMIT 5000 ) UPDATE work_item SET status = 'failed' FROM exceeded_max_retries AS emr WHERE work_item.ctid = emr.ctid;
🌐
Neon
neon.com › postgresql › tutorial › update-join
PostgreSQL UPDATE Join with Practical Examples
Sometimes, you need to update data in a table based on values in another table. In this case, you can use the PostgreSQL UPDATE join. Here’s the basic syntax of the UPDATE join statement: UPDATE table1 SET table1.c1 = new_value FROM table2 WHERE table1.c2 = table2.c2;
🌐
w3resource
w3resource.com › PostgreSQL › snippets › postgresql-update-from-select.php
PostgreSQL Update from Select with Examples and Explanation
December 31, 2024 - The salary column in employees is adjusted using the bonus_percent from the departments table. Rows in employees are matched with rows in departments based on department_id. ... -- Adjust salary using a subquery UPDATE employees SET salary = salary + ( SELECT salary * bonus_percent FROM departments WHERE employees.department_id = departments.id );
🌐
PostgreSQL
postgresql.org › docs › current › sql-select.html
PostgreSQL: Documentation: 18: SELECT
May 14, 2026 - PostgreSQL allows it in any SELECT query as well as in sub-SELECTs, but this is an extension. The FOR NO KEY UPDATE, FOR SHARE and FOR KEY SHARE variants, as well as the NOWAIT and SKIP LOCKED options, do not appear in the standard. PostgreSQL allows INSERT, UPDATE, DELETE, and MERGE to be used as WITH queries. This is not found in the SQL standard. DISTINCT ON ( ... ) is an extension of the SQL standard. ROWS FROM( ...
🌐
TigerData
tigerdata.com › learn › using-postgresql-update-with-join
Using PostgreSQL UPDATE With JOIN | Tiger Data
December 10, 2025 - UPDATE table_name SET column1 = new_value1, column2 = new_value2 FROM source_table WHERE table_name.column = source_table.column; Copy · The PostgreSQL UPDATE with JOIN syntax differs slightly from other database systems.
Find elsewhere
🌐
W3Schools
w3schools.com › postgresql › postgresql_update.php
PostgreSQL - The UPDATE Statement
PostgreSQL CREATE TABLE PostgreSQL INSERT INTO PostgreSQL Fetch Data PostgreSQL ADD COLUMN PostgreSQL UPDATE PostgreSQL ALTER COLUMN PostgreSQL DROP COLUMN PostgreSQL DELETE PostgreSQL DROP TABLE Create Demo Database · PostgreSQL Operators PostgreSQL SELECT PostgreSQL SELECT DISTINCT PostgreSQL ...
🌐
Sqlfordevs
sqlfordevs.com › update-from-select
UPDATE from a SELECT - Database Tip
SELECT category_id, discount FROM ... table, a more efficient approach is possible: The UPDATE can be joined to the result of a SELECT statement reducing it to a single query....
🌐
TIL
ryanguill.com › postgresql › sql › 2017 › 05 › 18 › postgres-update-with-select.html
Postgres update using subselect
For example: UPDATE table SET (foo, bar) = (SELECT 'foo', 'bar') WHERE id = 1; The subselect can be any query, just return your columns in the same order as the column list you provide.
🌐
PostgreSQL
postgresql.org › download
PostgreSQL: Downloads
Select your operating system family: Linux macOS Windows BSD Solaris
🌐
GeeksforGeeks
geeksforgeeks.org › postgresql › postgresql-update
PostgreSQL UPDATE Statement - GeeksforGeeks
July 12, 2025 - The PostgreSQL UPDATE statement is an important SQL command used to modify existing data in one or more rows of a table. It allows users to update specific columns or multiple columns at once, using conditions defined in the WHERE clause.
🌐
Quora
quora.com › How-can-I-update-multiple-rows-from-a-select-query-in-PostgreSQL
How to update multiple rows from a select query in PostgreSQL - Quora
Answer (1 of 4): How about this? [code] UPDATE mytable SET mycolumn = 'newvalue' WHERE myid in (1,2,3,...) [/code] This assumes that you have a set of unique IDs that each child process can own, separate from other child processes. Also it assumes you have a single new value for each of those ...
🌐
Postgresql
docs.postgresql.tw › reference › sql-commands › update
UPDATE | PostgreSQL 正體中文使用手冊
UPDATE changes the values of the specified columns in all rows that satisfy the condition. Only the columns to be modified need be mentioned in the SET clause; columns not explicitly modified retain their previous values. There are two ways to modify a table using information contained in other tables in the database: using sub-selects, or specifying additional tables in the FROM clause.
🌐
ZetCode
zetcode.com › postgresql › update-statement
PostgreSQL UPDATE Statement
-- CREATE TABLE books ( -- book_id INTEGER PRIMARY KEY, -- title VARCHAR(100) NOT NULL, -- author VARCHAR(100) NOT NULL, -- genre VARCHAR(50) NOT NULL, -- price NUMERIC(5,2) NOT NULL CHECK (price >= 0), -- publication_year INTEGER NOT NULL CHECK (publication_year BETWEEN 1900 AND 2025) -- ); UPDATE books SET price = price * 1.2 WHERE book_id IN ( SELECT book_id FROM books WHERE publication_year < 2000 );
🌐
EnterpriseDB
enterprisedb.com › postgres-tutorials › how-modify-data-postgresql-using-insert-update-update-joins-delete-and-upsert
How to modify data in PostgreSQL using INSERT, UPDATE, UPDATE JOINS, DELETE and UPSERT | EDB
UPDATE table_name SET column1 = value1, column2 = value2, ... WHERE condition; Modify a value department id to 50 for an employee whose id is 100 using the WHERE clause:: postgres=# select * from departments ; department_id | department_name | manager_id | location_id ---------------+------------------+------------+------------- 70 | Public Relations | 100 | 1700 10 | IT | 100 | 1100 (2 rows) postgres=# update departments set department_id=50 where department_name='IT'; UPDATE 1 postgres=# select * from departments ; department_id | department_name | manager_id | location_id ---------------+------------------+------------+------------- 70 | Public Relations | 100 | 1700 50 | IT | 100 | 1100 (2 rows)
🌐
DB Vis
dbvis.com › thetable › the-postgres-update-statement-a-deep-dive
The Postgres UPDATE Statement: A Deep Dive
September 5, 2024 - In this technical article, we will look into the Postgres UPDATE statement, and understand its statement syntax. From updating single and multiple rows to incorporating conditions, and expressions, we will learn the ins and outs behind data modification in PostgreSQL.
🌐
TechOnTheNet
techonthenet.com › postgresql › update.php
PostgreSQL: UPDATE Statement
This PostgreSQL UPDATE example would update the first_name to the default value for the field in the contacts table where the contact_id is 35. If no default value has been set for the first_name column in the contacts table, the first_name column will be set to NULL. Let's look at a PostgreSQL UPDATE example where you might want to update more than one column with a single UPDATE statement.
🌐
OneCompiler
onecompiler.com › oracle
Oracle Online Editor
-- Update single column UPDATE EMPLOYEE SET dept = 'Marketing' WHERE empId = 1; -- Update multiple columns UPDATE EMPLOYEE SET dept = 'Sales', salary = 52000 WHERE empId = 2; -- Update with calculation UPDATE EMPLOYEE SET salary = salary * 1.10 WHERE dept = 'Engineering'; -- Update with subquery UPDATE EMPLOYEE SET salary = (SELECT AVG(salary) FROM EMPLOYEE) WHERE empId = 3;
🌐
Hello Interview
hellointerview.com › home › system design › common problems › ticketmaster
Design a Ticket Booking Site Like Ticketmaster | Hello Interview System Design in a Hurry
February 15, 2026 - A bad solution many candidates ... ensuring exclusive access to the first user trying to book it. This is typically done using the SELECT FOR UPDATE statement in PostgreSQL, which locks the selected row(s) as part of a database transaction....