SQL обновляет поля одной таблицы из полей другой

124

У меня две таблицы:

A [ID, column1, column2, column3]
B [ID, column1, column2, column3, column4]

Aвсегда будет подмножеством B(то есть все столбцы Aтакже входят в B).

Я хочу обновить запись, указав IDв ней Bсвои данные Aдля всех столбцов A. Это IDсуществует как в, так Aи в B.

Есть ли UPDATEсинтаксис или какой-либо другой способ сделать это без указания имен столбцов, просто сказав «установить все столбцы A» ?

Я использую PostgreSQL, поэтому возможна также конкретная нестандартная команда (однако это не рекомендуется).

Nir
источник
Я думаю, это то, что вы хотите сделать, dba.stackexchange.com/a/58383
zubair-0

Ответы:

234

Вы можете использовать нестандартное предложение FROM .

UPDATE b
SET column1 = a.column1,
  column2 = a.column2,
  column3 = a.column3
FROM a
WHERE a.id = b.id
AND b.id = 1
Скотт Бэйли
источник
9
Вопрос в том, как это сделать без указания всех имен столбцов. (И я тоже.)
подсказка
2
Я согласен с @cluesque, но этот ответ - отличный способ использовать значения в одном столбце в таблице в качестве таблицы поиска для замены значений в столбце в другой таблице (см. SO 21657475 ), поэтому +1 ...
Виктория Стюарт
1
Зачем нужен b.id = 1?
Ясир Азгар
1
@YasirAzgar b.id = 1 - это ограничение того, какие строки в b обновляются. В противном случае мы обновили бы каждую строку в таблице. Иногда это может быть то, что вам нужно. Но исходный вопрос заключался в обновлении определенной строки в b.
Скотт Бейли
Это то, что мне нужно для моей конкретной проблемы: обновление столбца одной таблицы значениями из столбца с другим именем в другой таблице.
muad-dweeb
49

Вопрос старый, но я чувствовал, что лучшего ответа пока нет.

Есть ли UPDATEсинтаксис ... без указания имен столбцов ?

Общее решение с динамическим SQL

Вам не нужно знать какие-либо имена столбцов, кроме некоторых уникальных столбцов, к которым нужно присоединиться ( idв примере). Работает надежно для любого возможного углового случая, о котором я могу подумать.

Это характерно для PostgreSQL. Я создаю динамический код на основе information_schema , в частности таблицы information_schema.columns, которая определена в стандарте SQL, и большинство основных СУБД (кроме Oracle) имеют ее. Но DOоператор с кодом PL / pgSQL, выполняющий динамический SQL, является совершенно нестандартным синтаксисом PostgreSQL.

DO
$do$
BEGIN

EXECUTE (
SELECT
  'UPDATE b
   SET   (' || string_agg(        quote_ident(column_name), ',') || ')
       = (' || string_agg('a.' || quote_ident(column_name), ',') || ')
   FROM   a
   WHERE  b.id = 123
   AND    a.id = b.id'
FROM   information_schema.columns
WHERE  table_name   = 'a'       -- table name, case sensitive
AND    table_schema = 'public'  -- schema name, case sensitive
AND    column_name <> 'id'      -- all columns except id
);

END
$do$;

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

WHERE b.id = 123 не является обязательным, чтобы обновить выбранную строку.

SQL Fiddle.

Связанные ответы с дополнительными пояснениями:

Частичные решения с простым SQL

Со списком общих столбцов

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

UPDATE b
SET   (  column1,   column2,   column3)
    = (a.column1, a.column2, a.column3)
FROM   a
WHERE  b.id = 123    -- optional, to update only selected row
AND    a.id = b.id;

SQL Fiddle.

Этот синтаксис был представлен в Postgres 8.2 в 2006 году, задолго до того, как был задан вопрос. Подробности в инструкции.

Связанный:

Со списком столбцов в B

Если все столбцы Aопределены NOT NULL(но не обязательно B),
и вы знаете имена столбцов B(но не обязательно A).

UPDATE b
SET   (column1, column2, column3, column4)
    = (COALESCE(ab.column1, b.column1)
     , COALESCE(ab.column2, b.column2)
     , COALESCE(ab.column3, b.column3)
     , COALESCE(ab.column4, b.column4)
      )
FROM (
   SELECT *
   FROM   a
   NATURAL LEFT JOIN  b -- append missing columns
   WHERE  b.id IS NULL  -- only if anything actually changes
   AND    a.id = 123    -- optional, to update only selected row
   ) ab
WHERE b.id = ab.id;

Присоединяется NATURAL LEFT JOINк строке, в bкоторой все столбцы с одинаковым именем содержат одинаковые значения. В этом случае нам не нужно обновление (ничего не меняется), и мы можем удалить эти строки на ранней стадии процесса ( WHERE b.id IS NULL).
Нам все еще нужно найти соответствующую строку, поэтому b.id = ab.idво внешнем запросе.

db <> fiddle here
Старый sqlfiddle.

Это стандартный SQL, за исключением FROMпредложения .
Он работает независимо от того, какие столбцы на самом деле присутствуют A, но запрос не может различить фактические значения NULL и отсутствующие столбцы A, поэтому он является надежным только в том случае, если все столбцы Aопределены NOT NULL.

Есть несколько возможных вариантов, в зависимости от того, что вы знаете об обеих таблицах.

Эрвин Брандштеттер
источник
Сила SQL! Только что заметил, когда вы добавляете круглые скобки в предложение set ( SET (column1) = (a.column)), Postgres будет рассматривать это как еще один вид обновления и выдавать и выдавать ошибку следующим образом:source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression
Эдгар Ортега
26

Я работаю с базой данных IBM DB2 более десяти лет и сейчас пытаюсь изучить PostgreSQL.

Он работает в PostgreSQL 9.3.4, но не работает в DB2 10.5:

UPDATE B SET
     COLUMN1 = A.COLUMN1,
     COLUMN2 = A.COLUMN2,
     COLUMN3 = A.COLUMN3
FROM A
WHERE A.ID = B.ID

Примечание: Основная проблема заключается в причине, которая не поддерживается ни в DB2, ни в ANSI SQL.

Он работает в DB2 10.5, но НЕ работает в PostgreSQL 9.3.4:

UPDATE B SET
    (COLUMN1, COLUMN2, COLUMN3) =
               (SELECT COLUMN1, COLUMN2, COLUMN3 FROM A WHERE ID = B.ID)

НУ НАКОНЕЦ ТО! Он работает как с PostgreSQL 9.3.4, так и с DB2 10.5:

UPDATE B SET
     COLUMN1 = (SELECT COLUMN1 FROM A WHERE ID = B.ID),
     COLUMN2 = (SELECT COLUMN2 FROM A WHERE ID = B.ID),
     COLUMN3 = (SELECT COLUMN3 FROM A WHERE ID = B.ID)
jochan
источник
3
Обратите внимание, что второй и третий запросы не полностью эквивалентны первому. Если соответствующая строка не найдена B, первый оператор ничего не делает (исходная строка остается нетронутой), а два других заменяют столбцы значениями NULL.
Эрвин Брандштеттер
7

Это большое подспорье. Код

UPDATE tbl_b b
SET   (  column1,   column2,   column3)
    = (a.column1, a.column2, a.column3)
FROM   tbl_a a
WHERE  b.id = 1
AND    a.id = b.id;

работает отлично.

отметил, что вам нужна скобка "" в

From "tbl_a" a

чтобы заставить его работать.

user2493970
источник
5

Не обязательно то, что вы просили, но, может быть, может помочь наследование postgres?

CREATE TABLE A (
    ID            int,
    column1       text,
    column2       text,
    column3       text
);

CREATE TABLE B (
    column4       text
) INHERITS (A);

Это позволяет избежать необходимости обновлять B.

Но обязательно ознакомьтесь со всеми подробностями .

В противном случае то, что вы просите, не считается хорошей практикой - динамические вещи, такие как представления с, SELECT * ...не приветствуются (поскольку такое небольшое удобство может сломать больше вещей, чем помочь), и то, что вы просите, будет эквивалентно для UPDATE ... SETкоманды.

неразумность
источник
Я не уверен, как наследование решит эту проблему. Вы имеете в виду добавление триггера обновления для A, который также обновляет B? Я не хочу синхронизировать A с B все время, только по запросу. И в таком случае я не могу использовать триггеры.
Нир
2
Да, если это только в определенных случаях, наследование не будет работать, и в этом случае я не рекомендую использовать динамический запрос. (все же есть способы добиться этого с помощью процедурных языков postgres. Также, если вы хотите использовать триггеры, вы также можете использовать их - добавив поле синхронизации, например, запускающий триггер, только когда он установлен).
Unreason
0

вы можете создать и выполнить динамический sql для этого, но это действительно не идеально

Дэниел Бринк
источник
Я думала об этом. Я думал, что смогу сделать свой запрос совместимым с последующими изменениями в обеих таблицах, но динамический sql кажется слишком сложным, чем просто указать все поля и забыть о прямой совместимости.
Нир
да, это будет сложно, но оно должно быть совместимо с последующими добавлением или удалением столбцов. Сначала вам нужно будет выполнить запрос, чтобы получить имена столбцов из обеих таблиц, затем сопоставить имена столбцов, а затем написать динамический sql, чтобы выполнить обновление на основе совпадающих имен столбцов. на самом деле забавный проект :)
Дэниел Бринк
-4

Попробуйте подписаться

Update A a, B b, SET a.column1=b.column1 where b.id=1

РЕДАКТИРОВАТЬ: - Обновить более одного столбца

Update A a, B b, SET a.column1=b.column1, a.column2=b.column2 where b.id=1
Salil
источник
Я не понимаю, как он копирует column1, column2 и column3. И мне нужно явно упомянуть column1.
Нир
У меня не работает. Я получаю следующую ошибку: ОШИБКА: синтаксическая ошибка около ","
melbic
1
Этот нестандартный синтаксис будет работать UPDATEв MySQL , но недопустим для PostgreSQL.
Эрвин Брандштеттер