X-Original-To: pgsql-sql-postgresql.org@localhost.postgresql.org Received: from localhost (av.hub.org [200.46.204.144]) by postgresql.org (Postfix) with ESMTP id 7BFF39DC81C for ; Fri, 3 Mar 2006 01:13:42 -0400 (AST) Received: from postgresql.org ([200.46.204.71]) by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) with ESMTP id 58650-07 for ; Fri, 3 Mar 2006 01:13:41 -0400 (AST) X-Greylist: from auto-whitelisted by SQLgrey- Received: from tigger.fuhr.org (tigger.fuhr.org [63.214.45.158]) by postgresql.org (Postfix) with ESMTP id 0F1439DC806 for ; Fri, 3 Mar 2006 01:13:37 -0400 (AST) Received: from winnie.fuhr.org (winnie.fuhr.org [10.1.0.1]) by tigger.fuhr.org (8.13.3/8.13.3) with ESMTP id k235DV8k080042 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Thu, 2 Mar 2006 22:13:34 -0700 (MST) (envelope-from mfuhr@winnie.fuhr.org) Received: from winnie.fuhr.org (localhost [127.0.0.1]) by winnie.fuhr.org (8.13.4/8.13.4) with ESMTP id k235DVDa034695; Thu, 2 Mar 2006 22:13:31 -0700 (MST) (envelope-from mfuhr@winnie.fuhr.org) Received: (from mfuhr@localhost) by winnie.fuhr.org (8.13.4/8.13.4/Submit) id k235DUCP034694; Thu, 2 Mar 2006 22:13:30 -0700 (MST) (envelope-from mfuhr) Date: Thu, 2 Mar 2006 22:13:30 -0700 From: Michael Fuhr To: Andreas Cc: pgsql-sql@postgresql.org Subject: Re: How to check date-interval constraints Message-ID: <20060303051330.GA34533@winnie.fuhr.org> References: <4407B7C1.7030101@gmx.net> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <4407B7C1.7030101@gmx.net> X-Virus-Scanned: by amavisd-new at hub.org X-Spam-Status: No, score=0.122 required=5 tests=[AWL=0.122] X-Spam-Score: 0.122 X-Spam-Level: X-Archive-Number: 200603/56 X-Sequence-Number: 24402 On Fri, Mar 03, 2006 at 04:28:01AM +0100, Andreas wrote: > How can I have a constraint, that prohibits nesting or overlapping > intervals? > > 1 7 2006-1-1 2006-1-31 > 2 9 2006-2-1 2006-2-28 OK > 3 5 2006-1-10 2006-1-20 BAD lies within line 1 > 4 3 2006-1-20 2006-2-10 BAD starts within line 1 > and ends in line 2 This is just a brainstorm, but what about creating a composite type, a comparison function, and an operator class, then declaring a unique index on that composite type? Something like the following: CREATE TYPE drange AS ( dstart date, dstop date ); CREATE FUNCTION drange_cmp(drange, drange) RETURNS integer AS $$ BEGIN RETURN CASE WHEN $1.dstop < $2.dstart THEN -1 WHEN $1.dstart > $2.dstop THEN 1 ELSE 0 END; END; $$ LANGUAGE plpgsql IMMUTABLE STRICT; CREATE OPERATOR CLASS drange_ops DEFAULT FOR TYPE drange USING btree AS FUNCTION 1 drange_cmp(drange, drange); Here's an example that includes your additional constraint of the range being non-overlapping only for rows with the same type-column: CREATE TABLE foo ( id integer PRIMARY KEY, ftype integer NOT NULL, fstart date NOT NULL, fstop date NOT NULL CHECK (fstop >= fstart) ); CREATE UNIQUE INDEX foo_uniq_idx ON foo (ftype, (row(fstart, fstop)::drange)); INSERT INTO foo VALUES (1, 1, '2006-01-01', '2006-01-31'); -- ok INSERT INTO foo VALUES (2, 1, '2006-02-01', '2006-02-28'); -- ok INSERT INTO foo VALUES (3, 1, '2006-01-10', '2006-01-20'); -- bad INSERT INTO foo VALUES (4, 1, '2006-01-21', '2006-02-10'); -- bad INSERT INTO foo VALUES (5, 2, '2006-01-10', '2006-01-20'); -- ok INSERT INTO foo VALUES (6, 2, '2006-01-21', '2006-02-10'); -- ok I've done only minimal testing with this but so far it seems to work, even with concurrent transactions. However, I'm not sure this is the best way to approach the problem; if it's flawed then hopefully somebody will point out why and maybe suggest something else. -- Michael Fuhr