Ruby on rails ActiveRecord选择添加计数

Ruby on rails ActiveRecord选择添加计数,ruby-on-rails,ruby,activerecord,Ruby On Rails,Ruby,Activerecord,在我的ActiveRecord查询中,我需要在select方法中提供以下信息: (SELECT count(*) from likes where likes.spentit_id = spentits.id) as like_count, (SELECT count(*) from comments where comments.spentit_id = spentits.id) as comment_count 当然,我将这两个作为字符串传递给.select()部分,但我想知道这样做的正确

在我的ActiveRecord查询中,我需要在select方法中提供以下信息:

(SELECT count(*) from likes where likes.spentit_id = spentits.id) as like_count,
(SELECT count(*) from comments where comments.spentit_id = spentits.id) as comment_count
当然,我将这两个作为字符串传递给.select()部分,但我想知道这样做的正确/替代方法是什么

下面是我试图调用的完整查询:

SELECT DISTINCT
    spentits.*,
    username,
    (SELECT count(*) from likes where likes.spentit_id = spentits.id) as like_count,
    (SELECT count(*) from comments where comments.spentit_id = spentits.id) as comment_count,
    (SELECT count(*) from wishlist_items where wishlist_items.spentit_id = spentits.id) as wishlist_count,
    (case when likes.id is null then 0 else 1 end) as is_liked_by_me,
    (case when wishlist_items.id is null then 0 else 1 end) as is_wishlisted_by_me,
    (case when comments.id is null then 0 else 1 end) as is_commented_by_me
FROM spentits
LEFT JOIN users ON users.id = spentits.user_id
LEFT JOIN likes ON likes.user_id = 9 AND likes.spentit_id = spentits.id
LEFT JOIN wishlist_items ON wishlist_items.user_id = 9 AND wishlist_items.spentit_id = spentits.id
LEFT JOIN comments ON comments.user_id = 9 AND comments.spentit_id = spentits.id
WHERE spentits.user_id IN
    (SELECT follows.following_id
     FROM follows
     WHERE follows.follower_id = 9 AND follows.accepted = 1)
ORDER BY id DESC LIMIT 15 OFFSET 0;
这里的所有表都有各自的ActiveRecord对象。我真的很困惑如何以编写最少SQL的方式将此查询转换为“activerecord”/rails。假定“9”用户_id是一个参数

更新: 好的,这就是我在同一时间所做的,它比原始SQL语句好得多,但在我看来它仍然很难看:

class Spentit < ActiveRecord::Base
  belongs_to :user
  has_many :likes
  has_many :wishlist_items
  has_many :comments

  scope :include_author_info, lambda {
    joins([:user]).
    select("username").
    select("users.photo_uri as user_photo_uri").
    select("spentits.*")
  }

  scope :include_counts, lambda {
    select("(SELECT count(*) from likes where likes.spentit_id = spentits.id) as like_count").
    select("(SELECT count(*) from comments where comments.spentit_id = spentits.id) as comment_count").
    select("(SELECT count(*) from wishlist_items where wishlist_items.spentit_id = spentits.id) as wishlist_items_count").
    select("spentits.*")
  }
end
关于课程的一点。
用户
有许多
支出
Spentit
有许多
注释
喜欢
注释

好吧,你有点“做错了”。而不是

  scope :include_counts, lambda {
    select("(SELECT count(*) from likes where likes.spentit_id = spentits.id) as like_count").
    select("(SELECT count(*) from comments where comments.spentit_id = spentits.id) as comment_count").
    select("(SELECT count(*) from wishlist_items where wishlist_items.spentit_id = spentits.id) as wishlist_items_count").
    select("spentits.*")
  }

而不是

  scope :include_author_info, lambda {
    joins([:user]).
    select("username").
    select("users.photo_uri as user_photo_uri").
    select("spentits.*")
  }

此外,您还可以在引用的模型中定义范围,并使用这些范围:

class Follow < ActiveRecord::Base
  belongs_to :follower, :class_name => "User"
  belongs_to :following, :class_name => "User"

  scope :accepted, lambda{ where(:accepted => 1) }
end

Spentits.where(:user => Follow.where(:follower => User.find(9)).accepted)
但“在正常情况下”,你不需要做任何花哨的事情来获得这些计数,你已经拥有了它们


然而,请注意,您可能也希望这样做,您显然可以作为的一部分进行,或者您将执行比您需要的多得多的查询

这两个选项应该是单个查询的一部分,或者这两个选项是分开的
spentits
是Spentit的一个特定对象,对吗?@kiddorails单个查询,是的,spentits属于Spentit类型。我已经更新了我的整个查询,所以你知道我到底在找什么。好的,你能描述一下,在相关对象方面,你想得到什么信息吗?基本上,不要用SQL来思考问题,首先要用“我有一堆具有链接和关系的对象”来思考问题,然后用它们来定义查询,而不是用SQL。SQL只是序列化方法和存储,不是问题所在。@Narfanator我添加了更多信息,请检查。感谢您的详细回复。执行“Spentit.find(7520.likes.count)”,然后执行“wishlist”和“comment”三个查询。你要去db三次。那不是很糟糕吗?为什么不使用连接呢?除非您设置了急切加载,否则它会三次访问数据库-请参阅链接。此外,如果您在
选择
时出错,您最终会遇到不可变的对象。如果仍要执行联接,可以使用
.join(:relation)
等(与默认范围相同,但不同的部分),但这些似乎是用于
.where
语句中的。如果你所做的只是添加信息,那么看起来像是迫不及待地加载(可能使用
default\u scope
)是一种方式。至于“为什么”-在ActiveRecord中编写SQL是不好的,因为它很脆弱。如果您使用ActiveRecord代码而不是SQL,您可以毫无顾虑地交换您正在使用的数据库,ActiveRecord可以帮助您解决问题。请这样想:描述您想要做什么,而不是如何做。您将数据库交换到其他数据库的频率是多少?
  scope :include_author_info, lambda {
    joins([:user]).
    select("username").
    select("users.photo_uri as user_photo_uri").
    select("spentits.*")
  }
Spentit.find(7520).user.username
Spentit.find(7520).user.photo_uri
class Follow < ActiveRecord::Base
  belongs_to :follower, :class_name => "User"
  belongs_to :following, :class_name => "User"

  scope :accepted, lambda{ where(:accepted => 1) }
end

Spentits.where(:user => Follow.where(:follower => User.find(9)).accepted)
class Spentit
  def to_hash
    hash = self.attributes
    hash[:like_count] = self.like.count
    # ...
  end
end