PostgreSQL Slow Count() Workaround
Wednesday, December 12th, 2007You may also be interested in this. It’s a far more elegant solution, if you only need the entire number of rows in a table.
MySQL zealots tend to point to PostgreSQL’s slow count() as a weakness, however, in the real world, count() isn’t used very often, and if it’s really needed, most good database systems provide a framework for you to build a workaround. The reason MySQL has fast counts, is because, they’re cached. This works in MySQL because MySQL uses serialized INSERTs, but higher grade DBs are transactional, and PostgreSQL uses MVCC, so caching row counts produces inaccurate results.
Here’s an example count() workaround for PostgreSQL, using a combination of PL/PgSQL and TRIGGERs. You could certainly extend this model, adding more columns to the cached rows table, so you can simulate SELECT count(id) WHERE foo = ‘bar’, although you’d have to add a TRIGGER for UPDATEs.
--- This table will be used as a dummy table,
--- that we wan't to get a count() from.
CREATE TABLE tbl1 (
id INTEGER
);
--- This table is where the cached counts will be stored
CREATE TABLE tbl1_num_rows (
num_rows INTEGER NOT NULL
);
--- Function to update the count in tbl1_num_rows,
--- on an INSERT
CREATE OR REPLACE FUNCTION tbl1_up_rows_insert ()
RETURNS TRIGGER AS $$
DECLARE
row RECORD;
BEGIN
--- Auto-add record
SELECT INTO row num_rows FROM tbl1_num_rows;
IF NOT FOUND THEN
INSERT INTO tbl1_num_rows VALUES (0);
END IF;
UPDATE tbl1_num_rows SET num_rows =
((SELECT num_rows FROM tbl1_num_rows) + 1);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
--- Function to update the count in tbl1_num_rows,
--- on a DELETE
CREATE OR REPLACE FUNCTION tbl1_up_rows_delete ()
RETURNS TRIGGER AS $$
DECLARE
row RECORD;
BEGIN
--- Auto-add record
SELECT INTO row num_rows FROM tbl1_num_rows;
IF NOT FOUND THEN
INSERT INTO tbl1_num_rows VALUES (0);
ELSE
IF row.num_rows > 0 THEN
UPDATE tbl1_num_rows SET num_rows =
((SELECT num_rows FROM tbl1_num_rows) - 1);
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
--- Add the TRIGGERS to tbl1
CREATE TRIGGER tbl1_up_rows_on_insert AFTER INSERT ON tbl1 FOR EACH ROW
EXECUTE PROCEDURE tbl1_up_rows_insert();
CREATE TRIGGER tbl1_up_rows_on_delete AFTER DELETE ON tbl1 FOR EACH ROW
EXECUTE PROCEDURE tbl1_up_rows_delete();
