[英]Active record, SQL: select and count association with joins condition
I'm having an issue optimizing a SQL query, and utilizing active record to produce the query. 我在优化SQL查询以及利用活动记录生成查询时遇到问题。 it involves selecting the id and the count of an association as well as a conditional join with a variable input.
它涉及选择关联的ID和计数以及带有变量输入的条件连接。 Further, i havent been able to use a where statement because i want results that do not have any associations yet.
此外,我还不能使用where语句,因为我想要的结果还没有任何关联。 My models are as follows
我的模型如下
class Vote < ActiveRecord::Base
belongs_to :user
belongs_to :venue
belongs_to :catering_event
validates :catering_event_id, uniqueness: { scope: :user_id }
end
class Venue < ActiveRecord::Base
has_many :votes
has_many :catering_events, through: :votes
has_many :users, through: :votes
end
class CateringEvent < ActiveRecord::Base
has_many :votes
has_many :venues, through: :votes
has_many :users, through: :votes
end
What I have are venues, catering_events (events for short), and users. 我所拥有的是场地,catering_events(简称事件)和用户。 users can cast 1 vote per event, where a vote is a user, choosing a venue, for an event (user_id, venue_id, and catering_event_id).
用户可以为每个事件投1票,其中投票是用户,选择事件的场所(user_id,container_id和catering_event_id)。
On the show event page, i want to display all venues and their vote counts, where venues without vote counts display a vote count of 0, which i'm trying to accomplish with a single query. 在显示事件页面上,我想显示所有场所及其投票数,其中没有投票数的场所显示的投票数为0,这是我试图通过一个查询来完成的。 I havnt been able to utilize a where clause, because my attempts have filtered out venues which have not yet been voted on.
我之所以能够利用where子句,是因为我的尝试过滤掉了尚未投票的场所。
i currently have two functional solutions, both of which i'm unsatisfied with. 我目前有两个功能解决方案,而我都不满意。 the first is a direct find_by_sql query;
第一个是直接的find_by_sql查询; however i have to semi-hack the return values because find_by_sql is only returning attributes that the model currently has.
但是我必须半破解返回值,因为find_by_sql仅返回模型当前具有的属性。
For my current Data: 对于我当前的数据:
#<Vote:0x007fb9f681ac38 id: 1, user_id: 2, venue_id: 3, catering_event_id: 1>,
#<Vote:0x007fb9f681aa58 id: 2, user_id: 1, venue_id: 2, catering_event_id: 1>,
#<Vote:0x007fb9f681a8f0 id: 3, user_id: 1, venue_id: 1, catering_event_id: 2>
My functional find_by_sql query: 我的功能find_by_sql查询:
sql =
"SELECT COUNT(votes.id) AS id, venues.id AS venue_id
FROM venues
LEFT OUTER JOIN votes ON votes.venue_id = venues.id
AND votes.catering_event_id = ?
LEFT OUTER JOIN catering_events ON catering_events.id = votes.catering_event_id
GROUP BY venues.id"
=> "SELECT COUNT(votes.id) AS id, venues.id AS venue_id FROM venues LEFT OUTER JOIN votes ON votes.venue_id = venues.id AND votes.catering_event_id = ? LEFT OUTER JOIN catering_events ON catering_events.id = votes.catering_event_id GROUP BY venues.id"
pry(main)> Vote.find_by_sql [sql, '1']
Vote Load (9.0ms) SELECT COUNT(votes.id) AS id, venues.id AS venue_id FROM venues LEFT OUTER JOIN votes ON votes.venue_id = venues.id AND votes.catering_event_id = '1' LEFT OUTER JOIN catering_events ON catering_events.id = votes.catering_event_id GROUP BY venues.id
=> [
#<Vote:0x007fb9f33ca8c0 id: 0, venue_id: 1>,
#<Vote:0x007fb9f33ca668 id: 1, venue_id: 2>,
#<Vote:0x007fb9f33ca258 id: 1, venue_id: 3>
]
this returns all venues and their votecounts for catering event 1. however, i have to name the vote_count as some attribute on the model from which i call find_by_sql, and because a vote has 3 conditions, theres no 1 model to which i can add a votecount attribute. 这将返回餐饮事件1的所有场所及其票数。但是,我必须将表决数命名为模型上的某个属性,从中我将其称为find_by_sql,并且由于表决具有3个条件,因此无法向其中添加1个模型投票计数属性。 when i try to namespace the count to a custom field, i dont get an @attributes has as the docs say, it just omits the value:
当我尝试将计数命名空间命名为自定义字段时,我没有像文档所说的那样获得@attributes具有的值,它只是忽略了该值:
Vote Load (0.7ms) SELECT COUNT(votes.id) AS vote_count, venues.id AS venue_id
FROM venues
LEFT OUTER JOIN votes ON votes.venue_id = venues.id
AND votes.catering_event_id = '1'
LEFT OUTER JOIN catering_events ON catering_events.id = votes.catering_event_id
GROUP BY venues.id
=> [
#<Vote:0x007fb9f31d29a0 id: nil, venue_id: 1>,
#<Vote:0x007fb9f31d2248 id: nil, venue_id: 2>,
#<Vote:0x007fb9f31d1c58 id: nil, venue_id: 3>
]
The second solution i've worked up involves using active record to make the query, however i havent figured out how to properly sanitize the joins statement with the variable catering_event_id: (i dont actually run this on new lines, but for readability i split it up) 我已经解决的第二个解决方案涉及使用活动记录来进行查询,但是我还没有弄清楚如何使用变量catering_event_id正确清理joins语句:(我实际上没有在新行上运行它,但是出于可读性考虑,我将其拆分了达)
Venue.select('venues.id as venue_id')
.joins('LEFT OUTER JOIN votes ON votes.venue_id = venues.id AND votes.catering_event_id = 2')
.joins('LEFT OUTER JOIN catering_events ON catering_events.id = votes.catering_event_id').group('venues.id')
.count('votes.id')
(0.6ms) SELECT COUNT(votes.id) AS count_votes_id, venues.id AS venues_id FROM `venues` LEFT OUTER JOIN votes ON votes.venue_id = venues.id AND votes.catering_event_id = 2 LEFT OUTER JOIN catering_events ON catering_events.id = votes.catering_event_id GROUP BY venues.id
=> {1=>1, 2=>0, 3=>0}
notice the first joins statement, I had to hard code the 2 in: 注意第一个joins语句,我不得不在其中硬编码2:
AND votes.catering_event_id = 2
as i havnt figured out how to pass a variable to the active record joins statement. 正如我想出了如何将变量传递给活动记录joins语句。
in summary, I'm trying to perform a single query using either direct sql with find_by_sql or active record (my preference is with active record), to select all venues, and their vote count for a variable catering_event. 总而言之,我试图使用带有find_by_sql的直接sql或活动记录(我的偏好是活动记录)执行单个查询,以选择所有场所以及变量catering_event的投票计数。 I'd prefer to tweak one of my current solutions to either allow find_by_sql to return custom name-spaced attributes or figure out how to properly pass a joins statement an interpolated/sanitized variable.
我更愿意调整我当前的解决方案之一,以允许find_by_sql返回自定义的以名称分隔的属性,或者弄清楚如何正确地将一个joins语句传递给一个内插的/清理过的变量。
i'm on Rails 4.2.1, and ruby 2.2.0 我在Rails 4.2.1和ruby 2.2.0上
The answer ended up being in the find_by_sql
command. 答案最终出现在
find_by_sql
命令中。
Although it doesn't show it in the return values, a virtual attribute is created using the AS
keyword in the query. 尽管它没有在返回值中显示它,但是使用查询中的
AS
关键字创建了一个虚拟属性。 I can now do SELECT COUNT(votes.id) AS vote_count
and although the return objects don't show the attribute vote_count
, they respond to it. 现在,我可以执行
SELECT COUNT(votes.id) AS vote_count
,尽管返回对象未显示该属性的vote_count
,但它们会对此做出响应。
eg 例如
query = Vote.find_by_sql([sql, '1'])
query.map(&:vote_count)
=> [0,1,1]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.