ActiveRecord - Копирование таблицы соединений - PullRequest
0 голосов
/ 22 января 2020

Я запускаю грабли для копирования некоторых живых данных в демонстрационных целях. Одна часть занимает очень много времени, чтобы бежать. Модели выглядят примерно так:

class Host
  has_many :event_attendees, inverse_of: :host, dependent: :destroy
  has_many :events, -> { distinct }, through: :event_attendees
end
class Attendee
  has_many :event_attendees, inverse_of: :attendee, dependent: :destroy
  has_many :events, through: :event_attendees
end
class Event
  has_many :event_attendees, inverse_of: :event, dependent: :destroy
  has_many :attendees, through: :event_attendees
end
class EventAttendee
  belongs_to :event, inverse_of: :event_attendees, dependent: :destroy
  belongs_to :attendee, inverse_of: :event_attendees, dependent: :destroy
  belongs_to :host, inverse_of: :event_attendees, dependent: :destroy
end

Я копирую Хостов, Посетителей и События, отслеживая их идентификаторы (для копирования существующих EventAttendees). Кажется, все работает нормально.

def copy_events
    log("Copying events...")

    @event_ids_h = {}

    ActiveRecord::Base.transaction do

      Event.
        where(organization_id: source_org_id).
        distinct.
        find_in_batches do |events|
          fake_events = events.map do |event|
            atts = event.attributes.except('id').merge(
              ...atts
            )
            Event.new(atts)
          end

          Event.import(
            fake_events,
            validate: true,
            timestamps: false,
          )

          @event_ids_h = @event_ids_h.merge(Hash[events.map(&:id).zip(fake_events.map(&:id))])
        end
    end
  end

Следующая часть работает ... но она требует forevvvvver. Около 2,5 часов около 600 000 записей.

def copy_event_attendees
    log("Copying event attendee data...")

    arr = EventAttendee.
      where(attendee_id: @attendee_ids_h.keys, event_id: @event_ids_h.keys, host_id: @host_ids_h.keys).
      pluck(:attendee_id, :event_id, :host_id).
      map{ |ids| build_event_attendee_row(ids) }

    unless arr.empty?
      values_s = Arel::Nodes::ValuesList.new(arr).to_sql

      ActiveRecord::Base.connection.insert(<<~SQL)
        INSERT INTO event_attendees (attendee_id, event_id, host_id, created_at) #{values_s}
      SQL
    end

  end

def build_event_attendee_row(ids)
  ids[0] = @attendee_ids_h[ids.first]
  ids[1] = @event_ids_h[ids.second]
  ids[2] = @host_ids_h[ids.third]
  ids.push(Time.current)
end

Я догадываюсь , что это WHERE IN так долго. Но я не уверен, и я действительно хотел бы найти способ оптимизировать это, если это возможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...