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

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

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

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

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Active Record

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

Q&A

解決済

2回答

4300閲覧

副問合せを複数回行う複雑なSQLを、ActiveRecordで書く方法

hiepita1

総合スコア37

Ruby on Rails 5

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

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Active Record

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

0グッド

0クリップ

投稿2018/07/30 11:48

お世話になっております。
現在、複数回副問合せを行う複雑なSQLを、ActiveRecordで実現しようとしています。
当初はActiveModelで生SQLを使用していましたが、ActiveRecord依存のGemを使用する
ために、ActiveRecordで書き直しを試みています。

元となる生SQL SELECT * FROM Model1 query1 JOIN (SELECT * FROM Model2 query2 JOIN ( SELECT * FROM Model1 query3 WHERE EXIST(SELECT * FROM Model2 query4 WHERE...(たくさん AND EXIST(SELECT * FROM Model2 query5 WHERE...(たくさん)) ) ) )

実際にActiveRecordに書き換えた所、ModelとService層が非常に煩雑になってしまいました。
特に、副問合せ部分と、Modelをまたがっていることが原因で、可読性が落ちてしまいました。

Service query5 = Model2.scope().scope().scope().scope().scope().scope...to_sql query4 = Model2.scope().scope().scope().scope().scope().scope.where("EXIST( #{query5})")..to_sql query3 = Model1.scope().scope().scope().scope().scope().scope.where("EXIST( #{query4})")..to_sql query2 = Model2.joins("#{query3}").to_sql query1 = Model1.joins("#{query2}").select(column1,column2,,,,)
Model1 scope: scope:...20個ほど Model2 scope: scope:...50個ほど

このような、副問合せを含むような複雑なSQLを書く場合、どのようにすれば可読性を維持できるでしょうか?
当方Rails初心者のため、些細なことでもいいので、ご教示いただきたいです。
よろしくお願いいたします。

RubyOnRails 5.2
Postgresql 10

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

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

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

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

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

guest

回答2

0

各query内で過剰に分割しすぎないことだと思います
例えばquery1がscope6個以上繋げていますが、その各scopeは他機能でも利用するのでしょうか?
意図のある範囲でのscope化までに控えるのはどうでしょう?

ruby

1class Model2 2 scope :query5, (lambda do 3 where(***) # scope1に相当 4 .where(***) # scope2に相当 5 .where(***) # scope3に相当 6 .where(***) # scope4に相当 7 end) 8end

また上記方法ではscope内が複雑になりすぎる場合は Query object という実装パターンがあります
参考: Rails - ActiveRecord の scope を Query object で実装する

class Query5 def initialize(relation = Model1.all) @relation = relation end def call @relation.where(hoge1: condition1) .where(hoge2: condition2) .where(***3) .where(***4) end # 複雑になる部分は関数で抜き出す(テストもしやすい) def condition1 end def condition2 end end class Model2 scope :query5, Query5.new end

あとはこのquery3~5に明確な名前をつけ、個別にその意図を確認するテストをするくらいでしょうか
そうしたら全体の最終SQL構造の見通しが多少落ちても、さほど問題はないかなという認識です

投稿2018/08/03 00:33

編集2018/08/03 02:52
Ighrs

総合スコア656

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

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

hiepita1

2018/08/08 02:27

回答ありがとうございます。 このような考え方もあるんですね。勉強になりました。 今回は元のSQL自体をもう少し簡潔にするように修正したいと思います。 ありがとうございました。
guest

0

ベストアンサー

そもそも元のSQLが複雑怪奇です。
そこを見直した方が早道ではないでしょうか。

現れるモデルは2つなので、以下のように書き換えられるのではないかと思います。

SQL

1SELECT * 2FROM Model1 3 inner join Model2 4 on Model1.key=Model2.key 5where (たくさん)

それが出来ないという事なのかもしれませんが・・・

投稿2018/07/30 13:28

sazi

総合スコア25138

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

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

hiepita1

2018/07/31 00:15

おっしゃるとおりなんですが、元々別の方が作成したSQLが元となっているため、SQL自体は書き換えたくないという希望があります・・・
sazi

2018/07/31 01:17

発行されるSQLを同じにする事で、担保しようって訳ですか。 ネストを表現させるところが課題なんだと思いますけど、簡潔に表現するなら独自なもの作るしか無い気がします。(詳しくないので分かりませんけれど) ただ、その為に四苦八苦するのは本末転倒だと思いますけどね。 元から絶たなきゃ駄目な案件な気がします。
sazi

2018/07/31 01:21

SQL自体は書き換えたくない=SQLを書き換える自信がない だとすると、類似処理が発生した場合、コピペが横行することが予想できます。
hiepita1

2018/08/01 01:13

そもそもの案件としては、別言語で書かれた既存システムを、Railsに書き換える様な案件のため、RailsのORMで表現することが難しいという理由で、SQLを変更することは極力避けたいという気持ちです。(動きを担保するという気持ちももちろんあります。) しかし、saziさんの仰っているように、元を変更しないことには綺麗に書くことができないかもしれないので、変更することも検討したいと思います。ありがとうございます。
sazi

2018/08/03 04:11 編集

元を変更しないならそれを一つのモデルとして扱えばどうですか。 条件を最適化するには普通のモデルでは駄目でしょうけど、一々モデル間のつながりは考えなくて良いわけですし。
hiepita1

2018/08/08 02:25

ActiveModelで実装するということになるかと思うのですが、ActiveRecord依存のgemを使用する場合があるため、別のモデルとして作成するのは避けたいです。 今回は最終的に元のSQLを書き換えるような方向にしました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問