Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OUTER JOIN result is missing rows, no WHERE clause (Workaround found)

Update at the bottom.

I am trying to do a self outer join that, for each record, returns it and all other records occuring later than it, or NULL if it itself is the latest record. This is my sql code:

SELECT A.[CR#], A.REGIS_STATUSDATE, B.REGIS_STATUSDATE
FROM CR_ADMIN_REGIS_STATUS A LEFT OUTER JOIN CR_ADMIN_REGIS_STATUS B
ON A.[CR#]=B.[CR#] AND A.REGIS_STATUSDATE < B.REGIS_STATUSDATE

My issue is this is that when, for a given [CR#], A.REGIS_STATUSDATE is a maximum (and therefore the B.REGIS_STATUSDATE cannot be greater than it), that row is not included in my result.

For example, if CR_ADMIN_REGIS_STATUS looks like this:

CR#   REGIS_STATUSDATE
1     5/1/12
1     5/2/12
1     5/3/12
2     5/1/12
2     5/2/12

I expect the result of my query to be

CR#   A.REGIS_STATUSDATE B.REGIS_STATUSDATE
1     5/1/12             5/2/12
1     5/1/12             5/3/12
1     5/2/12             5/3/12
1     5/3/12             NULL
2     5/1/12             5/2/12
2     5/2/12             NULL

Instead I get this:

CR#   A.REGIS_STATUSDATE B.REGIS_STATUSDATE
1     5/1/12             5/2/12
1     5/1/12             5/3/12
1     5/2/12             5/3/12
2     5/1/12             5/2/12

Given that my query is a LEFT OUTER JOIN, and I have no WHERE clause, I expect all rows from my original table to be in the result, but this is not the case. What am I missing here?

Edit: This is in Access 2007

Update: I decided to see what would happen if I copied sections of the table CR_ADMIN_REGIS_STATUS into a separate table and ran my query against that. Even when I had just straight up copied the entire table into the new one (manually) the query worked! This was only the case when actually copying and pasting though, when I would SELECT * INTO another table the problem would persist.
Eventually I discovered that if I ran the query against

SELECT *
FROM CR_ADMIN_REGIS_STATUS
UNION ALL SELECT TOP 1 * 
FROM CR_ADMIN_REGIS_STATUS;

rather than CR_ADMIN_REGIS_STATUS itself my query returned the desired result. Weird. I also had a similar query against a similar table which worked from the start, so it seems this was an issue limited to this one table.

like image 250
msender Avatar asked Sep 24 '12 22:09

msender


People also ask

Can I use where clause in outer join?

Introduction. Although an ON clause is required for each operation in an outer join in a FROM clause, an outer join can itself also include a WHERE clause. Any restriction in the WHERE clause is applied only to the table that is the final result of the outer join.

Does outer join result include non matching rows?

An outer join returns all of the rows that the equivalent inner join would return, plus non-matching rows from one or both tables. In the FROM clause, you can specify left, right, and full outer joins.

Why is full outer join not working?

You're getting that error because MySQL does not support (or recognize) the FULL OUTER JOIN syntax. However, it is possible emulate a FULL OUTER JOIN in MySQL. We actually need two queries. One query return all the rows from the table on the left.

How do I write a full outer join query in MySQL?

Here is the SQL query to do full outer join without any duplicate rows in result. mysql> SELECT * FROM sales LEFT JOIN orders ON sales.id = orders.id UNION SELECT * FROM sales RIGHT JOIN orders ON sales.id = orders.id; Need a reporting tool for MySQL?


1 Answers

You are not missing anything. If this happens, it's a bug.

The engine used by MS-Access has several bugs. I've seen similar, inavlid behaviour in joins that had "complex" ON conditions. See another SO question where Access gives buggy results: Why does my left join in Access have fewer rows than the left table?

You can try the query with identical data in SQL-Server, Oracle, Postgres, even MySQL and you will get the correct, expected results.


As a workaround, you can try rewriting the query with a UNION, but one can never be sure about the correctness:

SELECT A.[CR#], A.REGIS_STATUSDATE, B.REGIS_STATUSDATE
FROM CR_ADMIN_REGIS_STATUS A 
  INNER JOIN CR_ADMIN_REGIS_STATUS B
    ON  A.[CR#]=B.[CR#] 
    AND A.REGIS_STATUSDATE < B.REGIS_STATUSDATE

UNION ALL

SELECT A.[CR#], A.REGIS_STATUSDATE, NULL
FROM CR_ADMIN_REGIS_STATUS A 
WHERE NOT EXISTS
      ( SELECT *
        FROM CR_ADMIN_REGIS_STATUS B
        WHERE A.[CR#]=B.[CR#] 
          AND A.REGIS_STATUSDATE < B.REGIS_STATUSDATE
      ) ;
like image 80
ypercubeᵀᴹ Avatar answered Oct 12 '22 19:10

ypercubeᵀᴹ