Oracle SQL: оновіть таблицю з даними з іншої таблиці


251

Таблиця 1:

id    name    desc
-----------------------
1     a       abc
2     b       def
3     c       adf

Таблиця 2:

id    name    desc
-----------------------
1     x       123
2     y       345

У Oracle SQL, як я запустити оновлення SQL запит , який може оновити таблицю 1 з таблицею 2 - х nameі з descвикористанням тих же id? Таким чином, я отримав би кінцевий результат

Таблиця 1:

id    name    desc
-----------------------
1     x       123
2     y       345
3     c       adf

Питання береться з оновлення однієї таблиці з даними іншої , але спеціально для Oracle SQL.



Потрібно повернутися до іншого питання, скасувати його, і конкретно зазначити, що вам потрібен синтаксис Oracle PLSQL.
p.campbell

3
@ p.campbell, це не моє запитання ...
Muhd

1
О Я бачу. Отже, ви скопіювали тіло запитання, але його змінили, щоб включити біт Oracle.
p.campbell

2
Так. І це, мабуть, не найкращий приклад, оскільки "desc" - це зарезервоване слово, але так добре.
Muhd

Відповіді:


512

Це називається корельованим оновленням

UPDATE table1 t1
   SET (name, desc) = (SELECT t2.name, t2.desc
                         FROM table2 t2
                        WHERE t1.id = t2.id)
 WHERE EXISTS (
    SELECT 1
      FROM table2 t2
     WHERE t1.id = t2.id )

Якщо припустити результати з'єднання у збереженому ключі, ви також можете

UPDATE (SELECT t1.id, 
               t1.name name1,
               t1.desc desc1,
               t2.name name2,
               t2.desc desc2
          FROM table1 t1,
               table2 t2
         WHERE t1.id = t2.id)
   SET name1 = name2,
       desc1 = desc2

8
У вашому першому прикладі коду: Чи потрібний зовнішній пункт WHERE для правильних результатів? Або ви використовуєте його лише для прискорення запиту?
Mathias Bader

41
@totoro - У першому прикладі WHERE EXISTSзапобігає оновленню рядка, t1якщо у ньому немає відповідного рядка t2. Без нього кожен рядок у t1буде оновлюватися, а значення встановлюватимуться, NULLякщо немає відповідного рядка в t2. Це, як правило, не те, що ви хочете, щоб це сталося, тому WHERE EXISTSзагалом потрібно.
Джастін Печера

3
Варто додати, що SELECT ... FROM t2 обов’язково вийде унікальний ряд. Це означає, що вам потрібно вибрати всі поля, які містять унікальний ключ - неповторного первинного ключа недостатньо. Без унікальності ви зводиться до чогось подібного до циклу @ PaulKarr - і якщо немає унікальної кореляції, то для кожного вихідного рядка може бути оновлено більше ніж один цільовий рядок.
Ендрю Ліч

2
Пояснення щодо збереженої у ключі вимоги до оновлених
Вадим

1
@RachitSharma - Це означає, що ваш підзапит (запит від table2) повертає кілька рядків для одного або декількох table1значень, і Oracle не знає, яке саме ви хочете використовувати. Зазвичай це означає, що вам потрібно вдосконалити підзапит, щоб він повертав один окремий рядок.
Джастін Печера

132

Спробуйте це:

MERGE INTO table1 t1
USING
(
-- For more complicated queries you can use WITH clause here
SELECT * FROM table2
)t2
ON(t1.id = t2.id)
WHEN MATCHED THEN UPDATE SET
t1.name = t2.name,
t1.desc = t2.desc;

4
Дійсно, дуже швидко 1159477 рядків злилися за 15,5s
jefissu

3
Я сподіваюся, що всі, хто відвідує це питання після 2015 року, помітили цю відповідь. Зверніть увагу , що це також працює , якщо table1і table2той же таблиці, просто піклуватися про ON-частини і WHERE-clause для SELECT-statement з table2!
sjngm

1
Я вважаю, що кожного разу, коли мені потрібно зробити ще одне злиття, я все одно повертаюся до цієї відповіді для натхнення. Я можу роздрукувати його і
обрамлити

Працює як шарм !! Дякую!
davidwillianx

ВИБІРТЕ Ідентифікатор дистрибуції, FIELD1, FIELD1 З таблиці2, де ідентифікатор НЕ нульовий
Joseph Poirier

17

спробуйте

UPDATE Table1 T1 SET
T1.name = (SELECT T2.name FROM Table2 T2 WHERE T2.id = T1.id),
T1.desc = (SELECT T2.desc FROM Table2 T2 WHERE T2.id = T1.id)
WHERE T1.id IN (SELECT T2.id FROM Table2 T2 WHERE T2.id = T1.id);

4
Мінусом цього є те, що оператор SELECT повторюється 3 рази. У складних прикладах, які можуть бути розбіжниками угод.
Давид Балажич

9
Update table set column = (select...)

ніколи не працював для мене, оскільки набір очікує лише 1 значення - помилка SQL: ORA-01427: однорядний підзапит повертає більше ніж один рядок.

ось рішення:

BEGIN
For i in (select id, name, desc from table1) 
LOOP
Update table2 set name = i.name, desc = i.desc where id = i.id;
END LOOP;
END;

Ось як саме ви запускаєте його на робочому аркуші SQLDeveloper. Кажуть, це повільно, але це єдине рішення, яке працювало на мене у цій справі.


хтось, будь ласка, пояснить, чому це заслуговує -2 на репутацію? ЛОЛ.
Pau Karr

13
Я не знижував ставку, але це не гарне рішення. По-перше: якщо підвідбір повертав декілька значень, то цикл for буде перезаписати ім'я на table2 кілька разів для деяких / всіх записів (не чисто). По-друге: немає порядку за пунктом, тому це відбуватиметься непередбачувано (тобто виграє останнє значення у невпорядкованих даних). По-третє: це буде набагато повільніше. Якщо припустити, що підсумок циклу for призначений, оригінальний підвідбір міг бути переписаний деяким контрольованим способом, щоб повернути лише 1 значення для кожної записи ... найпростішим способом було б (вибрати min (ім'я) ...)
Генератор

Це саме те, що мені було потрібно. Спасибі (+1)
Роберт Хайат

3
Якщо ви отримаєте декілька значень у своєму підзапиті, ви можете переосмислити запит і використовувати DISTINCT або GROUP BY з MIN, MAX. Просто ідея.
Франциск

Короткий виклад короткого оповідання: якщо ви взагалі можете цього уникнути, ніколи не використовуйте будь-якого типу LOOP у T-SQL операторі. Особисто, якщо це не було 0,001% часу, коли немає іншого рішення, я навіть не думаю, що це навіть може бути доступною функцією в T-SQL. T-SQL розроблений на основі заданих даних, тому він працює на цілих наборах даних у цілому; його НЕ слід використовувати для роботи над даними по рядку.
Рей К.

8

Ось, здається, ще краща відповідь із пунктом "in", який дозволяє декілька клавіш для об'єднання :

update fp_active set STATE='E', 
   LAST_DATE_MAJ = sysdate where (client,code) in (select (client,code) from fp_detail
  where valid = 1) ...

У яловичині є стовпці, які ви хочете використовувати як ключ у круглих дужках у пункті "де" перед "в", і у викладі в дужках є оператор select з тими ж назвами стовпців. де ( колонка1, стовпчик2 ) в ( виберіть ( стовпець1, стовпець2 ) з таблиці, де "набір я хочу" );


Посилання минуло. ( 404)
Дамбо

-3

Якщо ваша таблиця t1 і резервна копія t2 мають багато стовпців, ось компактний спосіб зробити це.

Крім того, моя пов’язана проблема полягала в тому, що лише деякі стовпці були змінені, і багато рядків не мали змін до цих стовпців, тому я хотів залишити їх у спокої - в основному відновити підмножину стовпців із резервної копії всієї таблиці. Якщо ви хочете просто відновити всі рядки, пропустіть пункт де.

Звичайно, найпростішим способом було б видалити та вставити як select, але в моєму випадку мені було потрібне рішення із лише оновленнями.

Хитрість полягає в тому, що коли ви вибираєте * з пари таблиць із дублюючими іменами стовпців, друга з них отримає ім'я _1. Отже ось що я придумав:

  update (
    select * from t1 join t2 on t2.id = t1.id
    where id in (
      select id from (
        select id, col1, col2, ... from t2
        minus select id, col1, col2, ... from t1
      )
    )
  ) set col1=col1_1, col2=col2_1, ...

Це не працює для мене в Oracle 11g. Чи можете ви створити робочий приклад цього методу?
Джон Хеллер

-3
BEGIN
For i in (select id, name, desc from table2) 
LOOP
Update table1 set name = i.name, desc = i.desc where id = i.id and (name is null or desc is null);
END LOOP;
END;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.