質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

MariaDB

MariaDBは、MySQL派生のオープンソースなリレーショナルデータベースシステムです。 また、MySQLとほぼ同じデータベースエンジンに対応しています。

Active Record

Active Recordは、一つのオブジェクトに対しドメインのロジックとストレージの抽象性を結合するデザインパターンです。

Q&A

0回答

894閲覧

Railsのeager_loadメソッドで発行されるSQLに結合条件を追加したい

s.a.kura

総合スコア5

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

MariaDB

MariaDBは、MySQL派生のオープンソースなリレーショナルデータベースシステムです。 また、MySQLとほぼ同じデータベースエンジンに対応しています。

Active Record

Active Recordは、一つのオブジェクトに対しドメインのロジックとストレージの抽象性を結合するデザインパターンです。

0グッド

0クリップ

投稿2022/10/24 10:52

編集2022/10/25 05:45

前提

RailsでWebアプリ開発をしており、関連のあるテーブルの外部結合を試みています。
外部結合の結合条件として、eager_loadでデフォルトの [テーブル1].id = [テーブル2].テーブル1_id のほか、
自身で結合条件を追加したいのですが、うまくいきません。

開発環境

  • OS: Linux (Windows Subsystem for Linux)
  • 言語: ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux]
  • フレームワーク: Rails 6.0.3.4
  • DBMS: mariadb Ver 15.1 Distrib 10.1.48-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2

実現したいこと

eager_loadメソッドを使った外部結合をするにあたり、結合条件に動的な値を使いたいです。

詳細

例として、以下のような3つのテーブルを想定します。

  • 利用者テーブル(users)
idnamecreated_atupdated_at
1利用者12022/10/25 11:462022/10/25 11:46
2利用者22022/10/25 11:472022/10/25 11:47
3利用者32022/10/25 11:472022/10/25 11:47
  • 貸出履歴テーブル(lending_records)
iduser_idbook_idlent_atreturned_atcreated_atupdated_at
1112022-10-01 9:002022-10-152022/10/25 11:472022/10/25 11:47
2122022-10-01 9:002022-10-152022/10/25 11:472022/10/25 11:47
3212022-10-16 15:302022-10-302022/10/25 11:482022/10/25 11:48
  • 蔵書テーブル(books)
idtitleauthorarrived_atcreated_atupdated_at
1吾輩は猫である夏目漱石2022-09-302022/10/25 11:472022/10/25 11:47
2たけくらべ樋口一葉2022-09-302022/10/25 11:472022/10/25 11:47
3人間失格太宰治2022-10-202022/10/25 11:472022/10/25 11:47

このとき、eager_loadメソッドを使って、以下のSQLにおいて

  1. 結合条件`lending_records`.`book_id` = 2 ANDの追加
  2. 動的なbook_idの指定

をしたいです。

SQL

1SELECT 2 `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`created_at` AS t0_r2, `users`.`updated_at` AS t0_r3, 3 `lending_records`.`id` AS t1_r0, `lending_records`.`user_id` AS t1_r1, `lending_records`.`book_id` AS t1_r2, `lending_records`.`lent_at` AS t1_r3, 4 `lending_records`.`returned_at` AS t1_r4, `lending_records`.`created_at` AS t1_r5, `lending_records`.`updated_at` AS t1_r6, 5 `books`.`id` AS t2_r0, `books`.`title` AS t2_r1, `books`.`author` AS t2_r2, `books`.`arrived_at` AS t2_r3, `books`.`created_at` AS t2_r4, `books`.`updated_at` AS t2_r5 6FROM `users` 7LEFT OUTER JOIN `lending_records` ON `lending_records`.`book_id` = 2 AND `lending_records`.`user_id` = `users`.`id` 8LEFT OUTER JOIN `books` ON `books`.`id` = `lending_records`.`book_id`
  • SQLの実行結果
t0_r0t0_r1t1_r0t1_r1t1_r2t1_r3t1_r4t2_r0t2_r1t2_r2t2_r3
1利用者12122022/10/1 9:002022/10/152たけくらべ樋口一葉2022-09-30
2利用者2NULLNULLNULLNULLNULLNULLNULLNULLNULL
3利用者3NULLNULLNULLNULLNULLNULLNULLNULLNULL

(画面幅を考慮し、各テーブルのcreated_at, updated_at列は省略しています)

似た状況のように思われる質問

https://stackoverflow.com/questions/41532444/eager-load-for-custom-joins-in-activerecord

試したこと

user.rb の has_many :lending_records 行へのスコープ追加。

  • 各モデルファイル

Ruby

1# user.rb 2class User < ApplicationRecord 3 has_many :lending_records, -> { self.where(book_id: 2) } 4end

Ruby

1# lending_record.rb 2class LendingRecord < ApplicationRecord 3 belongs_to :user 4 belongs_to :book 5end

Ruby

1# book.rb 2class Book < ApplicationRecord 3 has_many :lending_records 4end
  • 実行したコード

Ruby

1User.eager_load(lending_records: :book)

この方法で、実現したいことに記載したSQLが発行され、期待通りの結果が得られることは分かりました。
しかし、この方法ではbook_idを動的に指定することができません。
has_manyのスコープの中で変数を扱うことはできるのでしょうか。

補足情報

以下①は想定通りにレコードが取れず、②は使うことを避けたいです。

① 結合した後にWHERE句で絞り込む
② joinsメソッドで結合条件を付加する

① 結合した後にWHERE句で絞り込む

この場合は以下の結果になります。

  • モデルファイル

Ruby

1# user.rb 2class User < ApplicationRecord 3 has_many :lending_records 4end

(lending_record.rb および book.rb は上記と同様)

  • 実行したコード

Ruby

1User.eager_load(lending_records: :book).where(lending_records: { book_id: 2 })
  • 発行されるSQL

SQL

1SELECT 2 `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`created_at` AS t0_r2, `users`.`updated_at` AS t0_r3, 3 `lending_records`.`id` AS t1_r0, `lending_records`.`user_id` AS t1_r1, `lending_records`.`book_id` AS t1_r2, `lending_records`.`lent_at` AS t1_r3, 4 `lending_records`.`returned_at` AS t1_r4, `lending_records`.`created_at` AS t1_r5, `lending_records`.`updated_at` AS t1_r6, `books`.`id` AS t2_r0, 5 `books`.`title` AS t2_r1, `books`.`author` AS t2_r2, `books`.`arrived_at` AS t2_r3, `books`.`created_at` AS t2_r4, `books`.`updated_at` AS t2_r5 6FROM `users` 7LEFT OUTER JOIN `lending_records` ON `lending_records`.`user_id` = `users`.`id` 8LEFT OUTER JOIN `books` ON `books`.`id` = `lending_records`.`book_id` 9WHERE `lending_records`.`book_id` = 2
  • SQLの実行結果
t0_r0t0_r1t1_r0t1_r1t1_r2t1_r3t1_r4t2_r0t2_r1t2_r2t2_r3
1利用者12122022/10/1 9:002022/10/152たけくらべ樋口一葉2022-09-30

(画面幅を考慮し、各テーブルのcreated_at, updated_at列は省略しています)

このように、結合条件に指定した場合とは異なる結果となります。

② joinsメソッドで結合条件を付加する

作成しているWebアプリでは、例えば

Ruby

1model = User.eager_load(lending_records: :book).joins("AND `lending_records`.`book_id` = 2") 2if hoge.nil? 3 model = model.eager_load(:other_tables) 4end

といった形でチェーンして使うことを想定しています。
この場合に、LEFT OUTER JOIN句はチェーンした順でSQLに追加されていく仕様かと思います。
そのためjoinsメソッドで書いた文字列が期待する位置にならないおそれがあることからjoinsメソッドでの対応は避けたいです。
(.eager_load(lending_records: :book)を必ず最後にチェーンするようにすれば期待通りのSQLが得られるかとは思いますが、保守の観点から避けたい)

以上、よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2022/10/24 11:28

> といった方法では解決できません。 どうなるのですか?
s.a.kura

2022/10/25 05:46

より具体的な例を示すため、全文を改修いたしました。 何卒よろしくお願いいたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問