Апсертинг в MS-доступе - PullRequest
6 голосов
/ 01 июня 2011

Мне нужно написать SQL-запрос для MS-Access 2000, чтобы строка обновлялась, если она существует, но вставлялась, если ее нет. (Я верю, что это называется "upsert")

т.е.

Если строка существует ...

UPDATE Table1 SET (...) WHERE Column1='SomeValue'

Если его не существует ...

INSERT INTO Table1 VALUES (...)

Можно ли это сделать одним запросом?

Ответы [ 5 ]

7 голосов
/ 03 мая 2016

Вы можете имитировать переход в Access, используя запрос UPDATE с LEFT JOIN.

update b
left join a on b.id=a.id
set a.f1=b.f1
, a.f2=b.f2
, a.f3=b.f3

см .: https://www.experts -exchange.com / questions / 28713136 / Можно ли использовать SQL-MERGE-оператор-в-запросе-в-доступе-2010.html

5 голосов
/ 22 октября 2017

"upsert" возможен, если таблицы имеют уникальный ключ.

Этот старый совет от Smart Access - один из моих любимых:

Обновление и добавление записей одним запросом

Алан Биггс

Знаете ли вы, что вы можете использовать запрос на обновление в Access для обоих обновлений? и добавлять записи одновременно? Это полезно, если у вас есть два версии таблицы, tblOld и tblNew, и вы хотите интегрировать меняется с tblNew на tblOld.

Выполните следующие действия:

Создайте запрос на обновление и добавьте две таблицы. Присоединяйтесь к двум столам перетаскивая ключевое поле tblNew в соответствующее поле tblOld.

  1. Дважды щелкните по связи и выберите вариант объединения, который включает в себя все записи из tblNew и только те, которые соответствуют из tblOld.

  2. Выберите все поля из tblOld и перетащите их в сетку QBE.

  3. Для каждого поля в ячейке Обновить до введите tblNew.FieldName, где FieldName соответствует имени поля tblOld.

  4. Выберите Свойства запроса в меню Вид и измените Уникальные записи на Ложные. (Это отключает опцию DISTINCTROW в SQL Посмотреть. Если вы оставите это, вы получите только одну пустую запись в вашем результаты, но вы хотите добавить одну пустую запись для каждой новой записи до tblOld.)

  5. Запустите запрос, и вы увидите, что изменения в tblNew теперь находятся в tblOld.

Это добавит только те записи в tblOld, которые были добавлены в tblNew. Записи в tblOld, которых нет в tblNew, все равно останутся в tblOld.

5 голосов
/ 01 июня 2011

Если предположить уникальный индекс для Column1 , вы можете использовать выражение DCount, чтобы определить, есть ли у вас ноль или одна строка с Column1 = 'SomeValue' . Затем INSERT или UPDATE на основе этого количества.

If DCount("*", "Table1", "Column1 = 'SomeValue'") = 0 Then
    Debug.Print "do INSERT"
Else
    Debug.Print "do UPDATE"
End If

Я предпочитаю этот подход, чтобы сначала попытаться INSERT, перехватить ошибку нарушения ключа 3022 и выполнить UPDATE в ответ на ошибку. Однако я не могу требовать огромных выгод от моего подхода. Если ваша таблица содержит поле автонумерации, исключение неудачного INSERT не позволит вам излишне увеличивать следующее значение автонумерации. Я также могу избежать построения строки INSERT, когда она не нужна. Access Cookbook сказал мне, что конкатенация строк - это умеренно дорогая операция в VBA, поэтому я ищу возможности избежать создания строк, если они действительно не нужны. Этот подход также позволит избежать создания блокировки для ненужных INSERT.

Однако ни одна из этих причин не может быть очень убедительной для вас. И, честно говоря, я думаю, что мое предпочтение в этом случае может заключаться в том, что мне кажется правильным. Я согласен с этим комментарием @ David-W-Fenton к предыдущему вопросу переполнения стека : "Лучше написать свой SQL, чтобы не пытаться добавлять значения которые уже существуют, то есть не допускают возникновения ошибки в первую очередь, а не зависят от механизма базы данных, который спасет вас от вас. "

4 голосов
/ 01 июня 2011

Обычно я сначала запускаю оператор вставки, а затем проверяю, произошла ли ошибка 3022, которая указывает, что строка уже существует.Итак, что-то вроде этого:

On Error Resume Next
CurrentDb.Execute "INSERT INTO Table1 (Fields) VALUES (Data)", dbFailOnError
If Err.Number = 3022 Then
    Err.Clear        
    CurrentDb.Execute "UPDATE Table1 SET (Fields = Values) WHERE Column1 = 'SomeValue'", dbFailOnError
ElseIf Err.Number <> 0 Then
    'Handle the error here
    Err.Clear
End If

Edit1:
Я хочу упомянуть, что то, что я опубликовал здесь, является очень распространенным решением, но вы должны знать, что планирование ошибоки использование их как части нормального потока вашей программы обычно считается плохой идеей, особенно если есть другие способы достижения тех же результатов.Спасибо RolandTumble за указание на это.

3 голосов
/ 19 апреля 2013

Вам не нужно отлавливать ошибку.Вместо этого просто запустите оператор INSERT и затем проверьте

CurrentDb.RecordsAffected

Это будет либо 1, либо 0, в зависимости от этого.

Примечание: не рекомендуется выполнять с CurrentDB.Лучше захватить базу данных в локальную переменную:

Dim db As DAO.Database
Set db = CurrentDb
db.Execute(INSERT...)
If db.RecordsAffected = 0 Then
  db.Execute(UPDATE...)
End If
...