【问题标题】:How to efficiently get all the rows from a huge table in Rails?如何有效地从 Rails 的大表中获取所有行?
【发布时间】:2016-05-03 06:30:36
【问题描述】:

我有两个表 usersnotes,用户记录超过 50k,notes 记录超过 90k。

class User < ActiveRecord::Base
  has_many :notes
end

users_table 的架构:

id  email  created_at

notes_table 的架构:

id  user_id  created_at category impact_score

我正在尝试创建一个临时表 temp_user_notes,它可以包含两个表的数据,我可以在这些表上执行 ActiveRecord 查询以获取数据。

user_notes_table 的架构:

id  user_id  notes_id  email user_created_at notes_created_at category impact_score

我正在这样做

def self.populate_temp_user_notes
    users = User.all
    users.each do |user|
      user.notes.each do |note|
        user_notes = TempUserNote.new
        user_notes.user_id = user.id
        user_notes.notes_id = note.id
        user_notes.auth_token_created_at = user.auth_token_created_at
        user_notes.notes_recorded_at = note.recorded_at
        user_notes.category = note.category
        user_notes.well_being_score = note.perception_score
        user_notes.save
      end
    end
  end

遍历所有用途及其注释是一个非常长的记忆消耗过程我可以使用什么其他方法?

从这里编辑:-

我的要求是:我有一系列由 AND 和 OR 分隔的查询,这些查询使用下表:用户、注释、事务、订阅。假设我获取目标用户的查询是(Query1 OR Query2) AND Query3 AND Query4 那么每个查询的输出就是下一个查询的输入。

例如:

total users in DB = 1000
1. user_list =  (Query1 or Query2) #=> 500 users
2. taking 500 users from user_list as input for next query
3. user_list = user_list AND Query3  #=> 300 users
4. taking 300 users from point 3 as input for query in point 4
5. user_list = user_list AND Query4 #=> 50 users

在最后一个用户列表中,我有我的目标用户和注释。

【问题讨论】:

    标签: mysql ruby-on-rails ruby activerecord


    【解决方案1】:

    我会结合使用find_eachincludes 来减少内存使用量和数据库查询数量:

    def self.populate_temp_user_notes
      User.includes(:notes).find_each do |user|
        user.notes.each do |note|
          TempUserNote.create(
            user_id:               user.id,
            notes_id:              note.id,
            auth_token_created_at: user.auth_token_created_at,
            notes_recorded_at:     note.recorded_at,
            category:              note.category,
            well_being_score:      note.perception_score,
          )
        end
      end
    end
    

    另一个非常快速的选择可能是使用这样的普通 SQL 来执行此操作:

    INSERT INTO temp_user_notes
      (user_id, notes_id, auth_token_created_at, notes_recorded_at, category, well_being_score)
      SELECT users.id, notes.id, users.auth_token_created_at, notes.recorded_at, notes.category, notes.perception_score
        FROM users INNER JOIN notes ON users.id = notes.user_id;
    

    【讨论】:

      【解决方案2】:

      对于海量数据,使用User.all 永远不会有好处。想象一下提取 50k 行数据,然后为每一行创建一个全新的 User 类对象,并用该行中的数据填充该对象。是的,这会给你带来麻烦,Rails 知道这一点。

      你可以使用:

      User.find_each do |user|
        # do your thing
      end
      

      find_each 一次会给你 1000 条记录,从而减少内存操作。

      您可以通过在find_each 中提供batch_size 来覆盖默认值:

      User.find_each(batch_size: 3000) do |user|
        # do your thing
      end
      

      【讨论】:

        【解决方案3】:

        我正在尝试创建一个临时表 temp_user_notes,它可以包含两个表的数据,我可以在这些表上执行 ActiveRecord 查询以获取数据。

        更标准的做法是加入两个表。如果您使用以下子句:

        User.joins(:notes)
        

        那么这将为您提供一个可用于进一步查询的范围(例如,您可以在两个表上添加 where 条件)并且返回的记录将包括来自 UserNote 模型的列,例如:

        User.joins(:notes).where("notes.impact_score > 10")
        

        会给你所有得分足够高的笔记,连同他们的用户。

        如果您需要遍历所有已加入的记录,您应该使用批量返回记录的find_each method

        【讨论】:

        • 我同意@BoraMa 使用联接是执行此操作的有效方法,但这样做并不能解决我的目的。我的要求是:我有一系列由 AND 和 OR 条件分隔的查询。例如: 1. user_list = (Query1 or Query2) 2. 以 user_list 作为输入 3. user_list = user_list AND Query3 4. user_list = user_list(fulfillig condition 3) AND Query4 在最后一个用户列表中我有我的目标用户和注释。
        猜你喜欢
        • 1970-01-01
        • 2016-02-17
        • 1970-01-01
        • 1970-01-01
        • 2011-04-10
        • 1970-01-01
        • 1970-01-01
        • 2013-02-21
        • 2012-11-20
        相关资源
        最近更新 更多