繁体   English   中英

Rails删除重复的关联记录

[英]Rails remove duplicated associated records

假设我有一个User和一个用户has_many :tags ,我想删除所有name重复的@users标签。 例如,

@user.tags #=> [<Tag name: 'A'>, <Tag name: 'A'>, <Tag name: 'B'>]

我只想保留具有唯一名称的标签,并从数据库中删除其余的标签。

我知道我可以从用户标签中提取出唯一标签名称列表,然后删除所有用户标签,然后仅使用唯一名称重新创建用户标签,但这效率低下吗?

另一方面, select将不起作用,因为它仅返回选定的列。 uniq也将不起作用:

@user.tags.uniq #=> returns all tags

有没有更有效的方法?

更新:我想在迁移中做到这一点。

此方法将为您提供带有重复标签的ActiveRecord :: Relation:

class Tag < ApplicationRecord
  belongs_to :user

  def self.duplicate_tags
    unique = self.select('DISTINCT ON(tags.name, tags.user_id) tags.id')
     .order(:name, :user_id, :id)
    self.where.not(id: unique)
  end
end

它实际上是作为单个查询运行的:

SELECT  "tags".* FROM "tags" 
WHERE "tags"."id" NOT IN 
 (SELECT DISTINCT ON(tags.name) tags.id 
  FROM "tags" GROUP BY "tags"."id", "tags"."user_id" 
  ORDER BY tags.name, tags.id)

您可以使用#delete_all在单个查询中删除重复#delete_all

# Warning! This can't be undone!
Tag.duplicate_tags.destroy_all

如果您需要销毁相关联的关联或调用before_*after_destroy回调,请改用#destroy_all方法。 但是您应该将其与#in_batches一起使用,以免耗尽内存。

# Warning! This can't be undone!
Tag.duplicate_tags.in_batches do |batch|
  # destroys a batch of 1000 records
  batch.destroy_all
end

您可以在迁移中编写与SQL模型无关的查询。 这是特定于PostgreSQL的迁移代码:

execute <<-SQL
  DELETE FROM tags
  WHERE id NOT IN (
    SELECT DISTINCT ON(user_id, name) id FROM tags
    ORDER BY user_id, name, id ASC
  )
SQL

这是更常见的SQL:

execute <<-SQL
  DELETE FROM tags
  WHERE id IN (
    SELECT DISTINCT t2.id FROM tags t1
    INNER JOIN tags t2
    ON (
      t1.user_id = t2.user_id AND
      t1.name = t2.name AND
      t1.id < t2.id
    )
  )
SQL

此SQL小提琴显示了不同的查询,您可以根据自己的目标在DELETE查询中用作子选择 :删除第一个/最后一个/所有重复项。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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