I have many tables with large amount of data. The PK is the column (TAB_ID) which has data type RAW(16). I created the hash partitions with partition key having the TAB_ID column.
My issue is: the SQL statement (select * from my_table where tab_id = 'aas1df') does not use partition pruning. If I change the column datatype to varchar2(32), partition pruning works.
Why does not partition pruning work with partition key which have datatype RAW(16)?
I'm just guessing: try select * from my_table where 'aas1df' = tab_id.
Probably the datatype conversion works other way that expected. Anyway you should use the function UTL_RAW.CAST_TO_RAW.
Edited:
Is your table partitioned by TAB_ID? If yes, then there is something wrong with your design, you usually partition table by some useful business value, NOT by surrogate key.
If you know the PK value you do not need partition pruning at all. When Oracle traverses the PK index it gets ROWID value. This ROWID contains file-number, block ID and also row number within the block. So Oracle can access directly the row.
HEXTORAW enables partition pruning.
In the sample below the Pstart and Pstop are literal numbers, implying partition pruning occurs.
create table my_table
(
TAB_ID raw(16),
a number,
constraint my_table_pk primary key (tab_id)
)
partition by hash(tab_id) partitions 16;
explain plan for
select *
from my_table
where tab_id = hextoraw('1BCDB0E06E7C498CBE42B72A1758B432');
select * from table(dbms_xplan.display(format => 'basic +partition'));
Plan hash value: 1204448714
--------------------------------------------------------------------------
| Id | Operation | Name | Pstart| Pstop |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | |
| 1 | TABLE ACCESS BY GLOBAL INDEX ROWID| MY_TABLE | 2 | 2 |
| 2 | INDEX UNIQUE SCAN | MY_TABLE_PK | | |
--------------------------------------------------------------------------
Related
I am hoping for an explanation of some view + optimizer behavior
lets make a view like this
select e.employee_name, e.employee_active, ei.marital_status
from employees e
left join employee_info ei on ei.employee_id = e.employee_id
Then lets access it like this
select distinct employee_name from employee_view where employee_id = 13
In my head, I'm thinking we do not need to access employee_info table at all for this query to execute in a functionally correct way. Yet, when I explain plans on this query, I see it accessing employee_info. Am I missing something that makes it necessary to actually perform that join? If it is functionally equivalent to not hit that table, is there a way to stop that from happening in 12c?
You deduction is basically correct, but you must take in account than not every optimization that is possible is also implemented.
Oracle pragmatically observes the potential and decides where the optimization should be extended.
For example there were times where the predicate where 1=1 and ... could confuse the optimizer, but as this "construction" is used rather often, in recent version the optimizer recognised it and ignore it without side effects.
Your setup of outer join to child, while selecting distinct from the parent columns only is probably not very frequent and therefore not yet covered.
Here a similar example, where Oracle can skip a table from a view (Note the PK and FK definition, which is crutial)
create table employees (
employee_id int primary key,
employee_name varchar2(10),
employee_active varchar2(10));
create table employee_info (
employee_id int,
marital_status varchar2(10));
alter table employee_info
add constraint emp_info_fk1 foreign key (employee_id)
references employees (employee_id);
create or replace view employee_view as
select e.EMPLOYEE_ID, e.employee_name, e.employee_active, ei.marital_status
from employees e
join employee_info ei on ei.employee_id = e.employee_id;
The query select marital_status from employee_view where employee_id = 13 can skip the employees (parent) table and access only the child table
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 20 | 2 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMPLOYEE_INFO | 1 | 20 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("EI"."EMPLOYEE_ID"=13)
More information and links in similar answer
I have a range interval partitioned table. It has 6 trillion data for 1 year.
CREATE TABLE eip.Meter_Read_Alert_test
(
Mfg_serial_num VARCHAR2(50 BYTE) ,
Channel_id NUMBER NOT NULL,
Read_time TIMESTAMP(0),
CONSTRAINT pk_Alert_test PRIMARY KEY (ID,channel_id,Read_time)
)
PARTITION BY RANGE (Read_time) INTERVAL(NUMTOYMINTERVAL(1, 'MONTH'))
(
PARTITION p1 VALUES less than('01-09-19 12:00:00.000000000 AM')
) ;
Local index created on below columns:
CREATE INDEX mfg_SNo_test_idx on eip.Meter_Read_Alert_test ( Mfg_serial_num ) tablespace SPRING_METER_READ Local ;
CREATE INDEX channel_ID_test_idx on eip.Meter_Read_Alert_test (Channel_ID) tablespace SPRING_METER_READ Local ;
CREATE INDEX ReadTime_test_idx on eip.Meter_Read_Alert_test (Read_Time) tablespace SPRING_METER_READ Local ;
Issue:
When i run below query, ReadTime_test_idx index is not getting used. Full table scan is happening.
select * from meter_read_alert_test
where read_time between '19-11-2019 12:00:00 AM' and '19-11-2019 11:00:00 PM';
Plan hash value: 2722527583
-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9090K| 728M| 411K (6)| 00:00:17 | | |
| 1 | PARTITION RANGE ITERATOR| | 9090K| 728M| 411K (6)| 00:00:17 | KEY | KEY |
|* 2 | TABLE ACCESS FULL | METER_READ_ALERT_TEST | 9090K| 728M| 411K (6)| 00:00:17 | KEY | KEY |
-----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("READ_TIME">=TO_TIMESTAMP('19-11-2019 12:00:00 AM') AND
"READ_TIME"<=TO_TIMESTAMP('19-11-2019 11:00:00 PM'))
Please suggest what is wrong here and how to fix it.
Oracle is not necessarily doing anything wrong here by using a full table scan instead of an index range scan. Full table scans are best when reading a large percentage of data, because full table scans can use a multi-block read and don't have to traverse a tree structure for every value, and if the index data is unordered an index read may have to retrieve all of the blocks from the table anyway.
While your query is only reading a small fraction of the overall data, it is reading a "large" percentage of data from the partition. Since the table is month partitioned, Oracle is using partition pruning to instantly eliminate the majority of data (you can see that in action with the "Key" start and stop partitions). Inside that partition, the query is reading about one day's worth of data, which is about 3% of the data in a partition. There's not a generic number that represents what a "large" percentage is, but there are many cases where 3% is better read with a full table scan than an index.
There's a possibility that Oracle is making a wrong guess here. You might want to try the query with an index hint like select /*+ index(meter_read_alert_test) */ .... If that improves the performance, first try regathering stats. You shouldn't normally need to use index hints.
I have created a cluster in Oracle
CREATE CLUSTER myLovelyCluster (clust_id NUMBER(38,0))
SIZE 1024 SINGLE TABLE HASHKEYS 11;
Than a table for the cluster
CREATE TABLE Table_cluster
CLUSTER myLovelyCluster (columnRandom)
AS SELECT * FROM myTable ;
the columnRandom is well defined as NUMBER(38,0) but why I am getting an error assuming incompatible column definition?
Thanks
Are you sure that columnRandom is number(38,0)? In oracle NUMBER != NUMBER(38,0)
Let's create two table.
create table src_table ( a number);
create table src_table2( a number(38,0));
select column_name,data_precision,Data_scale from user_tab_cols where table_name like 'SRC_TABLE%';
Result of query is. Definitions of column are different.
+-------------+----------------+------------+
| Column_name | Data_Precision | Data_scale |
+-------------+----------------+------------+
| A | | |
| A | 38 | 0 |
+-------------+----------------+------------+
And if i try creat cluster for first table.
CREATE TABLE Table_cluster
CLUSTER myLovelyCluster (a)
AS SELECT * FROM src_table ;
ORA-01753: column definition incompatible with clustered column definition
For 2-nd every thing is ok.
CREATE TABLE Table_cluster
CLUSTER myLovelyCluster (a)
AS SELECT * FROM src_table2 ;
If you add cast into select. Execution also is correct.
CREATE TABLE Table_cluster CLUSTER myLovelyCluster (a)
AS SELECT cast(a as number(38,0)) FROM src_table;
I have a table which has 70 columns, Where primary key is the combination of 15 columns (which includes number and varchar2) . Please see below query
select * from tab1 where k1=1234567889;
Plan hash value: 1179808636
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6044 | 2201K| 4585K (1)| 15:17:04 |
|* 1 | TABLE ACCESS FULL| tab1 | 6044 | 2201K| 4585K (1)| 15:17:04 |
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 – filter ("K1"=30064825087)
Where tab1 is a table mentions above and k1 is a column which is part of primary key. Table is not partitioned. Table is also analyzed (table, index and columns) after data has been inserted. Output for above query returns like 100000 plus records. The problem is even after having PK on the k1 column, the query is doing full table scan, which is not acceptable. On the other hand using index hints does not really speed up the process.
Please advise what would be the possible solution.
For this query:
select *
from tab1
where k1 = 1234567889;
The best index is one that has k1 as the first key in the index. There can be a composite index, by k1 has to be the first key. It sounds like you have a composite primary key and k1 is not the first key.
I would recommend that you simply define another index:
create index idx_tab1_k1 on tab1(k1);
There are several ways to avoid a full-table scan
Indexes: Ensure that indexes exist on the key value and that the index has been analyzed with dbms_stats.
Use_nl hint: You can direct that the optimizer use a nested loops join (which requires indexes).
index hint: You can specify the indexes that you want to use.
I need to hold multiple pairs of 70,000 rows and perform a comparison difference like operation between them using a minus operator. At any time there could be comparisons (table scans).
I currently have one table with this sort of design:
primary key (sequenced)
foreign key to identify set
key to identify set #1 or set #2
then the data here i need to minus against
The data would look something like this
| PK | FK | Key | Data |
| 1 | 1 | Left | Some data |
| 1 | 1 | Left | Diff data |
| 1 | 1 | Right | Some data |
My query would be:
SELECT data
FROM diffTable
WHERE pk = 1
AND fk = 1
AND key = 'Left'
MINUS
SELECT data
FROM diffTable
WHERE pk = 1
AND fk = 1
AND key = 'Right'
I am fearing the full table scans will monopolise resources and subsequent inserts and minus' will suffer.
How should I design my tables and why?
create index PK_FK on diff_table
(PK, FK, Key);
The query you posted in your question would run very fast with this index.
Btw, the PK column is not, by itself, the primary key. See the other comments. Perhaps you want:
alter table diff_table
add constraint PK_FK primary key (PK, FK, Key);
maybe pick a better name...