繁体   English   中英

通过PostgreSQL和Rails 3查找按字段排序的唯一记录?

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

......这似乎完全符合我的要求。 但是,我仍然希望解决两个缺点:

  1. order()子句使用直接SQL而不是Arel(无法弄清楚)。
  2. 在上面的查询上调用.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.

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