Skip to content

Commit

Permalink
Describe antijoins
Browse files Browse the repository at this point in the history
  • Loading branch information
ocharles committed Mar 16, 2017
1 parent 9720c00 commit 709e460
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 0 deletions.
27 changes: 27 additions & 0 deletions docs/concepts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,30 @@ I generally suggest that if you need to perform comparisons against ``NULL`` in
predicates you retain ``NULL`` and use the "``null`` lifted" operators (``==?``,
``&&?``) along with ``toNullable``. While unfortunate, these queries will often
compile down to considerably more performant queries.

.. _antijoins:

Antijoins
---------

`Wikipedia describes antijoins <https://en.wikipedia.org/wiki/Relational_algebra#Antijoin_.28.E2.96.B7.29>`_ as

The **antijoin**, written as R ▷ S where R and S are relations, is similar to
the semijoin, but the result of an antijoin is only those tuples in R for
which there is no tuple in S that is equal on their common attribute names.

To be a little less technical, an antijoin is a way to restricting the rows in
one query by the absence of rows in another query. For example, we might want to
query all rows in the ``user`` table where those users haven't placed an order.

You can achieve this with a left-join, but PostgreSQL has specific support for
antijoins via the ``NOT EXISTS`` operator, and using this can lead to increased
performance. Rel8 wraps ``EXISTS`` and ``NOT EXISTS`` with the ``exists`` and
``notExists`` arrow commands. Considering on from the above example, we can
write::

usersNotOrdered :: Query (User Expr)
usersNotOrdered = proc _ -> do
user <- queryTable -< () -- Select all users
(| notExists (do order <- queryTable -< () -- all orders
where_ -< userId user ==. orderUserId order) |)
6 changes: 6 additions & 0 deletions docs/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ query to find parts where there are no suppliers in the same city::
where_ -< isNull (maybeSupplier $? supplierId)
returnA -< part

.. note::

This type of query is what is known as an *antijoin*. A more efficient way to
write the above is by using the ``notExists`` function. For more information,
see :ref:`antijoins` in :doc:`concepts`.

We are filtering our query for suppliers where the id is null. Ordinarily this
would be a type error - we declared that ``supplierId`` contains ``Int``, rather
than ``Maybe Int``. However, because suppliers come from a left join, when we
Expand Down

0 comments on commit 709e460

Please sign in to comment.