0

私が次のものを持っているとしましょう:

class Movie < ActiveRecord::Base
  has_many :reviews
end

ユーザーがレビューしたものの前に最初にレビューしなかった、新しく作成された映画をリストしたいと思います。

したがって、これらの 2 つのクエリをマージするようなものです。

@reviewed_movies   = Movie.joins(:reviews)
                          .where("reviews.user_id != ?", user.id)
                          .order("created_at DESC")

@unreviewed_movies = Movie.joins(:reviews)
                          .where("reviews.user_id = ?", user.id)
                          .order("created_at DESC")

@movies   = @reviewed_movies.all + @unreviewed_movies.all

1回のクエリでこれを行う方法を知っている人はいますか?

4

7 に答える 7

3

Ruby の構文に慣れていませんが、以下のようなクエリですべてを実行できます。たとえば、ユーザー ID が「3」であるとします。

(SELECT * FROM `movies` m  LEFT JOIN  reviews r ON (m.id =r.movie_id)
WHERE user_id =3 ORDER BY created_at DESC)
UNION ALL
(SELECT * FROM `movies` 
LEFT JOIN  reviews r ON (m.id =r.movie_id)
WHERE user_id !=3 ORDER BY created_at DESC)

またはユーザー条件でテーブルを直接結合します

(SELECT * FROM `movies` m  
LEFT JOIN  reviews r ON (m.id =r.movie_id AND user_id =3)
ORDER BY created_at DESC)
UNION ALL
(SELECT * FROM `movies` 
LEFT JOIN  reviews r ON (m.id =r.movie_id AND user_id !=3 )
ORDER BY created_at DESC)

Mysql ユニオン

mysql で union と order by 句を使用する

Rails 3 ActiveRecord: UNION

Rails 3の2つのアクティブなレコード関係オブジェクトの結合

于 2013-09-16T07:45:00.937 に答える
1
@movies   = Movie.joins(:reviews)
                 .select("movies.*, reviews.id, SUM(reviews.user_id = #{user.id}) AS counter ")
                 .group("movies.id ")
                 .order("counter ASC, movies.created_at DESC")

counterこれにより、映画が現在のユーザーから持っているレビューの数が作成されます。

于 2013-09-18T01:19:33.817 に答える
0

注 : 私は Ruby を「話せません」。SQL を書きます。

まず、1つのクエリでそれを取得する私のショット:

-- add a boolean column computed from the join
SELECT m.*, (r.id != null) as reviewed
FROM movies m LEFT JOIN reviews r ON r.movieID = m.id AND r.userID = @userID
ORDER BY reviewed ASC, created_at DESC

次の注意事項: リストが大きくなった場合、各リクエストで索引付けされていない列でのソートを避けたい場合があります。行を取得するクエリを 1 つ作成し、Ruby コードでそれを並べ替えることができます (2 つのリストをマージする必要がありますが、SQL クエリは 1 つだけです)。

-- don't sort on "reviewed"
SELECT m.*, (r.id != null) as reviewed
FROM movies m LEFT JOIN reviews r ON r.movieID = m.id AND r.userID = @userID
ORDER BY created_at DESC

最後に、別の注意: SQL のキャッシュをより尊重する 2 つのクエリを実行することもできます。

-- extract movies : this query is the same for all users. Once cached : all users will get the cached version
SELECT * FROM movies ORDER BY created_at DESC

-- extract the user's reviewed movies. With the right index (userID, movieID), this will only hit the index
SELECT movieID FROM reviews WHERE reviews.userID = @userID

次に、Ruby で、2 番目のクエリからマッピングを作成reviewed[moviedID] -> {true, false}し、映画のリストを 2 つの部分に分割する必要があります。頻度が上がると、このバージョンの方が実際には高速である可能性があります。

于 2013-09-22T18:42:19.480 に答える
0

小さなアップデート:

@movies = Movie.includes(:reviews).where('reviews.movie_id = movies.id AND reviews.user_id = ?', user.id).order('reviews.movie_id ASC, created_at DESC')

「includes」を使用することは、左外部結合のレールアクティブレコード方法です:)

どうやらいくつかの副作用もあるため、eagerload を使用できます。(rails 3.x +)

@movies = Movie.eager_load(:reviews).where('reviews.movie_id = movies.id AND reviews.user_id = ?', user.id).order('reviews.movie_id ASC, created_at DESC')

レビューIDの条件が必要ない場合は、推測します。巨大なデータの場合は少し速くなります;)

于 2013-09-16T06:11:17.983 に答える
0

テーブルに movie_id がある場合reviews、これは Rails 4 で機能する可能性があります。サブ選択を使用して単一のクエリを実行する必要があります。

movies = Movie.where.not(id: Review.select("movie_id").where(user_id: user.id)).order("counter ASC, movies.created_at DESC")

automagical サブセレクトは Rails 4 で新しく追加されました。

于 2013-09-19T20:33:40.003 に答える
-1

外部結合を使用できます- http://www.w3schools.com/sql/sql_join_left.asp

@movies_1 = Movie.joins('LEFT JOIN reviews ON reviews.movie_id = movies.id').where('reviews.movie_id IS NULL').where("reviews.user_id != ?", user.id).order("created_at")
@movies_2 = Movie.joins('LEFT JOIN reviews ON reviews.movie_id = movies.id').where(where('reviews.movie_id IS NOT NULL')).where("reviews.user_id = ?", user.id).order("created_at")

または、counter_cache http://guides.rubyonrails.org/association_basics.htmlを使用して、どの映画の reviews_count が 0 であるかを確認することもできます。

于 2013-09-12T18:52:06.530 に答える