PostgreSQL - 使用子查询更新多个列值
Posted
技术标签:
【中文标题】PostgreSQL - 使用子查询更新多个列值【英文标题】:PostgreSQL - Using a Subquery to Update Multiple Column Values 【发布时间】:2011-11-19 13:55:28 【问题描述】:我需要能够使用子查询的结果更新表中的多个列。一个简单的示例如下所示 -
UPDATE table1
SET (col1, col2) =
((SELECT MIN (ship_charge), MAX (ship_charge) FROM orders))
WHERE col4 = 1001;
如何在 PostgreSQL 中做到这一点?
感谢您的任何提示!
更新:对于让示例对于我的实际用例而言过于简单,我深表歉意。下面的查询更准确 -
UPDATE table1
SET (TOTAL_MIN_RATE, TOTAL_MAX_RATE) = (SELECT AVG(o.MIN_RATE), AVG(o.MAX_RATE)
FROM ORDR o INNER JOIN table2 ba ON (o.PAY_ACCT_ID = ba.ACCT_ID)
INNER JOIN table3 mb ON (ba.BANK_ID = mb.BANK_ID)
WHERE ba.CNTRY_ID = table1.CNTRY_ID AND
o.STUS_CD IN ('01','02','03','04','05','06') AND
((o.FRO_CRNCY_ID = table1.TO_CRNCY_ID AND o.TO_CRNCY_ID = table1.FRO_CRNCY_ID) OR
(o.TO_CRNCY_ID = table1.TO_CRNCY_ID AND o.FRO_CRNCY_ID = table1.FRO_CRNCY_ID))
GROUP BY ba.CNTRY_ID)
【问题讨论】:
updating table rows in postgres using subquery 的可能重复项 【参考方案1】:一个选项(但不是唯一一个)是使用两个单独的子查询:
update table1
set col1 = (select min(ship_charge) from orders),
col2 = (select max(ship_charge) from orders)
where col4 = 1001;
来自fine manual for PostgreSQL 9.0's UPDATE:
根据标准,column-list 语法应该允许从单个行值表达式分配列列表,例如子选择:
UPDATE accounts SET (contact_last_name, contact_first_name) = (SELECT last_name, first_name FROM salesmen WHERE salesmen.id = accounts.sales_id);
这目前没有实现——源必须是一个独立表达式的列表。
【讨论】:
【参考方案2】:这不是最有效的方法,但很简单:
UPDATE table1 SET
col1 = (SELECT MIN (ship_charge) FROM orders),
col2 = (SELECT MAX (ship_charge) FROM orders)
WHERE col4 = 1001;
【讨论】:
【参考方案3】:如果你想避免两个子选择,查询可以这样重写:
UPDATE table1
SET col1 = o_min, col2 = o_max
FROM (
SELECT min(ship_charge) as o_min,
max(ship_charge) as o_max
FROM orders
) t
WHERE col4 = 1001
如果 ship_charge 没有被索引,这应该比两个子选择更快。如果 ship_charge 被索引,它可能不会有很大的不同
编辑
从 Postgres 9.5 开始,这也可以写成:
UPDATE table1
SET (col1, col2) = (SELECT min(ship_charge), max(ship_charge) FROM orders)
WHERE col4 = 1001
【讨论】:
【参考方案4】:UPDATE table1
SET
col1 = subquery.min_value,
col2 = subquery.max_value
FROM
(
SELECT
1001 AS col4,
MIN (ship_charge) AS min_value,
MAX (ship_charge) AS max_value
FROM orders
) AS subquery
WHERE table1.col4 = subquery.col4
如果要一次更新table1中的多行,也可以在子查询中返回多行。
【讨论】:
【参考方案5】:当您没有简单的子选择时,使用UPDATE FROM
是一个很好的解决方案。在这个UPDATE
中,我想将photos
表的event_profile_id
设置为照片所属照片集的所有者(事件配置文件也是所有者)。
UPDATE photos
SET event_profile_id=photos_and_events.event_profile_id
FROM (
SELECT
ph.id photo_id,
pr.id event_profile_id
FROM photos ph, profiles pr, photo_sets ps
WHERE ph.main_photo_set_id=ps.id AND ps.owner_profile_id=pr.id
) AS photos_and_events
WHERE photos.id=photos_and_events.photo_id;
【讨论】:
【参考方案6】:正如official document所说:你可以使用PostgreSQL更新的标准更新概要
UPDATE table
SET column = expression | DEFAULT |
( column [, ...] ) = ( expression | DEFAULT [, ...] ) [, ...]
[ FROM from_list ]
[ WHERE condition ]
所以你可以这样写:
UPDATE table1
SET TOTAL_MIN_RATE = subQuery."minRate",
TOTAL_MAX_RATE = subQuery.maxRate
FROM
(
SELECT
AVG (o.MIN_RATE) AS minRate,
AVG (o.MAX_RATE) AS maxRate
FROM
ORDR o
INNER JOIN table2 ba ON (o.PAY_ACCT_ID = ba.ACCT_ID)
INNER JOIN table3 mb ON (ba.BANK_ID = mb.BANK_ID)
WHERE
ba.CNTRY_ID = table1.CNTRY_ID
AND o.STUS_CD IN (
'01',
'02',
'03',
'04',
'05',
'06'
)
AND (
(
o.FRO_CRNCY_ID = table1.TO_CRNCY_ID
AND o.TO_CRNCY_ID = table1.FRO_CRNCY_ID
)
OR (
o.TO_CRNCY_ID = table1.TO_CRNCY_ID
AND o.FRO_CRNCY_ID = table1.FRO_CRNCY_ID
)
)
GROUP BY
ba.CNTRY_ID
) subQuery;
或者更简单的方法:
UPDATE table1
SET (
TOTAL_MIN_RATE,
TOTAL_MAX_RATE
) = (
SELECT
AVG (o.MIN_RATE) AS minRate,
AVG (o.MAX_RATE) AS maxRate
FROM
ORDR o
INNER JOIN table2 ba ON (o.PAY_ACCT_ID = ba.ACCT_ID)
INNER JOIN table3 mb ON (ba.BANK_ID = mb.BANK_ID)
WHERE
ba.CNTRY_ID = table1.CNTRY_ID
AND o.STUS_CD IN (
'01',
'02',
'03',
'04',
'05',
'06'
)
AND (
(
o.FRO_CRNCY_ID = table1.TO_CRNCY_ID
AND o.TO_CRNCY_ID = table1.FRO_CRNCY_ID
)
OR (
o.TO_CRNCY_ID = table1.TO_CRNCY_ID
AND o.FRO_CRNCY_ID = table1.FRO_CRNCY_ID
)
)
GROUP BY
ba.CNTRY_ID
);
【讨论】:
【参考方案7】:我需要在一个表上进行多次插入,从两个表中获取数据,它们之间没有公共列,并忽略已经存在的记录。
以下 sql 在 Postgresql 11 上进行了测试,尽管它在 v9+ 上应该可以正常工作:
WITH permission_info AS (
SELECT id
FROM permission
WHERE permission."key" LIKE 'prefix_for_admin_%'
), role_info AS (
SELECT id
FROM role
WHERE role."name" = 'Admin'
)
INSERT INTO role_permission_table
(
role_id,
permission_id
)
SELECT role_info.id, permission_info.id FROM role_info, permission_info
ON CONFLICT DO NOTHING
;
【讨论】:
以上是关于PostgreSQL - 使用子查询更新多个列值的主要内容,如果未能解决你的问题,请参考以下文章