X-Original-To: pgsql-sql@postgresql.org Received: from localhost (developer.postgresql.org [64.117.224.193]) by developer.postgresql.org (Postfix) with ESMTP id ECCD79263F6 for ; Mon, 2 Jun 2003 12:25:02 -0400 (EDT) Received: from developer.postgresql.org ([64.117.224.193]) by localhost (developer.postgresql.org [64.117.224.193]) (amavisd-new, port 10024) with ESMTP id 20046-03 for ; Mon, 2 Jun 2003 12:24:52 -0400 (EDT) Received: from main.gmane.org (main.gmane.org [80.91.224.249]) by developer.postgresql.org (Postfix) with ESMTP id 9648A9262CA for ; Mon, 2 Jun 2003 12:24:51 -0400 (EDT) Received: from root by main.gmane.org with local (Exim 3.35 #1 (Debian)) id 19Ms3E-0002UR-00 for ; Mon, 02 Jun 2003 18:20:48 +0200 X-Injected-Via-Gmane: http://gmane.org/ To: pgsql-sql@postgresql.org Received: from news by main.gmane.org with local (Exim 3.35 #1 (Debian)) id 19MrV6-0007H1-00 for ; Mon, 02 Jun 2003 17:45:32 +0200 From: Samuel Tardieu Subject: Maintaining a counter up-to-date Date: Mon, 02 Jun 2003 17:47:35 +0200 Organization: Avian Carrier & Friends Lines: 80 Message-ID: <871xyc5vmw.fsf@inf.enst.fr> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Complaints-To: usenet@main.gmane.org Mail-Copies-To: sam@rfc1149.net User-Agent: Gnus/5.090007 (Oort Gnus v0.07) XEmacs/21.4 (Military Intelligence, i386--freebsd) Cancel-Lock: sha1:4z/BlyC6pB378+4Wp29qRBZPQPc= X-Virus-Scanned: by amavisd-new X-Archive-Number: 200306/20 X-Sequence-Number: 13778 I have an "ips" table with 100000+ records, each record having a "catid" field representing its category. "catid" references a row in a table called "categories". For statistics purpose (generation of images with the evolution of the number of rows by category), I am trying to reduce the load on the database. The request I was doing at the beginning was: SELECT catid, COUNT(*) FROM ips GROUP BY catid; I then added a "nentries" field to the "categories" table with some rules to maintain the counters up-to-date: CREATE RULE cat_ins AS ON INSERT TO ips DO UPDATE categories SET nentries = (categories.nentries + 1) WHERE (categories.catid = new.catid); CREATE RULE cat_del AS ON DELETE TO ips DO UPDATE categories SET nentries = (categories.nentries - 1) WHERE (categories.catid = old.catid); CREATE RULE cat_upd AS ON UPDATE TO ips WHERE old.catid <> new.catid DO (UPDATE categories SET nentries = (categories.nentries - 1) WHERE (categories.catid = old.catid); UPDATE categories SET nentries = (categories.nentries + 1) WHERE (categories.catid = new.catid); ); This works fine when inserting, deleting or updating one row in the "ips" table. However, when i/d/u several rows at a time with the same "catid", I only got an increment or decrement by one of the counter. I have not found an easy way to maintain the counter up-to-date. I have found a complex solution: I created a "counter" table with two fields, "catid" and "value". The idea is to put 1 in "value" for every insertion or new value for update, or -1 for every deletion or old value for update. CREATE RULE counter_ins AS ON INSERT TO ips DO (INSERT INTO counter (catid, value) VALUES (new.catid, 1); UPDATE categories SET nentries = nentries + (SELECT sum(*) FROM counter WHERE counter.catid = categories.catid) WHERE (categories.catid = counter.catid); DELETE FROM counter; ); (I do not show the equivalent "ON DELETE" and "ON UPDATE" rules) I have two questions: 1) Is this way of doing things correct? Do I have the guarantee that all the commands in the "DO" part will be executed in a transaction even if the initial insertion into "ips" isn't? 2) What is the simplest way of doing this? I guess doing stats in a database is quite a pretty usual operation. Thanks in advance. Sam PS/ the real problem is more complex, as we need to do those statistics on several fields, not only "catid" -- Samuel Tardieu -- sam@rfc1149.net -- http://www.rfc1149.net/sam