简体   繁体   中英

Postgres UPDATE position of all items in table in INSERT/UPDATE/DELETE trigger

I am writing an app that has a kanban-style card view, similar to Trello or Jira .

  • Cards exists in lists
  • Cards can be drag-and-dropped re-ordered within a list
  • Cards can be drag-and-dropped horizontally between lists.

I am trying to write a Postgres trigger + procedure so that, on insert, update or delete, position values are updated to reflect the new ordering.

I am a novice with postgres (and particularly with triggers/procedures) so I am a bit stuck on how to achieve this.

I have the following tables:

create table lists (
  id INT GENERATED BY DEFAULT AS IDENTITY UNIQUE NOT NULL,
  name text,
  
  PRIMARY KEY (id)
);

create table cards (
  id INT GENERATED BY DEFAULT AS IDENTITY UNIQUE NOT NULL,
  listId INT NOT NULL,
  content text,
  position INT,
  
  PRIMARY KEY (id),
  FOREIGN KEY (listId) REFERENCES lists(id) 
);

When a Card is DELETE ed, INSERT ed or UPDATE ed with a given position , all the other Card s belonging to the same List must have their position updated to reflect the updated ordering.

ie given the below data:

-- cards
| id | listId | content | position |
|----|--------|---------|----------|
| 1  | 1      | A       | 1        |
| 2  | 1      | B       | 2        |
| 3  | 1      | C       | 3        |

If I were to perform the following operation:

UPDATE cards SET position = 2 WHERE id = 3;

The final data should update like so:

-- cards
| id | listId | content | position |
|----|--------|---------|----------|
| 1  | 1      | A       | 1        |
| 3  | 1      | C       | 2        |
| 2  | 1      | B       | 3        |

I think I can accomplish this with a DB trigger and a procedure. The trigger will fire on INSERT , UPDATE , DELETE , but I am not sure how the procedure should be written.

CREATE OR REPLACE PROCEDURE update_order() 
LANGUAGE plpgsql
AS $$
DECLARE
BEGIN
  -- how do I update the position field without triggering infinite loop?
COMMIT
END $$;

CREATE TRIGGER update_order
    AFTER INSERT OR UPDATE OR DELETE
    ON "cards"
    FOR EACH ROW WHERE listId = NEW.listId
    EXECUTE PROCEDURE update_order()

I think that triggers are not suitable for you due to the fact that this will cause an infinite loop. My suggestion is to create a regular function that needs to be called after every modification of the cards table. The function will update the position column from the cards table, sorting rows by column position and modified (you need to add).

CREATE OR REPLACE FUNCTION update_order() RETURNS void LANGUAGE plpgsql AS $$
BEGIN 
with ordered_cards AS
(select *, row_number() over()  from (select *   from cards ORDER BY position, modified DESC) t)

update cards set position = ordered_cards.row_number from ordered_cards where cards.id = ordered_cards.id;
END
$$;
 
select update_order();

Demo in DBfiddle

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