@@ -161,9 +161,44 @@ We could rewrite the above query using ``with`` as::
161Inner joins
161Inner joins
162-----------
162-----------
164−.. todo::
164+Inner joins are SQL queries of the form ``SELECT .. FROM x JOIN y ON ..``. Rel8
165+doesn't offer a special function for these queries, as the same query can be
166+expressed by selecting from two tables (this is called taking the *cartesian
167+product* of two queries) and then filtering the result.
166− Write this
169+If we wanted to join each blog post with the author of the blog post, we would
172+ SELECT * FROM blog_post JOIN author ON author.id = blog_post.id
174+The alternative way to write this query with ``WHERE`` is::
176+ SELECT * FROM blog_post, author WHERE author.id = blog_post.id
178+and this query can be written in Rel8 as::
180+ blogPost <- each blogPostSchema
181+ author <- each authorSchema
182+ where_ $ blogPostAuthorId blogPost ==. authorId author
186+ A good pattern to adopt is to abstract out these joins as functions. A
187+ suggested way to write the above would be to extract out an "author for blog
190+ blogPost <- each blogPostSchema
191+ author <- authorForBlogPost blogPost
195+ authorForBlogPost :: BlogPost Expr -> Query (Author Expr)
196+ authorForBlogPost blogPost =
197+ filter ((blogPostAuthorId blogPost ==.) authorId author) =<<
200+ While this is a little more code over all, in our experience this style
201+ dramatically increases the readabality of queries using joins.
168Left (outer) joins with ``optional``
203Left (outer) joins with ``optional``
169------------------------------------
204------------------------------------