Mastering PostgreSQL Updates: Subqueries vs. Alternatives



  • SQL (Structured Query Language): A standardized language for interacting with relational databases, including querying, inserting, updating, and deleting data.
  • PostgreSQL: A powerful, open-source object-relational database management system (DBMS) that implements SQL.
  • Subquery: A nested query embedded within another SQL statement (like UPDATE). It retrieves data used for calculations or comparisons in the outer query.

How it Works:

  1. Subquery Definition: You create a subquery that selects the desired values for updating the target table. This subquery often joins tables or uses filtering conditions to determine the specific values.
  2. UPDATE Statement: You construct the UPDATE statement that specifies the table to update, the columns to modify, and the WHERE clause to filter the rows.
  3. Correlated Subquery: Within the UPDATE statement's SET clause, you reference the subquery using a correlated subquery. This correlated subquery allows you to access the current row being updated in the main table and compare it with data from the subquery.
  4. Updating Values: Based on the comparison between the current row and the subquery results, the UPDATE statement assigns new values to the specified columns.


Suppose you have a table customers with columns customer_id, name, and email, and another table orders with columns order_id, customer_id, and total_amount. You want to update the customers table with the total amount of each customer's most recent order.

UPDATE customers c
SET c.last_order_amount = (
  SELECT o.total_amount
  FROM orders o
  WHERE o.customer_id = c.customer_id
  ORDER BY o.order_date DESC


  • The subquery retrieves the total_amount of the most recent order (based on order_date descending) for each customer (o.customer_id = c.customer_id).
  • In the UPDATE statement, the correlated subquery references the current customer row (c) being updated.
  • If a match is found, the last_order_amount for that customer is updated with the corresponding total_amount from the subquery.

Benefits of Using Subqueries:

  • Flexibility: Subqueries allow for complex logic in updating table rows based on calculations or comparisons with data from other tables.
  • Conciseness: You can achieve complex updates in a single statement, improving code readability.


  • Performance: Subqueries can sometimes impact performance, especially for large datasets. Consider alternative approaches if performance is critical.
  • Readability: Ensure your subqueries are clear and easy to understand, especially for nested subqueries.

By effectively using subqueries in your PostgreSQL UPDATE statements, you can achieve more sophisticated data manipulation tasks within your database applications.

Updating a Table Based on a Calculation:

This example updates a table products with a discounted price by multiplying the original price by a discount factor stored in another table discounts:

UPDATE products p
SET p.discounted_price = p.original_price * (1 - d.discount_factor)
FROM products p
INNER JOIN discounts d ON p.category_id = d.category_id;


  • The subquery joins the products and discounts tables using an INNER JOIN to match products with their corresponding discount factor based on category_id.
  • The SET clause calculates the discounted price for each product using the subquery's discount_factor.

This example updates a table students with a rank based on their scores in a specific subject:

UPDATE students s
SET s.rank = (
  FROM students ss
  WHERE ss.subject = s.subject AND ss.score >= s.score
ORDER BY s.subject, s.score DESC;
  • The subquery counts the number of students in the same subject who have a score greater than or equal to the current student's score (s).
  • This count is used to assign a rank (including the current student) for each student within their subject, ordered by score descending.
  • The UPDATE statement assigns the rank calculated by the subquery to each student's rank column.

Updating a Table with Conditional Values from a Subquery:

This example updates a table subscriptions with a new plan based on the customer's total spent in another table orders:

UPDATE subscriptions s
SET s.plan = (
    WHEN o.total_spent >= 1000 THEN 'premium'
    WHEN o.total_spent >= 500 THEN 'standard'
    ELSE 'basic'
FROM subscriptions s
  SELECT customer_id, SUM(amount) AS total_spent
  FROM orders
  GROUP BY customer_id
) AS o ON s.customer_id = o.customer_id;
  • The subquery calculates the total amount spent by each customer using a subquery within the FROM clause.
  • The CASE expression in the main UPDATE statement uses the subquery's total_spent to assign a new plan based on spending thresholds.


  • This approach leverages a JOIN operation within the UPDATE statement to directly reference columns from another table for updating the target table. It can be suitable when updating based on simple comparisons between tables.


UPDATE customers c
SET c.last_order_amount = o.total_amount
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
ORDER BY o.order_date DESC


  • Similar to the subquery example, this code joins the customers and orders tables.
  • The SET clause directly references the total_amount from the joined orders table (o) to update the last_order_amount for each customer (c).

CTE (Common Table Expression):

  • A CTE allows you to define a temporary named result set that can be used in the UPDATE statement. This can improve readability and modularity, especially for complex update logic.
WITH recent_orders AS (
  SELECT customer_id, MAX(order_date) AS recent_date
  FROM orders
  GROUP BY customer_id
UPDATE customers c
SET c.last_order_amount = o.total_amount
FROM customers c
INNER JOIN recent_orders ro ON c.customer_id = ro.customer_id
INNER JOIN orders o ON o.customer_id = ro.customer_id AND o.order_date = ro.recent_date;
  • The CTE named recent_orders identifies the most recent order date for each customer.
  • The UPDATE statement then joins the customers table with both recent_orders and orders tables to update the last_order_amount based on the recent order.

MERGE Statement (PostgreSQL 11+):

  • Introduced in PostgreSQL 11, the MERGE statement offers a more concise way to combine insert, update, and delete operations in a single statement. While not a direct replacement for subqueries in all cases, it can be useful for specific scenarios involving conditional updates or inserts.

Example (assuming PostgreSQL 11 or later):

MERGE INTO customers c
  SELECT customer_id, MAX(order_date) AS recent_date
  FROM orders
  GROUP BY customer_id
) AS ro
ON c.customer_id = ro.customer_id
  UPDATE SET c.last_order_amount = (
    SELECT o.total_amount
    FROM orders o
    WHERE o.customer_id = c.customer_id AND o.order_date = ro.recent_date
  INSERT (customer_id) VALUES (DEFAULT);  -- Optional insert for new customers
  • The MERGE statement targets the customers table and uses a subquery similar to the CTE example to identify recent orders.
  • The WHEN MATCHED clause updates existing customer records with the last_order_amount from the recent order.
  • The optional WHEN NOT MATCHED clause can be used to insert new customer records (replace DEFAULT with appropriate values).

sql postgresql subquery

SQL Tricks: Swapping Unique Values While Maintaining Database Integrity

Unique Indexes: A unique index ensures that no two rows in a table have the same value for a specific column (or set of columns). This helps maintain data integrity and prevents duplicates...

Beyond the Maximum: Efficiently Locating the Nth Highest Value in Your Database

Using LIMIT and OFFSET:Imagine you have a table with a column of values, and you want the 5th highest value. This method involves two steps:...

Updating a Table in Oracle Based on Another Table Using Inner Join

Update Statement:In SQL (Structured Query Language), the UPDATE statement is used to modify existing data within a table...

Understanding the PostgreSQL Error: "Password Authentication Failed for User postgres"

Error Breakdown:password authentication failed: This indicates PostgreSQL couldn't verify the password you provided for the user attempting to connect...

Finding the Row Count in PostgreSQL: Accuracy vs. Speed

Methods for Counting Rows in PostgreSQL:SELECT count(*) FROM table_name:This is the standard SQL way to get an exact count of all rows in a table...

sql postgresql subquery