ActiveRecord joins is used to combine records from multiple tables based on associations. In this blog, we will discuss how UPDATE statements with outer joins are handled in PostgreSQL and SQLite.
class Client < ApplicationRecord
has_many :projects
end
class Project < ApplicationRecord
belongs_to :client
endBefore
When we do UPDATE with an OUTER JOIN
and reference the updated table in the ON clause in PostgreSQL
and SQLite, Rails generated subqueries as the join condition cannot be safely moved to the WHERE clause without breaking the query.
Client.joins("LEFT JOIN projects ON projects.client_id = clients.id")
.where("projects.id IS NULL")
.update_all(name: 'Archived Client')UPDATE "clients"
SET "name" = 'Archived Client'
WHERE ("clients"."id") IN (
SELECT "clients"."id"
FROM "clients"
LEFT JOIN projects ON projects.client_id = clients.id
WHERE (projects.id IS NULL)
)This approach was less efficient, especially for large datasets, and generated more complex SQL. The subquery added unnecessary overhead, making updates slower and harder to optimize.
After
Rails no longer generate the subqueries as it uses self-join for UPDATE with outer joins on PostgreSQL
and SQLite. As the updated table is added to the FROM clause with a self-join on the primary key.
UPDATE "clients" AS "__active_record_update_alias"
SET "name" = 'Archived Client'
FROM "clients"
LEFT JOIN projects ON projects.client_id = clients.id
WHERE (projects.id IS NULL)
AND "clients"."id" = "__active_record_update_alias"."id" Refer
