如何将此SQL转换为ActiveRecord查询?

时间:2014-08-27 20:16:24

标签: sql ruby-on-rails ruby activerecord

好的,我有这个Concern,其中包含一个方法top,用于按照某人收听的次数检索记录顺序(如果记录是一首歌,则直接检索记录顺序;如果是它是别的东西,比如流派或艺术家)。如果出现平局,它会根据其他网站上的受欢迎程度对记录进行排序。

以下代码几乎完美无缺。它以正确的顺序返回对象的数组。我的主要问题是我得到一个数组而不是关系。因此,来电者无法进一步添加Song.top.limit 3Genre.top.offset(10).limit(5)等内容。

这是我的方法:

def top(options = {})
    tname = self.name.tableize

    inner_select = self.select("#{tname}.*,COUNT(DISTINCT(listens.id)) AS listens_count")
    inner_select = inner_select.joins(:songs) unless self.name == 'Song'
    inner_select = inner_select.joins("LEFT JOIN listens ON listens.song_id = songs.id")
    inner_select = inner_select.where("listens.user_id = ?", options[:for].id) if options[:for].is_a? User
    inner_select = inner_select.group("#{tname}.id").to_sql

    # this is the part that needs fixin'
    find_by_sql("
        SELECT
            #{tname}.*,
            #{tname}.listens_count,
            SUM(sources.popularity) AS popularity_count
        FROM (#{inner_select}) AS #{tname}
        LEFT JOIN sources ON
            sources.resource_id = #{tname}.id
            AND
            sources.resource_type = '#{self.name}
        GROUP BY #{tname}.id
        ORDER BY listens_count DESC, popularity_count DESC
    ")
end

以下是根据请求生成的SQL查询。这来自Song.top

SELECT
    songs.*,
    songs.listens_count,
    SUM(sources.popularity) AS popularity_count
FROM (SELECT songs.*,COUNT(DISTINCT(listens.id)) AS listens_count FROM "songs" LEFT JOIN listens ON listens.song_id = songs.id GROUP BY songs.id) AS songs
LEFT JOIN sources ON
    sources.resource_id = songs.id
    AND
    sources.resource_type = 'Song'
GROUP BY songs.id
ORDER BY listens_count DESC, popularity_count DESC

这来自Artist.top

SELECT
    artists.*,
    artists.listens_count,
    SUM(sources.popularity) AS popularity_count
FROM (SELECT artists.*,COUNT(DISTINCT(listens.id)) AS listens_count FROM "artists" INNER JOIN "artists_songs" ON "artists_songs"."artist_id" = "artists"."id" INNER JOIN "songs" ON "songs"."id" = "artists_songs"."song_id" LEFT JOIN listens ON listens.song_id = songs.id GROUP BY artists.id) AS artists
LEFT JOIN sources ON
    sources.resource_id = artists.id
    AND
    sources.resource_type = 'Artist'
GROUP BY artists.id
ORDER BY listens_count DESC, popularity_count DESC

1 个答案:

答案 0 :(得分:0)

所以我找到答案,结果我完全错过了from方法。我会在这里发布最终方法,以防万一其他人像我一样盲目:

def top(options = {})
    tname = self.name.tableize

    inner_select = self.select("#{tname}.*,COUNT(DISTINCT(listens.id)) AS listens_count").
        joins("LEFT JOIN listens ON listens.song_id = songs.id").
        group("#{tname}.id")

    inner_select = inner_select.joins(:songs) unless self.name == 'Song'
    inner_select = inner_select.where("listens.user_id = ?", options[:for].id) if options[:for].is_a? User

    inner_select = "(#{inner_select.to_sql}) as #{tname}"

    select("#{tname}.*,#{tname}.listens_count,SUM(sources.popularity) AS popularity_count").
        from(inner_select).
        joins("LEFT JOIN sources ON sources.resource_id = #{tname}.id AND sources.resource_type = '#{self.name}'").
        group("#{tname}.id").
        order("listens_count DESC, popularity_count DESC")
end