Использование подзапроса вместо имени таблицы в операторе обновления Oracle - PullRequest
13 голосов
/ 23 октября 2008

Мне нужно написать оператор обновления, который использовал несколько таблиц, чтобы определить, какие строки обновлять, поскольку в Oracle несколько таблиц не допускаются. Следующий запрос вернет ошибку «ORA-00971: отсутствует ключевое слово SET»

UPDATE
  TABLE1 a,
  TABLE2 b
SET
  a.COL1 = 'VALUE'
WHERE
  a.FK = b.PK
  AND b.COL2 IN ('SET OF VALUES')

Просматривая синтаксис оператора UPDATE для oracle, я обнаружил следующую ссылку , которая показывает, что вы можете использовать подзапрос вместо имени таблицы.

Когда я пытался написать запрос, подобный этому, я получил «ORA-01779: Невозможно изменить столбец, который сопоставляется с таблицей без сохранения ключа»

UPDATE
  (
    SELECT
      a.COL1
    FROM
      TABLE1 a,
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
  ) update_tbl
SET
  update_tbl.COL1 = 'VALUE'

Я переписал запрос (см. Ниже), используя вместо этого оператор EXISTS, и он отлично работает, но все же хотел бы знать, как это делается.

UPDATE
  TABLE1 update_tbl
SET
  update_tbl.COL1 = 'VALUE'
WHERE
  EXISTS (
    SELECT
      1
    FROM
      TABLE1 a
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
      AND update_tbl.PK = a.PK
  )

Спасибо! -Nate

Ответы [ 6 ]

7 голосов
/ 24 октября 2008

Я считаю, что хороший, быстрый и последовательный способ превратить оператор SELECT в ОБНОВЛЕНИЕ - это сделать обновление на основе ROWID.

UPDATE
  TABLE1
SET
  COL1 = 'VALUE'
WHERE
  ROWID in
    (
    SELECT
      a.rowid
    FROM
      TABLE1 a,
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
    )

Итак, ваш внутренний запрос определяет строки для обновления.

7 голосов
/ 23 октября 2008

Другой вариант:

UPDATE TABLE1 a
SET a.COL1 = 'VALUE'
WHERE a.FK IN
( SELECT b.PK FROM TABLE2 b
  WHERE b.COL2 IN ('SET OF VALUES')
)

Ваш второй пример будет работать, если (а) представление включает объявленный PK таблицы TABLE1:

UPDATE
  (
    SELECT
      a.COL1, a.PKCOL
    FROM
      TABLE1 a,
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
  ) update_tbl
SET
  update_tbl.COL1 = 'VALUE'

... и (b) TABLE1.FK был объявленным внешним ключом для TABLE2

(Под объявленным я подразумеваю, что ограничение существует и включено).

3 голосов
/ 23 октября 2008

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

В связанной заметке вы также можете использовать скобки, чтобы использовать 2 или более полей в операторе IN, например:

UPDATE
  TABLE1 update_tbl
SET
  update_tbl.COL1 = 'VALUE'
WHERE
  (update_tbl.PK1, update_tbl.pk2) in(
                      select some_field1, some_field2
                      from some_table st
                      where st.some_fields = 'some conditions'
                      );
2 голосов
/ 23 октября 2008

Когда вы выполняете обновление, вы, очевидно, можете только сказать системе обновить значение до единого нового значения - указание обновить «X» на «Y» и «Z» не имеет смысла. Таким образом, когда вы основываете обновление на результате встроенного просмотра, Oracle выполняет проверку на наличие достаточных ограничений, чтобы предотвратить возможность изменения измененного столбца дважды.

В вашем случае я ожидаю, что TABLE2.PK на самом деле не является объявленным первичным ключом. Если вы поставите первичное или уникальное ограничение на этот столбец, тогда вам будет хорошо.

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

Одним из способов решения этой проблемы является использование оператора MERGE, который не подлежит тому же тесту.

1 голос
/ 27 мая 2011

Я нашел то, что мне было нужно здесь: Полезные команды SQL

Мне нужно было обновить одну таблицу с результатом объединения
Я пробовал вышеупомянутые решения без успеха: (

Вот выдержка из страницы, которую я указал выше
Используя курсоры, я смог успешно решить задачу
Я уверен, что есть другое решение, но это сработало так ...

DECLARE

 /* Output variables to hold the result of the query: */
 a T1.e%TYPE;
 b T2.f%TYPE;
 c T2.g%TYPE;

 /* Cursor declaration: */
 CURSOR T1Cursor IS
   SELECT T1.e, T2.f, T2.g
   FROM T1, T2
   WHERE T1.id = T2.id AND T1.e <> T2.f

 FOR UPDATE;

BEGIN

  OPEN T1Cursor;

  LOOP

    /* Retrieve each row of the result of the above query
    into PL/SQL variables: */
    FETCH T1Cursor INTO a, b;

    /* If there are no more rows to fetch, exit the loop: */
    EXIT WHEN T1Cursor%NOTFOUND;

    /* Delete the current tuple: */
    DELETE FROM T1 WHERE CURRENT OF T1Cursor;

    /* Insert the reverse tuple: */
    INSERT INTO T1 VALUES(b, a);

    /* Here is my stuff using the variables to update my table */
    UPDATE T2
    SET T2.f = a
    WHERE T2.id = c;

  END LOOP;

  /* Free cursor used by the query. */
  CLOSE T1Cursor;

END;
.
run;


Примечание: не забудьте зафиксировать; -)

0 голосов
/ 23 октября 2008

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

По сути, это означает, что вам нужно включить первичный ключ таблицы назначения в этот запрос. Возможно, вы также сможете использовать некоторые другие уникальные индексные поля, но я не могу гарантировать, что СУБД Oracle достаточно умна, чтобы допустить это.

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