简体   繁体   English

活动记录,SQL:选择并计算与联接条件的关联

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM