开发Rails Job合并含相似电话号码的重复用户记录技术问询
问题解答
你的核心方向是对的——通过号码最后10位匹配同用户的重复记录,符合不同国家码的号码特征,也能覆盖你提到的场景。但当前代码存在几个关键问题,导致无法正确获取匹配项,以下是具体修正方案和合并逻辑:
一、修正查询逻辑
当前代码里的user[user.length * -1..-1]完全错误,你要操作的是用户的phone字段而非user对象本身。另外,号码里的非数字字符(比如-)会干扰匹配,必须先清洗提取纯数字,再取最后10位做匹配。
修正后的Job代码:
class MergeRecordsJob < ApplicationJob queue_as :default def perform(user_id) # 通过ID取用户,避免直接传对象引发序列化问题 user = User.find(user_id) # 清洗电话号码,提取纯数字 cleaned_phone = user.phone.gsub(/\D/, '') # 取纯数字的最后10位作为匹配键 last_10_digits = cleaned_phone[-10..-1] # 查询所有纯数字结尾为这10位的用户,排除当前用户自身 matches = User.where("regexp_replace(phone, '\\D', '', 'g') LIKE ?", "%#{last_10_digits}") .where.not(id: user.id) p "找到#{matches.count}条重复记录" # 执行合并逻辑 merge_duplicates(user, matches) unless matches.empty? end private def merge_duplicates(primary_user, duplicates) # 开启事务,确保合并操作的原子性,避免数据不一致 ActiveRecord::Base.transaction do # 1. 迁移关联数据(按需调整):将重复记录的关联资源(如订单、评论)转移到主记录 duplicates.each do |dup| # 示例:若存在orders关联,将dup的订单归属更新为主记录ID # dup.orders.update_all(user_id: primary_user.id) # 其他关联模型同理处理 end # 2. 确保主记录保留带国家码的号码:如果主记录无国家码,从重复记录中选取带国家码的替换 primary_cleaned = primary_user.phone.gsub(/\D/, '') best_phone = duplicates.map { |d| d.phone }.select { |p| p.gsub(/\D/, '').length > 10 }.first || primary_user.phone primary_user.update!(phone: best_phone) # 3. 删除重复记录 duplicates.destroy_all end end end
二、优化与建议
- 批量分组处理:直接单条调用Job处理1000条样本会重复查询同一组记录,建议先按清洗后的最后10位分组,批量发起Job,同时给User表添加
merged_at字段标记已处理记录,避免重复执行。 - 添加函数索引:为避免全表扫描,提升查询速度,给phone字段创建函数索引:
CREATE INDEX idx_users_cleaned_phone ON users (regexp_replace(phone, '\D', '', 'g')); - 测试验证:先在测试环境用小样本验证逻辑,重点确认关联数据迁移、号码保留规则的正确性,避免线上数据损坏。
内容的提问来源于stack exchange,提问作者ra-11
相关产品推荐
相关产品推荐

