2

内部結合を実行しようとしているテーブルが2つあります。

1つはusers、主キーがであるテーブルですid

もう1つのテーブルは、外部キーがbarsどこにあるかです。また、テーブルへの外部キーであるwhereという列もあります。user_idbarsfoo_idfood_idfoos

N日前またはそれ以前に作成されたすべてのユーザーを選択できるActiveRecordクエリをまとめようとしていますが、特定のIDに等しいfoos場所はありません。bars.foo_id私はこのようなことをしてみました:

users = User.where("users.created_at <= ?", 50.days.ago).joins(:bars).where("bars.foo_id != 5")

このクエリは30,000を超える結果をフィールドしますが、これは正しくないため、Usersテーブルには12,000行しかありません。

私はここで何を間違っているのですか?

4

4 に答える 4

2

結合の計算が間違っており、ユーザーとfooの組み合わせごとに行を作成する効果があります。これが完全結合の仕組みです。このスリップの理由は、barsテーブルをusersテーブルに実際に結合していないためです。通常、条件付きで参加する必要があります。たとえば、あなたの場合bars.user_id=users.idは良い考えです。

そうは言っても、代わりに実行したいのは、どのユーザーが適格かを判断し、それらをロードすることです。

users = User.where('id IN (SELECT DISTINCT user_id FROM bars WHERE bars.foo_id!=?)', 5)

この副選択は、それ自体で実行された場合、その特定のfooのないユーザーのリストを返すだけです。これをWHERE条件として使用すると、それらのユーザーのみがロードされます。

于 2012-11-08T04:38:10.430 に答える
2

これを使ってみてください

    User.includes(:bars).where("users.created_at <= ?", 50.days.ago).where("bars.foo_id != 5")
于 2012-11-08T05:19:22.597 に答える
1

これはうまくいくはずです-

User.joins(:bars).where("bars.foo_id != ? and users.created_at <= ?", 5, 50.days.ago).select("distinct users.*")

次のSQLを生成します-

 select distinct users.* from users 
 INNER JOIN bars on bars.user_id = user.id
 WHERE bars.foo_id != 5 and users.created_at <= '2012-09-19 10:59:54'
于 2012-11-08T05:43:25.277 に答える
0

結合すると、where句を満たすすべてのユーザー/バーの条件に対して結果セットに1つの行が作成されます。ユーザーに5つのバーがある場合、5回表示されます。

結合を実現できるすべての行を選択するため、これを明確に修正することはできません。少なくとも1つのバーがあり、foo_idが5ではないすべてのユーザーではなく、そのようなバーがないすべてのユーザーが選択されます。

これは、左結合で行うことができます。

User.joins("left join bars on bars.user_id = users.id and bars.foo_id = 5").where("users.created_at < ? AND bars.id is null", 50.days.ago")

これは、「悪い」バー(foo_id = 5)をユーザーに結合しようとします。これは左結合であるため、そのようなバーが存在しない場合は、バーテーブルのすべての列がnullに設定された行が結果セットに返され、これをフィルタリングできます。結合後にフィルタリングするのではなく、結合がどのように行われるかに寄与するように、ON句のバー(foo_id = 5)に条件を設定することが重要です。

于 2012-11-08T09:09:07.533 に答える