There exists a phenomenon in JPQL/SQL that isn’t quite intuitive in the first place, but gets clearer after you have understood how it works. A normal JOIN in a JPQL query is evaluated as an INNER JOIN. That means, that a row will not be selected, if the right hand side of the JOIN is NULL
. This may sometimes lead to unexpected results…
In the next listing the employes with one of the given superiors and department are returned, for example:
SELECT emp FROM Employee AS emp, IN(emp.superior) sup WHERE ( sup = IN(:superiors) AND emp.department = :dep )
Imagine, you would like to return the same employees and additionally the superiors themselves. You might be tempted to write the following query:
SELECT emp FROM Employee AS emp, IN(emp.superior) sup WHERE ( ( emp = IN(:employees) OR sup = IN(:employees) ) AND emp.department = :dep )
But, the IN([alias1].[step]) [alias2]
statement in the FROM clause represents an inner JOIN, so that values that are NULL
in the right set ([alias2]) lead to omitting the row even though the boolean condition sup = IN(:employees)
is in an OR clause. So every employee, that does not have a superior will be omitted (em.superior == null). But this is not intended of course. So, using a LEFT JOIN tackles this problem:
SELECT emp FROM Employee AS emp LEFT JOIN emp.superior sup WHERE ( ( emp = IN(:employees) OR sup = IN(:employees) ) AND emp.department = :dep )
The same problem exists for the semantically equivalent variants [INNER] JOIN
(as a replacement for IN()) and implicit INNER JOINs like this:
SELECT emp FROM Employee AS emp, Employee AS sup WHERE ( sup = IN(:employees) AND ( emp = IN(:employees) OR emp.superior = sup ) AND emp.department = :dep )