<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
		>
<channel>
	<title>Comments on: Temporal Keys, Part 2</title>
	<atom:link href="http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/feed/" rel="self" type="application/rss+xml" />
	<link>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/</link>
	<description>Ideas on Databases, Logic, and Language by Jeff Davis</description>
	<lastBuildDate>Thu, 11 Mar 2010 04:54:05 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
		<item>
		<title>By: BillR</title>
		<link>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/comment-page-1/#comment-175</link>
		<dc:creator>BillR</dc:creator>
		<pubDate>Thu, 21 Jan 2010 03:19:40 +0000</pubDate>
		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=180#comment-175</guid>
		<description>I tend to agree that millisecond precision might be overkill, but who is to know to what purpose Postgres will be put. I would not doubt that there is someone who would ask for this type of precision if you were to not provide it. ;) I know for certain that there are applications out there that definitely are down to the hour, as I found out returning my car an hour late to Avis Rent A Car.  :)

Anyway, I would suggest (or maybe second) the notion of finest granularity by default, and a mechanism (as you propose), to back off on the precision as needed.</description>
		<content:encoded><![CDATA[<p>I tend to agree that millisecond precision might be overkill, but who is to know to what purpose Postgres will be put. I would not doubt that there is someone who would ask for this type of precision if you were to not provide it. <img src='http://thoughts.j-davis.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  I know for certain that there are applications out there that definitely are down to the hour, as I found out returning my car an hour late to Avis Rent A Car.  <img src='http://thoughts.j-davis.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Anyway, I would suggest (or maybe second) the notion of finest granularity by default, and a mechanism (as you propose), to back off on the precision as needed.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jeff Davis</title>
		<link>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/comment-page-1/#comment-133</link>
		<dc:creator>Jeff Davis</dc:creator>
		<pubDate>Mon, 09 Nov 2009 17:56:38 +0000</pubDate>
		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=180#comment-133</guid>
		<description>&lt;blockquote&gt;PERIOD needs some more docs and test cases as well.&lt;/blockquote&gt;

Yes. I&#039;ve been focusing on the core engine changes required, and Scott is working on the data types and their semantics.

&lt;blockquote&gt;first endpoint is inclusive and the second point is exclusive&lt;/blockquote&gt;

As a default, that&#039;s fine. But there are cases where you want an inclusive end time, as well.

&lt;blockquote&gt;Also think it would be useful to have a way to specify the granularity of the PERIOD.&lt;/blockquote&gt;

Scott had the same idea. I think this can be accomplished with a CHECK constraint, like
&lt;pre&gt;CHECK (
  first(during) = date_trunc(&#039;hour&#039;, first(during)) AND
  next(during) = date_trunc(&#039;hour&#039;, next(during))
)&lt;/pre&gt;
but it would be nice to make it a little easier to express.</description>
		<content:encoded><![CDATA[<blockquote><p>PERIOD needs some more docs and test cases as well.</p></blockquote>
<p>Yes. I&#8217;ve been focusing on the core engine changes required, and Scott is working on the data types and their semantics.</p>
<blockquote><p>first endpoint is inclusive and the second point is exclusive</p></blockquote>
<p>As a default, that&#8217;s fine. But there are cases where you want an inclusive end time, as well.</p>
<blockquote><p>Also think it would be useful to have a way to specify the granularity of the PERIOD.</p></blockquote>
<p>Scott had the same idea. I think this can be accomplished with a CHECK constraint, like</p>
<pre>CHECK (
  first(during) = date_trunc('hour', first(during)) AND
  next(during) = date_trunc('hour', next(during))
)</pre>
<p>but it would be nice to make it a little easier to express.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Scott Bailey</title>
		<link>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/comment-page-1/#comment-132</link>
		<dc:creator>Scott Bailey</dc:creator>
		<pubDate>Mon, 09 Nov 2009 17:01:40 +0000</pubDate>
		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=180#comment-132</guid>
		<description>Simon,

A period defines a contiguous set of values but we can define it by the end points. The defaults are inclusive start, exclusive end.

I think if you gave from() a little more thought, you&#039;d change your mind.

I agree with you on being able to define the granularity. But my thought is that it would have to be set at the database level rather than say at the column level.

I&#039;d like to generalize the concept so that we&#039;ll be able to define a range of ints, dates, timestamps, numeric, etc.</description>
		<content:encoded><![CDATA[<p>Simon,</p>
<p>A period defines a contiguous set of values but we can define it by the end points. The defaults are inclusive start, exclusive end.</p>
<p>I think if you gave from() a little more thought, you&#8217;d change your mind.</p>
<p>I agree with you on being able to define the granularity. But my thought is that it would have to be set at the database level rather than say at the column level.</p>
<p>I&#8217;d like to generalize the concept so that we&#8217;ll be able to define a range of ints, dates, timestamps, numeric, etc.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Adrian Klaver</title>
		<link>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/comment-page-1/#comment-131</link>
		<dc:creator>Adrian Klaver</dc:creator>
		<pubDate>Mon, 09 Nov 2009 16:05:45 +0000</pubDate>
		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=180#comment-131</guid>
		<description>Simon,
I don&#039;t know if you have access to the postgresqlconference.org site. If you do check out Scott Baileys talk on his temporal extensions to Postgres:

http://www.postgresqlconference.org/2009/west/talks/temporal_data

It is based on his CHRONOS project:
http://sourceforge.net/projects/chronosdb/

The extensions cover some of the points you raised.</description>
		<content:encoded><![CDATA[<p>Simon,<br />
I don&#8217;t know if you have access to the postgresqlconference.org site. If you do check out Scott Baileys talk on his temporal extensions to Postgres:</p>
<p><a href="http://www.postgresqlconference.org/2009/west/talks/temporal_data" rel="nofollow">http://www.postgresqlconference.org/2009/west/talks/temporal_data</a></p>
<p>It is based on his CHRONOS project:<br />
<a href="http://sourceforge.net/projects/chronosdb/" rel="nofollow">http://sourceforge.net/projects/chronosdb/</a></p>
<p>The extensions cover some of the points you raised.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: simon@2ndQuadrant.com</title>
		<link>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/comment-page-1/#comment-130</link>
		<dc:creator>simon@2ndQuadrant.com</dc:creator>
		<pubDate>Mon, 09 Nov 2009 13:23:46 +0000</pubDate>
		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=180#comment-130</guid>
		<description>Jeff,

PERIOD needs some more docs and test cases as well.

The docs that are there describe PERIOD as a &quot;set&quot; rather than as two endpoints, which is how all examples are laid out. I think a PERIOD should have two endpoints (+/- infinity included). If you have need for a more fancy datatype it can have a slightly different name. So the next() and prior() functions just look a little overcooked. Better to have just from() and to() or similar.

Think it would be useful to have the default as the first endpoint is inclusive and the second point is exclusive, so they stack together neatly by default.

Also think it would be useful to have a way to specify the granularity of the PERIOD. In many cases a period is expressed only in days, not timestamps. For example in your hotel room example you don&#039;t book a hotel room for an exact second, you book one night at a time. Of course, some hotels do book by the hour, but very few by the second. It wouldn&#039;t be much use for businesses to refuse a booking because there was a 10ms overlap between 2 5 day bookings - nor would anybody really want to record that detail. 

So perhaps we need DATEPERIOD (2 DATEs) and TIMEPERIOD (2 TIMESTAMPs). It might be useful to be able to specify this like PERIOD(INTERVAL), so you can book holiday cottages in weeks, scaffolding by month etc.. Whatever the business needs.</description>
		<content:encoded><![CDATA[<p>Jeff,</p>
<p>PERIOD needs some more docs and test cases as well.</p>
<p>The docs that are there describe PERIOD as a &#8220;set&#8221; rather than as two endpoints, which is how all examples are laid out. I think a PERIOD should have two endpoints (+/- infinity included). If you have need for a more fancy datatype it can have a slightly different name. So the next() and prior() functions just look a little overcooked. Better to have just from() and to() or similar.</p>
<p>Think it would be useful to have the default as the first endpoint is inclusive and the second point is exclusive, so they stack together neatly by default.</p>
<p>Also think it would be useful to have a way to specify the granularity of the PERIOD. In many cases a period is expressed only in days, not timestamps. For example in your hotel room example you don&#8217;t book a hotel room for an exact second, you book one night at a time. Of course, some hotels do book by the hour, but very few by the second. It wouldn&#8217;t be much use for businesses to refuse a booking because there was a 10ms overlap between 2 5 day bookings &#8211; nor would anybody really want to record that detail. </p>
<p>So perhaps we need DATEPERIOD (2 DATEs) and TIMEPERIOD (2 TIMESTAMPs). It might be useful to be able to specify this like PERIOD(INTERVAL), so you can book holiday cottages in weeks, scaffolding by month etc.. Whatever the business needs.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: simon@2ndQuadrant.com</title>
		<link>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/comment-page-1/#comment-129</link>
		<dc:creator>simon@2ndQuadrant.com</dc:creator>
		<pubDate>Mon, 09 Nov 2009 13:11:22 +0000</pubDate>
		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=180#comment-129</guid>
		<description>Tom, interesting post. Good solutions thinking.

The trigger method, as described, doesn&#039;t correctly handle concurrent inserts. That illustrates my main concern which is that there is too much code there and it is likely to have errors - I think you showed that in the number of posts taken as well. Anyhow, it is certainly slower than the in-database approach Jeff has designed.</description>
		<content:encoded><![CDATA[<p>Tom, interesting post. Good solutions thinking.</p>
<p>The trigger method, as described, doesn&#8217;t correctly handle concurrent inserts. That illustrates my main concern which is that there is too much code there and it is likely to have errors &#8211; I think you showed that in the number of posts taken as well. Anyhow, it is certainly slower than the in-database approach Jeff has designed.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Tom Davis</title>
		<link>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/comment-page-1/#comment-128</link>
		<dc:creator>Tom Davis</dc:creator>
		<pubDate>Mon, 09 Nov 2009 09:40:08 +0000</pubDate>
		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=180#comment-128</guid>
		<description>Wow! I did it again!

CREATE OR REPLACE FUNCTION dont_overlap_reservations() RETURNS trigger AS $$
DECLARE
  overlaps    text;
BEGIN
  SELECT whom INTO overlaps
    FROM  room_reservation
    WHERE room      = NEW.room
      AND beginning &lt;= NEW.until
      AND until     &gt;= NEW.beginning
      LIMIT 1;
  IF FOUND THEN
      RETURN NULL;
  ELSE
      RETURN NEW;
  END IF;
END;
$$ LANGUAGE plpgsql;</description>
		<content:encoded><![CDATA[<p>Wow! I did it again!</p>
<p>CREATE OR REPLACE FUNCTION dont_overlap_reservations() RETURNS trigger AS $$<br />
DECLARE<br />
  overlaps    text;<br />
BEGIN<br />
  SELECT whom INTO overlaps<br />
    FROM  room_reservation<br />
    WHERE room      = NEW.room<br />
      AND beginning &lt;= NEW.until<br />
      AND until     &gt;= NEW.beginning<br />
      LIMIT 1;<br />
  IF FOUND THEN<br />
      RETURN NULL;<br />
  ELSE<br />
      RETURN NEW;<br />
  END IF;<br />
END;<br />
$$ LANGUAGE plpgsql;</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Tom Davis</title>
		<link>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/comment-page-1/#comment-127</link>
		<dc:creator>Tom Davis</dc:creator>
		<pubDate>Mon, 09 Nov 2009 09:38:56 +0000</pubDate>
		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=180#comment-127</guid>
		<description>Sorry, forgot to escape the greater than and less than operators, the function should be

CREATE OR REPLACE FUNCTION dont_overlap_reservations() RETURNS trigger AS $$
DECLARE
  overlaps    text;
BEGIN
  SELECT whom INTO overlaps
    FROM  room_reservation
    WHERE room      = NEW.room
      AND beginning = NEW.beginning
      LIMIT 1;
  IF FOUND THEN
      RETURN NULL;
  ELSE
      RETURN NEW;
  END IF;
END;
$$ LANGUAGE plpgsql;


Plus, obviously, you would use a timestamp rather than a time,

and it&#039;s probably a good idea to declare (room,beginning,until) as the primary key. For more than five rows an index might be useful.</description>
		<content:encoded><![CDATA[<p>Sorry, forgot to escape the greater than and less than operators, the function should be</p>
<p>CREATE OR REPLACE FUNCTION dont_overlap_reservations() RETURNS trigger AS $$<br />
DECLARE<br />
  overlaps    text;<br />
BEGIN<br />
  SELECT whom INTO overlaps<br />
    FROM  room_reservation<br />
    WHERE room      = NEW.room<br />
      AND beginning = NEW.beginning<br />
      LIMIT 1;<br />
  IF FOUND THEN<br />
      RETURN NULL;<br />
  ELSE<br />
      RETURN NEW;<br />
  END IF;<br />
END;<br />
$$ LANGUAGE plpgsql;</p>
<p>Plus, obviously, you would use a timestamp rather than a time,</p>
<p>and it&#8217;s probably a good idea to declare (room,beginning,until) as the primary key. For more than five rows an index might be useful.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Tom Davis</title>
		<link>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/comment-page-1/#comment-126</link>
		<dc:creator>Tom Davis</dc:creator>
		<pubDate>Mon, 09 Nov 2009 09:28:34 +0000</pubDate>
		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=180#comment-126</guid>
		<description>Obviously the EXCLUDES constraint is more general, and easier for the user to implement, so that&#039;s awesome, but I think the following solution works as well for this specific instance.

DROP TABLE IF EXISTS room_reservation CASCADE;

CREATE TABLE room_reservation (
    whom        TEXT NOT NULL,
    room        TEXT NOT NULL,
    beginning   TIME WITHOUT TIME ZONE NOT NULL,
    until       TIME WITHOUT TIME ZONE NOT NULL
  );

CREATE OR REPLACE FUNCTION dont_overlap_reservations() RETURNS trigger AS $$
    DECLARE
        overlaps    text;
    BEGIN
        SELECT whom INTO overlaps
          FROM  room_reservation
          WHERE room      = NEW.room
            AND beginning  NEW.beginning
            LIMIT 1;
        IF FOUND THEN
            RETURN NULL;
        ELSE
            RETURN NEW;
        END IF;
    END;
$$ LANGUAGE plpgsql;

-- WARNING
-- multiple triggers on a single table are called in alphabetical
-- order so lets name our trigger something z-ish
CREATE TRIGGER zzz_no_overlap
    BEFORE INSERT OR UPDATE ON room_reservation
    FOR EACH ROW
    EXECUTE PROCEDURE dont_overlap_reservations();


INSERT INTO room_reservation (whom,room,beginning,until)
    VALUES (&#039;Ashley&#039;, &#039;2B&#039;, &#039;8:00&#039;,  &#039;9:00&#039;);
INSERT INTO room_reservation (whom,room,beginning,until)
    VALUES (&#039;Brett&#039;,  &#039;2B&#039;,&#039;10:00&#039;, &#039;13:00&#039;);
INSERT INTO room_reservation (whom,room,beginning,until)
    VALUES (&#039;Chris&#039;,  &#039;2B&#039;,&#039;14:00&#039;, &#039;15:00&#039;);
INSERT INTO room_reservation (whom,room,beginning,until)
    VALUES (&#039;Dale&#039;,   &#039;2B&#039;,&#039;17:00&#039;, &#039;18:00&#039;);
INSERT INTO room_reservation (whom,room,beginning,until)
    VALUES (&#039;Eden&#039;,   &#039;2B&#039;,&#039;12:00&#039;, &#039;16:00&#039;);</description>
		<content:encoded><![CDATA[<p>Obviously the EXCLUDES constraint is more general, and easier for the user to implement, so that&#8217;s awesome, but I think the following solution works as well for this specific instance.</p>
<p>DROP TABLE IF EXISTS room_reservation CASCADE;</p>
<p>CREATE TABLE room_reservation (<br />
    whom        TEXT NOT NULL,<br />
    room        TEXT NOT NULL,<br />
    beginning   TIME WITHOUT TIME ZONE NOT NULL,<br />
    until       TIME WITHOUT TIME ZONE NOT NULL<br />
  );</p>
<p>CREATE OR REPLACE FUNCTION dont_overlap_reservations() RETURNS trigger AS $$<br />
    DECLARE<br />
        overlaps    text;<br />
    BEGIN<br />
        SELECT whom INTO overlaps<br />
          FROM  room_reservation<br />
          WHERE room      = NEW.room<br />
            AND beginning  NEW.beginning<br />
            LIMIT 1;<br />
        IF FOUND THEN<br />
            RETURN NULL;<br />
        ELSE<br />
            RETURN NEW;<br />
        END IF;<br />
    END;<br />
$$ LANGUAGE plpgsql;</p>
<p>&#8211; WARNING<br />
&#8211; multiple triggers on a single table are called in alphabetical<br />
&#8211; order so lets name our trigger something z-ish<br />
CREATE TRIGGER zzz_no_overlap<br />
    BEFORE INSERT OR UPDATE ON room_reservation<br />
    FOR EACH ROW<br />
    EXECUTE PROCEDURE dont_overlap_reservations();</p>
<p>INSERT INTO room_reservation (whom,room,beginning,until)<br />
    VALUES (&#8216;Ashley&#8217;, &#8216;2B&#8217;, &#8216;8:00&#8242;,  &#8216;9:00&#8242;);<br />
INSERT INTO room_reservation (whom,room,beginning,until)<br />
    VALUES (&#8216;Brett&#8217;,  &#8216;2B&#8217;,'10:00&#8242;, &#8216;13:00&#8242;);<br />
INSERT INTO room_reservation (whom,room,beginning,until)<br />
    VALUES (&#8216;Chris&#8217;,  &#8216;2B&#8217;,'14:00&#8242;, &#8216;15:00&#8242;);<br />
INSERT INTO room_reservation (whom,room,beginning,until)<br />
    VALUES (&#8216;Dale&#8217;,   &#8216;2B&#8217;,'17:00&#8242;, &#8216;18:00&#8242;);<br />
INSERT INTO room_reservation (whom,room,beginning,until)<br />
    VALUES (&#8216;Eden&#8217;,   &#8216;2B&#8217;,'12:00&#8242;, &#8216;16:00&#8242;);</p>
]]></content:encoded>
	</item>
</channel>
</rss>
