Last active
November 8, 2023 17:21
-
-
Save bfolkens/1c9f6a8f7fbe54d3fc734aea0574dd95 to your computer and use it in GitHub Desktop.
PostgreSQL Counter Cache
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Courtesy | |
-- http://shuber.io/porting-activerecord-counter-cache-behavior-to-postgres/ | |
CREATE FUNCTION increment_counter(table_name text, column_name text, id integer, step integer) | |
RETURNS VOID AS $$ | |
DECLARE | |
table_name text := quote_ident(table_name); | |
column_name text := quote_ident(column_name); | |
conditions text := ' WHERE id = $1'; | |
updates text := column_name || '=' || column_name || '+' || step; | |
BEGIN | |
EXECUTE 'UPDATE ' || table_name || ' SET ' || updates || conditions | |
USING id; | |
END; | |
$$ LANGUAGE plpgsql; | |
CREATE FUNCTION counter_cache() | |
RETURNS trigger AS $$ | |
DECLARE | |
table_name text := quote_ident(TG_ARGV[0]); | |
counter_name text := quote_ident(TG_ARGV[1]); | |
fk_name text := quote_ident(TG_ARGV[2]); | |
fk_changed boolean := false; | |
fk_value integer; | |
record record; | |
BEGIN | |
IF TG_OP = 'UPDATE' THEN | |
record := NEW; | |
EXECUTE 'SELECT ($1).' || fk_name || ' != ' || '($2).' || fk_name | |
INTO fk_changed | |
USING OLD, NEW; | |
END IF; | |
IF TG_OP = 'DELETE' OR fk_changed THEN | |
record := OLD; | |
EXECUTE 'SELECT ($1).' || fk_name INTO fk_value USING record; | |
PERFORM increment_counter(table_name, counter_name, fk_value, -1); | |
END IF; | |
IF TG_OP = 'INSERT' OR fk_changed THEN | |
record := NEW; | |
EXECUTE 'SELECT ($1).' || fk_name INTO fk_value USING record; | |
PERFORM increment_counter(table_name, counter_name, fk_value, 1); | |
END IF; | |
RETURN record; | |
END; | |
$$ LANGUAGE plpgsql; | |
/* | |
Example use: | |
CREATE TABLE posts ( | |
id serial PRIMARY KEY, | |
title text NOT NULL, | |
body text NOT NULL, | |
comments_count integer NOT NULL DEFAULT 0 | |
); | |
CREATE TABLE comments ( | |
id serial PRIMARY KEY, | |
post_id integer NOT NULL REFERENCES posts(id), | |
body text NOT NULL | |
); | |
CREATE TRIGGER update_post_comments_count | |
AFTER INSERT OR UPDATE OR DELETE ON comments | |
FOR EACH ROW EXECUTE PROCEDURE counter_cache('posts', 'comments_count', 'post_id'); | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For foreign-key of type UUID take a look here