Я думаю, у вас есть два варианта: (1) UPSERT или (2) параметризованных запросов.
Первый имеет преимущество в скорости (часто в зависимости от СУБД) при за счет СУБД-специфика c диалект SQL и небольшая сложность. Второй имеет преимущество простоты, но может занять больше времени, если у вас много строк.
1. UPSERT
Шаги: создать временную таблицу (см. Примечания); загрузить данные; выполните операцию обновления с разрешением конфликтов.
Я использую temp_table_997
в качестве временной таблицы здесь, но есть много способов иметь дело с временными таблицами, чтобы вы случайно не оставили их. Я обнаружил, что успех зависит от СУБД, поэтому я оставлю это на усмотрение читателя.
DBI::dbExecute(con, "
CREATE TABLE temp_table_997 AS
SELECT oseast1m, osnrth1m, Postcode FROM ons LIMIT 0") # [1,2]
DBI::dbWriteTable(con, "temp_table_997", result[,c("east", "west", "postcode")]) # [3,4]
DBI::dbExecute(con, "
INSERT INTO ons (oseast1m, osnrth1m)
SELECT oseast1m, osnrth1m
FROM temp_table_997
ON CONFLICT ( Postcode ) DO
UPDATE SET oseast1m=EXCLUDED.oseast1m, osnrth1m=EXCLUDED.osnrth1m
") # [5]
Примечания:
Другие ответы / статьи которые используют эту технику, могут использовать select * ...
, хотя лучшие практики это не одобряют Как правило, лучше указывать в таблице и только необходимые поля.
Я использую create table ... as select ...
, чтобы типы столбцов были сохранены. Особенно со всеми различными типами чисел (float, integer, bigint, smallint, даже "bit") и другими полями ... и с тем фактом, что R не go до этого уровня детализации, я считаю, что лучше всего быть явным при загрузке данных. Использование этого метода гарантирует, что тип, используемый в целевой таблице, является тем, что фактически используется. Это может быть необязательно в некоторых СУБД, но я не думаю, что это больно.
Как и в примечании 1, вам, вероятно, следует загружать только необходимые столбцы, включая поле идентификации и поля с обновлениями; если есть поля, которые никогда не обновляются, нет необходимости тратить пропускную способность, а для больших наборов данных это может оказать значительное влияние на время загрузки. (Например, results[,c("Postcode",...)]
).
Хотя инструменты и базы данных, которые я использую, достаточно умны, чтобы справляться со столбцами не по порядку, я не знаю, что это так все СУБД, поэтому, вероятно, лучше и проще сохранять порядок столбцов одинаковым.
Я предполагаю, что Postcode
совершенно уникален в таблице. Это не обязательно должен быть ключ в таблице (это отдельное обсуждение), но предполагается, что это поле однозначно определяет строки. Если нет, то приведенный выше запрос может повлиять на гораздо большее количество строк, чем предполагалось.
Это работает (для меня) на SQLite и Postgres, но язык для других СУБД может быть таким же или очень похожий.
Для SQL Server вам нужен немного другой запрос. (Поймите, что CREATE ... SELECT ... LIMIT 0
выше должно быть CREATE ... SELECT TOP 0 ...
, из-за SQL диалекта Сервера.)
DBI::dbExecute(con, "
DECLARE @dummy int;
MERGE ons WITH (HOLDLOCK) as tgt
USING (SELECT ... FROM temp_table_997) as src
ON ( tgt.Postcode )
WHEN MATCHED THEN UPDATE SET tgt.oseast1m=src.oseast1m, tgt.osnrth1m=src.osnrth1m
WHEN NOT MATCHED THEN INSERT (oseast1m, oseast1m) values (src.oseast1m, src.oseast1m)
")
Если вы используете этот метод, не забудьте очистить:
DBI::dbExecute(con, "drop table temp_table_997")
2. Связывание (параметризованные запросы)
Если это всего лишь несколько строк или вы действительно не видите временных ограничений, делающих это таким образом, попробуйте это.
res <- DBI::dbSendStatement(con, "
UPDATE ons
SET ons.oseast1m=?, ons.osnrth1m=?
WHERE ons.Postcode=?")
DBI::dbBind(res, result[,c("east", "west", "postcode")]) # order and number of columns is important
DBI::dbGetRowsAffected(res)
Метод указания параметров (?
выше) зависит исключительно от СУБД, а не от пакетов DBI
или odbc
; вам нужно найти конкретный метод c для вашего. Это может быть ?
, ?name
, $name
или :name
; возможно, существуют и другие.
(Я признаю, что это может быть столь же эффективно. Несколько лет я пробовал несколько методов go, и из-за используемого драйвера или из-за одной версии DBI
или даже мое недопонимание вещей ... возможно, что это так же эффективно, как упор. Я не собираюсь проверять это сейчас, так как разница может иметь отношение только к большим наборам данных. YMMV.)