Rails 8.1: PostgreSQL 및 SQLite에서 ActiveRecord `update_all`과 `joins` 조합 지원

Rails 8.1 introduces support for joins in update_all for Postgresql and SQLite.

작성자
발행일
2026년 01월 02일

핵심 요약

  • 1 Rails 8.1부터 PostgreSQL 및 SQLite에서 `ActiveRecord`의 `update_all` 메서드와 `joins`를 함께 사용하여 연관 테이블의 데이터를 참조한 대량 업데이트가 가능해졌습니다.
  • 2 이전에는 `update_all`과 `joins`를 함께 사용할 경우 서브쿼리 생성 방식으로 인해 `ActiveRecord::StatementInvalid` 오류가 발생했으나, 이제는 `UPDATE ... FROM ...` 구문으로 변경되어 문제를 해결했습니다.
  • 3 단, `LIMIT`, `ORDER`, `GROUP BY` 절이 포함된 관계에서는 여전히 이전 서브쿼리 방식으로 동작하여 연관 테이블 참조 시 오류가 발생할 수 있습니다.

도입

ActiveRecord의 `joins`는 연관 관계를 기반으로 여러 테이블의 레코드를 결합하여 SQL `INNER JOIN`을 생성하는 데 사용됩니다. `update_all`은 모델 유효성 검사, 콜백, 타임스탬프 수정 없이 여러 레코드를 빠르게 업데이트할 수 있는 메서드입니다. 이 두 메서드를 결합하는 것은 연관 테이블의 데이터를 기반으로 대량 업데이트를 수행해야 할 때 매우 유용하지만, Rails 8.1 이전에는 PostgreSQL 및 SQLite에서 특정 제약 사항으로 인해 오류가 발생했습니다.

이전에는 ActiveRecord에서 joinsupdate_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

Rails 8.1 이후 변경점

Rails 8.1부터는 PostgreSQL 및 SQLite에서도 joinsupdate_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 오류가 다시 발생할 수 있습니다.

결론

Rails 8.1 업데이트로 ActiveRecord의 `update_all`과 `joins` 조합이 PostgreSQL 및 SQLite에서도 완벽히 지원되어, 연관 테이블 데이터를 기반으로 효율적인 대량 업데이트가 가능해졌습니다. 이는 부모-자식 관계 컬럼 값 동기화 등에서 단일 쿼리 처리를 통해 성능 최적화에 기여합니다. 다만, `LIMIT`, `ORDER`, `GROUP BY` 절 사용 시에는 이전 서브쿼리 방식으로 동작하여 오류가 발생할 수 있으므로, 해당 제약 사항을 인지하고 활용하는 것이 중요합니다.

댓글 0

로그인이 필요합니다

댓글을 작성하거나 대화에 참여하려면 로그인이 필요합니다.

로그인 하러 가기

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!