This is very similar to this question with a full minimal example.
I have a simple select query (from a non-empty table) with only left joins. The last left join happens to be with an empty table.
The query returns 2 non-null rows as it should, but simply changing it to a count(*) query makes it return 0 as the count of rows.
The same SQL works properly on both MySQL and MSSQL (after fixing the PK syntax).
Full (re-runnable if uncomented) example:
-- DROP TABLE first;
-- DROP TABLE second;
-- DROP TABLE empty;
CREATE TABLE first (
pk int,
fk int
);
ALTER TABLE first
ADD CONSTRAINT PK_first PRIMARY KEY (pk);
CREATE TABLE second (
pk int
);
ALTER TABLE second
ADD CONSTRAINT PK_second PRIMARY KEY (pk);
CREATE TABLE empty (
pk int
);
ALTER TABLE first ADD CONSTRAINT FK_first FOREIGN KEY (fk)
REFERENCES second (pk) ENABLE;
INSERT INTO second (pk)
VALUES (5);
INSERT INTO first (pk, fk)
VALUES (1, 5);
INSERT INTO first (pk, fk)
VALUES (2, 5);
SELECT
COUNT(*)
FROM first
LEFT OUTER JOIN second
ON (first.fk = second.pk)
LEFT OUTER JOIN empty
ON (1 = 1);
The last query returns 0 on my machine, but changing the count(*)
to just *
makes it return 2 rows.
Can anyone reproduce this? My db_version is 11.2.0.2.
Explain plan seems to see the 2 rows that should be returned:
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | MERGE JOIN CARTESIAN| | 2 | 26 | 3 (0)| 00:00:01 |
| 3 | VIEW | | 1 | | 2 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL | EMPTY | 1 | | 2 (0)| 00:00:01 |
| 5 | BUFFER SORT | | 2 | 26 | 3 (0)| 00:00:01 |
| 6 | INDEX FULL SCAN | PK_FIRST | 2 | 26 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
I don't know much about dynamic sampling, but if i alter session set OPTIMIZER_DYNAMIC_SAMPLING=0;
, then the plan shows 82 rows in each step.
Removing the primary keys fixes the problem on Oracle, but that is hardly a proper solution.
Removing the join into the empty table also fixes the problem, but it is an outer join with tautological filter, so it should be a noop.
Is this actually the intended behavior on Oracle for some reason? Or is my server just bugged? Both MSSQL and MySQL return 2 as the count.
Edit: Round 2
It was enough to add 2 more tables and the bug shows also in 11.2.0.4. Can anyone reproduce it on more current Oracle versions?
An online fiddle here.
CREATE TABLE first (
pk int,
fk int
);
ALTER TABLE first
ADD CONSTRAINT PK_first PRIMARY KEY (pk);
CREATE TABLE second (
pk int,
fk int
);
ALTER TABLE second
ADD CONSTRAINT PK_second PRIMARY KEY (pk);
CREATE TABLE third (
pk int,
fk int
);
ALTER TABLE third
ADD CONSTRAINT PK_third PRIMARY KEY (pk);
CREATE TABLE fourth (
pk int
);
ALTER TABLE fourth
ADD CONSTRAINT PK_fourth PRIMARY KEY (pk);
CREATE TABLE empty (
pk int
);
ALTER TABLE first ADD CONSTRAINT FK_first FOREIGN KEY (fk)
REFERENCES second (pk) ENABLE;
ALTER TABLE second ADD CONSTRAINT FK_second FOREIGN KEY (fk)
REFERENCES third (pk) ENABLE;
ALTER TABLE third ADD CONSTRAINT FK_third FOREIGN KEY (fk)
REFERENCES fourth (pk) ENABLE;
INSERT INTO fourth (pk)
VALUES (50);
INSERT INTO third (pk, fk)
VALUES (10, 50);
INSERT INTO third (pk, fk)
VALUES (11, 50);
INSERT INTO second (pk, fk)
VALUES (5, 10);
INSERT INTO second (pk, fk)
VALUES (6, 10);
INSERT INTO first (pk, fk)
VALUES (1, 5);
INSERT INTO first (pk, fk)
VALUES (2, 5);
SELECT
COUNT(*)
FROM first
LEFT OUTER JOIN second
ON (first.fk = second.pk)
LEFT OUTER JOIN third
ON (first.pk = third.pk)
LEFT OUTER JOIN fourth
ON (third.fk = fourth.pk)
LEFT OUTER JOIN empty
ON (1 = 1);
Anyway the consensus seems to be that this is a bug in obsolete Oracle releases.