The RETURNING
clause in PostgreSQL is a powerful feature that allows you to retrieve values of one or more columns immediately after performing an INSERT
, UPDATE
or DELETE
operation. This can save an additional query to fetch the data and is especially useful in applications where you need immediate feedback from the database operation. Below are different scenarios demonstrating how the RETURNING
clause can be used effectively in PostgreSQL.
Inserting data and returning the id
A common use case is inserting data into a table that contains an auto-incrementing primary key or using a sequence, and you want to get the new ID value.
INSERT INTO employees (name, department)
VALUES ('John Doe', 'Engineering')
RETURNING id;
This statement will insert a new employee into the employees
table and immediately return the id
of the new employee, which is useful for linking further operations without needing a separate query to find out what the id
was.
Update operations and return the affected data
The RETURNING
clause is also useful to verify which rows were affected by an UPDATE
operation, and to see the before and after states of the data.
UPDATE products
SET price = price * 1.1
WHERE category = 'Electronics'
RETURNING id, price AS new_price;
This updates the price of all products in the ‘Electronics’ category by increasing it by 10%, and returns the id
and new price
of all products that were updated.
Deleting data and capturing the deleted rows
When deleting rows, it can be helpful to log or process the deleted data further. The RETURNING
clause can be used to capture information about rows before they are permanently deleted.
DELETE FROM orders
WHERE order_date < '2021-01-01'
RETURNING *;
This will delete all orders that were placed before January 1, 2021, and return all columns of the deleted rows, which could then be logged or archived.
Inserting multiple rows and returning multiple fields
The RETURNING
clause can also be used when inserting multiple rows to get feedback on all the rows inserted, which is especially useful when using bulk insert operations.
INSERT INTO employees (name, department)
VALUES
('Alice Smith', 'HR'),
('Bob Johnson', 'HR')
RETURNING id, name;
This will insert two new employees into the employees
table and return the id
and name
for both new records.
Using RETURNING
with WITH
Queries (Common Table Expressions)
You can combine RETURNING
with WITH
queries to perform complex operations, like inserting data into one table and using the returned values to update another table.
WITH new_employee AS (
INSERT INTO employees (name, department)
VALUES ('Cathy Matthews', 'Marketing')
RETURNING id
)
UPDATE sales_leads
SET employee_id = (SELECT id FROM new_employee)
WHERE lead_name = 'XYZ Corp'
RETURNING lead_name, employee_id;
This inserts a new employee, captures the new employee’s id
, and then uses that id
to update a corresponding sales lead, returning details about the updated lead.
Downsides and pitfalls
The RETURNING
clause is versatile and can greatly reduce the need for additional queries to verify changes or to fetch new row identifiers, making database interactions more efficient and reducing round trips between the application and the database. That being said, there are some potential downsides and pitfalls to consider:
The RETURNING
clause in PostgreSQL is a highly useful feature, allowing you to retrieve column values directly from rows affected by INSERT
, UPDATE
, or DELETE
operations. While it offers significant advantages in terms of reducing round trips to the database and simplifying code, there are some potential downsides and pitfalls to consider:
- Increased Load: When you use the
RETURNING
clause, the database server does extra work to collect and return the requested data. For large-scale operations involving many rows, this can lead to increased load and slower performance compared to operations that simply modify data without returning it. - Network Overhead: If large amounts of data are being returned, this can also increase the network traffic between the database server and your application, which might be significant in environments where bandwidth is a constraint.
- Extended Locking: By using the
RETURNING
clause, especially inDELETE
orUPDATE
operations, you might hold locks on the rows for a longer duration than if you were merely modifying the data. This can potentially lead to increased contention and impact the performance of other transactions needing access to these rows. - Error Handling: If you are using the
RETURNING
clause as part of a larger transaction, you need to be prepared to handle errors not just from the data modification query but also from processing the returned data. This can complicate error handling logic, especially in distributed applications where different parts of a transaction are handled by different components. - Transaction Size: Returning data in the middle of a transaction can sometimes lead to large transaction scopes, which can be harder to manage and debug.
- Security Considerations: When data is returned directly from the database, it may inadvertently expose sensitive information that should not be logged or transmitted outside of secured channels. This requires careful handling of the returned data, especially in environments with strict data handling policies.
- Resource Intensive: Processing large sets of returned data can be resource-intensive on the client side. If the client application is not well-optimized to handle large datasets efficiently, this can lead to performance bottlenecks.
- Lack of Universal Support: Not all database systems support a
RETURNING
clause (for example, MySQL does not have an equivalent feature). This can lead to portability issues if you ever need to adapt your application to another database system. You might have to rewrite significant parts of your data access layer to accommodate a different approach.
Summary
Despite these potential downsides, the RETURNING
clause is often very useful and the benefits can outweigh the disadvantages, particularly for:
- Applications that need immediate feedback about the results of a database operation.
- Reducing database round trips in scenarios where immediately after inserting or updating a record, you need to use the new or modified data.