What's the closest we can get to `REFRESH COMPLETE ON COMMIT` in PostgresQL?

心不动则不痛 提交于 2020-06-29 04:02:08

问题


So we're looking for a way to enforce constraints that span multiple tables.

We've come across this old blog post, which suggests:

  • Create a materialized view to select data that violates the desired constraint. The MV must be defined with REFRESH COMPLETE ON COMMIT so that it is updated before the end of the transaction.

  • Create a check constraint on the materialized view that always evaluates to FALSE – e.g. CHECK (1=0)

That’s it. Whenever the underlying tables are updated, the materialized view is refreshed. If the update violates the rule, then a row will be inserted into the materialized view; but the check constraint on the MV disallows any inserts into it, and so the transaction fails.

And although there are some performance questionmarks, the idea sounds reasonable enough.

However, postgresql does not -- to our knowledge -- support something like REFRESH ON COMMIT.

What we can do, of course, is install triggers on the tables that form the view that will trigger a refresh on update/delete/insert.

But not only would that potentially mean having to execute a refresh for each of the tables involved, we may also have long ended the transaction by the time the refresh is executed.

Maybe we could do something with locking, but then it becomes an issue of locking the right thing quick enough, which sounds like a terrible idea.

So is there something we can do or are we better off forgetting about this?

What's the closest we can get to "refresh before commit" behaviour?


回答1:


Don't create a materialized view, roll it by hand.

As an example, we have two tables a and b, and we want to make sure that the sum of the rows in these two tables is less than 1000:

BEGIN;

CREATE TABLE counter (
   total bigint NOT NULL,
   CHECK (total < 1000)
);

CREATE FUNCTION count_trig() RETURNS trigger
   LANGUAGE plpgsql AS
$$BEGIN
   CASE
      WHEN TG_OP = 'INSERT' THEN
         UPDATE counter SET total = total + 1;
         RETURN NEW;
      WHEN TG_OP = 'DELETE' THEN
         UPDATE counter SET total = total - 1;
         RETURN OLD;
   END CASE;
END;$$;

CREATE TRIGGER count_trig AFTER INSERT OR DELETE ON a
   FOR EACH ROW EXECUTE PROCEDURE count_trig();

CREATE TRIGGER count_trig AFTER INSERT OR DELETE ON b
   FOR EACH ROW EXECUTE PROCEDURE count_trig();

INSERT INTO counter (total)
   VALUES ((SELECT count(*) FROM a) + (SELECT count(*) FROM b));

COMMIT;


来源:https://stackoverflow.com/questions/59952825/whats-the-closest-we-can-get-to-refresh-complete-on-commit-in-postgresql

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!