【问题标题】:Update multiple rows in same query using PostgreSQL使用 PostgreSQL 更新同一查询中的多行
【发布时间】:2013-09-18 19:23:29
【问题描述】:

我希望在一个语句中更新 PostgreSQL 中的多行。有没有办法做如下的事情?

UPDATE table 
SET 
 column_a = 1 where column_b = '123',
 column_a = 2 where column_b = '345'

【问题讨论】:

  • 我一直试图在该页面上找到它,但我无法找到它。我看到在哪里可以使用一个 where 语句更新多行,但我不知道如何使用它自己的 where 语句更新多行。我也搜索了谷歌,并没有找到一个真正明确的答案,所以我希望有人可以提供一个明确的例子。

标签: sql postgresql


【解决方案1】:

@zero323 提供的答案在 Postgre 12 上效果很好。如果有人对 column_b 有多个值(在 OP 的问题中提到)

UPDATE conupdate SET orientation_status = CASE
   when id in (66934, 39) then 66
   when id in (66938, 49) then 77
END
WHERE id IN (66934, 39, 66938, 49)

在上述查询中,id 类似于column_borientation_status 类似于问题的column_a

【讨论】:

    【解决方案2】:

    基于@Roman的解决方案,可以设置多个值:

    update users as u set -- postgres FTW
      email = u2.email,
      first_name = u2.first_name,
      last_name = u2.last_name
    from (values
      (1, 'hollis@weimann.biz', 'Hollis', 'Connell'),
      (2, 'robert@duncan.info', 'Robert', 'Duncan')
    ) as u2(id, email, first_name, last_name)
    where u2.id = u.id;
    

    【讨论】:

    • 这似乎是他的解决方案.. UPDATE FROM (VALUES...) WHERE。它是如何仅基于的?
    • 我更喜欢这个答案,因为变量名更容易理解发生了什么。
    • 哇。准确而清晰。我正在尝试在 GoLang 中实现类似的东西。那么我可以为值传递一个结构数组吗?像这样,from (values $1) 其中 $1 是一个结构数组。在上述情况下,strict 将具有 id、first_name 和 last_name 作为属性。
    【解决方案3】:

    @Roman 感谢您的解决方案,对于任何使用节点的人,我制作了这个实用方法来抽出一个查询字符串来用 n 条记录更新 n 列。

    遗憾的是它只处理具有相同列的 n 条记录,因此 recordRows 参数非常严格。

    const payload = {
      rows: [
        {
            id: 1,
            ext_id: 3
        },
        {
            id: 2,
            ext_id: 3
        },
        {
            id: 3,
            ext_id: 3
        } ,
            {
            id: 4,
            ext_id: 3
        } 
      ]
    };
    
    var result = updateMultiple('t', payload);
    
    console.log(result);
    /*
    qstring returned is:
    
    UPDATE t AS t SET id = c.id, ext_id = c.ext_id FROM (VALUES (1,3),(2,3),(3,3),(4,3)) AS c(id,ext_id) WHERE c.id = t.id
    */
    
    
    
    function updateMultiple(table, recordRows){
      var valueSets = new Array();
      var cSet = new Set();
      var columns = new Array();
      for (const [key, value] of Object.entries(recordRows.rows)) {
        var groupArray = new Array();
        for ( const [key2, value2] of Object.entries(recordRows.rows[key])){    
          if(!cSet.has(key2)){
            cSet.add(`${key2}`);
            columns.push(key2);
          }
          groupArray.push(`${value2}`); 
        }
        valueSets.push(`(${groupArray.toString()})`);
      }
      var valueSetsString = valueSets.join();  
      var setMappings = new String();
      for(var i = 0; i < columns.length; i++){
        var fieldSet = columns[i];
        
          setMappings += `${fieldSet} = c.${fieldSet}`;
          if(i < columns.length -1){
            setMappings += ', ';
          }
      }
      var qstring = `UPDATE ${table} AS t SET ${setMappings} FROM (VALUES ${valueSetsString}) AS c(${columns}) WHERE c.id = t.id`;
      return qstring;
    }

    【讨论】:

      【解决方案4】:

      我认为接受的答案并不完全正确。它取决于顺序。这是一个无法与答案中的方法正常工作的示例。

      create table xxx (
          id varchar(64),
          is_enabled boolean
      );
      
      insert into xxx (id, is_enabled) values ('1',true);
      insert into xxx (id, is_enabled) values ('2',true);
      insert into xxx (id, is_enabled) values ('3',true);
      
      UPDATE public.xxx AS pns
              SET is_enabled         = u.is_enabled
                  FROM (
                  VALUES
               (
                  '3',
                  false
               ,
                  '1',
                  true
               ,
                  '2',
                  false
               )
              ) AS u(id, is_enabled)
                  WHERE u.id = pns.id;
      
      select * from xxx;
      

      所以问题仍然存在,有没有办法以独立于顺序的方式做到这一点?

      ----在尝试了一些事情之后,这似乎与订单无关

      UPDATE public.xxx AS pns
              SET is_enabled         = u.is_enabled
                  FROM (
                  SELECT '3' as id, false as is_enabled UNION
                  SELECT '1' as id, true as is_enabled UNION
                  SELECT '2' as id, false as is_enabled
               ) as u
                  WHERE u.id = pns.id;
      

      【讨论】:

        【解决方案5】:

        除了其他答案、cmets 和文档之外,还可以将数据类型转换放在用法上。这样可以更轻松地进行复制粘贴:

        update test as t set
            column_a = c.column_a::number
        from (values
            ('123', 1),
            ('345', 2)  
        ) as c(column_b, column_a) 
        where t.column_b = c.column_b::text;
        

        【讨论】:

          【解决方案6】:

          要在单个查询中更新多个,您可以试试这个

          UPDATE table_name
          SET 
          column_1 = CASE WHEN any_column = value and any_column = value THEN column_1_value end,
          column_2 = CASE WHEN any_column = value and any_column = value THEN column_2_value end,
          column_3 = CASE WHEN any_column = value and any_column = value THEN column_3_value end,
          .
          .
          .
          column_n = CASE WHEN any_column = value and any_column = value THEN column_n_value end
          

          如果您不需要附加条件,则删除此查询的 and 部分

          【讨论】:

            【解决方案7】:

            您还可以使用update ... from 语法并使用映射表。如果你想更新不止一列,它更通用:

            update test as t set
                column_a = c.column_a
            from (values
                ('123', 1),
                ('345', 2)  
            ) as c(column_b, column_a) 
            where c.column_b = t.column_b;
            

            您可以添加任意数量的列:

            update test as t set
                column_a = c.column_a,
                column_c = c.column_c
            from (values
                ('123', 1, '---'),
                ('345', 2, '+++')  
            ) as c(column_b, column_a, column_c) 
            where c.column_b = t.column_b;
            

            sql fiddle demo

            【讨论】:

            • 另外,可能必须指定正确的数据类型。带有日期的示例:... from (values ('2014-07-21'::timestamp, 1), ('2014-07-20', 2), ... 更多详细信息请访问PostgreSQL Documentation
            • 效果很好,感谢您的澄清!这方面的 Postgres 文档让人读起来有点混乱。
            • 太酷了!没想到今天学习了一个新的sql构造
            • 如何动态构建 VALUES(...) ?即向 VALUES() 添加 100 行。可以是数组还是什么的?
            • 仅供参考,这是我正在寻找的最接近的版本。我们可以动态地向数组中添加行:``` SELECT s.id, s.marked_for_deletion, s.name FROM unnest( array[ ('2f888809-2777-524b-abb7-13df413440f5',true,'Salad fork'), ('f2924dda-8e63-264b-be55-2f366d9c3caa',false,'甜瓜球手'), ('d9ecd18d-34fd-5548-90ea-0183a72de849',true,'火锅叉') ]::item[] ) s; ; ```stackoverflow.com/questions/57517980/…
            【解决方案8】:

            遇到类似的情况,CASE 表达式对我很有用。

            UPDATE reports SET is_default = 
            case 
             when report_id = 123 then true
             when report_id != 123 then false
            end
            WHERE account_id = 321;
            

            Reports - 这里是一个表格,account_id 与上面提到的 report_ids 相同。上面的查询会将 1 条记录(与条件匹配的记录)设置为 true,将所有不匹配的记录设置为 false。

            【讨论】:

              【解决方案9】:

              假设您有一个 IDs 数组和等效的 statuses 数组 - 下面是一个示例,如何使用静态 SQL(一个不t 由于不同的值而变化)的数组:

              drop table if exists results_dummy;
              create table results_dummy (id int, status text, created_at timestamp default now(), updated_at timestamp default now());
              -- populate table with dummy rows
              insert into results_dummy
              (id, status)
              select unnest(array[1,2,3,4,5]::int[]) as id, unnest(array['a','b','c','d','e']::text[]) as status;
              
              select * from results_dummy;
              
              -- THE update of multiple rows with/by different values
              update results_dummy as rd
              set    status=new.status, updated_at=now()
              from (select unnest(array[1,2,5]::int[]) as id,unnest(array['a`','b`','e`']::text[]) as status) as new
              where rd.id=new.id;
              
              select * from results_dummy;
              
              -- in code using **IDs** as first bind variable and **statuses** as the second bind variable:
              update results_dummy as rd
              set    status=new.status, updated_at=now()
              from (select unnest(:1::int[]) as id,unnest(:2::text[]) as status) as new
              where rd.id=new.id;
              

              【讨论】:

                【解决方案10】:

                是的,您可以:

                UPDATE foobar SET column_a = CASE
                   WHEN column_b = '123' THEN 1
                   WHEN column_b = '345' THEN 2
                END
                WHERE column_b IN ('123','345')
                

                工作证明:http://sqlfiddle.com/#!2/97c7ea/1

                【讨论】:

                • 这是错误的...您将更新所有行,即使它不是'123' 也不是'345'。你应该使用WHERE column_b IN ('123','456')...
                • 我认为'456' 应该是'345'
                • 如果您在最后一行 WHEN ? THEN ? 之后添加 ELSE column_b,则该列将设置为其当前值,从而防止发生 MatheusQI 所说的事情。
                • 这不是他要求的..他需要更新多个列,而不是根据列B设置列A。
                • 这不正是 OP 要求的 - 只有 column_a 需要更新(基于 column_b 的值),而不是多个列,对吧?
                猜你喜欢
                • 2020-11-03
                • 2021-02-13
                • 2022-12-17
                • 2021-11-12
                • 2011-11-19
                • 2021-01-19
                • 1970-01-01
                • 2021-03-26
                • 2016-10-30
                相关资源
                最近更新 更多