Создать или инициализировать. Как минимизировать нагрузку на базу данных и сервер

Я пишу на Ruby, в фреймворке Ruby on Rails. Я не могу понять гипотетическую ситуацию. Допустим, функция get_date возвращает массив из примерно 10 миллионов элементов. Каждый элемент содержит поля: фк, имя, деталь. Существует также таблица, содержащая те же поля. Он частично заполнен. fk — уникальное индексированное поле. Моя задача — проверить, существует ли запись в таблице. Если записи нет, мне придется ее создать. Если запись существует, мне придется обновить поля имени и сведений, поскольку они могут стать неактуальными. Мне нужно написать код, который будет как можно меньше нагружать базу данных и сервер.

Я предлагаю:

get_data.each do |item|
          field = MyField.find_or_initialize_by_fk(item['fk'])
          field.update_attributes(
              {
                  :name       => item['name'],
                  :detail    => item['detail']
              }
          )
          field.save
end

или

get_data.each do |item|
          field = MyField.find_or_create_by_fk(item['fk'])
          field.update_attributes(
              {
                  :name       => item['name'],
                  :detail    => item['detail']
              }
          )
end

Что из того или другого будет лучше?

Ни один. Загрузите новые данные во временную таблицу и сделайте это двумя запросами к базе данных. Это база данных, и она гораздо лучше справляется с данными, чем Ruby.

Richard Huxton 28.02.2024 07:47

Но, возможно, загрузка данных во временную таблицу потребует такого же количества ресурсов?

Test Title 28.02.2024 07:59
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Первая версия была бы лучше, поскольку она выполняет только два запроса: один для поиска потенциально существующей записи, а другой для обновления существующей или создания новой записи.

Вторая версия будет выполнять три запроса к базе данных, когда ни одна существующая запись не найдена: один пытается найти запись, второй — для создания новых записей и третий — для обновления вновь созданной записи.

Но вместо этого я предлагаю использовать upsert, который вставляет новые или обновляет существующие записи всего за один запрос:

get_data.each do |item|
  MyField.upsert(
    { fk: item['fk'], name: item['name'], detail: item['detail'] },
    on_duplicate: 'name = EXCLUDED.name, detail = EXCLUDED.detail',
    unique_by: :fk
  )
end

Обратите внимание, что существует также upsert_all, который позволит вам вставлять или обновлять большие пакеты записей всего за один запрос. Это немного зависит от того, как реализован get_data и как его использовать. Но общая идея состоит в том, чтобы загружать записи пакетами, а затем upsert_all каждый пакет в одном запросе.

Другой, еще более эффективный способ, безусловно, — сделать это непосредственно в SQL с помощью одного запроса и, вероятно, снова использовать команду SQL UPSERT. Как это будет выглядеть, зависит от точной схемы используемых таблиц базы данных.

Другие вопросы по теме