0

我有两个 ActiveRecord 模型:PostVote. 我想做一个简单的查询:

SELECT *,
  (SELECT COUNT(*)
   FROM votes
   WHERE votes.id = posts.id) AS vote_count
FROM posts

我想知道在 activerecord DSL 中最好的方法是什么。我的目标是尽量减少我必须编写的 SQL 数量。

我可以Post.select("COUNT(*) from votes where votes.id = posts.id as vote_count")

这有两个问题:

  1. 原始 SQL。无论如何用DSL写这个?
  2. 这仅返回属性vote_count而不是“*”+ vote_count。我可以追加.select("*"),但我每次都会重复。有没有更好/干燥的方法来做到这一点?

谢谢

4

4 回答 4

2

好吧,如果您想减少 SQL 的数量,您可以将该查询拆分为较小的两端分别执行它们。例如,可以提取计票部分来查询:

SELECT votes.id, COUNT(*) FROM votes GROUP BY votes.id;

您可以使用 ActiveRecord 方法编写如下:

Vote.group(:id).count

您可以存储结果以供以后使用并直接从 Post 模型访问它,例如您可以定义#votes_count为一个方法:

class Post
  def votes_count
    @@votes_count_cache ||= Vote.group(:id).count
    @@votes_count_cache[id] || 0
  end
end

(当然,每次使用缓存都会引发一个关于使其无效或更新的问题,但这超出了本主题的范围。)

但我强烈建议您考虑另一种方法。

我相信使用 ActiveRecord 方法编写像您这样的复杂查询(即使有可能)或者像我之前提出的那样将查询分成两个都是坏主意。它们导致代码极其混乱,远不如原始 SQL 可读。相反,我建议引入查询对象。IMO 当隐藏在漂亮的界面后面时,使用原始、复杂的 SQL 并没有错。请参阅: M. Fowler 的EAA 的 P和 Brynary 在Code Climate 博客上的帖子。

于 2013-11-11T11:59:46.160 回答
0

完全不使用额外的 SQL 来做这件事怎么样 - 考虑使用 Rails 的 counter_cache 特性。

如果你votes_count向表中添加一个整数列,你可以通过将Vote 中posts的声明更改为:让 Rails 自动递增和递减该计数器:belongs_to

belongs_to :post, counter_cache: true

然后,Rails 将更新每个 Post 的投票数。这样计数就已经计算好了,不需要子查询。

于 2013-11-08T23:09:12.150 回答
0

也许您可以创建 mysql 视图并将其映射到新的 AR 模型。它的工作方式与表类似,您只需要指定 set_table_name "your_view_name"....也许在 DB 级别上它会运行得更快并且会自动重新计算。

于 2013-11-10T23:19:45.597 回答
0

刚刚偶然发现了postgres_ext gem,它增加了对 Arel 和 ActiveRecord 中的 Common Table Expressions 的支持,这正是您所要求的。Gem 不适用于 SQLite,但也许可以提取某些部分或用作示例。

于 2013-12-11T20:22:36.267 回答