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

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

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

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

Ruby on Rails 4

Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

Active Record

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

Q&A

解決済

2回答

1136閲覧

Railsで配列の値でAND検索

kentos

総合スコア68

SQL

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

Ruby on Rails 4

Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

Active Record

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

0グッド

0クリップ

投稿2017/06/29 11:17

編集2017/06/30 15:17

お世話になっております。
Railsアプリでの検索についてです。

やりたいこと

配列の値が全て含まれるレコードを取得したい。

SQLでのINがあると思うのですが、それのAND検索というイメージです。

DB

user

  • id
  • name

tag

  • id

user_tags

  • user_id
  • tag_id
class User < ActiveRecord::Base has_many :tags end class Tag < ActiveRecord::Base end class UserTag < ActiveRecord::Base belongs_to :user belongs_to :tag end

実際の使い方としては、以下のようなシチュエーションです。

「タグID 10と20の両方を持っているユーザーを取得したい」

この目的がある時のactive recordの書き方です。

現在以下のようにやろうとしているのですが、期待通りに動きません。

tag_ids = [10,20] users = User.joins(:user_tag) tag_ids.each do |tag| users = users.where(user_tag: {tag_id: tag}) end users.count => 0

発行されるSQLは以下の通りです。

SELECT COUNT(*) FROM `users` INNER JOIN `user_tags` ON `user_tags`.`user_id` = `users`.`id` WHERE `user_tags`.`tag_id` = 10 AND `user_tags`.`tag_id` = 20;

10と20の両方のtag_idを持っているuserを取得する方法をご教授ください。
このtag_idの配列の数は可変です。

[10,20]や[10,20,30]など、最大で13個の数値が入ります。

ちなみに生のSQLでの方法も探りましたが、こちらもうまくいきませんでした。

実際の開発環境のDB構造よりも簡略化しているため、不備があるかもしれません。何なりとお申し付けください。

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

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

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

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

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

suama

2017/06/30 11:15

tag_idsは例だと2つですが、可変なんでしょうか?
kentos

2017/06/30 15:15

suana様 ご質問ありがとうございます。 おっしゃる通り可変でございます。
suama

2017/07/01 00:00

ありがとうございます。2つだけなら where().where() でチェーンでつなげれば行けそうですが、動的に変わる可能性があるのですね。
guest

回答2

0

ベストアンサー

こんにちは。
最初のSQLでは、おそらく普通に発行してもデータは抽出できないかな?と思いました。
こういうSQLになればいいでしょうか?

-- 1つめ SELECT * FROM `users` LEFT JOIN user_tags ON users.id = user_tags.user_id WHERE users.id IN (SELECT user_id FROM user_tags WHERE user_tags.tag_id = 20) AND users.id IN (select user_id FROM user_tags WHERE user_tags.tag_id = 10) -- 2つめ SELECT * from users LEFT JOIN user_tags ON users.id = user_tags.user_id WHERE user_tags.tag_id IN (10, 20) GROUP BY users.id HAVING COUNT(users.id) = 2

※それぞれのユーザが持つtag_idは、重複しないものと想定。
(user: 1 -> tag_id: 10, 20, 10 と10が重複することは無し)

また、Userはこうだったとして考えてみました。

class User < ActiveRecord::Base has_many :user_tags # has_many: tagsでない end

2つめのパターンでの例ですが、これでできますでしょうか..
(Tagは使っていないので、前提が間違っていたらすみません)

ids = [10, 20] # 基本の条件 condition = User.joins(:user_tags) # 長くなってすみません... result = condition.merge(UserTag.where(tag_id: ids)).group(:user_id).having('count(users.id) = ?', ids.length) result.length # 数を算出 result.to_sql # sqlを出力して確認

投稿2017/07/01 06:35

suama

総合スコア1997

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

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

kentos

2017/07/04 02:40

suama 様 ご回答ありがとうございます。 またコードも文章も非常にわかりやすくありがたいです。 提示していただいた”ふたつめのパターン”を試してみたのですが、このコードだと以下の問題が発生してしまいます。。。 user1のタグが 10,20,30,40,50の時 ``` ids = [10,20] ~ ~.having('count(users.id) = ?', ids.length) ~ ``` このhavingで、 `ids.length` が2になるので5個のtagがついているため、取得できません。 「両方のタグを持っているUserを取得する」ことが目標なので、この状況でもuser 1 は取得したいところです。。。 言葉足らずで申し訳ないです。
suama

2017/07/04 03:55 編集

お返事ありがとうございます! .having('count(users.id) >= ?', ids.length) でもダメでしょうか? 実際のデータを拝見していないので、もっと複雑かと思いますが....
moke

2017/07/04 04:46

ids = [10,20] を ids = [10,20,30,40,50] に書き換えるのでは? パターン2は目から鱗ですが squeelというgemを使うと パターン1も割と簡単にかけます。
guest

0

当方Rails使いでは無いのでできるかわかりませんが、冗長でいいならこのようなSQLで作れます。

SQL

1select 2 u.id 3from 4 ( 5 select 6 u.id 7 from 8 user_tags as ut 9 inner join user as u on ut.user_id = u.id 10 where 11 ut.tag_id = 1 12 union all 13 select 14 u.id 15 from 16 user_tags as ut 17 inner join user as u on ut.user_id = u.id 18 where 19 ut.tag_id = 3 20 ) as u 21group by 22 u.id 23having 24 count(u.id) >= 2 25;

union allでそれぞれのタグに紐づくすべてのuserを取得し、それらすべてを持っているものをgroup by及びhavingで絞り込む方法です。
検索タグが増えた場合は上記union all以下のコードのut.tag_idの指定を変更して繋げることで可能です。
その際はhavingの数も検索するタグ数に変更してください。

もっと少なく書ける気がしますが、当方の腕ではここまでが限界でした。
ご参考になればと思います。

投稿2017/07/01 01:18

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問