Auto increment without sequence - oracle

I have a select statement that generate set value thereafter I want insert that set of values into another table, MY concern is I'm using select statement in select I'm using one one more select clause((select max(org_id)+1 from org)) where I'm trying to get max value and increment by one but I'm not able get incremented value instead I'm getting same value you can see column name id_limit
select abc,abc1,abc3,abc4,(select max(org_id)+1 from org) as id_limit from xyz
current output
-----------------------------------------------------------------
| abc | abc1 | abc3 | abc4 | id_limit |
----------------------------------------------------------------|
| BUSINESS_UNIT | 0 | 100 | London | 6 |
| BUSINESS_UNIT | 0 | 200 | Sydney | 6 |
| BUSINESS_UNIT | 0 | 300 | Kiev | 6 |
-----------------------------------------------------------------
I'm trying to get expected out output
-----------------------------------------------------------------
| abc | abc1 | abc3 | abc4 | id_limit |
----------------------------------------------------------------|
| BUSINESS_UNIT | 0 | 100 | London | 6 |
| BUSINESS_UNIT | 0 | 200 | Sydney | 7 |
| BUSINESS_UNIT | 0 | 300 | Kiev | 8 |
-----------------------------------------------------------------

Yes, in Oracle 12.
create table foo (
id number generated by default on null as identity
);
https://oracle-base.com/articles/12c/identity-columns-in-oracle-12cr1
In previous versions you use sequence/trigger as explained here:
How to create id with AUTO_INCREMENT on Oracle?

Related

Proxysql - Please help me clarify "chains of rules" feature

I'm newbie with proxysql, this my enviroment:
Centos 7 , proxysql-1.4.13 . I define 2 host group ids : host group id 2 for mysql server that can write , host group id 3 for mysql server that can read.
All queries that begin with insert, update, delete, alter should be routed to host group id 2.
All queries that begin with select should be routed to host group id 3.
So here are my rules :
Admin> select rule_id,active,digest,match_digest,destination_hostgroup,flagIN,flagOUT,next_query_flagIN,sticky_conn,apply from mysql_query_rules;
+---------+--------+--------------------+--------------+-----------------------+--------+---------+-------------------+-------------+-------+
| rule_id | active | digest | match_digest | destination_hostgroup | flagIN | flagOUT | next_query_flagIN | sticky_conn | apply |
+---------+--------+--------------------+--------------+-----------------------+--------+---------+-------------------+-------------+-------+
| 101 | 1 | NULL | ^insert | 2 | 0 | NULL | NULL | NULL | 1 |
| 102 | 1 | NULL | ^update | 2 | 0 | NULL | NULL | NULL | 1 |
| 103 | 1 | NULL | ^delete | 2 | 0 | NULL | NULL | NULL | 1 |
| 104 | 1 | NULL | ^alter | 2 | 0 | NULL | NULL | NULL | 1 |
| 105 | 1 | NULL | ^select | 3 | 0 | NULL | NULL | NULL | 1 |
+---------+--------+--------------------+--------------+-----------------------+--------+---------+-------------------+-------------+-------+
And they works fine. By the way, are these rules ok ? Should I replace match_digest by match_pattern ?
However , my application has 1 feature that insert data into a table (create booking) then select (almost immediately) new data from that table .
So if select query fails (because new data has not replicated to host group id 3 yet), application feature will run wrong.
I want to route the select query right after insert query to host group id 2 so it will get new data successfully.
I read proxysql document https://github.com/sysown/proxysql/wiki/Main-(runtime)#mysql_query_rules and this discussion https://github.com/sysown/proxysql/pull/825 , I think this is solution for me ,isn't it ? I still don't understand about these flagIN,flagOUT,next_query_flagIN,sticky_conn stuff clearly but I will give it a try.
I know the insert query digest is 0xCDD6DB677604AFA7
The select query digest is 0x0DCD2E8ADF6A66CB
Then I add 2 new rules:
Admin> select rule_id,active,digest,match_digest,destination_hostgroup,flagIN,flagOUT,next_query_flagIN,sticky_conn,apply from mysql_query_rules;
+---------+--------+--------------------+--------------+-----------------------+--------+---------+-------------------+-------------+-------+
| rule_id | active | digest | match_digest | destination_hostgroup | flagIN | flagOUT | next_query_flagIN | sticky_conn | apply |
+---------+--------+--------------------+--------------+-----------------------+--------+---------+-------------------+-------------+-------+
| 1 | 1 | 0xCDD6DB677604AFA7 | NULL | 2 | 0 | NULL | 1 | 1 | 1 |
| 2 | 1 | 0x0DCD2E8ADF6A66CB | NULL | 2 | 1 | NULL | NULL | NULL | 1 |
| 101 | 1 | NULL | ^insert | 2 | 0 | NULL | NULL | NULL | 1 |
| 102 | 1 | NULL | ^update | 2 | 0 | NULL | NULL | NULL | 1 |
| 103 | 1 | NULL | ^delete | 2 | 0 | NULL | NULL | NULL | 1 |
| 104 | 1 | NULL | ^alter | 2 | 0 | NULL | NULL | NULL | 1 |
| 105 | 1 | NULL | ^select | 3 | 0 | NULL | NULL | NULL | 1 |
+---------+--------+--------------------+--------------+-----------------------+--------+---------+-------------------+-------------+-------+
They work fine, the select query right after insert query is route to host group id 2 so it gets new data successfully and application feature runs ok.
But am I doing right ?
I'm confused because stats_mysql_query_rules result:
Before application feature run:
Admin> select * from stats_mysql_query_rules;
+---------+------+
| rule_id | hits |
+---------+------+
| 1 | 20 |
| 2 | 20 |
| 101 | 33 |
| 102 | 0 |
| 103 | 2 |
| 104 | 0 |
| 105 | 903 |
+---------+------+
After application feature run (10 bookings):
Admin> select * from stats_mysql_query_rules;
+---------+------+
| rule_id | hits |
+---------+------+
| 1 | 30 |
| 2 | 30 |
| 101 | 43 |
| 102 | 0 |
| 103 | 2 |
| 104 | 0 |
| 105 | 1313 |
+---------+------+
Why rule_id 101 hits rate increase from 33 --> 43 ? So rule_id 101 (match_digest ^insert) also match insert query in application feature ? Does it mean I'm doing wrong ?

Explain Plan in Oracle - how do I detect the most efficient query?

Intro
I am writing a query to compare 2 different oracle database tables containing hashes.
I want to find which hashes from location 1, have not been migrated over to location 2.
I have three different queries, and have written explain plan for statements for them.
However, the results don't tell me that much.
Question
How do I find which is the most efficient and fastest?
Current Guess
My suspicion however is that the first query is the fastest since it makes a one-off use of the remote link. But this is just a guess that is not supported by actual results.
Code
--------------------------
EXPLAIN PLAN
SET statement_id = 'ex_plan1' FOR
select* from document doc left outer join migrated_document#V2_PROD migrated on doc.hash = migrated.document_hash AND migrated.document_hash is null ;
SELECT PLAN_TABLE_OUTPUT
FROM TABLE(DBMS_XPLAN.DISPLAY(NULL, 'ex_plan1','BASIC'));
---------------------------------------------------
| Id | Operation | Name |
---------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | HASH JOIN RIGHT OUTER| |
| 2 | REMOTE | MIGRATED_DOCUMENT |
| 3 | TABLE ACCESS FULL | DOCUMENT |
---------------------------------------------------
--------------------------
EXPLAIN PLAN
SET statement_id = 'ex_plan2' FOR
select* from document doc where not exists( select 1 from migrated_document#V2_PROD migrated where migrated.document_hash = doc.HASH ) ;
SELECT PLAN_TABLE_OUTPUT
FROM TABLE(DBMS_XPLAN.DISPLAY(NULL, 'ex_plan2','BASIC'));
------------------------------------------------
| Id | Operation | Name |
------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | FILTER | |
| 2 | TABLE ACCESS FULL| DOCUMENT |
| 3 | REMOTE | MIGRATED_DOCUMENT |
------------------------------------------------
--------------------------
EXPLAIN PLAN
SET statement_id = 'ex_plan3' FOR
select* from document doc where doc.hash not in ( select migrated.document_hash from migrated_document#V2_PROD migrated) ;
SELECT PLAN_TABLE_OUTPUT
FROM TABLE(DBMS_XPLAN.DISPLAY(NULL, 'ex_plan3','BASIC'));
------------------------------------------------
| Id | Operation | Name |
------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | NESTED LOOPS ANTI | |
| 2 | TABLE ACCESS FULL| DOCUMENT |
| 3 | REMOTE | MIGRATED_DOCUMENT |
------------------------------------------------
--------------------------
Update
I updated the explain plan statements to get more results.
Due to the remote operation... could something cost less but be more slow?
If I am reading the data correctly it seems that option 2 is best.
But I still think option 1 is quicker.
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Inst |IN-OUT|
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 105K| 51M| 194 | | |
| 1 | HASH JOIN RIGHT OUTER| | 105K| 51M| 194 | | |
| 2 | REMOTE | MIGRATED_DOCUMENT | 1 | 275 | 2 | V2_MN~ | R->S |
| 3 | TABLE ACCESS FULL | DOCUMENT | 105K| 23M| 192 | | |
-------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Inst |IN-OUT|
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 105K| 23M| 104K| | |
| 1 | FILTER | | | | | | |
| 2 | TABLE ACCESS FULL| DOCUMENT | 105K| 23M| 192 | | |
| 3 | REMOTE | MIGRATED_DOCUMENT | 1 | 50 | 1 | V2_MN~ | R->S |
----------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Inst |IN-OUT|
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 105K| 29M| 526 | | |
| 1 | NESTED LOOPS ANTI | | 105K| 29M| 526 | | |
| 2 | TABLE ACCESS FULL| DOCUMENT | 105K| 23M| 192 | | |
| 3 | REMOTE | MIGRATED_DOCUMENT | 1 | 50 | 0 | V2_MN~ | R->S |
----------------------------------------------------------------------------------------
In a perfect world, you would choose the plan with the lowest cost. However I do not think your first query does what you want. It looks to me like no rows will join; you should be using a filter predicate rather than a join.
Instead of
select * from document doc
left outer join migrated_document#V2_PROD migrated
on doc.hash = migrated.document_hash
AND migrated.document_hash is null
it should be
select * from document doc
left outer join migrated_document#V2_PROD migrated
on doc.hash = migrated.document_hash
WHERE migrated.document_hash is null

Query running long despite having "good" plan

I have a query which is a UNION query which joins multiple tables. All the stats are updated. The query used to run fine and completed within 30-40 seconds but in 12c it's taking insane amount of time(~10 mins to 5 mins)
I have captured the actual plan.
lan hash value: 1038754298
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | O/1/M |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 50 |00:00:49.93 | 960K| 7 | | | |
| 1 | SORT ORDER BY | | 1 | 1791 | 50 |00:00:49.93 | 960K| 7 | 267K| 267K| 1/0/0|
| 2 | UNION-ALL | | 1 | | 2648 |00:00:49.93 | 960K| 7 | | | |
| 3 | NESTED LOOPS SEMI | | 1 | 1 | 2648 |00:00:42.36 | 498K| 0 | | | |
|* 4 | HASH JOIN | | 1 | 1589 | 9111 |00:00:42.22 | 469K| 0 | 7900K| 2299K| 1/0/0|
|* 5 | HASH JOIN | | 1 | 1140K| 50569 |00:00:19.50 | 342K| 0 | 8321K| 2379K| 1/0/0|
|* 6 | HASH JOIN | | 1 | 44335 | 59660 |00:00:16.01 | 252K| 0 | 1817K| 1817K| 1/0/0|
|* 7 | TABLE ACCESS FULL | PROMHEAD | 1 | 870 | 870 |00:00:00.01 | 602 | 0 | | | |
|* 8 | HASH JOIN | | 1 | 1168K| 172K|00:00:15.94 | 251K| 0 | 1148K| 1148K| 1/0/0|
|* 9 | TABLE ACCESS FULL | PRICE_BATCH_TRAN | 1 | 857 | 857 |00:00:00.01 | 315 | 0 | | | |
| 10 | TABLE ACCESS FULL | PROMSTORE | 1 | 36M| 36M|00:00:04.64 | 251K| 0 | | | |
|* 11 | TABLE ACCESS FULL | PROMSKU | 1 | 6665K| 6665K|00:00:01.49 | 90224 | 0 | | | |
| 12 | PARTITION RANGE ALL | | 1 | 37M| 37M|00:00:10.72 | 127K| 0 | | | |
| 13 | INDEX FAST FULL SCAN | PK_ITEM_ZONE_PRICE | 24 | 37M| 37M|00:00:04.75 | 127K| 0 | | | |
| 14 | PARTITION RANGE ITERATOR | | 9111 | 32376 | 2648 |00:00:00.13 | 28752 | 0 | | | |
| 15 | PARTITION HASH ITERATOR | | 9111 | 32376 | 2648 |00:00:00.11 | 28752 | 0 | | | |
|* 16 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED | FDT_PRICE_FUTURE | 9111 | 32376 | 2648 |00:00:00.09 | 28752 | 0 | | | |
|* 17 | INDEX RANGE SCAN | FDT_PRICE_FUTURE_PK | 9111 | 1 | 2668 |00:00:00.07 | 26089 | 0 | | | |
|* 18 | HASH JOIN | | 1 | 1790 | 0 |00:00:07.31 | 450K| 7 | 1599K| 1599K| 1/0/0|
| 19 | TABLE ACCESS FULL | PRICE_SUSP_HEAD | 1 | 2056 | 2056 |00:00:00.01 | 965 | 0 | | | |
|* 20 | HASH JOIN | | 1 | 1912 | 0 |00:00:07.30 | 449K| 7 | 39M| 8299K| 1/0/0|
| 21 | PART JOIN FILTER CREATE | :BF0000 | 1 | 745K| 745K|00:00:00.12 | 1682 | 0 | | | |
| 22 | TABLE ACCESS FULL | PRICE_ZONE_GROUP_STORE | 1 | 745K| 745K|00:00:00.03 | 1682 | 0 | | | |
|* 23 | HASH JOIN | | 1 | 496K| 0 |00:00:07.03 | 448K| 7 | 73M| 6638K| 1/0/0|
| 24 | PARTITION RANGE ALL | | 1 | 1138K| 1138K|00:00:01.70 | 154K| 7 | | | |
| 25 | PARTITION HASH ALL | | 26 | 1138K| 1138K|00:00:01.53 | 154K| 7 | | | |
| 26 | TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| FDT_PRICE_FUTURE | 208 | 1138K| 1138K|00:00:01.35 | 154K| 7 | | | |
|* 27 | INDEX SKIP SCAN | FDT_PRICE_FUTURE_I1 | 208 | 1138K| 1138K|00:00:00.89 | 125K| 7 | | | |
|* 28 | HASH JOIN | | 1 | 25M| 0 |00:00:04.84 | 293K| 0 | 977K| 977K| 1/0/0|
|* 29 | HASH JOIN | | 1 | 252K| 1123 |00:00:03.33 | 90826 | 0 | 1344K| 1344K| 1/0/0|
|* 30 | TABLE ACCESS FULL | PROMHEAD | 1 | 870 | 870 |00:00:00.01 | 602 | 0 | | | |
|* 31 | TABLE ACCESS FULL | PROMSKU | 1 | 6665K| 6665K|00:00:01.47 | 90224 | 0 | | | |
| 32 | PARTITION RANGE JOIN-FILTER | | 1 | 6719K| 32778 |00:00:01.51 | 202K| 0 | | | |
|* 33 | TABLE ACCESS FULL | PRICE_SUSP_DETAIL | 24 | 6719K| 32778 |00:00:01.51 | 202K| 0 | | | |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("IZP"."ITEM"="PSK"."SKU" AND "IZP"."ZONE_GROUP_ID"="PBT"."ZONE_GROUP_ID" AND "IZP"."ZONE_ID"="PBT"."NEW_ZONE_ID")
5 - access("PS"."PROMOTION"="PSK"."PROMOTION")
6 - access("PH"."PROMOTION"="PS"."PROMOTION")
7 - filter("PH"."STATUS"='A')
8 - access("PS"."STORE"="PBT"."STORE")
filter("PS"."START_DATE">"PBT"."EFFECTIVE_DATE")
9 - filter("PBT"."FDC_STATUS"='A')
11 - filter(("PSK"."CHANGE_TYPE"='MA' OR "PSK"."CHANGE_TYPE"='PC'))
16 - filter("PRICE_MOD_TYPE"='ZC')
17 - access("STORE"="PS"."STORE" AND "SKU"="PSK"."SKU" AND "START_DATE"<"PS"."START_DATE")
18 - access("PSH"."PRICE_CHANGE"="PSD"."PRICE_CHANGE")
filter("FDT"."START_DATE">"PSH"."ACTIVE_DATE")
20 - access("PSD"."ZONE_ID"="PZGS"."ZONE_ID" AND "PSD"."ZONE_GROUP_ID"="PZGS"."ZONE_GROUP_ID" AND "FDT"."STORE"="PZGS"."STORE")
23 - access("PSK"."PROMOTION"="FDT"."PRICE_MOD_NO" AND "PSD"."SKU"="FDT"."SKU")
27 - access("FDT"."PRICE_MOD_TYPE"='PR')
filter("FDT"."PRICE_MOD_TYPE"='PR')
28 - access("PSK"."SKU"="PSD"."SKU")
29 - access("PH"."PROMOTION"="PSK"."PROMOTION")
30 - filter("PH"."STATUS"='A')
31 - filter(("PSK"."CHANGE_TYPE"='MA' OR "PSK"."CHANGE_TYPE"='PC'))
33 - filter("PSD"."STATUS"='A')
Note
-----
- this is an adaptive plan
As you can see Oracle predicts the cardinality correctly in most cases. Can you suggest where should I start looking from.
SELECT
PBT.STORE ,
PSK.SKU ,
0 MULTI_UNITS ,
0 MULTI_UNIT_RETAIL ,
PS.START_DATE PS_START_DATE ,
PBT.EFFECTIVE_DATE PC_ACTIVE_DATE ,
PBT.ZONE_CHANGE PRICE_CHANGE ,
PH.PROMOTION PRICE_MOD_NO ,
'PR' PRICE_TYPE ,
'U' REC_TYPE
FROM PROMHEAD PH ,
PROMSTORE PS ,
PROMSKU PSK ,
PRICE_BATCH_TRAN PBT,
ITEM_ZONE_PRICE IZP
WHERE PH.PROMOTION =PS.PROMOTION
AND PS.PROMOTION =PSK.PROMOTION
AND PBT.FDC_STATUS ='A'
AND PH.STATUS ='A'
AND PS.STORE =PBT.STORE
AND PSK.CHANGE_TYPE IN('MA','PC')
AND IZP.ITEM =PSK.SKU
AND IZP.ZONE_GROUP_ID=PBT.ZONE_GROUP_ID
AND IZP.ZONE_ID =PBT.NEW_ZONE_ID
AND PS.START_DATE > PBT.EFFECTIVE_DATE
AND EXISTS
(
SELECT 1
FROM FDT_PRICE_FUTURE
WHERE SKU =PSK.SKU
AND STORE =PS.STORE
AND START_DATE < PS.START_DATE
AND PRICE_MOD_TYPE = 'ZC'
)
UNION ALL
SELECT FDT.STORE ,
FDT.SKU ,
0 MULTI_UNITS ,
0 MULTI_UNIT_RETAIL ,
FDT.START_DATE PS_START_DATE ,
PSH.ACTIVE_DATE PC_ACTIVE_DATE ,
PSD.PRICE_CHANGE PRICE_CHANGE ,
PH.PROMOTION PRICE_MOD_NO ,
'PR' PRICE_TYPE ,
'U' REC_TYPE
FROM FDT_PRICE_FUTURE FDT ,
PROMHEAD PH ,
PROMSKU PSK ,
PRICE_SUSP_DETAIL PSD,
PRICE_SUSP_HEAD PSH ,
PRICE_ZONE_GROUP_STORE PZGS
WHERE PH.PROMOTION =PSK.PROMOTION
AND PSK.PROMOTION =FDT.PRICE_MOD_NO
AND FDT.PRICE_MOD_TYPE='PR'
AND PH.STATUS ='A'
AND PSH.PRICE_CHANGE =PSD.PRICE_CHANGE
AND PSD.STATUS ='A'
AND PSD.ZONE_ID =PZGS.ZONE_ID
AND PSD.ZONE_GROUP_ID =PZGS.ZONE_GROUP_ID
AND FDT.STORE =PZGS.STORE
AND PSK.SKU =PSD.SKU
AND PSD.SKU =FDT.SKU
AND PSK.CHANGE_TYPE IN('MA','PC')
AND FDT.START_DATE > PSH.ACTIVE_DATE
ORDER BY 1,2,7;
Also, if Diagnostics and Tuning Pack licenses available, you can run the SQL Tuning Advisor on the SQL.
-- SQLTUNE.sql
-- Updated 04-Mar-2015 RDCornejo for spool error
-- takes sql_id,plan_hash_value, start and ent snap_id's as parameters
-- created sql tuning report on c:\temp
set echo off
set feedback off
set term off
set verify off
set head on
-- uncomment for debugging if neeed
-- set echo on term on
set termout on
accept SQL_ID char prompt 'Enter the sql_id to Tune? '
accept hash_value char prompt 'Enter the hash_value to Tune? '
accept BEGIN_SNAP char prompt 'Enter the begin snap_id to Tune? '
accept END_SNAP char prompt 'Enter the end snap_id to Tune? '
-- using bind variables:
variable sql_id_var varchar2(25);
variable plan_hash_value varchar2(25);
variable begin_snap_var varchar2(25);
variable end_snap_var varchar2(25);
variable task_name varchar2(100);
exec :sql_id_var := '&SQL_ID';
exec :plan_hash_value := '&hash_value';
exec :begin_snap_var := '&BEGIN_SNAP';
exec :end_snap_var := '&END_SNAP';
exec :task_name := :sql_id_var || '_' || :plan_hash_value || '_AWR';
column sql_id format a25
column plan_hash_value format 999999999999
column begin_snap format a10
column end_snap format a10
select :sql_id_var as sql_id
, :plan_hash_value as plan_hash_value
, :begin_snap_var as begin_snap
, :end_snap_var as end_snap
from dual;
-- get my prefered text in file name
column MY_TEXT new_val FILE_NAME noprint
Select instance_name||'_'|| :SQL_ID_VAR || '_' || :plan_hash_value|| '_' || :BEGIN_SNAP_VAR || '_' || :END_SNAP_VAR || '_' || to_char(sysdate, 'yyyy_mm_dd_hh24_mi') MY_TEXT
from
(select upper(substr(global_name, 1, instr(global_name,'.' )-1)) instance_name from global_name);
-- turn spooling on to capture any error and the report.
set serveroutput on
spool c:\temp\AWR_Tuning_Task_&FILE_NAME..txt
set linesize 210
set pagesize 9999
set long 10000000
set time on
set timing on
SET SERVEROUTPUT ON
prompt Drop existing task if exists. Ignore error if not
-- Drop existig tuning task; assuming I'm running new tasks and if one exists, it it no longer needed; can ignore error if no task exists yet
execute DBMS_SQLTUNE.drop_tuning_task (task_name => :task_name);
prompt Create Tuning Task
-- Tuning task created for specific a statement from the AWR. [SEE BELOW FOR EXAPLES USING OTHER METHODS OF IDENTIFYING THE SQL]
--If the TASK_NAME parameter is specified it's value is returned as the SQL tune task identifier.
--If ommitted a system generated name like "TASK_1478" is returned.
--If the SCOPE parameter is set to scope_limited the SQL profiling analysis is omitted.
--The TIME_LIMIT parameter simply restricts the time the optimizer can spend compiling the recommendations.
/**/
DECLARE
l_sql_tune_task_id VARCHAR2(100);
BEGIN
l_sql_tune_task_id := DBMS_SQLTUNE.create_tuning_task (
begin_snap => to_number(:BEGIN_SNAP_VAR) ,
end_snap => to_number(:END_SNAP_VAR) ,
sql_id => :sql_id_var,
plan_hash_value => to_number(:plan_hash_value),
scope => DBMS_SQLTUNE.scope_comprehensive,
time_limit => 1200, -- in seconds
task_name => :task_name,
description => 'Tuning task for statement '|| :sql_id_var || ' hash: ' || :plan_hash_value|| ' in AWR ' || :BEGIN_SNAP_VAR || ' - ' || :END_SNAP_VAR || ' .');
DBMS_OUTPUT.put_line('l_sql_tune_task_id: ' || l_sql_tune_task_id);
END;
/
prompt Execute Tuning Task
-- With the tuning task defined the next step is to execute it using the EXECUTE_TUNING_TASK procedure.
EXEC DBMS_SQLTUNE.execute_tuning_task(task_name => :task_name);
/**/
-- Once the tuning task has executed successfully the recommendations can be displayed using the REPORT_TUNING_TASK function.
set linesize 210
set pagesize 9999
set long 10000000
set time on
set timing on
SET SERVEROUTPUT ON
-- width of output should not need more than 200
column recommendations format a200
prompt Report Tuning Task
SELECT DBMS_SQLTUNE.report_tuning_task(:task_name) AS recommendations FROM dual;
spool off

Oracle query execution plan

I am little confused over the execution plan of an Oracle query. This is in Oracle Enterprise Edition 11.2.0.1.0 on platform IBM AIX 6.1. I have a table TEST1 (1 million rows) and another table TEST2 (50,000 rows). Both tables have identical columns. There is a view created as a union of these 2 tables. I am firing a query on this view, with an indexed column in the WHERE clause. What I could find is that the index is not used and a full table scan is resulted. With a slight modification of the query, it started using the index. I am wondering how this particular change can result in the plan change.
Please find the complete DDL + DML below. I have given simplified example. Actual schema and requirements are bit more complex. In fact the query in question is dynamically constructed and executed by an OCI code generator. My intention here is not to get alternatives, but to really understand what could be the logical reasoning behind the plan change (between, I am an application programmer and not a database administrator). Your help is much appreciated.
DROP TABLE TEST1 CASCADE CONSTRAINTS ;
DROP TABLE TEST2 CASCADE CONSTRAINTS ;
CREATE TABLE TEST1
(
ID NUMBER(20) NOT NULL,
NAME VARCHAR2(40),
DAY NUMBER(20)
)
PARTITION BY RANGE (DAY)
(
PARTITION P001 VALUES LESS THAN (2),
PARTITION P002 VALUES LESS THAN (3),
PARTITION P003 VALUES LESS THAN (4),
PARTITION P004 VALUES LESS THAN (5),
PARTITION P005 VALUES LESS THAN (6),
PARTITION P006 VALUES LESS THAN (7),
PARTITION P007 VALUES LESS THAN (8),
PARTITION P008 VALUES LESS THAN (9),
PARTITION P009 VALUES LESS THAN (10),
PARTITION P010 VALUES LESS THAN (11),
PARTITION P011 VALUES LESS THAN (12),
PARTITION P012 VALUES LESS THAN (13),
PARTITION P013 VALUES LESS THAN (14),
PARTITION P014 VALUES LESS THAN (15),
PARTITION P015 VALUES LESS THAN (16),
PARTITION P016 VALUES LESS THAN (17),
PARTITION P017 VALUES LESS THAN (18),
PARTITION P018 VALUES LESS THAN (19),
PARTITION P019 VALUES LESS THAN (20),
PARTITION P020 VALUES LESS THAN (21),
PARTITION P021 VALUES LESS THAN (22),
PARTITION P022 VALUES LESS THAN (23),
PARTITION P023 VALUES LESS THAN (24),
PARTITION P024 VALUES LESS THAN (25),
PARTITION P025 VALUES LESS THAN (26),
PARTITION P026 VALUES LESS THAN (27),
PARTITION P027 VALUES LESS THAN (28),
PARTITION P028 VALUES LESS THAN (29),
PARTITION P029 VALUES LESS THAN (30),
PARTITION P030 VALUES LESS THAN (31)
) ;
CREATE INDEX IX_ID on TEST1 (ID) INITRANS 4 STORAGE(FREELISTS 16) LOCAL
(
PARTITION P001,
PARTITION P002,
PARTITION P003,
PARTITION P004,
PARTITION P005,
PARTITION P006,
PARTITION P007,
PARTITION P008,
PARTITION P009,
PARTITION P010,
PARTITION P011,
PARTITION P012,
PARTITION P013,
PARTITION P014,
PARTITION P015,
PARTITION P016,
PARTITION P017,
PARTITION P018,
PARTITION P019,
PARTITION P020,
PARTITION P021,
PARTITION P022,
PARTITION P023,
PARTITION P024,
PARTITION P025,
PARTITION P026,
PARTITION P027,
PARTITION P028,
PARTITION P029,
PARTITION P030
) ;
CREATE TABLE TEST2
(
ID NUMBER(20) PRIMARY KEY NOT NULL,
NAME VARCHAR2(40),
DAY NUMBER(20)
) ;
CREATE OR REPLACE VIEW TEST_V AS
SELECT
ID, NAME, DAY
FROM
TEST1
UNION
SELECT
ID, NAME, DAY
FROM
TEST2 ;
begin
for count in 1..1000000
loop
insert into test1 values(count, 'John', mod(count, 30) + 1) ;
end loop ;
end ;
/
begin
for count in 1000000..1050000
loop
insert into test2 values(count, 'Mary', mod(count, 30) + 1) ;
end loop ;
end ;
/
commit ;
set lines 300 ;
set pages 1000 ;
-- Actual query
explain plan for
SELECT Key FROM
(
WITH recs AS
(
SELECT * FROM TEST_V WHERE ID = 70000
)
(
SELECT 1 AS Key FROM recs WHERE NAME = 'John'
)
UNION
(
SELECT 2 AS Key FROM recs WHERE NAME = 'Mary'
)
) ;
select * from table(dbms_xplan.display()) ;
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | TempSpc | Cost (%CPU) | Time | Pstart | Pstop |
------------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1611K | 4721K | | 13559 (1) | 00:02:43 | | |
| 1 | VIEW | | 1611K | 4721K | | 13559 (1) | 00:02:43 | | |
| 2 | TEMP TABLE TRANSFORMATION | | | | | | | | |
| 3 | LOAD AS SELECT | SYS_TEMP_0FD9D6610_34D3B6C | | | | | | | |
|* 4 | VIEW | TEST_V | 805K | 36M | | 10403 (1) | 00:02:05 | | |
| 5 | SORT UNIQUE | | 805K | 36M | 46M | 10403 (8) | 00:02:05 | | |
| 6 | UNION-ALL | | | | | | | | |
| 7 | PARTITION RANGE ALL | | 752K | 34M | | 721 (1) | 00:00:09 | 1 | 30 |
| 8 | TABLE ACCESS FULL | TEST1 | 752K | 34M | | 721 (1) | 00:00:09 | 1 | 30 |
| 9 | TABLE ACCESS FULL | TEST2 | 53262 | 2496K | | 68 (0) | 00:00:01 | | |
| 10 | SORT UNIQUE | | 1611K | 33M | 43M | 13559 (51) | 00:02:43 | | |
| 11 | UNION-ALL | | | | | | | | |
|* 12 | VIEW | | 805K | 16M | | 1429 (1) | 00:00:18 | | |
| 13 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6610_34D3B6C | 805K | 36M | | 1429 (1) | 00:00:18 | | |
|* 14 | VIEW | | 805K | 16M | | 1429 (1) | 00:00:18 | | |
| 15 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6610_34D3B6C | 805K | 36M | | 1429 (1) | 00:00:18 | | |
------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("ID"=70000)
12 - filter("NAME"='John')
14 - filter("NAME"='Mary')
-- Modified query (only change is absence of outermost SELECT)
explain plan for
WITH recs AS
(
SELECT * FROM TEST_V WHERE ID = 70000
)
(
SELECT 1 AS Key FROM recs WHERE NAME = 'John'
)
UNION
(
SELECT 2 AS Key FROM recs WHERE NAME = 'Mary'
) ;
select * from table(dbms_xplan.display()) ;
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time | Pstart | Pstop |
-----------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 88 | 6 (67) | 00:00:01 | | |
| 1 | TEMP TABLE TRANSFORMATION | | | | | | | |
| 2 | LOAD AS SELECT | SYS_TEMP_0FD9D6611_34D3B6C | | | | | | |
| 3 | VIEW | TEST_V | 2 | 96 | 4 (50) | 00:00:01 | | |
| 4 | SORT UNIQUE | | 2 | 96 | 4 (75) | 00:00:01 | | |
| 5 | UNION-ALL | | | | | | | |
| 6 | PARTITION RANGE ALL | | 1 | 48 | 1 (0) | 00:00:01 | 1 | 30 |
| 7 | TABLE ACCESS BY LOCAL INDEX ROWID | TEST1 | 1 | 48 | 1 (0) | 00:00:01 | 1 | 30 |
|* 8 | INDEX RANGE SCAN | IX_ID | 1 | | 1 (0) | 00:00:01 | 1 | 30 |
| 9 | TABLE ACCESS BY INDEX ROWID | TEST2 | 1 | 48 | 1 (0) | 00:00:01 | | |
|* 10 | INDEX UNIQUE SCAN | SYS_C001242692 | 1 | | 1 (0) | 00:00:01 | | |
| 11 | SORT UNIQUE | | 4 | 88 | 6 (67) | 00:00:01 | | |
| 12 | UNION-ALL | | | | | | | |
|* 13 | VIEW | | 2 | 44 | 2 (0) | 00:00:01 | | |
| 14 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6611_34D3B6C | 2 | 96 | 2 (0) | 00:00:01 | | |
|* 15 | VIEW | | 2 | 44 | 2 (0) | 00:00:01 | | |
| 16 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6611_34D3B6C | 2 | 96 | 2 (0) | 00:00:01 | | |
-----------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
8 - access("ID"=70000)
10 - access("ID"=70000)
13 - filter("NAME"='John')
15 - filter("NAME"='Mary')
quit ;
thanks & regards,
Reji
I can not reproduce this in 11.2.0.3, I don't think there is a logical explanation for this behavior other than: you hit a bug, that apparently is solved in 11.2.0.3.
One thing that jumped immediately in my eye is the lack of object statistics and - if your output was complete - the fact that OPTIMIZER_DYNAMIC_SAMPLING is set to 0. You could try to reproduce with OPTIMIZER_DYNAMIC_SAMPLING=2. In that case the dynamic sampler kicks in if the object statistics are missing. BTW: don't use this feature instead of correct optimizer statistics. More info about dynamic sampling Dynamic sampling and its impact on the Optimizer
In your - nice documented - question and script/test case you try to make use of append and nologging. This only works for bulk inserts, not for row inserts with values. What would happen is for every insert: push-up the highwater mark and dump a full block of data in the free block, in your case that would have only 1 row .... Luckily, the database ignores this instruction.
Before you fire SQL to a table, make sure that you give it optimizer statistics. This will certainly help your case.

MySQL equivalent of ORACLES rank()

Oracle has 2 functions - rank() and dense_rank() - which i've found very useful for some applications. I am doing something in mysql now and was wondering if they have something equivalent to those?
Nothing directly equivalent, but you can fake it with some (not terribly efficient) self-joins. Some sample code from a collection of MySQL query howtos:
SELECT v1.name, v1.votes, COUNT(v2.votes) AS Rank
FROM votes v1
JOIN votes v2 ON v1.votes < v2.votes OR (v1.votes=v2.votes and v1.name = v2.name)
GROUP BY v1.name, v1.votes
ORDER BY v1.votes DESC, v1.name DESC;
+-------+-------+------+
| name | votes | Rank |
+-------+-------+------+
| Green | 50 | 1 |
| Black | 40 | 2 |
| White | 20 | 3 |
| Brown | 20 | 3 |
| Jones | 15 | 5 |
| Smith | 10 | 6 |
+-------+-------+------+
how about this "dense_rank implement" in MySQL
CREATE TABLE `person` (
`id` int(11) DEFAULT NULL,
`first_name` varchar(20) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`gender` char(1) DEFAULT NULL);
INSERT INTO `person` VALUES
(1,'Bob',25,'M'),
(2,'Jane',20,'F'),
(3,'Jack',30,'M'),
(4,'Bill',32,'M'),
(5,'Nick',22,'M'),
(6,'Kathy',18,'F'),
(7,'Steve',36,'M'),
(8,'Anne',25,'F'),
(9,'Mike',25,'M');
the data before dense_rank() like this
mysql> select * from person;
+------+------------+------+--------+
| id | first_name | age | gender |
+------+------------+------+--------+
| 1 | Bob | 25 | M |
| 2 | Jane | 20 | F |
| 3 | Jack | 30 | M |
| 4 | Bill | 32 | M |
| 5 | Nick | 22 | M |
| 6 | Kathy | 18 | F |
| 7 | Steve | 36 | M |
| 8 | Anne | 25 | F |
| 9 | Mike | 25 | M |
+------+------------+------+--------+
9 rows in set (0.00 sec)
the data after dense_rank() like this,including "partition by" function
+------------+--------+------+------+
| first_name | gender | age | rank |
+------------+--------+------+------+
| Anne | F | 25 | 1 |
| Jane | F | 20 | 2 |
| Kathy | F | 18 | 3 |
| Steve | M | 36 | 1 |
| Bill | M | 32 | 2 |
| Jack | M | 30 | 3 |
| Mike | M | 25 | 4 |
| Bob | M | 25 | 4 |
| Nick | M | 22 | 6 |
+------------+--------+------+------+
9 rows in set (0.00 sec)
the query statement is
select first_name,t1.gender,age,FIND_IN_SET(age,t1.age_set) as rank from person t2,
(select gender,group_concat(age order by age desc) as age_set from person group by gender) t1
where t1.gender=t2.gender
order by t1.gender,rank

Resources