Обновление оператора с внутренним соединением в Oracle


298

У меня есть запрос, который отлично работает в MySQL, но когда я запускаю его в Oracle, я получаю следующую ошибку:

Ошибка SQL: ORA-00933: команда SQL неправильно завершилась
00933. 00000 - «Команда SQL неправильно завершена»

Запрос:

UPDATE table1
INNER JOIN table2 ON table1.value = table2.DESC
SET table1.value = table2.CODE
WHERE table1.UPDATETYPE='blah';

Когда я попытался настроить table2 в Oracle для проверки своего ответа, я обнаружил, что Oracle отклонил DESC как имя столбца.
Янек Богуцки

Извините, я только что сократил исходное имя столбца, чтобы описать его явно не в db
user169743

Ответы:


412

Этот синтаксис недопустим в Oracle. Ты можешь сделать это:

UPDATE table1 SET table1.value = (SELECT table2.CODE
                                  FROM table2 
                                  WHERE table1.value = table2.DESC)
WHERE table1.UPDATETYPE='blah'
AND EXISTS (SELECT table2.CODE
            FROM table2 
            WHERE table1.value = table2.DESC);

Или вы могли бы сделать это:

UPDATE 
(SELECT table1.value as OLD, table2.CODE as NEW
 FROM table1
 INNER JOIN table2
 ON table1.value = table2.DESC
 WHERE table1.UPDATETYPE='blah'
) t
SET t.OLD = t.NEW

Это зависит от того, считается ли встроенное представление обновляемым в Oracle ( возможность обновления для второго оператора зависит от некоторых правил, перечисленных здесь ).


5
Я сделал второй пример, но мне пришлось добавить псевдонимы к именам столбцов в select, а затем ссылаться на них по их именам в SET, но это сработало, спасибо
Gustavo Rubio

41
Преимущество второго примера состоит в том, что вы можете протестировать SQL перед выполнением обновления.
Даниэль Рейс

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

5
Второй работал на меня :). Оракул - одно сильное, но странное животное: /
elrado

10
Объяснение требования сохранения ключей для обновляемых объединений: asktom.oracle.com/pls/asktom/…
Вадим

202

Использовать это:

MERGE
INTO    table1 trg
USING   (
        SELECT  t1.rowid AS rid, t2.code
        FROM    table1 t1
        JOIN    table2 t2
        ON      table1.value = table2.DESC
        WHERE   table1.UPDATETYPE='blah'
        ) src
ON      (trg.rowid = src.rid)
WHEN MATCHED THEN UPDATE
    SET trg.value = code;

2
Работает отлично, но Oracle потребовал, чтобы я сказал merge into table 1 tи так далее.
Майкл-О

1
Поздно на вечеринку, но это все еще хорошая нить. Мне нужно знать, правда ... я что-то пропустил? Мастер стол, "таблица1". В ИСПОЛЬЗОВАНИИ table1 с псевдонимом t1. Таблица 2, с псевдонимом t2, но в ON, ссылки ...? Внешняя таблица1 - не t1 - это ссылка на внешнюю таблицу или тип? Таблица 2? Не т2? Je suis смущен. Фанат лучших псевдонимов ...
Марк

Просто отметьте, что если в вашем ключе (trg.rowid или src.rid) есть один дублированный элемент, то в этом пункте выдается
Henrique

@Marc In ON, trgэто псевдоним для главной таблицы table1(«внешняя» таблица по вашей логике) и srcссылается на USINGгруппу («внутренняя таблица» по вашей логике). Но да, возможно, на него можно было бы ссылаться лучше, но я был в состоянии следовать за этим.
vapcguy

1
@supernova: ответ Тони обновляет встроенное представление. В некоторых случаях это может работать, но представление должно быть «сохранено ключом» (каждая объединенная таблица должна быть объединена равенством по первичному ключу или иным уникальным наборам полей). Это гарантирует, что каждая запись в целевой таблице вносит вклад не более чем в одну запись в результирующем наборе строк, и, следовательно, каждая запись в целевой таблице обновляется не более одного раза.
Кассной

25

MERGEс WHEREпунктом:

MERGE into table1
USING table2
ON (table1.id = table2.id)
WHEN MATCHED THEN UPDATE SET table1.startdate = table2.start_date
WHERE table1.startdate > table2.start_date;

Вы нуждаетесь в WHEREпредложении, потому что столбцы, на которые есть ссылки в ONпредложении, не могут быть обновлены.


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


11

Не используйте некоторые ответы выше.

Некоторые предлагают использовать вложенный SELECT, не делайте этого, это мучительно медленно. Если вам нужно обновить много записей, используйте объединение, так что-то вроде:

update (select bonus 
        from employee_bonus b 
        inner join employees e on b.employee_id = e.employee_id 
        where e.bonus_eligible = 'N') t
set t.bonus = 0;

Смотрите эту ссылку для более подробной информации. http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx .

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


7

Как указано здесь , общий синтаксис для первого решения, предложенного Тони Эндрюсом:

update some_table s
set   (s.col1, s.col2) = (select x.col1, x.col2
                          from   other_table x
                          where  x.key_value = s.key_value
                         )
where exists             (select 1
                          from   other_table x
                          where  x.key_value = s.key_value
                         )

Я думаю, что это интересно, особенно если вы хотите обновить более одного поля.


Это не работает для меня. Он обновляет всю таблицу.
Наташа Таварес

3

Этот следующий синтаксис работает для меня.

UPDATE
(SELECT A.utl_id,
    b.utl1_id
    FROM trb_pi_joint A
    JOIN trb_tpr B
    ON A.tp_id=B.tp_id Where A.pij_type=2 and a.utl_id is null
)
SET utl_id=utl1_id;

@JimGarrison Пожалуйста, отредактируйте этот ответ, чтобы я мог удалить свое пониженное голосование .... Я пытался использовать этот синтаксис, но он не обновлял мою таблицу. Я выяснил, почему - мой SETвыполнял REPLACEи я пытался очистить определенную строку в столбце - оказалось, что Oracle обрабатывает ''как ноль, и это поле не может быть обнулено. Я думал, что синтаксис просто обновляет временную таблицу вместо реальной, но я ошибся.
vapcguy

2

Используя описание вместо desc для table2,

update
  table1
set
  value = (select code from table2 where description = table1.value)
where
  exists (select 1 from table2 where description = table1.value)
  and
  table1.updatetype = 'blah'
;

почему вы хотите запустить два отдельных запроса на table2
Jitendra Vispute

2

Отлично работает оракул

merge into table1 t1
using (select * from table2) t2
on (t1.empid = t2.empid)
when matched then update set t1.salary = t2.salary

Можно установить несколько свойств, добавив запятую в конце этого. Мне нужно было сделать t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastNameтаблицу после сопоставления с ней в столбце «Имя пользователя» ( t1.UserName = t2.UserName), чтобы получить их имя из таблицы с именем UserInfo ( select * from UserInfo) t2). База данных была такой, где она использовала UserName в качестве первичного ключа для UserInfo везде вместо непосредственного размещения FirstName и LastName в таблице. Это исправлено!
vapcguy

Этот ответ ничего не добавляет к ответу, уже предоставленному Quassnoi за пять лет до вашего.
Фураж

0
UPDATE table1 t1
SET t1.value = 
    (select t2.CODE from table2 t2 
     where t1.value = t2.DESC) 
WHERE t1.UPDATETYPE='blah';

0
UPDATE IP_ADMISSION_REQUEST ip1
SET IP1.WRIST_BAND_PRINT_STATUS=0
WHERE IP1.IP_ADM_REQ_ID        =
  (SELECT IP.IP_ADM_REQ_ID
  FROM IP_ADMISSION_REQUEST ip
  INNER JOIN VISIT v
  ON ip.ip_visit_id=v.visit_id
  AND v.pat_id     =3702
  ); `enter code here`

0

Просто для полноты и из-за того, что мы говорим об Oracle, это тоже может быть сделано:

declare
begin
  for sel in (
    select table2.code, table2.desc
    from table1
    join table2 on table1.value = table2.desc
    where table1.updatetype = 'blah'
  ) loop
    update table1 
    set table1.value = sel.code
    where table1.updatetype = 'blah' and table1.value = sel.desc;    
  end loop;
end;
/

1
Это может сделать это, но это самый медленный путь.
APC

-1
UPDATE (SELECT T.FIELD A, S.FIELD B
FROM TABLE_T T INNER JOIN TABLE_S S
ON T.ID = S.ID)
SET B = A;

A и B - поля псевдонимов, вам не нужно указывать таблицу.


1
Привет, Дэн. Вы пишете на довольно старый вопрос, на который уже есть очень хорошие ответы. Можете ли вы объяснить, когда ваш вопрос предпочтительнее других решений?
Ноэль Видмер

1
Конечно, я видел ответ, где b = a были написаны с указанием имени таблицы (table1.B = table2.A), но указывать таблицу не нужно.
Дэн Андерсон

Вы фактически обновляете поля из представления, которые отображаются в таблицу. Если бы внутреннее представление было псевдонимом h, то «самодокументируемой» версией было бы «set hb = ha».
sf_jeff

-4
update table1  a 
   set a.col1='Y' 
 where exists(select 1 
                from table2 b
               where a.col1=b.col1 
                 and a.col2=b.col2
             )
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.