Як я можу зробити операцію UPDATE з JOIN в SQL Server?


1314

Мені потрібно оновити цю таблицю в SQL Server даними з її батьківської таблиці, див. Нижче:

Таблиця: продаж

id (int)
udid (int)
assid (int)

Таблиця: ud

id  (int)
assid  (int)

sale.assidмістить правильне значення для оновлення ud.assid.

Який запит буде робити це? Я думаю про це, joinале не впевнений, чи можливо.


3
Які RDBMS ви використовуєте? MySQL, SQL Server, Oracle, PostgreSQL чи щось інше?
Кріс Дж

якісь відносини між таблицями? Як можна знати, який запис із продажу відповідає якому запису з ud? Чи ґрунтується на id в якості основного ключа в обох таблицях?
Cătălin Pitiș

Як можна оновити UD? У нього є лише assid і власний ідентифікатор. Чи можете ви навести приклад щодо фактичних значень, які існують, і записів, які ви хотіли б змінити або додати в результаті сценарію?
Бернхард Хофманн

2
Дивіться також SO питання ... stackoverflow.com/questions/982919/sql-update-query-using-joins
SteveC

2
Псевдонім користувача у запиті, як stackoverflow.com/questions/982919/sql-update-query-using-joins
Імран Мухаммед

Відповіді:


2383

Синтаксис суворо залежить від того, яку SQL СУБД ви використовуєте. Ось декілька способів зробити це в ANSI / ISO (він же повинен працювати в будь-яких SQL СУБД), MySQL, SQL Server та Oracle. Зауважте, що запропонований нами метод ANSI / ISO, як правило, буде набагато повільніше, ніж два інші методи, але якщо ви використовуєте SQL СУБД, крім MySQL, SQL Server або Oracle, це може бути єдиний шлях (наприклад, якщо ваша SQL СУБД не підтримує MERGE):

ANSI / ISO:

update ud 
     set assid = (
          select sale.assid 
          from sale 
          where sale.udid = ud.id
     )
 where exists (
      select * 
      from sale 
      where sale.udid = ud.id
 );

MySQL:

update ud u
inner join sale s on
    u.id = s.udid
set u.assid = s.assid

SQL Server:

update u
set u.assid = s.assid
from ud u
    inner join sale s on
        u.id = s.udid

PostgreSQL:

update ud
  set assid = s.assid
from sale s 
where ud.id = s.udid;

Зауважте, що цільова таблиця не повинна повторюватися в FROMпункті про Postgres.

Oracle:

update
    (select
        u.assid as new_assid,
        s.assid as old_assid
    from ud u
        inner join sale s on
            u.id = s.udid) up
set up.new_assid = up.old_assid

SQLite:

update ud 
     set assid = (
          select sale.assid 
          from sale 
          where sale.udid = ud.id
     )
 where RowID in (
      select RowID 
      from ud 
      where sale.udid = ud.id
 );

3
Мені здається, що set assid = s.assidмає бути MySQL set u.assid = s.assid.
dotancohen

2
Що в синтаксисі ANSI, що станеться, якщо SELECT після =повернення більше одного рядка?
Викиньте рахунок

2
@ ThrowawayAccount3Million Це, ймовірно, не вдасться. AFAIK, така операція очікує скалярного значення і призведе до помилки, якщо замість цього буде встановлено результат.
Лорд Френсіс

6
Я бажаю, щоб ОП обрала кілька кращих імен для свого столу та колонок !! це не таке читабельне / інтуїтивне ...
С.Серпоошан

4
Postgre 9.3 працював лише з використаннямupdate ud set assid = s.assid
StackUnder

143

Це має працювати в SQL Server:

update ud 
set assid = sale.assid
from sale
where sale.udid = id

98

постгреси

UPDATE table1
SET    COLUMN = value
FROM   table2,
       table3
WHERE  table1.column_id = table2.id
       AND table1.column_id = table3.id
       AND table1.COLUMN = value
       AND table2.COLUMN = value
       AND table3.COLUMN = value 

20
Відповідь була б більш зручною, якщо вона використовувала б імена таблиці / стовпців, які використовуються у питанні. Чому у вашій відповіді є 3 таблиці?
alfonx

50

Стандартний підхід SQL був би

UPDATE ud
SET assid = (SELECT assid FROM sale s WHERE ud.id=s.id)

На SQL Server ви можете використовувати з'єднання

UPDATE ud
SET assid = s.assid
FROM ud u
JOIN sale s ON u.id=s.id

1
З першим ви не можете збігатися на 2+ стовпчиках, але приєднання працює чудово.
makciook

6
@makciook: так? Ви можете просто додати більше умов у WHEREпункті, якщо ви хочете співставити додаткові стовпці.
siride

2
Просто ніт ... але я думаю, що ОП означав sale.udid = ud.id. І не sale.id.
Skippy VonDrake

39

PostgreSQL :

CREATE TABLE ud (id integer, assid integer);
CREATE TABLE sales (id integer, udid integer, assid integer);

UPDATE ud
SET assid = sales.assid
FROM sales
WHERE sales.id = ud.id;

26

Спрощений запит на оновлення з використанням JOIN -ву декількох таблиць.

   UPDATE
        first_table ft
        JOIN second_table st ON st.some_id = ft.some_id
        JOIN third_table tt  ON tt.some_id = st.some_id
        .....
    SET
        ft.some_column = some_value
    WHERE ft.some_column = 123456 AND st.some_column = 123456

Примітка - first_table, second_table, third_table та some_column, як 123456, - ​​назви демонстраційних таблиць, назви стовпців та ідентифікатори. Замініть їх дійсними іменами.


16

Ще один приклад, чому SQL насправді не портативний.

Для MySQL це було б:

update ud, sale
set ud.assid = sale.assid
where sale.udid = ud.id;

Для отримання додаткової інформації читайте кілька оновлень таблиці: http://dev.mysql.com/doc/refman/5.0/uk/update.html

UPDATE [LOW_PRIORITY] [IGNORE] table_references
    SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ...
    [WHERE where_condition]

2
+1 на коментар "чому SQL насправді не портативний" коментар! Переносність настільки крихка, що лише оголошення оголошення змінної порушить портативність серед багатьох популярних двигунів бази даних.
Джефф Моден

8

Терадата Астер пропонує ще один цікавий спосіб досягнення мети:

MERGE INTO ud --what trable should be updated
USING sale -- from what table/relation update info should be taken
ON ud.id = sale.udid --join condition
WHEN MATCHED THEN 
    UPDATE SET ud.assid = sale.assid; -- how to update

8

Я думав, що SQL-сервер у верхній публікації буде працювати для Sybase, оскільки вони обидва T-SQL, але, на жаль, ні.

Для Sybase я виявив, що оновлення повинно бути в самій таблиці, а не псевдонімом:

update ud
set u.assid = s.assid
from ud u
    inner join sale s on
        u.id = s.udid

7

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

UPDATE users 
set users.DivisionId=divisions.DivisionId
from divisions join users on divisions.Name=users.Division

7

MySQL

Ви отримаєте найкращу ефективність, якщо забудете пункт де і розмістите всі умови у виразі ON.

Я думаю, що це тому, що запит спочатку повинен приєднатись до таблиць, а потім запускає пункт де на цьому, тож якщо ви можете зменшити те, що потрібно для приєднання, то це швидкий спосіб отримати результати / зробити udpate.

Приклад

Сценарій

У вас є таблиця користувачів. Вони можуть увійти в систему, використовуючи своє ім’я користувача або електронну пошту чи номер_раху. Ці облікові записи можуть бути активними (1) або неактивними (0). Ця таблиця має 50000 рядків

Потім у вас є таблиця користувачів, яку вимкніть за один раз, оскільки ви дізнаєтесь, що всі вони зробили щось погане. Однак у цій таблиці є один стовпець із змішаними іменами користувачів, електронними листами та номерами рахунків. Він також має індикатор "has_run", який потрібно встановити на 1 (true), коли він був запущений

Запит

UPDATE users User
    INNER JOIN
        blacklist_users BlacklistUser
        ON
        (
            User.username = BlacklistUser.account_ref
            OR
            User.email = BlacklistedUser.account_ref
            OR
            User.phone_number = BlacklistUser.account_ref
            AND
            User.is_active = 1
            AND
            BlacklistUser.has_run = 0
        )
    SET
        User.is_active = 0,
        BlacklistUser.has_run = 1;

Обґрунтування

Якби нам довелося приєднатися лише за умовами АБО, то, по суті, потрібно було б перевірити кожен рядок 4 рази, щоб побачити, чи повинен він приєднуватися, і потенційно повернути набагато більше рядків. Однак, надавши йому більше умов, він може "пропустити" багато рядків, якщо вони не відповідають усім умовам при приєднанні.

Бонус

Це читабельніше. Усі умови знаходяться в одному місці, а рядки для оновлення - в одному місці


4

І в MS ACCESS:

UPDATE ud 
INNER JOIN sale ON ud.id = sale.udid
SET ud.assid = sale.assid;

1
Як застереження, SET повинен надходити відразу після визначення набору записів! Я щойно намагався розробити аналогічний сценарій у базі даних Access, для якої потрібне було слово WHERE (воно не сприймало б його як дійсну умову ON). ДІЙ доводилося останніми, щоб уникнути помилок синтаксису.
Додекафон



3

Спробуйте це, я думаю, це допоможе вам

update ud

set ud.assid = sale.assid

from ud 

Inner join sale on ud.id = sale.udid

where sale.udid is not null

2

Для SQLite використовуйте властивість RowID для оновлення:

update Table set column = 'NewValue'
where RowID = 
(select t1.RowID from Table t1
join Table2 t2 on t1.JoinField = t2.JoinField
where t2.SelectValue = 'FooMyBarPlease');

1
Чи можете ви пояснити це трохи?
Мохаммед Нурелдін

1
@MohammedNoureldin Я спробую пояснити. Проблема полягає в тому, як оновити таблицю за результатами запиту на приєднання за допомогою тієї ж таблиці. Оператор (sub-select) діє як об'єднання та повертає системне поле RowID, яке є унікальним номером для кожного рядка таблиці. Оскільки суб-вибір може повернути кілька рядків, "де RowID =" вибирає один правильний рядок із отриманого під-вибору і робить оновлення до стовпця. Повідомте мене, якщо вам потрібно більше роз’яснень або вам потрібно розібратися у варіанті цієї теми.
KeithTheBiped
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.