更新:感谢@Erwin Brandstetter,我现在有了这个:
def self.unique_users_by_company(company)
users = User.arel_table
cards = Card.arel_table
users_columns = User.column_names.map { |col| users[col.to_sym] }
cards_condition = cards[:company_id].eq(company.id).
and(cards[:user_id].eq(users[:id]))
User.joins(:cards).where(cards_condition).group(users_columns).
order('min(cards.created_at)')
end
......这似乎完全符合我的要求。然而,我仍然希望解决两个缺点:
order()
子句使用的是直接SQL而不是Arel(无法弄清楚)。在上面的查询中调用.count
会给我这个错误:
NoMethodError:未定义的方法'to_sym' #<阿雷尔::属性::属性:0x007f870dc42c50>从 /Users/neezer/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-3.1.1/lib/active_record/relation/calculations.rb:227:in 'execute_grouped_calculation'
...我认为这可能与我如何映射users_columns
有关,因此我不必在group
子句中手动输入所有这些内容。
如何解决这两个问题?
原始问题:
这是我到目前为止所解决的问题的第一部分:
def self.unique_users_by_company(company)
users = User.arel_table
cards = Card.arel_table
cards_condition = cards[:company_id].eq(company.id)
.and(cards[:user_id].eq(users[:id]))
User.where(Card.where(cards_condition).exists)
end
这给了我84条独特的记录,这是正确的。
问题是我需要User
排序的cards[:created_at]
条记录(对于该特定用户而言最早)。在上述方法结束时将.order(cards[:created_at])
附加到范围绝对没有。
我尝试添加.joins(:cards)
,但这会返回587条记录,这是不正确的(重复User
s)。 group_by
据我所知,由于how PostgreSQL handles it,它在这里几乎没用。
我需要我的结果是ActiveRecord::Relation
(因此它是可链接的),它返回一个唯一用户列表,这些用户拥有属于给定公司的卡片,按创建日期排序他们的第一张卡片......用Ruby编写的查询与数据库无关。我怎么能这样做?
class Company
has_many :cards
end
class Card
belongs_to :user
belongs_to :company
end
class User
has_many :cards
end
如果您需要任何其他信息,或者我的问题不清楚,请告诉我。
答案 0 :(得分:4)
您要查找的查询应如下所示:
SELECT user_id, min(created_at) AS min_created_at
FROM cards
WHERE company_id = 1
GROUP BY user_id
ORDER BY min(created_at)
如果您需要在结果中显示该表的列,则可以加入表user
。否则您甚至不需要它用于查询。
如果min_created_at
列表中不需要SELECT
,则可以将其删除。
应该很容易翻译成Ruby(我不擅长)。
获取整个用户记录(因为我从您的评论中获得):
SELECT u.*,
FROM user u
JOIN (
SELECT user_id, min(created_at) AS min_created_at
FROM cards
WHERE company_id = 1
GROUP BY user_id
) c ON u.id = c.user_id
ORDER BY min_created_at
或者:
SELECT u.*
FROM user u
JOIN cards c ON u.id = c.user_id
WHERE c.company_id = 1
GROUP BY u.id, u.col1, u.col2, .. -- You have to spell out all columns!
ORDER BY min(c.created_at)
使用PostgreSQL 9.1+,您只需编写:
GROUP BY u.id
(就像在MySQL中一样)..提供id
是主键。
我引用release notes:
在主要时,允许查询目标列表中的非GROUP BY列 key在GROUP BY子句中指定(Peter Eisentraut)
SQL标准允许此行为,并且由于主键, 结果是明确的。
答案 1 :(得分:1)
您需要它可链接的事实使事情变得复杂,否则您可以自己下载到SQL或只通过select("users.id")
选择所需的列来解决Postgres问题。因为它的核心是你的查询类似
SELECT users.id
FROM users
INNER JOIN cards ON users.id = cards.user_id
WHERE cards.company_id = 1
GROUP BY users.id, DATE(cards.created_at)
ORDER BY DATE(cards.created_at) DESC
在Arel语法中或多或少:
User.select("id").joins(:cards).where(:"cards.company_id" => company.id).group_by("users.id, DATE(cards.created_at)").order("DATE(cards.created_at) DESC")