Я пишу на 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
Что из того или другого будет лучше?
Но, возможно, загрузка данных во временную таблицу потребует такого же количества ресурсов?
Первая версия была бы лучше, поскольку она выполняет только два запроса: один для поиска потенциально существующей записи, а другой для обновления существующей или создания новой записи.
Вторая версия будет выполнять три запроса к базе данных, когда ни одна существующая запись не найдена: один пытается найти запись, второй — для создания новых записей и третий — для обновления вновь созданной записи.
Но вместо этого я предлагаю использовать 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
. Как это будет выглядеть, зависит от точной схемы используемых таблиц базы данных.
Ни один. Загрузите новые данные во временную таблицу и сделайте это двумя запросами к базе данных. Это база данных, и она гораздо лучше справляется с данными, чем Ruby.