During some testing I encountered an ORA-000214 during startup of an Oracle 11.2.0.2 database instance:
ORA-00214: control file '+RECO_XXXX/test/controlfile/current.334.755391511'
version 268 inconsistent with file
'+DATA_XXXX/test/controlfile/current.299.755390399' version 265
This is a RAC instance on Exadata, but all techniques in this article will work on any Oracle 11.2.x database using ASM.
This message means the database found two controlfiles which have a different version. If this message appears when the database is open, the database will crash. If an instance is startup after this message, the same error appears, and the database remains in nomount state. Further diagnosis: the control file version in the recovery area is more recent than the version in the data diskgroup (version 268 versus version 265).
The following question appeared in a comment to an earlier posting on multi-column bitmap indexes and the inability of Oracle to create a bitmap index join when (to the human eye) the strategy was an obvious choice.
could you please explain me why ?
SQL_ID 5z0a50a2yzdky, child number 0
-------------------------------------
select count(1) from (select distinct SRC_SYS,PROD_KEY from dds.REV_F)
Plan hash value: 867747470
--------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 221K(100)| | | |
| 1 | SORT AGGREGATE | | 1 | | | | | |
| 2 | VIEW | | 24533 | | 221K (6)| 00:44:22 | | |
| 3 | HASH UNIQUE | | 24533 | 479K| 221K (6)| 00:44:22 | | |
| 4 | VIEW | index$_join$_002 | 63M| 1209M| 212K (2)| 00:42:28 | | |
|* 5 | HASH JOIN | | | | | | | |
| 6 | PARTITION LIST ALL | | 63M| 1209M| 3591 (1)| 00:00:44 | 1 | 145 |
| 7 | BITMAP CONVERSION TO ROWIDS| | 63M| 1209M| 3591 (1)| 00:00:44 | | |
| 8 | BITMAP INDEX FULL SCAN | REV_F_IDX1 | | | | | 1 | 145 |
| 9 | PARTITION LIST ALL | | 63M| 1209M| 13724 (1)| 00:02:45 | 1 | 145 |
| 10 | BITMAP CONVERSION TO ROWIDS| | 63M| 1209M| 13724 (1)| 00:02:45 | | |
| 11 | BITMAP INDEX FULL SCAN | REV_F_IDX5 | | | | | 1 | 145 |
--------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access(ROWID=ROWID)
28 rows selected.
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 610.89 1464.86 707459 17090 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 610.90 1464.87 707459 17090 0 1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: SYS
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=17090 pr=707459 pw=446115 time=1464867976 us)
26066 VIEW (cr=17090 pr=707459 pw=446115 time=1464795748 us)
26066 HASH UNIQUE (cr=17090 pr=707459 pw=446115 time=1464769678 us)
63422824 VIEW index$_join$_002 (cr=17090 pr=707459 pw=446115 time=1084846545 us)
63422824 HASH JOIN (cr=17090 pr=707459 pw=446115 time=958000889 us)
63422824 PARTITION LIST ALL PARTITION: 1 145 (cr=3561 pr=0 pw=0 time=63423134 us)
63422824 BITMAP CONVERSION TO ROWIDS (cr=3561 pr=0 pw=0 time=9554 us)
7112 BITMAP INDEX FULL SCAN REV_F_IDX1 PARTITION: 1 145 (cr=3561 pr=0 pw=0 time=155525 us)(object id 366074)
63422824 PARTITION LIST ALL PARTITION: 1 145 (cr=13529 pr=8864 pw=0 time=190268771 us)
63422824 BITMAP CONVERSION TO ROWIDS (cr=13529 pr=8864 pw=0 time=63553723 us)
432700 BITMAP INDEX FULL SCAN REV_F_IDX5 PARTITION: 1 145 (cr=13529 pr=8864 pw=0 time=3157351 us)(object id 366658)
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 2 0.00 0.00
direct path write temp 29741 1.62 107.05
db file sequential read 8864 0.20 2.35
direct path read temp 46573 0.79 211.02
SQL*Net message from client 2 29.22 29.22
In this case Oracle is clearly doing the bitmap join, but it’s managing to do something very inefficient. The problem lies in the partitioning or, to be more precise, Oracle’s failure to take advantage of partitioning. The OP complains of using 3GB of memory and several minutes of elapsed time. The plan shows us that the we have 145 partitions (PARTITION LIST ALL PARTITION: 1 145), and we have been told that the table is about 17GB is size, so the “average” partition is about 120MB – so why isn’t Oracle using a partition-wise approach and processing the data one partition at a time ? The answer is simple – it can’t be done (at present).
An index join works by doing a hash join between rowids – and since we are using bitmap indexes we have to convert bitmaps to rowids as part of the plan. In this query we then want to count the number of distinct combintations of (SRC_SYS,PROD_KEY) – and the same combination may appear in different partitions, so Oracle has had to generate a plan that handles the entire data set in a single join rather than trying to handle each partition separately (notice how the “partition list all” operator appears twice, once for each index).
The “Row Source Operation” tells us we only had to scan a few thousand block, but we have to build a hash table of 64 million entries:
63422824 BITMAP CONVERSION TO ROWIDS (cr=3561 pr=0 pw=0 time=9554 us)
At 10 bytes per rowid (for a partitioned table), plus the length of the input column, plus linked list overheads (8 bytes per pointer) you can see the 3 GB beginning to appear out of the volume of work being done. And then the Oracle dumped the whole thing to disc (perhaps in anticipation of the impending “hash unique”, perhaps because it still needed to do a one-pass operation – it would have been interesting to see the full row source execution statistics from a call to dbms_xplan.display_cursor())
What we need to see is a plan more like the following:
--------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | --------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 221K(100)| | | | | 1 | SORT AGGREGATE | | 1 | | | | | | | 2 | VIEW | | 24533 | | 221K (6)| 00:44:22 | | | | 3 | HASH UNIQUE | | 24533 | 479K| 221K (6)| 00:44:22 | | | | 4 | PARTITION LIST ALL | | 63M| 1209M| 3591 (1)| 00:00:44 | 1 | 145 | |* 5 | HASH UNIQUE | | | | | | | | | 6 | VIEW | index$_join$_002 | 63M| 1209M| 212K (2)| 00:42:28 | | | |* 7 | HASH JOIN | | | | | | | | | 8 | BITMAP CONVERSION TO ROWIDS| | 63M| 1209M| 3591 (1)| 00:00:44 | | | | 9 | BITMAP INDEX FULL SCAN | REV_F_IDX1 | | | | | 1 | 145 | | 10 | BITMAP CONVERSION TO ROWIDS| | 63M| 1209M| 13724 (1)| 00:02:45 | | | | 11 | BITMAP INDEX FULL SCAN | REV_F_IDX5 | | | | | 1 | 145 | -------------------------------------------- ------------------------------------------------------------------------------
Line 4 shows us that we do something for each partition in turn. The sub-plan to line 4 tells us that we are collecting the unique combinations of (SRC_SYS,PROD_KEY) for a given partition. Line 3 tells us that we are collating the results from the different partitions and generating the set of values that is unique across all partitions.
The problem is this: can we engineer a strange piece of SQL that makes plan appear – because the optimizer isn’t going to do it automatically (yet).
Obviously it would be pretty easy to write some sort of solution using pl/sql and pipelined functions - perhaps a function that takes a table_name loops through each partition of the table in turn returning the distinct combinations for that partition, as this would allow you to “select count(distinct(…)) from table_function(…);” to get your result.
You might be able to avoid pl/sql by creating a piece of SQL joining the table’s metadata to the table by partition identifier – except you would probably need to use a lateral view, which Oracle doesn’t support, and make the partition extended syntax part of the lateral dependency .. which Oracle definitely doesn’t support.
So is there an alternative, purely SQL, strategy ?
I’m thinking about it – I have a cunning plan, but I haven’t had time to test it yet.
to be continued …
I’ve finally managed to make a bit of time to write up my notes about this – and in the interim Randolf Geist has said everything that needs to be said, and the OP has pointed out that a full tablescan works faster anyway. However, here goes …
My cunning plans involved finding ways of forcing Oracle into doing a single partition hash join for each partition in turn by playing clever tricks with dbms_mview.pmarker or the dba_tab_partitions view, or even the tbl$or$idx$part$num() function. But I realised I was in trouble as soon as I wrote the first little test that was supposed to do an index hash join on a single explicitly referenced partition. Here’s my table description, index definitions, query and execution plan:
SQL> desc t1 Name Null? Type ----------------------- -------- ---------------- NVCUSTATUS VARCHAR2(20) -- list partitined on this column, multiple values in some partitions FREQ_COLUMN NUMBER(4) HBAL_COLUMN NUMBER(8) RAND_COLUMN NUMBER(8) create bitmap index t1_freq on t1(freq_column) local; create bitmap index t1_hbal on t1(hbal_column) local; select count(1) from ( select /*+index_join(t1) */ distinct freq_column, hbal_column from t1 partition (p6) ) ; ------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | ------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 894K| 6112K| 2966 (2)| 00:00:36 | | | | 1 | VIEW | index$_join$_001 | 894K| 6112K| 2966 (2)| 00:00:36 | | | |* 2 | HASH JOIN | | | | | | | | | 3 | PARTITION LIST SINGLE | | 894K| 6112K| 208 (0)| 00:00:03 | KEY | KEY | | 4 | BITMAP CONVERSION TO ROWIDS| | 894K| 6112K| 208 (0)| 00:00:03 | | | | 5 | BITMAP INDEX FULL SCAN | T1_FREQ | | | | | 6 | 6 | | 6 | PARTITION LIST SINGLE | | 894K| 6112K| 299 (0)| 00:00:04 | KEY | KEY | | 7 | BITMAP CONVERSION TO ROWIDS| | 894K| 6112K| 299 (0)| 00:00:04 | | | | 8 | BITMAP INDEX FULL SCAN | T1_HBAL | | | | | 6 | 6 | -------------------------------------------------------------------------------------------------------------------
Even though we address a single partition explicitly, and even though the optimizer recognises it as partition 6 in lines 5 and 8, the plan isn’t doing a “partition-wise” join between the two indexes – the hash join calls two independent partition operations. In effect, Oracle is joining two different tables, and there isn’t a join condition on the partitioning column.
If we change the index definitions, though, to append the partitioning column (and given the small number of distinct values in each partition this didn’t affect the index size by much), and then rewrite the query to include the join, we get a better result – but only if we do a manual rewrite of the query (and if the partitioning column is declared not null).
alter table t1 modify nvcustatus not null;
drop index t1_freq;
drop index t1_hbal;
create bitmap index t1_freq on t1(freq_column, nvcustatus) local;
create bitmap index t1_hbal on t1(hbal_column, nvcustatus) local;
select
count(*)
from (
select
/*+
qb_name(main)
no_eliminate_join(@SEL$96BB32CC t1@hbal)
no_eliminate_join(@SEL$96BB32CC t1@freq)
*/
distinct ftb.freq_column, htb.hbal_column
from
(
select
/*+ qb_name(freq) index(t1 t1_freq) */
nvcustatus, freq_column, rowid frid
from
t1
) ftb,
(
select
/*+ qb_name(hbal) index(t1 t1_hbal) */
nvcustatus, hbal_column, rowid hrid
from
t1
) htb
where
ftb.frid = htb.hrid
and ftb.nvcustatus= htb.nvcustatus
)
;
-------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | Writes | OMem | 1Mem | Used-Mem | Used-Tmp|
-------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:05.44 | 719 | 4380 | 3660 | | | | |
| 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:05.44 | 719 | 4380 | 3660 | | | | |
| 2 | VIEW | | 1 | 35118 | 23223 |00:00:05.50 | 719 | 4380 | 3660 | | | | |
| 3 | HASH UNIQUE | | 1 | 35118 | 23223 |00:00:05.46 | 719 | 4380 | 3660 | 1268K| 1268K| 2647K (0)| |
| 4 | PARTITION LIST ALL | | 1 | 330K| 1000K|00:00:18.44 | 719 | 4380 | 3660 | | | | |
|* 5 | HASH JOIN | | 8 | 330K| 1000K|00:00:15.34 | 719 | 4380 | 3660 | 283M| 16M| 27M (1)| 31744 |
| 6 | BITMAP CONVERSION TO ROWIDS| | 8 | 1000K| 1000K|00:00:00.99 | 296 | 289 | 0 | | | | |
| 7 | BITMAP INDEX FULL SCAN | T1_FREQ | 8 | | 1831 |00:00:00.30 | 296 | 289 | 0 | | | | |
| 8 | BITMAP CONVERSION TO ROWIDS| | 7 | 1000K| 1000K|00:00:06.48 | 423 | 416 | 0 | | | | |
| 9 | BITMAP INDEX FULL SCAN | T1_HBAL | 7 | | 7353 |00:00:00.41 | 423 | 416 | 0 | | | | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("NVCUSTATUS"="NVCUSTATUS" AND ROWID=ROWID)
As you can see from the plan, I ran one of my tests with rowsource execution statistics enabled, so that I could count starts.
My query basically breaks the implicit join of two indexes into an explcit join so that I can include the join on the partitioning column. You’ll notice that I now iterate through each partition – line 4 – and do a hash join for each pair of partitions. This is the target I was hoping for. The theory was that by doing (up to) eight small hash joins, each join will operate in memory, rather than flooding one large build table to disc. Of course, the thing I’m building the hash table with is now three values (rowid, freq_value, nvcustatus) rather than the two values from the implicit join (rowid, freq_value), so if I’m unlucky this version of the query could take even longer than the original because it may have to do more I/O.
If you’re wondering why I only started lines 8 and 9 once, this was because one of may partitions was empty, so Oracle had to scan it once as the build table to find that it was empty, then didn’t need to scan it as the probe table.
Footnote: Although I got the plan I wanted – it really didn’t make much difference, and even with the million rows I was using it was fast to solve the problem with a tablescan. The relative benefit of the different plans would be dicatated by the length of rows, the lengths of the relevant columns, the number of partitions, and the available memory for hashing.
July 1, 1011 A recent thread in the comp.databases.oracle.server Usenet group (actually two threads) asked an interesting question. Assume that you had a detail table that contained several attributes for each of the unique key values. How would one go about finding all of the unique key values that share the same set of attributes? [...]![]()
Recent comments
21 weeks 6 hours ago
30 weeks 5 days ago
32 weeks 3 days ago
35 weeks 4 days ago
37 weeks 6 days ago
47 weeks 3 days ago
49 weeks 8 hours ago
50 weeks 9 hours ago
50 weeks 1 day ago
1 year 5 days ago