简体   繁体   中英

Update table in Postgresql by grouping rows

I want to update a table by grouping (or combining) some rows together based on a certain criteria. I basically have this table currently (I want to group by 'id_number' and 'date' and sum 'count'):

Table: foo
---------------------------------------
|  id_number  |   date      | count  |
---------------------------------------
| 1           |   2001      |    1   |
| 1           |   2001      |    2   |
| 1           |   2002      |    1   |
| 2           |   2001      |    6   |
| 2           |   2003      |    12  |
| 2           |   2003      |    2   |
---------------------------------------

And I want to get this:

Table: foo
 ---------------------------------------
|  id_number  |   date      | count  |
---------------------------------------
| 1           |   2001      |    3   |
| 1           |   2002      |    1   |
| 2           |   2001      |    6   |
| 2           |   2003      |    14  |
---------------------------------------

I know that I can easily create a new table with the pertinent info. But how can I modify an existing table like this without making a "temp" table? (Note: I have nothing against using a temporary table, I'm just interested in seeing if I can do it this way)

If you want to delete rows you can add a primary key (for distinguish rows) and use two sentences, an UPDATE for the sum and a DELETE for obtain less rows.

You can do something like this:

create table foo (
  id        integer primary key,
  id_number integer,
  date      integer,
  count     integer
);

insert into foo values
  (1,    1      ,   2001      ,    1   ),
  (2,    1      ,   2001      ,    2   ),
  (3,    1      ,   2002      ,    1   ),
  (4,    2      ,   2001      ,    6   ),
  (5,    2      ,   2003      ,    12  ),
  (6,    2      ,   2003      ,    2   );

select * from foo;

update foo 
  set count = count_sum
  from (
      select id, id_number, date, 
             sum(count) over (partition by id_number, date) as count_sum
        from foo
        ) foo_added
  where foo.id_number = foo_added.id_number 
    and foo.date      = foo_added.date; 

delete from foo 
  using (
      select id, id_number, date, 
             row_number() over (partition by id_number, date order by id) as inner_order
        from foo
        ) foo_ranked
  where foo.id = foo_ranked.id
    and foo_ranked.inner_order <> 1;

select * from foo;

You can try it here: http://rextester.com/PIL12447

With only one UPDATE

(but with a trigger) you can set a NULL value in count and trigger a DELETE in that case.

create table foo (
  id        integer primary key,
  id_number integer,
  date      integer,
  count     integer
);

create function delete_if_count_is_null() returns trigger 
  language plpgsql as
$BODY$
begin
  if new.count is null then
    delete from foo
      where id = new.id;
  end if;
  return new;
end;
$BODY$;

create trigger delete_if_count_is_null
  after update on foo
  for each row
  execute procedure delete_if_count_is_null();

insert into foo values
  (1,    1      ,   2001      ,    1   ),
  (2,    1      ,   2001      ,    2   ),
  (3,    1      ,   2002      ,    1   ),
  (4,    2      ,   2001      ,    6   ),
  (5,    2      ,   2003      ,    12  ),
  (6,    2      ,   2003      ,    2   );

select * from foo;

update foo 
  set count = case when inner_order = 1 then count_sum else null end
  from (
      select id, id_number, date, 
             sum(count) over (partition by id_number, date) as count_sum,
             row_number() over (partition by id_number, date order by id) as inner_order
        from foo
        ) foo_added
  where foo.id_number = foo_added.id_number 
    and foo.date      = foo_added.date
    and foo.id        = foo_added.id; 

select * from foo;

You can try it in: http://rextester.com/MWPRG10961

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM