HiveBrain v1.2.0
Get Started
← Back to all entries
patternsqlMinor

Placing a uniqueness constraint on a date range

Submitted by: @import:stackexchange-dba··
0
Viewed 0 times
placingrangedateconstraintuniqueness

Problem

I have a table reservation with columns roomno(INTEGER), startdate(DATE), enddate(DATE) with a primary key on (roomno, startdate).

How do I place a constraint on the table such that bookings are not allowed to overlap?

I am trying to implement this in SQLFIDDLE postgreSQL9.3

For example:

101  2016-01-01   2016-01-05
101  2016-01-03   2016-01-06 [This row should not be possible to insert]


startdate and enddate are data type date.

Solution

Use an exclusion constraint and be aware how bounds for the type daterange work. date is a discrete type, so ranges have default [) bounds. The manual:

The built-in range types int4range, int8range, and daterange all use a
canonical form that includes the lower bound and excludes the upper
bound; that is, [).

So, to book a room from 2016-01-01 to 2016-01-05, including first and last day, you have to specify bounds explicitly:

daterange(start_date, end_date, '[]')


Or you enter dates with exclusive upper bounds; to include 2016-01-05, enter 2016-01-06. Then you can use the simpler expression:

daterange(start_date, end_date)


Either create a functional index with the above expression in the exclusion constraint, like demonstrated here:

  • Uniqueness constraint with date range



This way you can keep columns like you have right now.

Or you can replace startdate and enddate with a daterange column and use the plain column in the exclusion constraint. Like in this related answer:

  • Updating table of versioned rows with historical records in PostgreSQL



In any case, assuming roomno is an integer column, you need to install the additional module btree_gist first, once per database. See:

-
Combining separate ranges into largest possible contiguous ranges

-
Preventing adjacent/overlapping entries with EXCLUDE in PostgreSQL

Code Snippets

daterange(start_date, end_date, '[]')
daterange(start_date, end_date)

Context

StackExchange Database Administrators Q#140024, answer score: 7

Revisions (0)

No revisions yet.