Необходимо вставить строку в таблицу, если первичные ключи не существуют, в противном случае обновить существующую строку

#sql #oracle

#sql #Oracle

Вопрос:

Я пытаюсь запустить набор SQL, который проверит, существует ли в таблице составное значение первичного ключа. если она существует, то обновите таблицу, в противном случае вставьте новые значения. Я думал, что оператор merge — это способ сделать это. Но слияние, которое я написал, приводит к объединению 0 строк, когда я его запускаю. Я ожидал бы, что условие NOT MATCHED возникнет в следующем примере, но это не так. Вот сценарий для его тестирования и СЛИЯНИЯ, которое я пытаюсь:

 CREATE TABLE curformat (storeent_id INTEGER,
                        setccurr VARCHAR2(3),
                        roundingmultiple INTEGER,
                        numbrusg_id INTEGER,
                        roundingmethod VARCHAR2(1),
                        decimalplaces INTEGER,
                        MINAPPROVEAMOUNT NUMBER,
                        optcounter INTEGER);

INSERT INTO curformat VALUES(-1, 'USD', 1, -1, 'R', 2, NULL, NULL);

----------------------------------------------------------------------

MERGE INTO curformat cf
USING (SELECT *
         FROM curformat
        WHERE storeent_id = 10201
          AND setccurr = 'USD'
          AND numbrusg_id = -1
      ) current_curformat
      ON (cf.storeent_id = current_curformat.storeent_id
          AND cf.setccurr = current_curformat.setccurr
          AND cf.numbrusg_id = current_curformat.numbrusg_id
         )
 WHEN MATCHED THEN UPDATE
      SET roundingmultiple = 1,
          roundingmethod = 'R',
          decimalplaces = 2,
          minapproveamount = NULL
 WHEN NOT MATCHED THEN INSERT (storeent_id, setccurr, roundingmultiple, numbrusg_id, roundingmethod, decimalplaces, minapproveamount)
      VALUES (10201, 'USD', 1, -1, 'R', 2, NULL
             );
 

В этом случае, поскольку комбинация

 WHERE storeent_id = 10201
AND setccurr = 'USD'
AND numbrusg_id = -1
 

неверно, я бы подумал, что КОГДА НЕ БУДЕТ ВЫПОЛНЕНО СОВПАДЕНИЕ.
Кто-нибудь может помочь мне понять, чего я здесь не понимаю? Я предполагаю, что я неправильно использую оператор СЛИЯНИЯ?

Ответ №1:

sstan уже объяснил, почему ваша версия MERGE не будет работать. Когда вы хотите обновить / вставить в curformat , вы не извлекаете строки из этой таблицы. У вас есть «новые» строки, которые вы либо хотите вставить в свою таблицу, либо хотите использовать для обновления.

Итак, на самом деле исправить ваше заявление о слиянии легко. Обратите пристальное внимание на то, что такое «дельта» ( current_format используется в предложении USING). В данном случае это строка, созданная «из воздуха» (что в Oracle означает «созданная из dual таблицы»).

 MERGE INTO curformat cf
USING (SELECT 10201 as storeent_id,
              'USD' as setccurr,
              -1    as numbrusg_id
       FROM   dual
      ) current_curformat
      ON (cf.storeent_id = current_curformat.storeent_id
          AND cf.setccurr = current_curformat.setccurr
          AND cf.numbrusg_id = current_curformat.numbrusg_id
         )
 WHEN MATCHED THEN UPDATE
      SET roundingmultiple = 1,
          roundingmethod = 'R',
          decimalplaces = 2,
          minapproveamount = NULL
 WHEN NOT MATCHED THEN INSERT (storeent_id, setccurr, roundingmultiple, numbrusg_id,
                                     roundingmethod, decimalplaces, minapproveamount)
      VALUES (10201, 'USD', 1, -1, 'R', 2, NULL
             );
 

Это сделает именно то, что вы хотите.

Однако здесь мы жестко запрограммировали значения, которые будут вставляться, когда совпадений нет. Это очень плохая практика. Правильный способ сделать это (просто показать последние две строки, которые являются единственными измененными):

       VALUES (current_format.storeent_id, current_format.setccurr,
                                 1, current_format.numbrusg_id, 'R', 2, NULL
             );
 

Ответ №2:

Похоже, вы смотрите на это в обратном направлении. Если мы возьмем базовый merge синтаксис:

 merge into <target> using <source> ...
 

… идея заключается в том, что у вас есть набор строк, определенных как the source , которые могут совпадать или не совпадать с некоторыми строками в вашем target , и для которых вы определяете логику того, как объединить source в target . Но для того, чтобы merge оператор вообще выполнял какую-либо работу, source он не может быть пустым.

В вашем случае source определяется как:

 SELECT *
  FROM curformat
 WHERE storeent_id = 10201
   AND setccurr = 'USD'
   AND numbrusg_id = -1
 

… который не возвращает строк, поэтому делать нечего. target хотя может быть пустым, но если source пусто, объединять не во target что.