LoginSignup
8
6

More than 5 years have passed since last update.

PostgreSQLのOVERLAPSの罠

Posted at

PostgreSQLには、二つの時間間隔が重なるかどうかを返すOVERLAPSという便利な演算子があります。

3/1~3/31に対して、

  • 3/30~3/31は重なる?
sql
SELECT (DATE '2016-03-01', DATE '2016-03-31') OVERLAPS
       (DATE '2016-03-30', DATE '2016-03-31')
=> true

うんうん。

  • 3/30は重なる?
sql
SELECT (DATE '2016-03-01', DATE '2016-03-31') OVERLAPS
       (DATE '2016-03-30', DATE '2016-03-30')
=> true

うんうん。

  • 3/1は重なる?
sql
SELECT (DATE '2016-03-01', DATE '2016-03-31') OVERLAPS
       (DATE '2016-03-01', DATE '2016-03-01')
=> true

うんうん。

  • 3/31は重なる?
sql
SELECT (DATE '2016-03-01', DATE '2016-03-31') OVERLAPS
       (DATE '2016-03-31', DATE '2016-03-31')
=> false

あれ?

どうやら、(from, to)の日付が同じ場合、それがもう片方の末端にあると重複しないという判定になるようです。

解決法

範囲型のdaterangeを使うと、正しく判定できます。

sql
SELECT daterange(DATE '2016-03-01', DATE '2016-03-31', '[]') &&
       daterange(DATE '2016-03-31', DATE '2016-03-31', '[]')
=> true

最後の'[]'は、開始・終了点がどちらも閉区間であることを指定するものです。

また、範囲型であれば共通部分を取得することもできます。

sql
SELECT daterange(DATE '2016-03-01', DATE '2016-03-31', '[]') *
       daterange(DATE '2016-03-31', DATE '2016-03-31', '[]') 
=> [2016-03-31,2016-04-01)

最後の')'は、開区間であることを示しています。

範囲型を使う上での注意点

共通部分がないことを表すのは、nullでも''などでもなく、emptyです。

sql
SELECT daterange(DATE '2016-03-01', DATE '2016-03-31', '[]') *
       daterange(DATE '2016-04-01', DATE '2016-04-02', '[]') = 'empty'
=> true

この場合のemptyは、daterange型のemptyです。

isempty()という関数も用意されています。

sql
SELECT isempty(daterange(DATE '2016-03-01', DATE '2016-03-31', '[]') *
               daterange(DATE '2016-04-01', DATE '2016-04-02', '[]'))
=> true
8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6