![](/img/trans.png)
[英]Rails + Postgres query for all records that meet a criteria with a unique association ID
[英]Finding unique records, ordered by field in association, with PostgreSQL and Rails 3?
更新 :感谢@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:来自/Users/neezer/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-3.1.1/lib/active_record的#<Arel :: Attributes :: Attribute:0x007f870dc42c50>的未定义方法'to_sym' /relation/calculations.rb:227:in'cute_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条独特的记录,这是正确的。
问题是我需要用cards[:created_at]
订购的那些User
记录(对于那个特定用户来说最早的)。 将.order(cards[:created_at])
附加到上面方法末尾的范围内绝对没有任何意义。
我尝试添加.joins(:cards)
,但是这会返回587条记录,这是不正确的(重复User
)。 group_by
因为我知道它在这里几乎没用,因为PostgreSQL如何处理它 。
我需要我的结果是一个ActiveRecord::Relation
(所以它是可链接的),它返回一个具有属于给定公司的卡的唯一用户列表,按照他们的第一张卡的创建日期排序...带有一个查询的查询在Ruby中并且与数据库无关。 我怎样才能做到这一点?
class Company
has_many :cards
end
class Card
belongs_to :user
belongs_to :company
end
class User
has_many :cards
end
如果您需要任何其他信息,或者我的问题不清楚,请告诉我。
您要查找的查询应如下所示:
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
,否则您甚至不需要查询。
如果您在SELECT
列表中不需要min_created_at
,则可以将其遗漏。
应该很容易翻译成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
是主键。
我引用发行说明 :
在GROUP BY子句中指定主键时,允许查询目标列表中的非GROUP BY列(Peter Eisentraut)
SQL标准允许这种行为,并且由于主键,结果是明确的。
您需要它可链接的事实使事情变得复杂,否则您可以自己下载到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")
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.