이전에는 ActiveRecord에서 joins와 update_all을 함께 사용할 때, Rails는 SET 절에서 조인된 테이블을 참조할 수 없는 서브쿼리를 생성하여 ActiveRecord::StatementInvalid 오류를 발생시켰습니다. 이는 PostgreSQL에서는 PG::UndefinedTable 오류로, SQLite에서는 SQLite3::SQLException: no such column 오류로 나타났습니다. 반면 MySQL 어댑터는 이미 이 기능을 지원하고 있었습니다.
이전 동작 (Rails 8.1 이전)
- PostgreSQL 및 SQLite:
Project.joins(:client).update_all("name = clients.name")와 같은 쿼리는 다음과 같은 SQL을 생성했습니다:sql UPDATE "projects" SET name = clients.name WHERE "projects"."id" IN (SELECT "projects"."id" FROM "projects" INNER JOIN "clients" ON "clients"."id" = "projects"."client_id")- 이 SQL에서
SET name = clients.name부분은 외부 쿼리이며,clients테이블은 서브쿼리 내에만 존재하므로 참조할 수 없어 오류가 발생했습니다.
- MySQL:
- MySQL은 이미
UPDATE ... INNER JOIN ... SET ...형태의 쿼리를 생성하여 이 기능을 지원했습니다.sql UPDATE `projects` INNER JOIN `clients` ON `clients`.`id` = `projects`.`client_id` SET `projects`.`name` = clients.name
- MySQL은 이미
Rails 8.1 이후 변경점
Rails 8.1부터는 PostgreSQL 및 SQLite에서도 joins와 update_all의 조합이 정식으로 지원됩니다. 이는 MySQL과 유사하게 UPDATE ... FROM ... WHERE 구문을 사용하여 문제를 해결합니다.
- PostgreSQL 및 SQLite:
Project.joins(:client).update_all("name = clients.name")와 같은 쿼리는 이제 다음과 같은 SQL을 생성합니다:sql UPDATE "projects" AS "__active_record_update_alias" SET name = clients.name FROM "projects" INNER JOIN "clients" ON "clients"."id" = "projects"."client_id" WHERE "projects"."id" = "__active_record_update_alias"."id"- 이 새로운 SQL 구문은
FROM절에clients테이블을 포함하여SET절에서clients.name을 직접 참조할 수 있도록 합니다.
제한 사항
이 기능은 관계(relation)에 LIMIT, ORDER, 또는 GROUP BY 절이 사용되지 않는 경우에만 지원됩니다. 이러한 절이 포함된 경우, Rails는 여전히 이전 방식의 서브쿼리를 생성하게 되며, 이 경우 PostgreSQL 및 SQLite에서는 ActiveRecord::StatementInvalid 오류가 다시 발생할 수 있습니다.