只选择带有海报的文章使用Arel

时间:2016-10-22 18:48:13

标签: sql ruby-on-rails postgresql activerecord arel

authorsarticlesposters个表格。作者has_many文章。第has_one条海报,但不需要在场。在输入上有作者'代表author_ids中的ID。 我有一个例子,如何使用Arel从每个给定的作者那里获得1篇发表的文章:

articles = Arel::Table.new(:articles, ActiveRecord::Base)

query = author_ids.map do |author_id|
  subselect = articles.project("*")
  .where(
    articles[:author_id].eq(author_id)
      .and(articles[:status].eq("published"))
  )
  .order(articles[:created_at].desc)
  .take(1)

  "(#{subselect.to_sql})"
end.join(" UNION ALL ")

Article.from("(#{query}) AS articles ORDER BY created_at DESC")

但我需要获得包含status = published和海报的文章。它是否是在子选择中使用INNER JOIN的唯一解决方案,如:

posters = Arel::Table.new(:posters, ActiveRecord::Base)
subselect = articles.join(posters).on(articles[:id].eq(posters[:article_id])).project("*")

还是存在更好的?另外,我想摆脱多个UNION ALL

1 个答案:

答案 0 :(得分:0)

考虑部分非规范化。

将指标列with_poster(布尔类型,默认为false)添加到表articles。 每次创建或删除海报时,都应在相应的文章上更新此列。 这需要一些额外的工作来写表格海报,但节省了从表格文章中读取的时间。

Alernative aproach用于获取每位作者的最新发表文章:

Article.select('DISTINCT ON (articles.author_id) articles.*').
  where(author_id: author_ids, status: 'published', with_poster: true).
  order('author_id, created_at DESC').sort_by(&:created_at).reverse

其他一些变种: https://www.periscopedata.com/blog/4-ways-to-join-only-the-first-row-in-sql.html