eager_load + find_by에서 쿼리가 두 번 발행되는 경우

결론



일반적인 1:N Relation이 있을 때
class User < ApplicationRecord
  has_many :comments
end

class Comment < ApplicationRecord
  belongs_to :user
end
User.eager_load(:comments).find_by(id: 1)

빗질하면
1. User.find_by(id: 1) SQL
2. User.eager_load(:comments) SQL
두 번 SQL 발급
User.eager_load(:comments).where(id: 1).load.first

라고 하면 쿼리가 1회가 된다.
( .load.to_a 라든지 OK)
ruby 2.5.1
rails 5.0.6/6.0.1(下記ログは6.0.1のものです)

에서 확인됨.

상세



보통 해 보았지만, 아무래도 단순한 find 같은 쿼리와,
함께 정보를 얻는 쿼리의 두 번이 발행되었습니다.
User.eager_load(:comments).find(1)
# SELECT DISTINCT `users`.`id` FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 LIMIT 1
# SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`created_at` AS t0_r2, `users`.`updated_at` AS t0_r3, `comments`.`id` AS t1_r0, `comments`.`user_id` AS t1_r1, `comments`.`created_at` AS t1_r2, `comments`.`updated_at` AS t1_r3 FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 AND `users`.`id` = 1

User.eager_load(:comments).find_by(id: 1)
# SELECT DISTINCT `users`.`id` FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 LIMIT 1
# SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`created_at` AS t0_r2, `users`.`updated_at` AS t0_r3, `comments`.`id` AS t1_r0, `comments`.`user_id` AS t1_r1, `comments`.`created_at` AS t1_r2, `comments`.`updated_at` AS t1_r3 FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 AND `users`.`id` = 1

User.eager_load(:comments).where(id: 1).first
# SELECT DISTINCT `users`.`id` AS alias_0, `users`.`id` FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 ORDER BY `users`.`id` ASC LIMIT 1
# SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`created_at` AS t0_r2, `users`.`updated_at` AS t0_r3, `comments`.`id` AS t1_r0, `comments`.`user_id` AS t1_r1, `comments`.`created_at` AS t1_r2, `comments`.`updated_at` AS t1_r3 FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 AND `users`.`id` = 1 ORDER BY `users`.`id` ASC
WHERE `users`.`id` = 1 AND `users`.`id` = 1 라든지 엄청 중복 같은 쿼리가 있다.
두 번째 쿼리만으로 원하는 정보를 얻을 수 있어야합니다.
어떻게든 한 번의 쿼리로 만들고 싶습니다
User.eager_load(:comments).where(id: 1)의 경우 쿼리가 한 번이므로,
이 시점에서 쿼리를 무리하게 발화시키고 나서, first를 취하면 좋을까?
User.eager_load(:comments).where(id: 1).load.first
# SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`created_at` AS t0_r2, `users`.`updated_at` AS t0_r3, `comments`.`id` AS t1_r0, `comments`.`user_id` AS t1_r1, `comments`.`created_at` AS t1_r2, `comments`.`updated_at` AS t1_r3 FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1

키타━━━━(゚∀゚)━━━━!!

끝.

쿼리 정형ver
User.eager_load(:comments).find(1)
# SQL (4.0ms)  SELECT DISTINCT `users`.`id` FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 LIMIT 1
# SQL (2.8ms)  SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`created_at` AS t0_r2, `users`.`updated_at` AS t0_r3, `comments`.`id` AS t1_r0, `comments`.`user_id` AS t1_r1, `comments`.`created_at` AS t1_r2, `comments`.`updated_at` AS t1_r3 FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 AND `users`.`id` = 1
# -- pretty format.
# SELECT
#     DISTINCT `users`.`id`
# FROM
#     `users`
#     LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id`
# WHERE
#     `users`.`id` = 1
# LIMIT
#     1;
#
# SELECT
#     `users`.`id` AS t0_r0,
#     `users`.`name` AS t0_r1,
#     `users`.`created_at` AS t0_r2,
#     `users`.`updated_at` AS t0_r3,
#     `comments`.`id` AS t1_r0,
#     `comments`.`user_id` AS t1_r1,
#     `comments`.`created_at` AS t1_r2,
#     `comments`.`updated_at` AS t1_r3
# FROM
#     `users`
#     LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id`
# WHERE
#     `users`.`id` = 1
#     AND `users`.`id` = 1;

#=> #<User id: 1, name: "hogehoge", created_at: "2019-11-15 04:54:42", updated_at: "2019-11-15 04:54:42">

User.eager_load(:comments).find_by(id: 1)
#  SQL (1.0ms)  SELECT DISTINCT `users`.`id` FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 LIMIT 1
#  SQL (2.7ms)  SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`created_at` AS t0_r2, `users`.`updated_at` AS t0_r3, `comments`.`id` AS t1_r0, `comments`.`user_id` AS t1_r1, `comments`.`created_at` AS t1_r2, `comments`.`updated_at` AS t1_r3 FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 AND `users`.`id` = 1
# SELECT
#     DISTINCT `users`.`id`
# FROM
#     `users`
#     LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id`
# WHERE
#     `users`.`id` = 1
# LIMIT
#     1;
#
# SELECT
#     `users`.`id` AS t0_r0,
#     `users`.`name` AS t0_r1,
#     `users`.`created_at` AS t0_r2,
#     `users`.`updated_at` AS t0_r3,
#     `comments`.`id` AS t1_r0,
#     `comments`.`user_id` AS t1_r1,
#     `comments`.`created_at` AS t1_r2,
#     `comments`.`updated_at` AS t1_r3
# FROM
#     `users`
#     LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id`
# WHERE
#     `users`.`id` = 1
#     AND `users`.`id` = 1;
#
#=> #<User id: 1, name: "hogehoge", created_at: "2019-11-15 04:54:42", updated_at: "2019-11-15 04:54:42">

User.eager_load(:comments).where(id: 1).first
#  SQL (0.6ms)  SELECT DISTINCT `users`.`id` AS alias_0, `users`.`id` FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 ORDER BY `users`.`id` ASC LIMIT 1
#  SQL (0.6ms)  SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`created_at` AS t0_r2, `users`.`updated_at` AS t0_r3, `comments`.`id` AS t1_r0, `comments`.`user_id` AS t1_r1, `comments`.`created_at` AS t1_r2, `comments`.`updated_at` AS t1_r3 FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 AND `users`.`id` = 1 ORDER BY `users`.`id` ASC
#
# SELECT
#     DISTINCT `users`.`id` AS alias_0,
#     `users`.`id`
# FROM
#     `users`
#     LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id`
# WHERE
#     `users`.`id` = 1
# ORDER BY
#     `users`.`id` ASC
# LIMIT
#     1;
#
# SELECT
#     `users`.`id` AS t0_r0,
#     `users`.`name` AS t0_r1,
#     `users`.`created_at` AS t0_r2,
#     `users`.`updated_at` AS t0_r3,
#     `comments`.`id` AS t1_r0,
#     `comments`.`user_id` AS t1_r1,
#     `comments`.`created_at` AS t1_r2,
#     `comments`.`updated_at` AS t1_r3
# FROM
#     `users`
#     LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id`
# WHERE
#     `users`.`id` = 1
#     AND `users`.`id` = 1
# ORDER BY
#     `users`.`id` ASC;
#=> #<User id: 1, name: "hogehoge", created_at: "2019-11-15 04:54:42", updated_at: "2019-11-15 04:54:42">

User.eager_load(:comments).where(id: 1).load.first
#  SQL (1.0ms)  SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`created_at` AS t0_r2, `users`.`updated_at` AS t0_r3, `comments`.`id` AS t1_r0, `comments`.`user_id` AS t1_r1, `comments`.`created_at` AS t1_r2, `comments`.`updated_at` AS t1_r3 FROM `users` LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id` WHERE `users`.`id` = 1
# SELECT
#     `users`.`id` AS t0_r0,
#     `users`.`name` AS t0_r1,
#     `users`.`created_at` AS t0_r2,
#     `users`.`updated_at` AS t0_r3,
#     `comments`.`id` AS t1_r0,
#     `comments`.`user_id` AS t1_r1,
#     `comments`.`created_at` AS t1_r2,
#     `comments`.`updated_at` AS t1_r3
# FROM
#     `users`
#     LEFT OUTER JOIN `comments` ON `comments`.`user_id` = `users`.`id`
# WHERE
#     `users`.`id` = 1;
#=> #<User id: 1, name: "hogehoge", created_at: "2019-11-15 04:54:42", updated_at: "2019-11-15 04:54:42">

좋은 웹페이지 즐겨찾기