1

I'm trying to create a table in postgres of product promotions, this is a simplified example:

create table promotions ( product_id text, start_date date, end_date date, discount numeric(5, 4) ) 

For a particular product there shouldn't be overlapping promotions, i.e., the table shouldn't allow to have two rows for the same product_id with overlapping date ranges.

I don't know if it is possible, but I see this best coded as a constraint.

I've been looking around for solutions but I couldn't find anyone for this particular problem.

3 Answers 3

2

You are looking for an exclusion constraint using a daterange:

alter table promotions add constraint no_overlapping_dates exclude using gist (product_id with =, daterange(start_date, end_date, '[]') with &&); 

The constraint is setup to include the end date in the range. If the end_date is the first date where the range is not valid, change the second parameter of the daterange() function to '[)'

You also need to install the extension btree_gist for this.

Sign up to request clarification or add additional context in comments.

Comments

1

You can also check it with a trigger and procedure Something like;

 CREATE OR REPLACE FUNCTION check_overlying_prom_dates() RETURNS trigger AS $$ BEGIN IF (select end_date from promotions where product_id = NEW.product_id) >= NEW.start_date THEN RAISE EXCEPTION 'cant overlay promotion dates'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER promotion_date_trigger BEFORE INSERT OR UPDATE ON promotions FOR EACH ROW EXECUTE PROCEDURE check_overlying_prom_dates(); 

Comments

0

Update

I have changed the implementation to use the exclusion constraint using btree_gist as @a_horse_with_no_name explained.

Though, as far I've seen, btree_gist is only a trusted extensions from Postgres 13.0, so I leave this solution in case anyone that is trying to solve this problem cannot install untrusted extensions on its database.


Thank you all. I tried @dkc approach. The final code is this:

/* If we have two date ranges [x1, y1] & [x2, y2], they can overlap in 4 ways: 1. ([)] -> x1 <= x2 && y1 >= x2 2. [(]) -> x1 <= y2 && y1 >= y2 3. [()] -> x1 >= x2 && y1 <= y2 4. ([]) -> x1 <= x2 && y1 >= y2 */ create or replace function check_overlying_prom_dates() returns trigger as $$ begin if ( -- case 1 select bool_or(new.start_date <= start_date and new.end_date >= start_date) from promotions where product_id = new.product_id ) or ( -- case 2 select bool_or(new.start_date <= end_date and new.end_date >= end_date) from promotions where product_id = new.product_id ) or ( -- case 3 select bool_or(new.start_date >= start_date and new.end_date <= end_date) from promotions where product_id = new.product_id ) or ( -- case 4 select bool_or(new.start_date <= start_date and new.end_date >= end_date) from promotions where product_id = new.product_id ) then raise exception 'cant overlay promotion dates'; end if; return new; end; $$ language plpgsql; create trigger promotion_date_trigger before insert or update on promotions for each row execute procedure check_overlying_prom_dates(); 

1 Comment

The exclusion constraint will be way more efficient

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.