<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	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/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Experimental Thoughts</title>
	<atom:link href="http://thoughts.j-davis.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://thoughts.j-davis.com</link>
	<description>Ideas on Databases, Logic, and Language by Jeff Davis</description>
	<lastBuildDate>Wed, 10 Mar 2010 04:56:27 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Temporal PostgreSQL Roadmap</title>
		<link>http://thoughts.j-davis.com/2010/03/09/temporal-postgresql-roadmap/</link>
		<comments>http://thoughts.j-davis.com/2010/03/09/temporal-postgresql-roadmap/#comments</comments>
		<pubDate>Wed, 10 Mar 2010 04:49:06 +0000</pubDate>
		<dc:creator>Jeff Davis</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Language]]></category>
		<category><![CDATA[Logic]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Temporal]]></category>

		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=254</guid>
		<description><![CDATA[Why are temporal extensions in PostgreSQL important? Quite simply, managing time data is one of the most common requirements, and current general-purpose database systems don&#8217;t provide us with the basic tools to do it. Every general-purpose DBMS falls short both in terms of usability and performance when trying to manage temporal data.
What is already done?


PERIOD [...]]]></description>
			<content:encoded><![CDATA[<p>Why are temporal extensions in PostgreSQL important? Quite simply, managing time data is one of the most common requirements, and current general-purpose database systems don&#8217;t provide us with the basic tools to do it. Every general-purpose DBMS falls short both in terms of usability and performance when trying to manage temporal data.</p>
<p>What is already done?</p>
<p><span id="more-254"></span></p>
<ul>
<li><a href="http://pgfoundry.org/projects/temporal">PERIOD data type</a>, which can represent anchored intervals of time; that is, a chunk of time with a definite beginning and a definite end (in contrast to a SQL INTERVAL, which is not anchored to any specific beginning or end time).
<ul>
<li>Critical for usability because it acts as a <em>set</em> of time, so you can easily test for containment and other operations without using awkward constructs like BETWEEN or lots of comparisons (and keeping track of inclusivity/exclusivity of boundary points).</li>
<li>Critical for performance because you can index the values for efficient &#8220;contains&#8221; and &#8220;overlaps&#8221; queries (among others).</li>
</ul>
</li>
</ul>
<ul>
<li>Temporal Keys (called Exclusion Constraints, and will be available in the next release of PostgreSQL, 9.0), which can enforce the constraint that no two periods of time (usually for a given resource, like a person) overlap. See the <a href="http://developer.postgresql.org/pgdocs/postgres/sql-createtable.html">documentation</a> (look for the word &#8220;EXCLUDE&#8221;), and see my previous articles (<a href="../2009/11/01/temporal-keys-part-1/">part 1</a> and <a href="../2009/11/08/temporal-keys-part-2/">part 2</a>) on the subject.
<ul>
<li>Critical for usability to avoid procedural, error-prone hacks to enforce the constraint with triggers or by splitting time into big chunks.</li>
<li>Critical for performance because it performs comparably to a UNIQUE index, unlike the other procedural hacks which are generally too slow to use for most real systems.</li>
</ul>
</li>
</ul>
<p>What needs to be done?</p>
<ul>
<li>Range Types &#8212; Aside from PERIOD, which is based on TIMESTAMPTZ, it would also be useful to have very similar types based on, for example, DATE. It doesn&#8217;t stop there, so the natural conclusion is to generalize PERIOD into &#8220;range types&#8221; which could be based on almost any subtype.</li>
<li>Range Keys, Foreign Range Keys &#8212; If Range Types are known to the Postgres engine, that means that we can have syntactic sugar for range keys (like temporal keys, except for any range type), etc., that would internally use Exclusion Constraints.</li>
<li>Range Join &#8212; If Range Types are known to the Postgres engine, there could be syntactic sugar for a &#8220;range join,&#8221; that is, a join based on &#8220;overlaps&#8221; rather than &#8220;equals&#8221;. More importantly, there could be a new join type, a Range Merge Join, that could perform this join efficiently (without a Range Merge Join, a range join would always be a nested loop join).</li>
<li>Simple table logs &#8212; The ability to easily create an effective &#8220;audit log&#8221; or similar trigger-based table log, that can record changes and be efficiently queried for historical state or state changes.</li>
</ul>
<p>I&#8217;ll be speaking on this subject (specifically, the new Exclusion Constraints feature) in the upcoming <a href="http://postgresqlconference.org">PostgreSQL Conference EAST 2010</a> (my <a href="http://postgresqlconference.org/2010/east/talks/not_just_unique_exclusion_constraints">talk description</a>) in Philadelphia later this month and <a href="http://pgcon.org">PGCon 2010</a> (my <a href="http://www.pgcon.org/2010/schedule/events/201.en.html">talk description</a>) in Ottawa this May. In the past, these conferences and others have been a great place to get ideas and help me move the temporal features forward.</p>
<p>The existing features have been picking up a little steam lately. The <a href="http://lists.pgfoundry.org/pipermail/temporal-general/">temporal-general mailing list</a> has some traffic now &#8212; fairly low, but enough that others contribute to the discussions, which is a great start. I&#8217;ve also received some great feedback from a number of people, including the folks at <a href="http://pgexperts.com">PGX</a>. There&#8217;s still a ways to go before we have all the features we want, but progress is being made.</p>
]]></content:encoded>
			<wfw:commentRss>http://thoughts.j-davis.com/2010/03/09/temporal-postgresql-roadmap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Scalability and the Relational Model</title>
		<link>http://thoughts.j-davis.com/2010/03/07/scalability-and-the-relational-model/</link>
		<comments>http://thoughts.j-davis.com/2010/03/07/scalability-and-the-relational-model/#comments</comments>
		<pubDate>Sun, 07 Mar 2010 21:37:24 +0000</pubDate>
		<dc:creator>Jeff Davis</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Language]]></category>
		<category><![CDATA[Logic]]></category>
		<category><![CDATA[NoSQL]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=242</guid>
		<description><![CDATA[The relational model is just a way to represent reality. It happens to have some very useful properties, such as closure over many useful operations &#8212; but it&#8217;s a purely logical model of reality. You can implement relational operations using hash joins, MapReduce, or pen and paper.
So, right away, it&#8217;s meaningless to talk about the [...]]]></description>
			<content:encoded><![CDATA[<p>The relational model is just a way to represent reality. It happens to have some very useful properties, such as closure over many useful operations &#8212; but it&#8217;s a purely logical model of reality. You can implement relational operations using hash joins, MapReduce, or pen and paper.</p>
<p>So, right away, it&#8217;s meaningless to talk about the scalability of the relational model. Given a particular question, it might be difficult to break it down into bite-sized pieces and distribute it to N worker nodes. But going with MapReduce doesn&#8217;t solve that scalability problem &#8212; it just means that you will have a hard time defining a useful map or reduce operation, or you will have to change the question into something easier to answer.</p>
<p><span id="more-242"></span>There may exist scalability problems in:</p>
<ul>
<li>SQL, which defines requirements outside the scope of the relational model, such as ACID properties and transactional semantics.</li>
<li>Traditional architectures and implementations of SQL, such as the &#8220;table is a file&#8221; equivalence, lack of sophisticated types, etc.</li>
<li>Particular implementations of SQL &#8212; e.g. &#8220;MySQL can&#8217;t do it, so the relational model doesn&#8217;t scale&#8221;.</li>
</ul>
<p>Why are these distinctions important? As with many debates, <a title="Terminology Confusion" href="http://thoughts.j-davis.com/2007/12/11/terminology-confusion/">terminology confusion</a> is at the core, and prevents us from dealing with the problems directly. If SQL is defined in a way that causes scalability problems, we need to identify precisely those requirements that cause a problem, so that we can proceed forward without losing all that has been gained. If the traditional architectures are not suitable for some important use-cases, they need to be adapted. If some particular implementations are not suitable, developers need to switch or demand that it be made competitive.</p>
<p>The NoSQL movement (or at least the hype surrounding it) is far too disorganized to make real progress. Usually, incremental progress is best; and sometimes a fresh start is best, after drawing on years of lessons learned. But it&#8217;s never a good idea to start over with complete disregard for the past. For instance, an <a href="http://about.digg.com/blog/looking-future-cassandra">article from Digg</a> starts off great:</p>
<blockquote><p>The fundamental problem is endemic to the relational database mindset, which places the burden of computation on reads rather than writes.</p></blockquote>
<p>That&#8217;s good because he blames it on the <em>mindset</em> not the <em>model</em>,<em> </em>and then identifies a specific problem. But then the article completely falls flat:</p>
<blockquote><p>Computing the intersection with a JOIN is much too slow in MySQL, so we have to do it in PHP.</p></blockquote>
<p>A join is faster in PHP than MySQL? Why bother even discussing SQL versus NoSQL if your particular implementation of SQL &#8212; MySQL &#8212; can&#8217;t even do a hash join, the exact operation that you need? Particularly when almost every other implementation can (including PostgreSQL)? That kind of reasoning won&#8217;t lead to solutions.</p>
<p>So, where do we go from here?</p>
<ol>
<li>Separate the SQL model from the other requirements (some of which may limit scalability) when discussing improvements.</li>
<li>Improve the SQL model (my readers know that I&#8217;ve criticized SQL&#8217;s logical problems many times in the past).</li>
<li>Improve the implementations of SQL, particularly how tables are physically stored.</li>
<li>If you&#8217;re particularly ambitious, come up with a relational alternative to SQL that takes into account what&#8217;s been learned after decades of SQL and can become the next general-purpose DBMS language.</li>
</ol>
<p>EDIT 2010-03-09: I should have cited Josh Berkus&#8217;s talk on <a href="http://www.pgexperts.com/document.html?id=40">Relational vs. Non-Relational</a> (complete list of <a href="http://www.pgexperts.com/presentations.html">PGX talks</a>), which was part of the inspiration for this post.<a href="http://www.pgexperts.com/document.html?id=40"><br />
</a></p>
]]></content:encoded>
			<wfw:commentRss>http://thoughts.j-davis.com/2010/03/07/scalability-and-the-relational-model/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Good Error Recovery is Hard, So Use PostgreSQL</title>
		<link>http://thoughts.j-davis.com/2009/12/23/good-error-recovery-is-hard-so-use-postgresql/</link>
		<comments>http://thoughts.j-davis.com/2009/12/23/good-error-recovery-is-hard-so-use-postgresql/#comments</comments>
		<pubDate>Thu, 24 Dec 2009 04:29:04 +0000</pubDate>
		<dc:creator>Jeff Davis</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=221</guid>
		<description><![CDATA[My last post about the Linux OOM Killer got a lot of attention, and I don&#8217;t intend to re-open the whole discussion. However, a couple themes developed that I think warrant some further discussion. One of those themes is that &#8220;error recovery is hard,&#8221; and that&#8217;s absolutely true. It&#8217;s so true that many of the [...]]]></description>
			<content:encoded><![CDATA[<p>My <a href="http://thoughts.j-davis.com/2009/11/29/linux-oom-killer/">last post about the Linux OOM Killer</a> got a lot of attention, and I don&#8217;t intend to re-open the whole discussion. However, a couple themes developed that I think warrant some further discussion. One of those themes is that &#8220;error recovery is hard,&#8221; and that&#8217;s absolutely true. It&#8217;s so true that many of the kernel developers seemed to develop a more extreme version: &#8220;error recovery is infeasible&#8221; (see this <a href="http://article.gmane.org/gmane.comp.audio.jackit/19998">post</a>) &#8212; but that is <strong>not</strong> true.</p>
<p><a href="http://postgresql.org">PostgreSQL</a> is a great example: it gets error recovery <em>right</em>. I didn&#8217;t say it&#8217;s perfect, but graceful degradation is designed into postgres from the start, and if it doesn&#8217;t degrade gracefully, that&#8217;s probably a bug. I am saying this as a person who experienced a not-so-graceful PostgreSQL problem first-hand back in 2006 that was due to an OOM condition on FreeBSD (and yes, <code>malloc()</code> returned <code>NULL</code>). But:</p>
<ul>
<li>The bug occurred under fairly strange circumstances where very small allocations ate the memory very completely, leaving no room (not even a few dozen bytes), during a transaction that included extensive DDL (note that most database systems can&#8217;t even do transactional DDL at all).</li>
<li>The bug was <a href="http://git.postgresql.org/gitweb?p=postgresql.git;a=commit;h=56fb6b53d215d88a581588450a997d31959df9e8">fixed</a>!</li>
</ul>
<p>A database system getting error recovery right is no accident, fluke, or statistical outlier. Database systems are meant to get this right <em>so that applications do not have to do it</em>. In other words, write applications against PostgreSQL (or another robust relational database system), and you get error recovery for free.</p>
<p>The application may be performing a complex series of updates or state changes, and trying to ensure graceful error recovery without a database system to help may be nearly impossible. But by simply wrapping those operations in a single transaction, the application only needs to know how to handle a few classes of errors, and will always see consistent data afterward.</p>
<p>Moral of the story: if you&#8217;re not building on top of a robust DBMS, and you have any significant amount of state to manage, then you are not getting error recovery right (unless you put in the serious analysis required). Similar problems apply to those using a DBMS like a filesystem, with no constraints (to prevent corruption) and related operations split across transactional boundaries. So: use PostgreSQL, use transactions, and use constraints.</p>
]]></content:encoded>
			<wfw:commentRss>http://thoughts.j-davis.com/2009/12/23/good-error-recovery-is-hard-so-use-postgresql/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Linux OOM Killer</title>
		<link>http://thoughts.j-davis.com/2009/11/29/linux-oom-killer/</link>
		<comments>http://thoughts.j-davis.com/2009/11/29/linux-oom-killer/#comments</comments>
		<pubDate>Mon, 30 Nov 2009 04:07:20 +0000</pubDate>
		<dc:creator>Jeff Davis</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[PostgreSQL]]></category>

		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=200</guid>
		<description><![CDATA[The Linux OOM Killer heuristic can be summed up as:

Run out of memory.
Kill PostgreSQL.
Look for processes that might be using too much memory, and kill them, hopefully freeing memory.

Notice that step #2 is independent of factors like:

Is PostgreSQL consuming a significant share of the memory resources?
Will killing PostgreSQL alleviate any memory pressure at all?

The reason [...]]]></description>
			<content:encoded><![CDATA[<p>The Linux OOM Killer heuristic can be summed up as:</p>
<ol>
<li>Run out of memory.</li>
<li>Kill PostgreSQL.</li>
<li>Look for processes that might be using too much memory, and kill them, hopefully freeing memory.</li>
</ol>
<p>Notice that step #2 is independent of factors like:</p>
<ul>
<li>Is PostgreSQL consuming a significant share of the memory resources?</li>
<li>Will killing PostgreSQL alleviate any memory pressure at all?<span id="more-200"></span></li>
</ul>
<p>The reason for this is because linux, when under memory pressure, invokes the <code><a href="http://linux-mm.org/OOM_Killer">badness()</a></code> function to determine which process to kill. One of the things that the function counts against a process is the total VM size of the process (no surprise), plus half of the total VM size of all of the children. That sounds reasonable &#8212; wait a minute, what about shared memory?</p>
<p>For every connection, PostgreSQL makes a child process, which shares memory with the parent; and if it is a big postgres instance, that can be a significant amount of memory. But the badness function counts the same shared memory against the parent 1+N/2 times, where N is the number of connections open! For example, if you have shared_buffers set to 1GB on an 8GB machine, and have 20 connections open, then the linux badness function thinks that PostgreSQL is using about 11GB of memory, when it is actually using 1GB!</p>
<p>It gets worse: killing a process does not free shared memory. And there are already administrator-controlled limits that start off fairly low (32MB, I think), so the administrator would have to make a mistake in order for shared memory to be the problem.</p>
<p>And it gets even worse: linux makes very little attempt to avoid getting into a bad situation in the first place. On most operating systems, if you ask for too much memory, <code>malloc()</code> will eventually fail, which will probably cause the application to terminate; or if the application is written to handle such conditions (like PostgreSQL), it will gracefully degrade. On linux, by default, <code>brk()</code> (the system call underlying <code>malloc()</code>) will happily return success in almost every situation, and the first hint that your application gets of a problem is a SIGKILL.</p>
<p>I have heard many excuses for this appalling set of behaviors, but none of them are satisfactory to me. Here is <a href="http://article.gmane.org/gmane.comp.audio.jackit/19998">one explanation</a>. Notice the implicit assumption is that the most important thing that a user may be running is a text editor, and it should just autosave every once in a while to avoid problems. Keeping processes actually running is apparently not important to Linux. The next general philosophy present in the email is that applications are stupid, and they will never get the error paths or rollback right, but PostgreSQL seems to do that just fine (it needs very little memory to roll back, and tries to free memory before that to make it less likely to run into a problem). And the author seems to think that the first hint of a system problem should be a SIGKILL based on some heuristic (which, in the case of linux, is seriously flawed, as I pointed out above).</p>
<p>Among other justifications are:</p>
<ul>
<li><em>&#8220;You can configure linux to prevent this problem.&#8221;</em> Sounds like MySQL, where you have to declare your tables to be transaction-safe. Why not safe by default, and configure for performance?</li>
<li><em>&#8220;Modern systems overcommit, and there are always edge cases where you get into problems.&#8221; </em>OK, so keep them as edge cases, and at least attempt to avoid problem situations. If some bizarre set of circumstances, or maybe a malicious process, is able to cause memory problems on your system, then so be it. But well-behaved processes should get some indication of trouble prior to SIGKILL. And the real problem processes should be identified with some accuracy and reasonable intelligence (i.e. recognize that shared memory can&#8217;t be freed by the OOM Killer, and therefore shouldn&#8217;t be considered).</li>
</ul>
<p>I have been complaining about this insanity for years:</p>
<ul>
<li><a href="http://www.mail-archive.com/linux-kernel@vger.kernel.org/msg120596.html">http://www.mail-archive.com/linux-kernel@vger.kernel.org/msg120596.html</a> (2007)</li>
<li><a href="http://lkml.org/lkml/2007/2/9/275">http://lkml.org/lkml/2007/2/9/275</a> (2007)</li>
<li><a href="http://lkml.org/lkml/2008/2/5/495">http://lkml.org/lkml/2008/2/5/495</a> (2008)</li>
</ul>
<p>It makes me happy that other free software operating systems are still under active development, as illustrated by the recent release of <a href="http://www.freebsd.org/releases/8.0R/announce.html">FreeBSD 8.0</a>. I am not saying FreeBSD is better in every way than Linux, but I do believe that competition is important even in the free software world.</p>
<p>This post is obviously a rant, and an ill-informed one, at that. I am no expert on memory management, so let me know if I am way off-base. However, I have never heard reasonable justifications for these things in response to my emails to lkml, in personal discussions with kernel hackers or other knowledgeable Linux folk, or anywhere else. If you do have a reasonable explanation, I am more than willing to listen.</p>
]]></content:encoded>
			<wfw:commentRss>http://thoughts.j-davis.com/2009/11/29/linux-oom-killer/feed/</wfw:commentRss>
		<slash:comments>30</slash:comments>
		</item>
		<item>
		<title>Temporal Keys, Part 2</title>
		<link>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/</link>
		<comments>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/#comments</comments>
		<pubDate>Mon, 09 Nov 2009 05:48:37 +0000</pubDate>
		<dc:creator>Jeff Davis</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Language]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Temporal]]></category>

		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=180</guid>
		<description><![CDATA[In the last article, I argued that:

A schedule conflict is a typical business problem.
The later you try to resolve a schedule conflict, the more costly it is to resolve.
In particular, there is a big jump in the cost the moment after conflicting data is recorded.
Therefore, it&#8217;s best for the DBMS itself to enforce the constraint, [...]]]></description>
			<content:encoded><![CDATA[<p>In the <a title="Temporal Keys, Part 1" href="http://thoughts.j-davis.com/2009/11/01/temporal-keys-part-1/">last article</a>, I argued that:</p>
<ul>
<li>A schedule conflict is a typical business problem.</li>
<li>The later you try to resolve a schedule conflict, the more costly it is to resolve.</li>
<li>In particular, there is a big jump in the cost the moment after conflicting data is recorded.</li>
<li>Therefore, it&#8217;s best for the DBMS itself to enforce the constraint, because only the DBMS can avoid the conflict effectively before the conflict is recorded.</li>
</ul>
<p>Then, I opened up a discussion to see how people are dealing with these schedule conflicts. In the comments I received at the end of the article, as well as other anecdotes from conferences, user groups, mailing lists, and my own experience, the solutions fall into a few categories:</p>
<p><span id="more-180"></span></p>
<ul>
<li>The rate of conflicts is so low that the costs are not important. For instance, you may make 0.1% of your customers unhappy, and need to refund them, but perhaps that&#8217;s a cost you&#8217;re willing to pay.</li>
<li>The application receives so few requests that performance is not an object, and serialization of all requests is a viable option. The serialization is done using big locks and a read-check-write cycle. Even if performance is not an object, these applications sometimes run into maintenance problems or unexpected outages because of the big locks required.</li>
<li>You can break the time slices into manageable chunks, e.g. one day chunks aligned at midnight. This kind of solution is highly specific to the business, reduces the flexibility of the business, and often requires a substantial amount of custom, error-prone procedural code.</li>
<li>Complex procedural code: usually a mix of application code, functions in the DBMS, row-level locking, static data in tables that only exists for the purposes of row-level locks, etc. This kind of solution is generally very specific to the application and the business, requires lots of very error-prone custom procedural code, is difficult to adequately test, and it&#8217;s hard to understand what&#8217;s going on in the system at any given time. Hunting down sporadic performance problems would be a nightmare.</li>
</ul>
<p>Those solutions just aren&#8217;t good enough. We use relational database systems because they are smart, declarative, generally useful for many problems, and maintainable (Note: these principles contrast with <a href="http://en.wikipedia.org/wiki/NoSQL">NoSQL</a>, which is moving in the opposite direction &#8212; more on that in another article).</p>
<p><em>[UPDATE: The following project has been committed for the next release of PostgreSQL; the feature is now called "Exclusion Constraints"; and the new version of PostgreSQL will be called 9.0 (not 8.5). See the <a href="http://developer.postgresql.org/pgdocs/postgres/sql-createtable.html">documentation</a> under the heading "EXCLUDE".]</em></p>
<p>The project that I&#8217;ve been working on for PostgreSQL 8.5 is called &#8220;<a href="https://commitfest.postgresql.org/action/patch_view?id=199">Operator Exclusion Constraints</a>&#8220;. These are a new type of constraint that most closely resembles the <code>UNIQUE</code> constraint, because one tuple can preclude the existence of other tuples. With a <code>UNIQUE</code> constraint on attribute <code>A</code> of a table with attributes <code>(A, B, C)</code>, the existence of the tuple <code>(5, 6, 7)</code> precludes the existence of any tuple <code>(5, _, _)</code> in that table at the same time. This is different from a foreign key, which <em>requires</em> the existence of a tuple in another table; and different from a <code>CHECK</code> constraint which rejects tuples independently from any other tuple in any table (and the same goes for NOT NULL).</p>
<p>The same semantics as a <code>UNIQUE</code> constraint can be easily specified as an Operator Exclusion Constraint, with a minor performance penalty at insert time (one additional index search, usually only touching pages that are already in cache). Exclusion constraints are more general than <code>UNIQUE</code>, however. For instance, with a complex type such as <a href="http://www.postgresql.org/docs/8.4/static/datatype-geometric.html#AEN6211">CIRCLE</a>, you can specify that no two circles in a table overlap &#8212; which is a constraint that is impossible to specify otherwise (without resorting to the poor solutions mentioned above<a title="Temporal Keys, Part 1" href="../2009/11/01/temporal-keys-part-1/"></a>).</p>
<p>This applies to temporal keys very nicely. First, get the <a href="http://pgfoundry.org/projects/temporal">PERIOD data type</a>, which allows you a better way to work with periods of time (sets of time, really), rather than points in time. Second, you need to install the <a href="http://www.postgresql.org/docs/8.4/static/btree-gist.html">btree_gist</a> contrib module. Then, use an exclusion constraint like so:</p>
<p><em>[UPDATE 2010-03-09: Syntax updated to reflect the version of this project committed for PostgreSQL 9.0. ]</em></p>
<pre>CREATE TABLE room_reservation
(
  name   TEXT,
  room   TEXT,
  during PERIOD,
  EXCLUDE USING gist (room WITH =, during WITH &amp;&amp;)
);</pre>
<p>That will prevent two reservations on the same room from overlapping. There are a few pieces to this that require explanation:</p>
<ul>
<li><code>&amp;&amp;</code> is the &#8220;overlaps&#8221; operator for the <code>PERIOD</code> data type.</li>
<li><code>USING gist</code> tells PostgreSQL what kind of index to create to enforce this constraint. The operators must map to search strategies for this index method, and searching for overlapping periods requires a GiST index.</li>
<li>Because we are using GiST, we need GiST support for equality searches for the <code>TEXT</code> data type, which is the reason we need the btree_gist contrib module.</li>
<li>Conflicts will only occur if two tuples have equal room numbers, <em>and</em> overlapping periods of time for the reservation.</li>
</ul>
<p>This solution:</p>
<ul>
<li>Performs well under light and heavy contention. Not quite as well as a UNIQUE constraint, but much better than the alternatives, and without the surprises you might get from using big locks. Note that the constraint <em>will </em>be enforced at some point, so ignoring the problem is not a high-performance alternative (interpersonal communication has higher latency than a computer).</li>
<li>Is declarative. The implementation shows through a little bit &#8212; the user will know that an index is being used, for instance &#8212; but it&#8217;s a relatively simple declaration. As a consequence, it&#8217;s not very error-prone from the schema designer&#8217;s standpoint.</li>
<li>Is not specific to the business. You don&#8217;t have to decide on an appropriate time slice (e.g. one hour, one day, etc.); you don&#8217;t have to try to partition locks in creative ways; you don&#8217;t have to write procedural code (in the database system or application); and you don&#8217;t have to come up with interesting ways to detect a conflict or notify the user.</li>
</ul>
<p>Temporal keys are just one part of the support required for effective temporal data management inside the DBMS. However, it&#8217;s one of the most important pieces that requires support from the core engine, and cannot be implemented as a module.</p>
]]></content:encoded>
			<wfw:commentRss>http://thoughts.j-davis.com/2009/11/08/temporal-keys-part-2/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Temporal Keys, Part 1</title>
		<link>http://thoughts.j-davis.com/2009/11/01/temporal-keys-part-1/</link>
		<comments>http://thoughts.j-davis.com/2009/11/01/temporal-keys-part-1/#comments</comments>
		<pubDate>Mon, 02 Nov 2009 00:55:34 +0000</pubDate>
		<dc:creator>Jeff Davis</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Logic]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Temporal]]></category>

		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=171</guid>
		<description><![CDATA[&#8220;Schedule conflict&#8221; &#8212; it&#8217;s one of the simplest and most common constraints for business or any other organization. One person cannot be in two places at the same time; and in many cases a only a single person can use a given resource at any time.
Does your database system know anything about a schedule conflict? [...]]]></description>
			<content:encoded><![CDATA[<p>&#8220;Schedule conflict&#8221; &#8212; it&#8217;s one of the simplest and most common constraints for business or any other organization. One person cannot be in two places at the same time; and in many cases a only a single person can use a given resource at any time.</p>
<p>Does your database system know anything about a schedule conflict? Should it?</p>
<p>Constraints are always enforced at some point, it&#8217;s just a matter of when, how, and the cost of correcting the situation.</p>
<p><span id="more-171"></span>Consider two professors who try to reserve the same lecture hall at overlapping times (let&#8217;s say 1-2pm, and 1:30-2:30pm). Now imagine two possible resolutions:</p>
<ol>
<li>They are blissfully unaware of the schedule conflict until they have already promoted their lecture, attracted an audience, and arrived at the building. The professor scheduled for 1:30-2:30pm will be disappointed to find it already in use, and the students will be confused. The second lecture may have to be canceled due to the confusion, or lose a substantial number of attendees.</li>
<li>At the time of scheduling, one professor is given an error message to the effect of &#8220;this room is already reserved, please choose a different time&#8221;.</li>
</ol>
<p>Observe that neither one really solves the problem: the second resolution still forces one professor to choose a less-desirable time. However, it is a <em>much</em> cheaper resolution. As with many problems, early detection is simplest and cheapest to resolve.</p>
<p>Of course, there are many resolutions that fall between the first and the second. For instance, you might run a program every hour that checks for conflicts and alerts the parties involved. That may work, but there are still problems:</p>
<ul>
<li>You&#8217;ve now introduced uncertainty into the system: do I have a reservation or not? If there is a conflict later, will I be kicked out or will they?</li>
<li>You have to come up with rules for resolution: does the first one win? How do you define &#8220;first&#8221; if transactions are running for a while? What if someone makes a reservation &#8220;first&#8221; but then makes all kinds of changes later; were they really first or did they just game the system?</li>
<li>If someone&#8217;s reservation gets bumped, you have to now have a new strange state for a reservation, in which it is disabled, the organizer has (hopefully) been notified, and it needs to change.</li>
<li>Notice that everything is very procedural and specific to the business. You have to have a notification mechanism, and rules for how to resolve it.</li>
<li>Let&#8217;s go back to the definition of &#8220;first&#8221;: say you have made a reservation, and you get bumped by the conflict detector. In between the time you made the reservation and the time you were notified of a conflict, someone else reserved your second choice. Are you now first in line for that time slot? If so, that has a cascading effect such that it&#8217;s almost impossible for the person that took the second-choice slot to know that they are at risk of being bumped.</li>
</ul>
<p>These thought experiments might seem like edge cases, but reservation systems have two common traits that make these problems very real:</p>
<ol>
<li>They tend to start allowing reservations of a specific resource at a specific time, published in advance.</li>
<li>There tend to be some resources and time slots that have a much higher value than the others.</li>
</ol>
<p>These two traits lead to heavy contention.</p>
<p>Now, how would you go about enforcing such a constraint (non-overlapping time periods) in the database? While considering possible solutions, think about:</p>
<ul>
<li>Does the solution work for a wide variety of use cases, or only in special cases?</li>
<li>How well would it perform, under low contention and high contention, and under varied workloads?</li>
<li>Can it be implemented in a general purpose RDBMS, like PostgreSQL?</li>
<li>Is it procedural in nature, or declarative?</li>
</ul>
<p>I think it&#8217;s worthwhile to consider the problem, so I will end this article now, and provide some approaches, as well as my real answer, in the next article. In the meantime, feel free to post ideas as comments.</p>
]]></content:encoded>
			<wfw:commentRss>http://thoughts.j-davis.com/2009/11/01/temporal-keys-part-1/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>PG West Slides</title>
		<link>http://thoughts.j-davis.com/2009/10/19/pg-west-slides/</link>
		<comments>http://thoughts.j-davis.com/2009/10/19/pg-west-slides/#comments</comments>
		<pubDate>Tue, 20 Oct 2009 03:53:22 +0000</pubDate>
		<dc:creator>Jeff Davis</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[PostgreSQL]]></category>

		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=160</guid>
		<description><![CDATA[I really enjoyed PostgreSQL Conference WEST this year. It&#8217;s a shame I didn&#8217;t have a little more time to explore Seattle, however; it looks like a nice city.
There were a lot of great talks. Scott Bailey&#8217;s temporal data talk was excellent, of course &#8212; but I&#8217;m not an impartial observer, I expressed enthusiasm before the [...]]]></description>
			<content:encoded><![CDATA[<p>I really enjoyed PostgreSQL Conference WEST this year. It&#8217;s a shame I didn&#8217;t have a little more time to explore Seattle, however; it looks like a nice city.</p>
<p>There were a lot of great talks. Scott Bailey&#8217;s temporal data talk was excellent, of course &#8212; but I&#8217;m not an impartial observer, I <a href="http://thoughts.j-davis.com/2009/10/12/postgresql-west-and-temporal-databases/">expressed enthusiasm</a> before the conference began. Another talk that I found very interesting was Bill Karwin&#8217;s talk on practical object oriented models in SQL. The talk was well-researched and the points were made very clearly, although I would have liked to see a little more depth. There was some audience discussion, and I&#8217;d like to see the points hashed out in a little more detail.</p>
<p>I&#8217;m always excited to present the projects that I&#8217;m working on, and this conference was no exception. I thoroughly enjoyed discussing Operator Exclusion Constraints, the new feature that I&#8217;m writing for PostgreSQL 8.5 that enables &#8220;temporal keys&#8221;. Slides are <a title="Not Just UNIQUE" href="http://j-davis.com/postgresql/operator_exclusion_constraints.pdf">here</a> &#8212; I&#8217;ll write in more detail in another article. I was pleased with the audience participation and questions during the talk, and I hope that everyone enjoyed listening to me as much as I enjoyed speaking.</p>
<p>Slides for my talk on extensibility are available <a title="PostgreSQL Extensibility" href="http://j-davis.com/postgresql/extensibility2.pdf">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://thoughts.j-davis.com/2009/10/19/pg-west-slides/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>PostgreSQL WEST and Temporal Databases</title>
		<link>http://thoughts.j-davis.com/2009/10/12/postgresql-west-and-temporal-databases/</link>
		<comments>http://thoughts.j-davis.com/2009/10/12/postgresql-west-and-temporal-databases/#comments</comments>
		<pubDate>Tue, 13 Oct 2009 05:09:57 +0000</pubDate>
		<dc:creator>Jeff Davis</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Language]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Temporal]]></category>

		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=152</guid>
		<description><![CDATA[I&#8217;ve been interested in temporal data and relational databases for quite some time. There are going to be at least two people talking about temporal data at PostgreSQL WEST in Seattle: Scott Bailey and me. See the talk descriptions.
In the past, I&#8217;ve worked on a temporal extension to PostgreSQL that implements the PERIOD data type. [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been interested in temporal data and relational databases for quite some time. There are going to be at least two people talking about temporal data at <a title="PG WEST" href="http://postgresqlconference.org">PostgreSQL WEST</a> in Seattle: <a href="http://scottrbailey.wordpress.com/">Scott Bailey</a> and me. See the <a href="http://postgresqlconference.org/2009/west/talks">talk descriptions</a>.</p>
<p>In the past, I&#8217;ve worked on a <a title="Temporal PostgreSQL" href="http://pgfoundry.org/projects/temporal">temporal extension</a> to PostgreSQL that implements the <code>PERIOD</code> data type. This is a data type that offers both a definite beginning and a definite end time, which is important for describing things that happen over a period of time, rather than instantaneously. Trying to use separate attributes for &#8220;start&#8221; and &#8220;end&#8221; is bad for a number of reasons, and will certainly be addressed in a subsequent blog entry. For now, I&#8217;ll just say that I believe the <code>PERIOD</code> data type is fundamentally important for handling all kinds of time data, which I believe is a common problem.</p>
<p>At WEST, I&#8217;ll be presenting my progress on <em>temporal keys</em>. Temporal keys are used to prevent overlapping periods of time &#8212; a schedule conflict &#8212; by using an index and following the same concurrent behavior as <code>UNIQUE</code> with minimal performance cost (one extra index search, to be precise).</p>
<p>Temporal keys cannot be expressed in PostgreSQL 8.4, unless you resort to triggers and a full table lock (ouch!). So, additional backend support is required. This is accomplished in my patch for <a title="Operator Exclusion Constraints GIT repo" href="http://git.postgresql.org/gitweb?p=users/jdavis/postgres.git;a=shortlog;h=refs/heads/operator-exclusion-constraints">operator exclusion constraints</a>, which are a more general way of using arbitrary operators and index searches to enforce a constraint. I plan to do what&#8217;s required for the patch to be accepted in PostgreSQL 8.5.</p>
<p>Temporal modeling is a common problem. It seems like almost every PostgreSQL conference has had at least one talk on the matter, so we know there is some demand for improvement. If you&#8217;re interested, I hope you come to WEST and chat with Scott or I, and let&#8217;s see if we can come up with some real solutions.</p>
]]></content:encoded>
			<wfw:commentRss>http://thoughts.j-davis.com/2009/10/12/postgresql-west-and-temporal-databases/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Choosing Data Types</title>
		<link>http://thoughts.j-davis.com/2009/09/30/choosing-data-types/</link>
		<comments>http://thoughts.j-davis.com/2009/09/30/choosing-data-types/#comments</comments>
		<pubDate>Thu, 01 Oct 2009 05:13:28 +0000</pubDate>
		<dc:creator>Jeff Davis</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[PostgreSQL]]></category>

		<guid isPermaLink="false">http://thoughts.j-davis.com/?p=130</guid>
		<description><![CDATA[Database design includes a lot of concepts, and it all starts from understanding the business. If you&#8217;ve done a good job, you can usually tell a lot about the business from the schema alone &#8212; without data, and without application code or a user interface. This article is about data types, which are the &#8220;nouns&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>Database design includes a lot of concepts, and it all starts from understanding the business. If you&#8217;ve done a good job, you can usually tell a lot about the business from the schema alone &#8212; without data, and without application code or a user interface. This article is about data types, which are the &#8220;nouns&#8221; of your business, loosely speaking (the analogy is borrowed from C.J. Date&#8217;s <em>An Introduction to Database Systems</em>).</p>
<p><span id="more-130"></span></p>
<p>The first observation I&#8217;d like to make is that, when considering a relational database, we care about sets of things. If you have an attribute that holds &#8220;notes&#8221;, there&#8217;s not much you can do with a set of such values. I&#8217;m not saying that notes are unimportant, or that such fields should be avoided; but you should recognize the limitations of such attributes. All you can do is store text that a user wrote, or present a user with a specifically-requested note. The user then must actually read the note to learn anything &#8212; there&#8217;s nothing automatic about it (a database doesn&#8217;t help much with manual processes).</p>
<p>In the above example, the data type doesn&#8217;t matter much at all. It really makes no logical difference if you store it as text or as a PNG image of the rendered text (there may be a difference in convenience and performace, as with all format choices, but that&#8217;s beside the point). If the noun you are working with is &#8220;note&#8221;, the database system can&#8217;t help you any more if the attribute is &#8220;text&#8221; than it can if the attribute is of type &#8220;bytea&#8221; holding an image of the rendered text.</p>
<p>Astute readers will now be thinking: &#8220;but what about full-text search?&#8221;. Exactly. PostgreSQL has an <a title="PostgreSQL Full-Text Search" href="http://www.postgresql.org/docs/8.4/static/textsearch.html">excellent full-text search system</a>, and powerful data types like &#8220;tsvector&#8221; and &#8220;tsquery&#8221;. But these aren&#8217;t just better data types that magically work, these represent a change in <em>nouns </em>that the business understands. A search engine doesn&#8217;t index a bunch of <em>text</em> fields, it indexes <em>documents</em>. You don&#8217;t submit <em>text</em> to the search engine, you submit a <em>query</em>. A document isn&#8217;t just a string of characters, it is a set of word stems with positional and weighting information. The business knows these things, and these nouns are spoken in conference rooms by executives and to customers by sales people.</p>
<p>I&#8217;m drawing a direct connection between the business and the data types because I sometimes think that data types are treated as an implementation detail. Let&#8217;s say you store the notes as the &#8220;text&#8221; type and are treating it as such. When you go to implement search, the natural thing to do would be something like: <tt>...WHERE note LIKE '%foo%' OR note like '%bar%' OR ...</tt>, which is absolutely the wrong approach. The right approach is using a real query against a real document type, not trying to match substrings.</p>
<p>Treating the data types as an implementation detail is also related to the idea of only using primitive types, as though SQL were java with no class library. Primitive data types lead to awkward application code trying to answer questions that the database system should be able to answer. The philosophy of primitive data types is reflected in the SQL standard, which defines few types and does not seem to encourage sophisticated types like other languages do (of course, the SQL standard breaks away from the primitive data types because some types are just too important). For some reason database people have a negative reaction to any complex type, despite how many times they have proven to be important (for another great example, check out the geometry type in <a title="PostGIS" href="http://postgis.org">PostGIS</a>, a sophisticated GIS system built entirely as a PostgreSQL plugin).</p>
<p>I believe a lot of this negative reaction is because of <a href="http://en.wikipedia.org/wiki/First_normal_form">1NF</a>, which essentially describes what a relation is. The concern is that any type more complex than a string might be called a violation of 1NF, which I think is ridiculous. There is <a href="http://en.wikipedia.org/wiki/First_normal_form#Atomicity">no useful definition of an atomic value</a> (except maybe a boolean or bit or something): a string is a list (or array) of characters, that can be spliced, cut, and concatenated in many ways; an integer has a sign portion and a magnitude; and a timestamp is quite obviously non-atomic (and often treated as separate components). Additionally, normalization doesn&#8217;t have anything to do with data type choices; it only has to do with attributes and certain specific types of constraints (join dependencies, of which functional dependencies are a special case). So choosing a different data type carries no danger of violating a normal form. That being said, normalization is a small part of database design, and there are very legitimate reasons to be skeptical of strange data type choices.</p>
<p>I&#8217;m very pleased that PostgreSQL offers such a powerful type system. In many ways this is more challenging for a database system than an application language &#8212; for instance, PostgreSQL offers a variety of index access methods, execution plans, and statistics that all depend on understanding your data types and how to operate on them. In PostgreSQL, a user-defined data type is truly first-class, meaning that you can make use of all of this infrastructure in creative ways. However, creating a good data type is not a trivial process, so it&#8217;s best to approach it as a real project by itself.</p>
<p>I started the <a title="Temporal PostgreSQL" href="http://pgfoundry.org/projects/temporal">Temporal PostgreSQL</a> project quite a while ago to offer a &#8220;period&#8221; data type (a time interval with an absolute beginning and end), because I think that is an important noun that is underserved by database systems. More on this later.</p>
]]></content:encoded>
			<wfw:commentRss>http://thoughts.j-davis.com/2009/09/30/choosing-data-types/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>What is the deal with NULLs?</title>
		<link>http://thoughts.j-davis.com/2009/08/02/what-is-the-deal-with-nulls/</link>
		<comments>http://thoughts.j-davis.com/2009/08/02/what-is-the-deal-with-nulls/#comments</comments>
		<pubDate>Sun, 02 Aug 2009 22:40:23 +0000</pubDate>
		<dc:creator>Jeff Davis</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Language]]></category>
		<category><![CDATA[Logic]]></category>
		<category><![CDATA[PostgreSQL]]></category>

		<guid isPermaLink="false">http://davisjeff.wordpress.com/?p=4</guid>
		<description><![CDATA[A recent thread on pgsql-hackers warrants some more extensive discussion. In the past, I&#8217;ve criticized NULL semantics, but in this post I&#8217;d just like to explain some corner cases that I think you&#8217;ll find interesting, and try to straighten out some myths and misconceptions.
First off, I&#8217;m strictly discussing SQL NULL here. SQL NULL is peculiar [...]]]></description>
			<content:encoded><![CDATA[<p>A recent <a title="recent thread on pgsql-hackers" href="http://archives.postgresql.org/pgsql-hackers/2009-07/msg01518.php">thread on pgsql-hackers</a> warrants some more extensive discussion. In the past, I&#8217;ve criticized NULL semantics, but in this post I&#8217;d just like to explain some corner cases that I think you&#8217;ll find interesting, and try to straighten out some myths and misconceptions.</p>
<p>First off, I&#8217;m strictly discussing SQL NULL here. SQL NULL is peculiar in a number of ways, and the general excuse for this is that there is a need to represent &#8220;missing information&#8221; &#8212; which may be true. But there are lots of ways to represent missing information, as I pointed out <a title="None, nil, Nothing, undef, NA, and SQL NULL" href="http://thoughts.j-davis.com/2008/08/13/none-nil-nothing-undef-na-and-sql-null/">in a previous post</a>, and SQL&#8217;s approach to missing information is, well, &#8220;unique&#8221;.</p>
<p><span id="more-4"></span></p>
<ul>
<li>&#8220;NULL is not a value&#8221; &#8212; If you hear this one, beware: it&#8217;s in direct contradiction to the SQL standard, which uses the phrase &#8220;null value&#8221; dozens of times. It&#8217;s hard to imagine that NULL is not any kind of value at all; because it&#8217;s routinely passed to functions and operators, predicates can evaluate to NULL, and SQL uses a kind of three-<strong>valued</strong> logic (3VL) in some contexts. The phrase &#8220;NULL is not a value&#8221; also raises the question: &#8220;what is it, then?&#8221;.</li>
<li>NULL means &#8220;unknown&#8221; (i.e. the third truth value) &#8212; This doesn&#8217;t hold up either. SUM of no tuples returns NULL, but clearly the SUM of no tuples is not unknown! SQL will happily generate NULLs from aggregates or outer joins without any NULLs at all in the database. Do you not know something you did know before, or do you now know that you don&#8217;t know something that you didn&#8217;t know you didn&#8217;t know before? Also, if NULL means &#8220;unknown&#8221;, how do you differentiate a boolean field for which you do not know the value, and a boolean field for which you do know the value, and it happens to be &#8220;unknown&#8221; (perhaps this is why boolean columns are a PostgreSQL extension and not part of the core SQL standard)?</li>
<li>&#8220;NULL is false-like&#8221; &#8212; Don&#8217;t think of NULL as false-like, or &#8220;more false than true&#8221;. It&#8217;s a tempting rule of thumb, but it&#8217;s misleading. For instance, in a WHERE clause, a NULL predicate is treated like FALSE. However, in a constraint (like a <code>CHECK</code> constraint), NULL is treated like TRUE. Perhaps most importantly, when in a 3VL context (like a boolean expression), this misconception leads to problems when you try to invert the logic, e.g., use <code>NOT</code>.</li>
<li>&#8220;Oh, that makes sense&#8221; &#8212; When you see individual behaviors of NULL, they look systematic, and your brain quickly sees a pattern and extrapolates what might happen in other situations. Often, that extrapolation is wrong, because NULL semantics are a mix of behaviors. I think the best way to think about NULL is as a Frankenstein monster of several philosophies and systems stitched together by a series of special cases.</li>
<li><code>p OR NOT p</code> &#8212; Everyone should know that this is not always true in SQL. But most people tend to reason assuming that this is always true, so you have to be very careful, and work against your intuition very deliberately, in order to form a correct SQL query.</li>
<li>SUM() versus + (addition) &#8212; SUM is not repeated addition. SUM of <code>1</code> and <code>NULL</code> is <code>1</code>, but <code>1 + NULL</code> is NULL.</li>
<li>Aggregates ignore NULLs &#8212; According to the standard, aggregates are supposed to ignore NULLs, because the information is missing. But why is it OK to ignore the missing information in an aggregate, but not, say, with the + operator? Is it really OK to just ignore it?</li>
<li>Aggregates return NULL &#8212; According to the standard, aggregates are supposed to return NULL when they have no non-NULL input. Just because you don&#8217;t have any input tuples, does that really mean that the result is undefined, missing, or unknown? It&#8217;s certainly not unknown! What about SUM over zero tuples, wouldn&#8217;t the most useful result be zero?</li>
<li>SQL breaks its own rules &#8212; The aforementioned aggregate rules don&#8217;t work very well for <code>COUNT()</code>, the simplest of all aggregates. So, they have two versions of count: <code>COUNT(*)</code> breaks the &#8220;aggregates ignore nulls&#8221; rule and the &#8220;aggregates return null&#8221; rule, and COUNT(x) only breaks the latter. But wait! There&#8217;s more: <code>ARRAY_AGG()</code> breaks the former but not the latter. But no exception is made for SUM &#8212; it still returns NULL when there are no input tuples.</li>
<li>NULLs appear even when you have complete information &#8212; Because of OUTER JOIN and aggregates, NULLs can appear even when you don&#8217;t have any NULLs in your database! As a thought experiment, try to reconcile this fact with the various &#8220;definitions&#8221; of NULL.</li>
<li><code>WHERE NOT IN (SELECT ...)</code> &#8212; This one gets everyone at one point or another. If the subselect produces any NULLs, then NOT IN can only evaluate to FALSE or NULL, meaning you get no tuples. Because it&#8217;s in a WHERE clause, it will return no results. You are less likely to have a bunch of NULLs in your data while testing, so chances are everything will work great until you get into production.</li>
<li><code>x &gt;= 10 or x &lt;= 10</code> &#8212; Not a tautology in SQL.</li>
<li><code>x IS NULL AND x IS DISTINCT FROM NULL</code> &#8212; You probably don&#8217;t know this, but this expression can evaluate to TRUE! That is, if x = ROW(NULL).</li>
<li><code>NOT x IS NULL</code> is not the same as <code>x IS NOT NULL</code> &#8212; If <code>x</code> is <code>ROW(1,NULL)</code>, then the former will evaluate to TRUE, and the latter will evaluate to FALSE. Enjoy.</li>
<li><code>NOT x IS NULL AND NOT x IS NOT NULL</code> &#8212; Want to know if you have a value like <code>ROW(1, NULL)</code>? To distinguish it from NULL and also from values like <code>ROW(1,1)</code> and <code>ROW(NULL,NULL)</code>, this expression might help you.</li>
<li>NULLs can exist inside some things, but not others &#8212; If you concatenate: <code>firstname || mi || lastname</code>, and &#8220;mi&#8221; happens to be null, the entire result will be null. So strings cannot contain a NULL, but as we see above, a record can.</li>
</ul>
<p>I believe the above shows, beyond a reasonable doubt, that NULL semantics are unintuitive, and if viewed according to most of the &#8220;standard explanations,&#8221; highly inconsistent. This may seem minor; that is, if you&#8217;re writing SQL you can overcome these things with training. But it is not minor, because NULL semantics are designed to make you <em>think</em> you understand them, and <em>think</em> that the semantics are intuitive, and <em>think</em> that it&#8217;s part of some ingenious consistent system for managing missing information. But none of those things are true.</p>
<p>I have seen lots of discussions about NULL in various forums and mailing lists. Many of the participants are obviously intelligent and experienced, and yet make bold statements that are, quite simply, false. I&#8217;m writing this article to make two important points:</p>
<ol>
<li>There is a good case to be made that NULL semantics are very counterproductive; as opposed to a simple &#8220;error early&#8221; system that forces you to write queries that explicitly account for missing information (e.g. with <code>COALESCE</code>). &#8220;Error early&#8221; is a more mainstream approach, similar to null pointers in java or None in python. If you want compile-time checking, you can use a construct like Maybe in haskell. SQL attempts to pass along the problem, hoping the next operator will turn ignorance into knowledge &#8212; but it does not appear that anyone thought through this idea, quite frankly.</li>
<li>You should not attempt to apply your intellect to NULL, it will lead you in the wrong direction. If you need to understand it, understand it, but always treat it with skepticism. Test the queries, read the standard, do what you need to do, but do <strong>not<em> </em></strong>attempt to extrapolate.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://thoughts.j-davis.com/2009/08/02/what-is-the-deal-with-nulls/feed/</wfw:commentRss>
		<slash:comments>41</slash:comments>
		</item>
	</channel>
</rss>
