Reputation: 1546
On ORACLE 12C (this does not occur on 11g), the following SQL excludes rows from the tOwners table when it shouldnt:
SELECT *
FROM
(
SELECT 1 As OwnerId, 'Fred' As OwnerName FROM DUAL UNION
SELECT 2 As OwnerId, 'Tim' As OwnerName FROM DUAL
) tOwners
LEFT JOIN
(
SELECT 1 As PetId, 1 As OwnerId, 'Cat' As Pet FROM DUAL UNION
SELECT 2 As PetId, 1 As OwnerId, 'Mouse' As Pet FROM DUAL
) tPets
ON
tOwners.OwnerId = tPets.OwnerId
LEFT JOIN
(SELECT 2 As PetId, 'Treats' As Food FROM DUAL) tFoods
ON
tPets.Pet = 'Cat' AND
tPets.PetId = tFoods.PetId
Does not return a row for 'Tim':
1 Fred 1 1 Cat
1 Fred 2 1 Mouse
Execution Plan:
Plan hash value: 3529061095
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 10 (0)| 00:00:01 |
|* 1 | HASH JOIN OUTER | | 1 | 19 | 10 (0)| 00:00:01 |
|* 2 | HASH JOIN | | 1 | 17 | 8 (0)| 00:00:01 |
| 3 | VIEW | | 2 | 14 | 4 (0)| 00:00:01 |
| 4 | SORT UNIQUE | | 2 | | 4 (50)| 00:00:01 |
| 5 | UNION-ALL | | | | | |
| 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 7 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 8 | VIEW | | 2 | 20 | 4 (0)| 00:00:01 |
| 9 | SORT UNIQUE | | 2 | | 4 (50)| 00:00:01 |
| 10 | UNION-ALL | | | | | |
| 11 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 12 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 13 | TABLE ACCESS FULL| DUAL | 1 | 2 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("TPETS"."PET"=CASE WHEN (CASE WHEN ROWID(+) IS NOT NULL
THEN 2 ELSE NULL END IS NOT NULL) THEN 'Cat' ELSE 'Cat' END AND
"TPETS"."PETID"=CASE WHEN (ROWID(+) IS NOT NULL) THEN 2 ELSE NULL END )
2 - access("TOWNERS"."OWNERID"="TPETS"."OWNERID")
Those predicates look pretty suspect here, but why is it doing this?
However, if you change the "tPets.Pet = 'Cat'" join predicate to use a subquery, it works just fine:
SELECT *
FROM
(
SELECT 1 As OwnerId, 'Fred' As OwnerName FROM DUAL UNION
SELECT 2 As OwnerId, 'Tim' As OwnerName FROM DUAL
) tOwners
LEFT JOIN
(
SELECT 1 As PetId, 1 As OwnerId, 'Cat' As Pet FROM DUAL UNION
SELECT 2 As PetId, 1 As OwnerId, 'Mouse' As Pet FROM DUAL
) tPets
ON
tOwners.OwnerId = tPets.OwnerId
LEFT JOIN
(SELECT 2 As PetId, 'Treats' As Food FROM DUAL) tFoods
ON
tPets.Pet = (SELECT 'Cat' FROM DUAL) AND
tPets.PetId = tFoods.PetId
With:
1 Fred 1 1 Cat
1 Fred 2 1 Mouse
2 Tim
Execution Plan:
Plan hash value: 1713688406
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 56 | 16 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER| | 2 | 56 | 16 (0)| 00:00:01 |
|* 2 | HASH JOIN OUTER | | 2 | 34 | 8 (0)| 00:00:01 |
| 3 | VIEW | | 2 | 14 | 4 (0)| 00:00:01 |
| 4 | SORT UNIQUE | | 2 | | 4 (50)| 00:00:01 |
| 5 | UNION-ALL | | | | | |
| 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 7 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 8 | VIEW | | 2 | 20 | 4 (0)| 00:00:01 |
| 9 | SORT UNIQUE | | 2 | | 4 (50)| 00:00:01 |
| 10 | UNION-ALL | | | | | |
| 11 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 12 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 13 | VIEW | VW_LAT_9BF0EE0C | 1 | 11 | 4 (0)| 00:00:01 |
|* 14 | FILTER | | | | | |
| 15 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 16 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("TOWNERS"."OWNERID"="TPETS"."OWNERID"(+))
14 - filter("TPETS"."PETID"=2 AND "TPETS"."PET"= (SELECT 'Cat' FROM
"SYS"."DUAL" "DUAL"))
It appears as if ORACLE is limiting the result set based on rows where there are no Foods for those Pets (only when using a literal), however, I would have thought it would always return rows form the tOwners table irrespective of the join predicates on the LEFT hanging joins, can someone explain this behaviour, or is actually a known bug of some sort?
NOTE: I have only tested in ORACLE version 11g and 12c, and it only occurs in 12c so far.
Upvotes: 1
Views: 402
Reputation: 11
Very interesting observation, although I could not reproduce it on my Oracle(version 12.1.0.2.0) database. I have to mention that I'm using Oracle Linux 6.5 and not Windows. Anyway, It would be good to post the execution plan too, for this simple, yet interesting query.
Thank you very much for posting the execution plans, this explains very well the behavior of the query. Then I shall explain, starting with the first execution plan:
|* 2 | HASH JOIN | | 1 | 17 | 8 (0)| 00:00:01 |
| 3 | VIEW | | 2 | 14 | 4 (0)| 00:00:01 |
| 4 | SORT UNIQUE | | 2 | | 4 (50)| 00:00:01 |
| 5 | UNION-ALL | | | | | |
| 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 7 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 8 | VIEW | | 2 | 20 | 4 (0)| 00:00:01 |
| 9 | SORT UNIQUE | | 2 | | 4 (50)| 00:00:01 |
| 10 | UNION-ALL | | | | | |
| 11 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
| 12 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
As you can see, the optimizer chooses to do an inner join, instead of the left join, and that is showed by the "HASH JOIN" and not "HASH JOIN OUTER" as it should be.
To be honest, I did not hear anything about a bug like this(so far), so I would suggest the following:
Upvotes: 1