ORA_ROWSCN queried properly but why is Oracle returning the wrong value in the results?

Viewed 137

Why does Oracle sometimes return the wrong ORA_ROWSCN, such as in the following? (Note this does not seem to be a ROWDEPENDENCIES issue or a "greater than expected SCN" issue, as I realize both these caveats when using ORA_ROWSCN.)

When I run:

  WITH maxIds as (
       SELECT table_name, record_rowid, MAX(changed_rows_log_id) AS changed_rows_log_id, ORA_ROWSCN as otherSCN
       FROM changed_rows_log
       GROUP BY table_name,  record_rowid 
  )
  SELECT l.changed_rows_log_id, l.ORA_ROWSCN, otherSCN, l.table_name, l.record_rowid
  FROM changed_rows_log l
  JOIN maxIds m on l.changed_rows_log_id = m.changed_rows_log_id and l.table_name=m.table_name and l.record_rowid=m.record_rowid
  WHERE ORA_ROWSCN > 7884576380618

My result is:

CHANGED_ROWS_LOG_ID ORA_ROWSCN  OTHERSCN    TABLE_NAME  RECORD_ROWID
1887507 7884576380617   7884576380617   FOO AAARiGAAMAAG4B4AA2
1887508 7884576380617   7884576380617   FOO AAARiGAAMAAG4B4AA3
1887512 7884576380617   7884576380617   FOO AAARiGAAMAAG4B4AA7
...

Yep, you see that right. The ORA_ROWSCN returned is less than my literal value that I asked for greater-than in the query WHERE clause. (I also included otherSCN to see if it was throwing me off somehow, but it appears to be irrelevant)

It appears that the Row in question in reality has a higher ORA_ROWSCN, and indeed the WHERE clause worked properly, as when I then do SELECT ORA_ROWSCN FROM changed_rows_log WHERE changed_rows_log_id=1887507, I get 7884576380644 not 7884576380617.

Also, when I add just one WHERE condition, I also get the correct data returned:

  WITH maxIds as (
       SELECT table_name, record_rowid, MAX(changed_rows_log_id) AS changed_rows_log_id, ORA_ROWSCN as otherSCN
       FROM changed_rows_log
       GROUP BY table_name,  record_rowid 
  )
  SELECT l.changed_rows_log_id, l.ORA_ROWSCN, otherSCN, l.table_name, l.record_rowid
  FROM changed_rows_log l
  JOIN maxIds m on l.changed_rows_log_id = m.changed_rows_log_id and l.table_name=m.table_name and l.record_rowid=m.record_rowid
  WHERE ORA_ROWSCN > 7884576380618 AND l.changed_rows_log_id=1887507

gives me this, as expected

CHANGED_ROWS_LOG_ID ORA_ROWSCN  OTHERSCN    TABLE_NAME  RECORD_ROWID
1887507 7884576380644   7884576380644   FOO AAARiGAAMAAG4B4AA2

So why does and how can SELECT ORA_ROWSCN give me simply incorrect data like this? Can I work around it somehow so I can get the expected ORA_ROWSCN that more particular queries give me?

(If it matters, changed_rows_log has ROWDEPENDENCIES enabled. I'm using Oracle Database 12.1.0.2.0 64-bit.)

More detail--the EXPLAIN PLAN for the first query (with bad value)

Plan hash value: 3153795477
 
-------------------------------------------------------------------------------------------------
| Id  | Operation            | Name             | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |                  |     1 |    62 |       | 30794   (1)| 00:00:02 |
|*  1 |  FILTER              |                  |       |       |       |            |          |
|   2 |   HASH GROUP BY      |                  |     1 |    62 |       | 30794   (1)| 00:00:02 |
|*  3 |    HASH JOIN         |                  |   208K|    12M|  3424K| 30787   (1)| 00:00:02 |
|*  4 |     TABLE ACCESS FULL| CHANGED_ROWS_LOG | 71438 |  2581K|       | 14052   (1)| 00:00:01 |
|   5 |     TABLE ACCESS FULL| CHANGED_ROWS_LOG |  1428K|    34M|       | 14058   (1)| 00:00:01 |
-------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("L"."CHANGED_ROWS_LOG_ID"=MAX("CHANGED_ROWS_LOG_ID"))
   3 - access("L"."TABLE_NAME"="TABLE_NAME" AND "L"."RECORD_ROWID"="RECORD_ROWID")
   4 - filter("ORA_ROWSCN">7884576380618)
 
Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
   - this is an adaptive plan
   - 2 Sql Plan Directives used for this statement

And the last query above (correct value)

Plan hash value: 402632295
 
---------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                      |     1 |    62 |     7  (15)| 00:00:01 |
|*  1 |  FILTER                                |                      |       |       |            |          |
|   2 |   HASH GROUP BY                        |                      |     1 |    62 |     7  (15)| 00:00:01 |
|   3 |    NESTED LOOPS                        |                      |     3 |   186 |     6   (0)| 00:00:01 |
|*  4 |     TABLE ACCESS BY INDEX ROWID        | CHANGED_ROWS_LOG     |     1 |    37 |     3   (0)| 00:00:01 |
|*  5 |      INDEX UNIQUE SCAN                 | SYS_C00141068        |     1 |       |     2   (0)| 00:00:01 |
|   6 |     TABLE ACCESS BY INDEX ROWID BATCHED| CHANGED_ROWS_LOG     |     3 |    75 |     3   (0)| 00:00:01 |
|*  7 |      INDEX RANGE SCAN                  | CHANGED_ROWS_LOG_IF1 |     1 |       |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter(MAX("CHANGED_ROWS_LOG_ID")=1887507)
   4 - filter("ORA_ROWSCN">7884576380618)
   5 - access("L"."CHANGED_ROWS_LOG_ID"=1887507)
   7 - access("L"."RECORD_ROWID"="RECORD_ROWID" AND "L"."TABLE_NAME"="TABLE_NAME")
 
Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
   - 1 Sql Plan Directive used for this statement

1 Answers

Adding the MATERIALIZE hint to the WITH subquery overcomes this issue. I'd love if someone could explain why the issue happens at all, but for now:

  WITH maxIds as (
       SELECT /*+ MATERIALIZE */ table_name, record_rowid, MAX(changed_rows_log_id) AS changed_rows_log_id, ORA_ROWSCN as otherSCN
       FROM changed_rows_log
       GROUP BY table_name,  record_rowid 
  )
  SELECT l.changed_rows_log_id, l.ORA_ROWSCN, otherSCN, l.table_name, l.record_rowid
  FROM changed_rows_log l
  JOIN maxIds m on l.changed_rows_log_id = m.changed_rows_log_id and l.table_name=m.table_name and l.record_rowid=m.record_rowid
  WHERE ORA_ROWSCN > 7884576380618
Related