【问题标题】:How do I join a table to itself in AREL to find duplicates?如何在 AREL 中将表连接到自身以查找重复项?
【发布时间】:2013-02-06 06:06:39
【问题描述】:

我有一个表 transactions,它可能包含重复项(对我们来说,重复项是 Transaction 具有相同的 account_iddateamount)。

我的英语功能要求是“我想查看所有存在超过 1 笔具有相同 account_id、日期和金额的交易的交易”。

暂时放弃 AREL,我在 SQL 中产生了这样的东西:

SELECT * FROM transactions t1, transactions t2
    WHERE t1.id != t2.id 
      AND t1.date = t2.date
      AND t1.amount = t2.amount
      AND t1.account_id = t2.account_id

我正在使用 Rails 3.2.x 和 Postgres。

最初,我在 AREL 中尝试过:

Transaction.group(:account_id, :date, :amount).having("count(id) > 1")

但这给了我关于聚合函数的 SQL 错误:

PG::Error: ERROR:  column "transactions.id" must appear in the GROUP BY clause or be used in an aggregate function

.. 令人沮丧,因为我确实想要 group by 子句中的 ID —— 重点是我希望在检查欺骗时忽略 ID。

如果有人能指出我需要将其设为范围的 AREL 的正确方向,我将不胜感激 -- find_by_sql 在您需要记录时非常有用,但我正在尝试创建一个 ActiveAdmin 范围 --它不喜欢数组。

【问题讨论】:

  • 我不介意投反对票,但我确实介意没有 cmet 的投反对票,为什么我被投反对票?
  • 可能是头像:P

标签: ruby-on-rails-3 postgresql activeadmin arel


【解决方案1】:

如果您愿意将结果以多行形式返回给您,您可以尝试这样的操作:

select account_id, amount, day, group_concat(id) 
  from purchases 
 group by account_id, amount, day having count(id) > 1;

这将返回一个结果集,其中每一行都包含给定帐户、日期和金额的重复项。

http://sqlfiddle.com/#!2/86e43/17

【讨论】:

  • 赞成把我放到这个网站上,但不幸的是,你提供的查询适用于 MySQL,而不是 Postgres。我在 Heroku,所以..
  • @makdad 不太熟悉 Postgres,但如果您的 Heroku Postgres 是 9.0 或更高版本,您可以使用 string_agg
【解决方案2】:

可能是这样的

def similar
  table = self.class.arel_table
  conditions = %w[ date amount ].map { |field| table[field].eq send(field) }.map &:to_sql
  self.class.where "id != #{ id } AND #{ conditions.join ' AND ' }"
end

【讨论】:

  • 这可能会被清理,但它会返回一个 ActiveRecord::Relation
【解决方案3】:

您可以像这样在 ActiveRecord 事务模型中使用 sql 定义范围:

scope :duplicate_transactions, where(<<-eosql.strip)
  transactions.id IN (
      SELECT 
          t1.id 
      FROM 
          transactions t1, transactions t2
      WHERE 
          t1.id != t2.id AND
          t1.date = t2.date AND
          t1.amount = t2.amount AND
          t1.account_id = t2.account_id
  )
eosql

但随后涉及到 id。可能不是您想要的,因为这是一个昂贵的查询。至少在

上创建一个非唯一索引
date, amount, account_id

为这张桌子。这应该会为您节省一些全表行扫描...另一种方法是执行类似

Transaction.joins(<<eosql.strip)
  LEFT OUTER JOIN transactions t ON 
      transactions.id         != t.id AND
      transactions.date        = t.date AND
      transactions.amount      = t.amount
eosql

两种方式都很昂贵,内存方面。祝你好运。

【讨论】:

  • 最后,深入研究 SQL 是最简单的——这是用于管理,而不是常规使用,所以我可以使用稍微慢一点的查询。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-15
  • 2012-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多