一个表的SQL更新字段来自另一个表的字段


124

我有两个表:

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

A将始终是的子集B(表示的所有列A也在B)。

我想使用的所有列中的数据更新特定ID于的记录。这在都存在和。BAAIDAB

是否有UPDATE语法或任何其他方式做到这一点而无需指定列名,而只是说“设置A的所有列”

我使用的是PostgreSQL,因此也可以接受特定的非标准命令(但是,不是首选)。


我想这就是您想要做的,dba.stackexchange.com / a / 58383
zubair-0

Answers:


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 ...
Victoria Stuart

1
为什么需要b.id = 1?
YasirAzgar '18 -10-4

1
@YasirAzgar b.id = 1是为了限制b中的行被更新。否则,我们将更新表中的每一行。有时,这可能就是您想要的。但是最初的问题是更新b中的特定行。
Scott Bailey

这就是我需要解决的特定问题:用另一个表的不同名称的列中的值更新一个表的列。
muad-dweeb

49

这个问题很老,但是我觉得还没有给出最好的答案。

是否有UPDATE语法... 未指定列名

动态SQL的一般解决方案

您不需要知道任何列名,只需加入一些唯一的列即可(id在示例中)。我可以想到的任何可能的情况都可以可靠地工作。

这是特定于PostgreSQL的。我正在基于information_schema构建动态代码,特别information_schema.columns是基于SQL标准中定义的table的表,并且大多数主要的RDBMS(Oracle除外)都有它。但是DO带有执行动态SQL的PL / pgSQL代码的语句完全是非标准的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提琴。

相关答案以及更多说明:

使用纯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提琴。

在问这个问题很久之前,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。

这是除了FROM子句之外的标准SQL 。
无论实际存在的列是什么,它都可以工作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
Edgar Ortega

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不支持的FROM原因。

它适用于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)

3
请注意,第二和第三查询并不完全等同于第一查询。如果在中找不到匹配的行B,则第一个语句不执行任何操作(原始行保持不变),而其他两个语句使用NULL值覆盖列。
Erwin Brandstetter 2014年

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

使它工作。


5

不一定是您要求的,但是也许使用postgres继承可能有所帮助?

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

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

这避免了更新B的需要。

但是请务必阅读所有详细信息

否则,您所要求的内容就不被视为一种好习惯- SELECT * ...不鼓励使用诸如view之类的动态内容(因为这种轻微的便利可能会破坏帮助内容,而不是帮助内容),并且您所要求的内容与UPDATE ... SET命令相同。


我不确定继承如何解决这个问题。您的意思是为A添加一个更新触发器,同时也会更新B吗?我不想一直仅根据要求将A与B同步。在这种情况下,我无法使用触发器。
Nir

2
是的,如果仅在某些情况下,继承将不起作用,在这种情况下,我建议您不要使用动态查询方法。(仍然有一些方法可以使用postgres程序语言来实现。同样,如果您想使用触发器,也可以使用它们-通过添加同步字段,例如仅在设置触发器时才触发)。
不合理

0

您可以构建并执行动态sql来执行此操作,但它确实不理想


我想到了 我以为我可以让我的查询与以后对这两个表的更改兼容,但是动态sql似乎比仅指定所有字段并忘记向前兼容性要复杂得多。
Nir

是的,这很复杂,但是应该与要添加或删除的更高版本的列向前兼容。您必须首先执行查询以从两个表中获取列名,然后匹配列名,然后编写动态sql以根据匹配的列名进行更新。一个有趣的项目:)
Daniel Brink 2010年

-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

我不明白它是如何复制column1,column2和column3的。我确实需要明确提及column1。
尼尔

对我不起作用。我收到以下错误:错误:“,”或附近的语法错误
melbic

1
这种非标准语法适用UPDATE于MySQL,但对PostgreSQL无效。
Erwin Brandstetter,2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.