2010-06-07 3 views
0

я вставив в таблицу значенияесли существует, то обновление, иначе вставить новую запись

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

до сих пор у меня есть этот код:

INSERT INTO table_name 
VALUES (value1, value2, value3,...) where pk="some_id"; 

, но мне нужно что-то вроде этого

if not pk="some_id" exists then INSERT INTO table_name 
    VALUES (value1, value2, value3,...) where pk="some_id"; else update table_name where pk="some_id" 

, что будет правильный синтаксис SQL для этого?

обратите внимание, что я использую SQL Access и что я предполагаю, что это может быть сочетание VBA и SQL

+3

Это может помочь вам найти, что это часто называют «upsert». – DOK

ответ

1

Первые строки обновления, которые соответствуют таблице импорта и главной таблице.

UPDATE table_name AS m 
    INNER JOIN tblImport AS i 
    ON m.pk = i.pk 
SET 
    m.field2 = i.field2, 
    m.field3 = i.field3, 
    m.field4 = i.field4; 

Затем добавьте любые импортированные записи, которых нет в основной таблице.

INSERT INTO table_name (
    pk, 
    field2, 
    field3, 
    field4) 
SELECT 
    i.pk, 
    i.field2, 
    i.field3, 
    i.field4 
FROM 
    tblImport AS i 
    LEFT JOIN table_name AS m 
    ON i.pk = m.pk 
WHERE 
    (((m.pk) Is Null)); 
+0

эй ты уверен (((m.pk) is Null)); синтаксис доступа? Я думал, что вам нужно сделать isnull (что-то) »???? –

+2

Вы можете использовать функцию IsNull. Однако механизм базы данных Access 'поддерживает NULL и НЕ ЯВЛЯЕТСЯ в SQL-запросах. Попробуйте! (Это сработало для меня.) – HansUp

1

Это можно легко сделать с набора записей. Тогда код будет выглядеть так (для набора записей ADODB):

myRecordset.find .... 
if myRecordset.EOF then 
    myRecordset.addNew 
endif 
.... 
myRecordset.fields(...) = ... 
.... 
myRecordset.update 
+0

это похоже на слишком сложное решение –

+0

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

0

Я отправил о моем подходе к этой проблеме много, много раз в самых разных форумах, но я просто резюмировать основную структуру подхода, который я использую. Однако нет способа сделать это за один шаг.

  1. обновить существующие записи из внешнего источника данных.

  2. Вставьте записи, которые еще не существуют.

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

Задача № 2 довольно тривиальна, просто внешнее соединение для записей, которые еще не существуют.

Можно использовать грубую силу для # 1, записывая инструкцию UPDATE с SET для каждого поля, отличного от первичного ключа, но я считаю, что это беспорядочно и ненужно. Кроме того, поскольку у меня много реплицированных приложений, я не могу этого сделать, поскольку это приведет к ложным конфликтам (когда поле обновляется до того же значения, с которого оно начиналось).

Итак, для этой цели я использую DAO и записываю оператор SQL на лету для обновления COLUMN-BY-COLUMN. Основная структура что-то вроде этого:

Dim db As DAO.Database 
    Dim rs As DAO.Recordset 
    Dim fld As DAO.Field 
    Dim strField As String 
    Dim strSet As String 
    Dim strWhere As String 
    Dim strSQL As String 

    Set db = CurrentDB 
    Set rs = db.OpenRecordset("DestinationTable") 
    For Each fld in rs.Fields 
    strField = fld.Name 
    If strField <> "PKField" Then 
     strSet = "DestinationTable." & strField & " = ExternalTable." & strField 
     strWhere = "Nz(DestinationTable." & strField & ",'') = Nz(ExternalTable." & strField & ", '')" 
     strSQL = "UPDATE DestinationTable " 
     strSQL = strSQL & " SET " & strSet 
     strSQL = strSQL & " WHERE " & strWhere 
     db.Execute strSQL, dbFailOnError 
     Debug.Print strField & ": " & db.RecordsAffected 
    End If 
    Next fld 

Теперь сложная часть обработки числовой против даты против строковых полей, так что вы должны иметь определенную логику, чтобы написать пункты, где использовать правильные кавычки и другие разделители в зависимости от типа поля. Вместо того, чтобы проверить тип поля, я обычно просто использовать СЛУЧАЙ ВЫБРАТЬ, как это, делает строку поля по умолчанию:

Dim strValueIfNull As String 
    Select Case strField 
    Case "DateField1", "DateField2", "NumericField2", "NumericField2", "NumericField3" 
     strValueIfNull = "0" 
    Case Else 
     strValueIfNull = "''" 
     strWhere = "Nz(DestinationTable." & strField & ", '') = Nz(ExternalTable." & strField & ", '')" 
    End Select 
    strWhere = "Nz(DestinationTable." & strField & ", " & strValueIfNull & ") = Nz(ExternalTable." & strField & ", " & strValueIfNull & ")" 

я мог бы иметь детали там не так, но вы получите идею, я думаю.

Это означает, что вы будете запускать столько обновлений SQL, сколько обновляемых полей, и что вы будете обновлять записи, требующие обновления. Если вы также запечатываете свои записи с «последней обновленной» датой, вы должны сделать это в UPDATE SQL, и вы только захотите сделать это в записях, которые действительно имели разные значения.

+0

Если вы столкнулись с проблемой перебора всех полей, почему бы просто не установить значение для каждого поля, а затем просто использовать одно заявление на обновление в наборе записей вместо выполнения нескольких обновлений sql-записей? – JeffO

+1

Я объяснил, что дважды: сначала я часто использовал репликацию Jet, и это вызвало бы ложные конфликты. Во-вторых, если вам нужно штамповать записи с помощью LAST UPDATED date/time, вы хотите сделать это только для записей, которые * необходимы * для обновления , –

Смежные вопросы