【问题标题】:PostgreSQL - Using a Subquery to Update Multiple Column ValuesPostgreSQL - 使用子查询更新多个列值
【发布时间】: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)

【问题讨论】:

标签: postgresql 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
              ;
              

              【讨论】:

                猜你喜欢
                • 2021-01-19
                • 2011-02-11
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-02-13
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多