Development
APEX Deep Linking, Authentication and Special Characters

Major Formspider Pricing Update for PL/SQL Developers
Token Configurations in Oracle SOA Suite PS6 ( 11.1.1.7.0)
jQuery - my first functions
https://apex.oracle.com/pls/apex/f?p=31517:279

Formspider Reference Application is now online
SQL*Plus tips #4: Branching execution
Today I’ll show trick how we can use branching in SQL*Plus.
Although I previously showed the conditional execution of scripts and it really can be used for branching, but today I’ll show how to do it without splitting the script into several smaller scripts. In contrast to the conditional script execution, I’ll use a different method.
It is very simple, as usual – if you want to execute only one part of script, you can just comment out all unnecessary. So depending on the conditions, we can execute a script which will start a comment.
Suppose we need to create a script, which, depending on the input parameter will be required to execute a specific query.
See how this can be done:
1. “test.sql”:
def param = &1 @if ¶m=1 select 1 from dual; /* end_if */ @if ¶m=2 select 2 from dual; /* end_if */ @if ¶m=3 select 3 from dual; /* end_if */
2. “if.sql”:
col do_next new_val do_next noprint;
select
case
when &1 then 'inc/null'
else 'inc/comment_on'
end as do_next
from dual;
@&do_next
3. “inc/comment_on.sql” contains only 2 chars:
/*
4. “inc/null.sql” is the same as in the previous examples – just empty file.
Ok, lets test it:
SQL> @test 1
1
----------
1
SQL> @test 2
2
----------
2
SQL> @test 3
3
----------
3
As you see, we got what we wanted. Please note that we have to close the multiline comments in the right places only(/* end_if */). So we cannot use in these parts another “*/”. But you can use it in another child scripts.
Same way we can make an analogue of switch/case:
“test2.sql”:
@switch &1
@when 1 then
select 1 from dual;
/* end when */
@when 2 then
select 2 from dual;
/* end when */
@when 3 then
select 3 from dual;
/* end when */
/* end switch */
Spoiler:: switch.sql
SelectShow
def switch_param=&1
Spoiler:: when.sql
SelectShow
col do_next new_val do_next noprint;
select
case
when &1 = &switch_param then 'inc/null'
else 'inc/comment_on'
end as do_next
from dual;
@&do_next
Example:
SQL> @test2 2
2
----------
2
SQL> @test2 3
3
----------
3
Download Formspider Reference Application
SQL*Plus tips #3: Iterators and recursions
We all used to iterators, recursions and branching in programming, but sql*plus does not have such commands. Today I will show how to do iterators/recusions.
Suppose we want to call a script 3 times. So it would be convenient if we can do it like:
@iterate 3 @some_script
It is very easy: We can recursively call the script by reducing variable until it is greater than zero. and if a variable is zero, then call an empty “NULL” script.
iterate.sql:
-- initializing input parameters as i showed in previous tips:
@inc/input_params_init.sql;
-- for example max number of input parameters = 14:
def _INIT_PARAMS = '"&3" "&4" "&5" "&6" "&7" "&8" "&9" "&10" "&11" "&12" "&13" "&14" "&15" "&16"'
def _ITER = "&1"
prompt ------ iteration &_ITER start: "@&2 -----------;
set termout off
col _SCRIPT new_val _SCRIPT noprint;
col _CONTINUE new_val _CONTINUE noprint;
col _PARAMS new_val _PARAMS noprint;
select
replace('"&3" "&4" "&5" "&6" "&7" "&8" "&9" "&10" "&11" "&12" "&13" "&14" "&15" "&16"'
,'${ITER}'
,'&_ITER'
) as "_PARAMS"
,case
when &1 > 0 then 'iterate '||(&1 - 1)
else 'inc/null'
end as "_CONTINUE"
,case
when &1 > 0 then '&2'
else 'inc/null'
end as "_SCRIPT"
from dual;
set termout on
@&_script &_PARAMS
@&_CONTINUE &_SCRIPT &_INIT_PARAMS
As you see i also added option to pass iteration_number as input parameter, so we can call it like: @iterate 3 script.sql ${ITER}
And null.sql in “inc” directory is just empty file.
Lets test it:
SQL> $cat test.sql
select &1 a,'&2' b,'&3' c from dual;
SQL> @iterate 3 test.sql 1 2 3
------ iteration 3 start: "@test.sql -----------
A B C
---------- - -
1 2 3
------ iteration 2 start: "@test.sql -----------
A B C
---------- - -
1 2 3
------ iteration 1 start: "@test.sql -----------
A B C
---------- - -
1 2 3
------ iteration 0 start: "@test.sql -----------
SQL> @iterate 2 test.sql 1 2 ${ITER}
------ iteration 2 start: "@test.sql -----------
A B C
---------- - -
1 2 2
------ iteration 1 start: "@test.sql -----------
A B C
---------- - -
1 2 1
------ iteration 0 start: "@test.sql -----------
Note that last string “iteration 0 start” just means that it is last empty call. I made this only for showing how we can do postprocessing after all iterations.
So if you call script with “iterate N …” it will iterate N times, and if you specify “iterate (X,Y,…,Z)” it will iterate by this list. PS. About a branching i will wrote later…
Ok, it works good, but with a little modification we can also add option to call such script with list of values too:
Spoiler:: final iterate.sql
SelectShow
@inc/input_params_init.sql;
set termout off
def _INIT_PARAMS = '"&3" "&4" "&5" "&6" "&7" "&8" "&9" "&10" "&11" "&12" "&13" "&14" "&15" "&16"'
col _ITER_CURR new_val _ITER_CURR noprint
col _ITER_NEXT new_val _ITER_NEXT noprint
select
case
-- simple N iterations:
when translate('&1','x0123456789','x') is null
then '&1'
-- list iteration:
when substr('&1',1,1)='('
then decode( instr('&1',',')
,0,substr('&1',2,length('&1')-2)
,substr('&1',2,instr('&1',',')-2)
)
end "_ITER_CURR",
case
when translate('&1','x0123456789','x') is null
then
case
when '&1'>1 then 'iterate '||('&1' - 1)
else 'inc/null'
end
when substr('&1',1,1)='('
then
case
when instr('&1',',')=0 or '&1'='()' then 'inc/null'
else 'iterate '
||'('
||decode( instr('&1',',')
,0,')'
,substr('&1',instr('&1',',')+1)
)
end
end "_ITER_NEXT"
from dual;
set termout on
prompt ****** iteration &_ITER_CURR start: "@&2 ***********;
set termout off
col _SCRIPT new_val _SCRIPT noprint;
col _PARAMS new_val _PARAMS noprint;
select
replace('"&3" "&4" "&5" "&6" "&7" "&8" "&9" "&10" "&11" "&12" "&13" "&14" "&15" "&16"'
,'${ITER}'
,'&_ITER_CURR'
) as "_PARAMS"
,case
when nvl('&_ITER_CURR',0) in ('0','()') then 'inc/null'
else '&2'
end as "_SCRIPT"
from dual;
set termout on
@&_script &_PARAMS
@&_ITER_NEXT &_SCRIPT &_INIT_PARAMS
See how it works:
-- iterate by list of values (7,3,10):
SQL> @iterate (7,3,10) test.sql 1 2 ${ITER}
****** iteration 7 start: "@test.sql ***********
A B C
---------- - -
1 2 7
****** iteration 3 start: "@test.sql ***********
A B C
---------- - -
1 2 3
****** iteration 10 start: "@test.sql ***********
A B C
---------- - --
1 2 10
-- now with simple 2 iterations:
SQL> @iterate 2 test.sql 1 2 ${ITER}
****** iteration 2 start: "@test.sql ***********
A B C
---------- - -
1 2 2
****** iteration 1 start: "@test.sql ***********
A B C
---------- - -
1 2 1
Formspider in Brazil
SQL*Plus tips. #2
I think you know the famous print_table procedure by Tom Kyte. It is really great, but a little hard(it requires create procedure or place it in anonymous block) and in last oracle versions we can do same with one simple query with xmltable/xmlsequence:
SQL> select * 2 from 3 xmltable( '/ROWSET/ROW/*' 4 passing xmltype(cursor(select * from hr.employees where rownum<3)) 5 columns 6 col varchar2(100) path 'name()' 7 ,val varchar2(100) path '.' 8 ); COL VAL ------------------------------ ------------------------------------------------------ EMPLOYEE_ID 198 FIRST_NAME Donald LAST_NAME OConnell EMAIL DOCONNEL PHONE_NUMBER 650.507.9833 HIRE_DATE 21-JUN-07 JOB_ID SH_CLERK SALARY 2600 MANAGER_ID 124 DEPARTMENT_ID 50 EMPLOYEE_ID 199 FIRST_NAME Douglas LAST_NAME Grant EMAIL DGRANT PHONE_NUMBER 650.507.9844 HIRE_DATE 13-JAN-08 JOB_ID SH_CLERK SALARY 2600 MANAGER_ID 124 DEPARTMENT_ID 50 20 rows selected.
It is very easy, but for conveniency we need to add “rownum”:
SQL> select *
2 from
3 xmltable( 'for $a at $i in /ROWSET/ROW
4 ,$r in $a/*
5 return element ROW{
6 element ROW_NUM{$i}
7 ,element COL_NAME{$r/name()}
8 ,element COL_VALUE{$r/text()}
9 }'
10 passing xmltype(cursor(select * from hr.employees where rownum<3))
11 columns
12 row_num int
13 ,col_name varchar2(30)
14 ,col_value varchar2(100)
15 );
ROW_NUM COL_NAME COL_VALUE
---------- ------------------------------ ------------------------------------------
1 EMPLOYEE_ID 198
1 FIRST_NAME Donald
1 LAST_NAME OConnell
1 EMAIL DOCONNEL
1 PHONE_NUMBER 650.507.9833
1 HIRE_DATE 21-JUN-07
1 JOB_ID SH_CLERK
1 SALARY 2600
1 MANAGER_ID 124
1 DEPARTMENT_ID 50
2 EMPLOYEE_ID 199
2 FIRST_NAME Douglas
2 LAST_NAME Grant
2 EMAIL DGRANT
2 PHONE_NUMBER 650.507.9844
2 HIRE_DATE 13-JAN-08
2 JOB_ID SH_CLERK
2 SALARY 2600
2 MANAGER_ID 124
2 DEPARTMENT_ID 50
20 rows selected.
Now we can create simple script for it with formatting:
A nice toy, but i’m sure it will not tolerate big data.
It is absolutely true, because at first, “xmltype(cursor(…))” aggregated data as xmltype, and only then xmltable returns data. However, we can easily change the our query to make it optimally with a lot of data too: Lets test it with pipelined function which will pipe rows infinitely and log count of fetched rows:
Spoiler:: New version of print_table: print_table2.sql
SelectShow
So we got what we wanted! PS. Full scripts:
Spoiler:: print_table.sql
SelectShow
-- show output
set termout on
-- but without echo
set echo off
-- without newpage on start:
set embedded on
-- scrolling control
set pause on
-- two lines between rows:
set newpage 2
-- text for prompt after each page:
set pause "Press Enter to view next row..."
-- new page on new "row_num"
break on row_num skip page
-- main query:
select *
from
xmltable( 'for $a at $i in /ROWSET/ROW
,$r in $a/*
return element ROW{
element ROW_NUM{$i}
,element COL_NAME{$r/name()}
,element COL_VALUE{$r/text()}
}'
passing xmltype(cursor( &1 ))
columns
row_num int
,col_name varchar2(30)
,col_value varchar2(100)
);
-- disabling pause and breaks:
set pause off
clear breaks
Usage example:
As you see script works fine, but it is require to pass query as parameter, though sometimes it is not so convenient. For example if we want started query and later decided to show it with print_table. In that case we can create scripts with tricks from previous part:
Spoiler:: print_last.sql
SelectShow
store set settings.sql replace
-- saving previous query:
save tmp.sql replace
-- OS-dependent removing trailing slash from file, choose one:
-- 1. for *nix through head:
!head -1 tmp.sql >tmp2.sql
-- 2. for for *nix through grep:
--!grep -v tmp.sql >tmp2.sql
-- 3. for windows without grep and head:
-- $cmd /C findstr /v /C:"/" tmp.sql > tmp2.sql
-- 4. for windows with "head"(eg from cygwin)
--$cmd /C head -1 tmp.sql > tmp2.sql
-- 5. for windows with "grep":
--$cmd /C grep -v "/" tmp.sql > tmp2.sql
-- same setting as in print_table:
set termout on echo off embedded on pause on newpage 2
set pause "Press Enter to view next row..."
break on row_num skip page
-- main query:
select *
from
xmltable( 'for $a at $i in /ROWSET/ROW
,$r in $a/*
return element ROW{
element ROW_NUM{$i}
,element COL_NAME{$r/name()}
,element COL_VALUE{$r/text()}
}'
passing dbms_xmlgen.getxmltype(
q'[
@tmp2.sql
]'
)
columns
row_num int
,col_name varchar2(30)
,col_value varchar2(100)
);
-- disabling pause and breaks:
set pause off
clear breaks
@settings.sql
Example:
Spoiler:
SelectShow
SQL> select * from hr.employees where rownum<3;
EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL PHONE_NUMBER HIRE_DATE JOB_ID SALARY COMMISSION_PCT MANAGER_ID DEPARTMENT_ID
----------- -------------------- ------------------------- ------------------------- -------------------- --------- ---------- ---------- -------------- ---------- -------------
198 Donald OConnell DOCONNEL 650.507.9833 21-JUN-07 SH_CLERK 2600 124 50
199 Douglas Grant DGRANT 650.507.9844 13-JAN-08 SH_CLERK 2600 124 50
Elapsed: 00:00:00.01
SQL> @print_last
Wrote file settings.sql
Wrote file tmp.sql
ROW_NUM COL_NAME COL_VALUE
---------- ------------------------------ ----------------------------------------------------------------------------------------------------
1 EMPLOYEE_ID 198
FIRST_NAME Donald
LAST_NAME OConnell
EMAIL DOCONNEL
PHONE_NUMBER 650.507.9833
HIRE_DATE 21-JUN-07
JOB_ID SH_CLERK
SALARY 2600
MANAGER_ID 124
DEPARTMENT_ID 50
Press Enter to view next row...
PS. if you will use xmltype(cursor(…)) on versions less that 11.2.0.3 you can get errors with xml rewriting. In this case you need to disable it:alter session set events '19027 trace name context forever, level 0x1';
Update:
Vladimir Przyjalkowski rightly pointed out that such approach will be suboptimal in case of big amount of data:
But I use print_table for small amount of data only, and in case of when i want to see sample of data, i usually add limitation by rownum, which also convenient because it automatically changes optimizer mode with enabled parameter “_optimizer_rownum_pred_based_fkr” (it is default):
SQL> @param_ rownum
NAME VALUE DEFLT TYPE DESCRIPTION
---------------------------------- ------- ------ -------- ------------------------------------------------------
_optimizer_rownum_bind_default 10 TRUE number Default value to use for rownum bind
_optimizer_rownum_pred_based_fkr TRUE TRUE boolean enable the use of first K rows due to rownum predicate
_px_rownum_pd TRUE TRUE boolean turn off/on parallel rownum pushdown optimization
select row_num
,t2.*
from
(select rownum row_num
, column_value x
from table(xmlsequence(cursor( &1 )))
) t1
,xmltable( '/ROW/*'
passing t1.x
columns
col_num for ordinality
,col_name varchar2(30) path 'name()'
,col_value varchar2(100) path '.'
)(+) t2;
-- drop table xt_log purge;
-- drop function f_infinite;
create table xt_log(n int);
create function f_infinite
return sys.ku$_objnumset pipelined
as
i int:=0;
pragma autonomous_transaction;
begin
loop
i:=i+1;
insert into xt_log values(i);
commit;
pipe row(i);
end loop;
exception
when NO_DATA_NEEDED then
commit;
end;
/
-- set arraysize for minimal value, so sqlplus did not fetch extra rows:
set arraysize 2
-- show output
set termout on
-- but without echo
set echo off
-- without newpage on start:
set embedded on
-- scrolling control
set pause on
-- two lines between rows:
set newpage 2
-- text for prompt after each page:
set pause "Press Enter to view next row..."
-- new page on new "row_num"
break on row_num skip page
-- main query:
select row_num
,t2.*
from
(select rownum row_num
, column_value x
from table(xmlsequence(cursor( &1 )))
) t1
,xmltable( '/ROW/*'
passing t1.x
columns
col_num for ordinality
,col_name varchar2(30) path 'name()'
,col_value varchar2(100) path '.'
)(+) t2;
-- disabling pause and breaks:
set pause off
clear breaks
And look final test:
SQL> select * from xt_log;
no rows selected
SQL> @print_table2 "select * from table(f_infinite)"
Press Enter to view next row...
ROW_NUM COL_NUM COL_NAME COL_VALUE
---------- ---------- ------------------------------ -----------
1 1 COLUMN_VALUE 1
Press Enter to view next row...
ROW_NUM COL_NUM COL_NAME COL_VALUE
---------- ---------- ------------------------------ -----------
2 1 COLUMN_VALUE 2
Press Enter to view next row...
ROW_NUM COL_NUM COL_NAME COL_VALUE
---------- ---------- ------------------------------ -----------
3 1 COLUMN_VALUE 3
Press Enter to view next row...
ROW_NUM COL_NUM COL_NAME COL_VALUE
---------- ---------- ------------------------------ -----------
4 1 COLUMN_VALUE 4
Press Enter to view next row...
ROW_NUM COL_NUM COL_NAME COL_VALUE
5 1 COLUMN_VALUE 5
Press Enter to view next row...
6 rows selected.
-------------------------------
SQL> select * from xt_log;
N
----------
1
2
3
4
5
6
7
7 rows selected.
1. Simple: print_table.sql
2. For big queries: print_table2.sql
Working with the Sunburst ADF Component
JDeveloper 11.1.1.7 just hit the streets and among the new features it contains are several new data visualization ADF Faces components including timeline, treemap, and sunburst.
I got to play with the sunburst component a while back while building some internal demos - so I thought I'll provide a quick overview of some of the things you can do with it, and how to work with it at design time.
The sunburst component is used to visually show numerical data along one or two axises relating to something. One axis of data will show as the size of slices and the other data will show as a color. You can think about it as a two axis pie chart. The sunburst also allow for drilling into detail levels.
In the below example I'm showing just one set of data that has to do with the total orders broken in several levels - region->country->customer.
So here is the demo:
For those interested in the actual JSF code it is below:
<dvt:sunburst id="s1" value="#{bindings.RegionSales1.treeModel}"
var="row" animationOnDataChange="alphaFade"
animationOnDisplay="fan" displayLevelsChildren="0"
styleClass="AFStretchWidth"
inlineStyle="height:620.0px;" legendSource="ag1"
colorLabel="Sales">
<af:switcher facetName="#{row.hierTypeBinding.name}" id="s2">
<f:facet name="RegionSales10">
<dvt:sunburstNode value="#{row.Total}" label="#{row.Name}"
id="sn3" drilling="insert">
<dvt:attributeGroups id="ag1" value="#{row.Total}"
label="#{row.Total}" type="color"
attributeType="continuous" minValue="0"
maxValue="3000000" minLabel="0"
maxLabel="3M">
<f:attribute name="color1" value="11AA55"/>
<f:attribute name="color2" value="44BB77"/>
<f:attribute name="color3" value="77DD99"/>
</dvt:attributeGroups>
</dvt:sunburstNode>
</f:facet>
<f:facet name="RegionSales11">
<dvt:sunburstNode value="#{row.Total}" label="#{row.Country}"
id="sn1" drilling="insert">
<dvt:attributeGroups id="ag2" value="#{row.Total}"
label="#{row.Total}" type="color"
attributeType="continuous" minValue="0"
maxValue="2800000" minLabel="0"
maxLabel="2.8M">
<f:attribute name="color1" value="11AA55"/>
<f:attribute name="color2" value="44BB77"/>
<f:attribute name="color3" value="77DD99"/>
</dvt:attributeGroups>
</dvt:sunburstNode>
</f:facet>
<f:facet name="RegionSales12">
<dvt:sunburstNode value="#{row.Total}" label="#{row.Name1}"
id="sn2" drilling="insertAndReplace"/>
</f:facet>
</af:switcher>
</dvt:sunburst>
Testing Activiti BPM on WebLogic 12c
SQL*Plus tips. #1
If you are using SQL*Plus, you are likely to use the input parameters. And if you omit one of them, SQL*Plus will show prompt for it, like this:
SQL> get test.sql 1 select 'Input variable 1 = &1' from dual 2 union all 3 select 'Input variable 2 = &2' from dual 4 union all 5* select 'Input variable 3 = &3' from dual SQL> @test var1 var2 Enter value for 3: 'INPUTVARIABLE1=VAR1' ----------------------- Input variable 1 = var1 Input variable 2 = var2 Input variable 3 = Elapsed: 00:00:00.01
It is allright, if all variables are needed, but what if we don’t want to press enter for all omitted variables or specify they(especially if script can be start in silent, non interactive mode) and want to use default values for omitted variables or these variables can be unnecessary?
So i can create 2 include files – for execution at the start and at the end of all scripts. I created directory “inc” for include files and files:
And for example get_index.sql:
Spoiler:: get_index.sql
SelectShow
Spoiler:: Sample output
SelectShow
As you see i can omit owner parameter and in this case it will search in all schemas.
Strictly speaking, there are many different techniques for solving it, see some of them:
Spoiler:: Comma-separated params
SelectShow
SQL> get test1
1 col var1 new_value var1 noprint
2 col var2 new_value var2 noprint
3 col var3 new_value var3 noprint
4 set termout off
5 with any_splitting_technique as (
6 select *
7 from xmltable('ora:tokenize(.,",")[position()>1]'
8 passing ','||'&1'
9 columns
10 i for ordinality
11 ,"." varchar2(30)
12 )
13 )
14 select
15 nvl("1",'default1') var1
16 ,nvl("2",'default2') var2
17 ,nvl("3",'default3') var3
18 from any_splitting_technique
19 pivot (max(".") for i in (1,2,3))
20 /
21 set termout on
22* prompt var1 = &var1, var2 = &var2, var3 = &var3;
SQL> @test1 1,2,3
var1 = 1, var2 = 2, var3 = 3
SQL> @test1 1,2
var1 = 1, var2 = 2, var3 = default3
Spoiler:: With SPOOL and DEFINE
SelectShow
SQL> get test2
1 set termout off
2 spool tmp.sql
3 def 1
4 def 2
5 def 3
6 spool off
7 col var1 new_value var1
8 col var2 new_value var2
9 col var3 new_value var3
10 with tmp as (
11 select '
12 @tmp.sql
13 ' params from dual
14 )
15 select
16 nvl(regexp_substr(params,'DEFINE 1\s+ = "([^"]*)',1,1,'i',1),'default1') var1
17 , nvl(regexp_substr(params,'DEFINE 2\s+ = "([^"]*)',1,1,'i',1),'default2') var2
18 , nvl(regexp_substr(params,'DEFINE 3\s+ = "([^"]*)',1,1,'i',1),'default3') var3
19 from tmp
20 ;
21 col var1 clear;
22 col var2 clear;
23 col var3 clear;
24 set termout on
25 prompt var1 = &var1, var2 = &var2, var3 = &var3;
26 undef 1
27 undef 2
28* undef 3
29 .
SQL> @test2 1 2 3
var1 = 1, var2 = 2, var3 = 3
SQL> @test2 1 2
var1 = 1, var2 = 2, var3 = default3
Last example also shows a very useful way to read file into a variable.
But i think, the best option for initializing parameters is the solution by Vladimir Begun:
SQL> get test3
1 set termout off
2 COLUMN 1 NEW_VALUE 1 noprint
3 COLUMN 2 NEW_VALUE 2 noprint
4 COLUMN 3 NEW_VALUE 3 noprint
5 SELECT '' "1", '' "2", '' "3" FROM dual WHERE 1=0;
6 SELECT nvl('&1','default1') "1"
7 , nvl('&2','default2') "2"
8 , nvl('&3','default3') "3"
9 FROM dual;
10 col var1 clear;
11 col var2 clear;
12 col var3 clear;
13 set termout on
14 prompt var1 = &1, var2 = &2, var3 = &3;
15 undef 1
16 undef 2
17* undef 3
18 .
SQL> @test3 1 2 3
var1 = 1, var2 = 2, var3 = 3
SQL> @test3 1 2
var1 = 1, var2 = 2, var3 = default3
Spoiler:: inc/s_begin.sql
SelectShow
store set splus_restore.sql replace
set termout off
COLUMN 1 NEW_VALUE 1 noprint
COLUMN 2 NEW_VALUE 2 noprint
COLUMN 4 NEW_VALUE 4 noprint
COLUMN 3 NEW_VALUE 3 noprint
COLUMN 5 NEW_VALUE 5 noprint
COLUMN 6 NEW_VALUE 6 noprint
COLUMN 7 NEW_VALUE 7 noprint
COLUMN 8 NEW_VALUE 8 noprint
COLUMN 9 NEW_VALUE 9 noprint
COLUMN 10 NEW_VALUE 10 noprint
COLUMN 11 NEW_VALUE 11 noprint
COLUMN 12 NEW_VALUE 12 noprint
COLUMN 13 NEW_VALUE 13 noprint
COLUMN 14 NEW_VALUE 14 noprint
COLUMN 15 NEW_VALUE 15 noprint
COLUMN 16 NEW_VALUE 16 noprint
SELECT '' "1", '' "5", '' "9", '' "13"
,'' "2", '' "6", '' "10", '' "14"
,'' "3", '' "7", '' "11", '' "15"
,'' "4", '' "8", '' "12", '' "16"
FROM dual
WHERE 1=0;
set termout on;
and
Spoiler:: inc/s_end.sql
SelectShow
undef 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
@splus_restore;
@inc/s_begin;
col table_owner format a12
col column_name format a30
col index_owner format a12
col index_name format a30
col "#" format 99
break on table_owner on table_name on index_owner on index_name on partition_name on mbytes on bytes on blocks
select
ic.table_owner
,ic.table_name
,ic.index_owner
,ic.index_name
,s.partition_name
,round(s.bytes/1024/1024) mbytes
,s.blocks
,ic.column_position "#"
,decode(ic.column_position,1,'',' ,')||ic.column_name column_name
from dba_ind_columns ic
,dba_segments s
where
upper(ic.table_name) like upper('&1')
and upper(ic.table_owner) like nvl(upper('&2'),'%')
and s.owner = ic.index_owner
and s.segment_name = ic.index_name
order by
1,2,3,4,8
/
clear break;
col "#" clear;
@inc/s_end;
SQL> @get_indexes wrh$%tab%stat xtender
Wrote file splus_restore.sql
no rows selected
Elapsed: 00:00:05.79
SQL> @get_indexes wrh$%undostat
Wrote file splus_restore.sql
TABLE_OWNER TABLE_NAME INDEX_OWNER INDEX_NAME PARTITION_NAME MBYTES BLOCKS # COLUMN_NAME
------------ -------------- ------------ ----------------- -------------- ------- ------- --- --------------------
SYS WRH$_UNDOSTAT SYS WRH$_UNDOSTAT_PK 0 16 1 BEGIN_TIME
2 ,END_TIME
3 ,DBID
4 ,INSTANCE_NUMBER
Maliakos-Kleidi Construction Uses Formspider
Select List with Dynamic LOV and ORA-06502: PL/SQL: numeric or value error: character string buffer too small
1. It will use the apex_collection package to parse the query and crate a collection
2. It will query the collection and use a pipelined function to return a simple LOV (SELECT d, r FROM table)
You can also use the get_long_dynamic_query function in the package to generate your dynamic SQL. The example at apex.oracle.com I provided extends the query with "dummy" conditions in order to make it long enough and prove the concept.
Example of controlling “direct path reads” decision through SQL profile hints (index_stats/table_stats)
Previously i showed not obvious example with hint “INDEX_STATS(“OWNER”.”TABLE_NAME”, “INDEX_NAME”, scale, blocks=X, rows=Y)“. Strictly speaking i don’t know how exactly cbo calculates number of index leaf blocks in that case: in those examples they was 1981 for “blocks=1, rows=50″ and 49525 for “blocks=5, rows=10″.
But i know that with “INDEX_STATS(“OWNER”.”TABLE_NAME”, “INDEX_NAME”, scale, blocks=X)” i can set exact blocks number.
Also those test-cases didn’t show when occurs decision changing. So todays my test will show it.
BTW, it is very interesting that a turning point was _very_large_object_threshold, but not 5 * _small_table_threshold. AFAIK, direct path reads decision depends on many factors (such as number of object blocks in buffer cache), and would be great to know all of them.
You can download script as file: controlling_direct_reads_with_profiles.sql
Spoiler:: Test code
SelectShow
/** SQLPLUS Example of controlling adaptive serial direct path reads through SQL profiles.
*/
set serverout on termout on timing off feed off;
clear scr;
def _IF_XT_RUNSTATS_EXISTS="--"
col if_xt_runstats_exists new_value _IF_XT_RUNSTATS_EXISTS noprint;
select decode(count(*),1,' ','--') if_xt_runstats_exists
from all_objects where object_name='XT_RUNSTATS' and rownum=1;
/**
* Main test.
* You can use it also in other clients,
* but in that case you have to manually
* set substitution variable _IF_XT_RUNSTATS_EXISTS:
* if you have not this package - to "--"
* otherwise - to space(" ")
* Latest version of XT_RUNSTATS you can get from:
* https://github.com/xtender/xt_runstats
*/
declare
C_SQL_TEXT constant varchar2(300):='SELECT SUM(A) FROM XT_IFFS T';
C_PROFILE_NAME constant varchar2(30) :='PRF_ADPR_TEST';
v_small_table_threshold int;
v_db_block_buffers int;
v_very_large_object_threshold int;
v__db_cache_size int;
procedure exec(p varchar2) is
e_table_is_not_created_yet exception;
e_index_is_not_created_yet exception;
pragma exception_init(e_table_is_not_created_yet, -942);
pragma exception_init(e_index_is_not_created_yet, -1418);
begin
execute immediate p;
exception
when e_table_is_not_created_yet
or e_index_is_not_created_yet
then null;
end;
/** Creating table and setting stats */
procedure create_table is
begin
exec('drop table xt_iffs purge');
exec('create table xt_iffs as
with gen as(
select level a,mod(level,10) b,lpad(1,50,1) c
from dual
connect by level<=1e3
)
select gen.*
from gen,gen gen2'
);
--dbms_stats.set_table_stats('','XT_IFFS',numrows => 1e6,numblks => 5e5,avgrlen => 800);
dbms_stats.gather_table_stats('','XT_IFFS');
end;
/** Recreating index and setting stats */
procedure create_index is
begin
exec('drop index ix_iffs');
exec('create index ix_iffs on xt_iffs(a,b)');
dbms_stats.set_index_stats('','IX_IFFS',numrows => 1e6,numlblks => 1e4);
end;
/** Setting index numblks for query through SQL profile */
procedure set_numblks(p_numblks int) is
e_profile_not_created_yet exception;
pragma exception_init( e_profile_not_created_yet, -13833);
begin
begin
dbms_sqltune.drop_sql_profile(C_PROFILE_NAME);
exception when e_profile_not_created_yet then null;
end;
dbms_sqltune.import_sql_profile(
sql_text => C_SQL_TEXT,
profile => sys.sqlprof_attr(
'INDEX_FFS(@"SEL$1" "T"@"SEL$1" ("XT_IFFS"."A"))'
,'INDEX_STATS("'||user||'"."XT_IFFS", "IX_IFFS", scale, blocks='||p_numblks||')'
),
category => 'DEFAULT',
name => C_PROFILE_NAME,
force_match => false,
replace => true
);
end;
procedure test( p_description varchar2
, p_numblks int default null) is
type t_seg_stat is
table of number
index by v$segstat_name.name%type;
-- segments statistics by index:
cursor c_stats is
select sn.name,nvl(st.value,0) value
from v$segstat_name sn
,v$segment_statistics st
where
sn.statistic# = st.statistic#(+)
and st.owner(+) = user
and st.object_name(+) ='IX_IFFS';
-- var for previous stats:
v_pre t_seg_stat;
v_delta number;
n number;
begin
dbms_output.put_line('-');
dbms_output.put_line('-');
dbms_output.put_line(lpad('-',150,'-'));
dbms_output.put_line(lpad('-',150,'-'));
dbms_output.put_line( '### '
|| p_description||': '
||nvl(to_char(p_numblks),'default')||' blocks');
dbms_output.put_line('-');
create_index;
-- if p_numblks is null then default stats used
if p_numblks is not null then
set_numblks(p_numblks);
end if;
execute immediate C_SQL_TEXT into n;
exec('alter system flush buffer_cache');
-- saving segment statistics
for r in c_stats loop
v_pre(r.name) := r.value;
end loop;
&_IF_XT_RUNSTATS_EXISTS xt_runstats.init(p_latches => false);
-- executing query
execute immediate C_SQL_TEXT into n;
&_IF_XT_RUNSTATS_EXISTS xt_runstats.snap;
&_IF_XT_RUNSTATS_EXISTS xt_runstats.print(
&_IF_XT_RUNSTATS_EXISTS p_stats_mask => '(reads (cache|direct)\.)|index fast full scans \((full|direct)'
&_IF_XT_RUNSTATS_EXISTS ,p_sta_diff_pct => 0);
-- printing segments stats delta:
for r in c_stats loop
v_delta:= r.value - v_pre(r.name);
if v_delta!=0 then
dbms_output.put_line( rpad(r.name,40,'.')||v_delta );
end if;
end loop;
end;
procedure load_and_print_params is
begin
select
max(decode(a.ksppinm, '_small_table_threshold' ,b.ksppstvl))
,max(decode(a.ksppinm, '_db_block_buffers' ,b.ksppstvl))
,max(decode(a.ksppinm, '_very_large_object_threshold' ,b.ksppstvl))
,max(decode(a.ksppinm, '__db_cache_size' ,b.ksppstvl))
into v_small_table_threshold,v_db_block_buffers,v_very_large_object_threshold,v__db_cache_size
from
sys.x$ksppi a
,sys.x$ksppcv b
where
a.indx = b.indx
and a.ksppinm in('_small_table_threshold','_db_block_buffers','_very_large_object_threshold','__db_cache_size');
dbms_output.put_line('_small_table_threshold = '||v_small_table_threshold);
dbms_output.put_line('_db_block_buffers = '||v_db_block_buffers);
dbms_output.put_line('_very_large_object_threshold = '||v_very_large_object_threshold);
dbms_output.put_line('__db_cache_size = '||v__db_cache_size);
end;
begin
create_table;
load_and_print_params;
test( '_very_large_object_threshold + 1'
,v_db_block_buffers * v_very_large_object_threshold/100 + 1 );
test( '_very_large_object_threshold'
,v_db_block_buffers * v_very_large_object_threshold/100 );
test( '_very_large_object_threshold - 1'
,v_db_block_buffers * v_very_large_object_threshold/100 - 1 );
test( '_db_block_buffers + 1'
,v_db_block_buffers + 1 );
test( '_db_block_buffers - 1'
,v_db_block_buffers - 1 );
test( '_small_table_threshold * 5 + 1'
,v_small_table_threshold * 5 + 1 );
test( '_small_table_threshold * 5 - 1'
,v_small_table_threshold * 5 - 1 );
test( ' 1 block ', 1);
test( ' Default ', null);
test( ' Again _very_large_object_threshold + 1'
,v_db_block_buffers * v_very_large_object_threshold/100 + 1 );
test( ' Again _very_large_object_threshold'
,v_db_block_buffers * v_very_large_object_threshold/100 );
end;
/
Spoiler:: Test results
SelectShow
_small_table_threshold = 166
_db_block_buffers = 8347
_very_large_object_threshold = 500
__db_cache_size = 4194304
-
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
### _very_large_object_threshold + 1: 41736 blocks
-
################ Results: ##################
Run # 01 ran in 11 hsecs
##########################################################
Statistics | Run # 1
##########################################################
physical reads cache.................... | 1
physical reads direct................... | 2,491
index fast full scans (full)............ | 1
index fast full scans (direct read)..... | 1
##########################################################
-
logical reads...........................2496
physical reads..........................2492
physical read requests..................86
physical reads direct...................2491
segment scans...........................1
-
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
### _very_large_object_threshold: 41735 blocks
-
################ Results: ##################
Run # 01 ran in 6 hsecs
##########################################################
Statistics | Run # 1
##########################################################
physical reads cache.................... | 2,494
physical reads direct................... | 0
index fast full scans (full)............ | 1
index fast full scans (direct read)..... | 0
##########################################################
-
logical reads...........................2496
physical reads..........................2494
physical read requests..................95
segment scans...........................1
-
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
### _very_large_object_threshold - 1: 41734 blocks
-
################ Results: ##################
Run # 01 ran in 11 hsecs
##########################################################
Statistics | Run # 1
##########################################################
physical reads cache.................... | 3,386
physical reads direct................... | 1
index fast full scans (full)............ | 1
index fast full scans (direct read)..... | 0
##########################################################
-
logical reads...........................2512
physical reads..........................2494
physical read requests..................95
segment scans...........................1
-
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
### _db_block_buffers + 1: 8348 blocks
-
################ Results: ##################
Run # 01 ran in 6 hsecs
##########################################################
Statistics | Run # 1
##########################################################
physical reads cache.................... | 2,494
physical reads direct................... | 0
index fast full scans (full)............ | 1
index fast full scans (direct read)..... | 0
##########################################################
-
logical reads...........................2512
physical reads..........................2494
physical read requests..................95
segment scans...........................1
-
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
### _db_block_buffers - 1: 8346 blocks
-
################ Results: ##################
Run # 01 ran in 8 hsecs
##########################################################
Statistics | Run # 1
##########################################################
physical reads cache.................... | 2,494
physical reads direct................... | 0
index fast full scans (full)............ | 1
index fast full scans (direct read)..... | 0
##########################################################
-
logical reads...........................2512
physical reads..........................2494
physical read requests..................95
segment scans...........................1
-
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
### _small_table_threshold * 5 + 1: 831 blocks
-
################ Results: ##################
Run # 01 ran in 6 hsecs
##########################################################
Statistics | Run # 1
##########################################################
physical reads cache.................... | 2,494
physical reads direct................... | 0
index fast full scans (full)............ | 1
index fast full scans (direct read)..... | 0
##########################################################
-
logical reads...........................2512
physical reads..........................2494
physical read requests..................95
segment scans...........................1
-
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
### _small_table_threshold * 5 - 1: 829 blocks
-
################ Results: ##################
Run # 01 ran in 7 hsecs
##########################################################
Statistics | Run # 1
##########################################################
physical reads cache.................... | 2,494
physical reads direct................... | 0
index fast full scans (full)............ | 1
index fast full scans (direct read)..... | 0
##########################################################
-
logical reads...........................2496
physical reads..........................2494
physical read requests..................95
segment scans...........................1
-
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
### 1 block : 1 blocks
-
################ Results: ##################
Run # 01 ran in 6 hsecs
##########################################################
Statistics | Run # 1
##########################################################
physical reads cache.................... | 2,494
physical reads direct................... | 0
index fast full scans (full)............ | 1
index fast full scans (direct read)..... | 0
##########################################################
-
logical reads...........................2512
physical reads..........................2494
physical read requests..................95
segment scans...........................1
-
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
### Default : default blocks
-
################ Results: ##################
Run # 01 ran in 7 hsecs
##########################################################
Statistics | Run # 1
##########################################################
physical reads cache.................... | 2,494
physical reads direct................... | 0
index fast full scans (full)............ | 1
index fast full scans (direct read)..... | 0
##########################################################
-
logical reads...........................2496
physical reads..........................2494
physical read requests..................95
segment scans...........................1
-
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
### Again _very_large_object_threshold + 1: 41736 blocks
-
################ Results: ##################
Run # 01 ran in 6 hsecs
##########################################################
Statistics | Run # 1
##########################################################
physical reads cache.................... | 1
physical reads direct................... | 2,491
index fast full scans (full)............ | 1
index fast full scans (direct read)..... | 1
##########################################################
-
logical reads...........................2496
physical reads..........................2492
physical read requests..................86
physical reads direct...................2491
segment scans...........................1
-
-
------------------------------------------------------------------------------
------------------------------------------------------------------------------
### Again _very_large_object_threshold: 41735 blocks
-
################ Results: ##################
Run # 01 ran in 7 hsecs
##########################################################
Statistics | Run # 1
##########################################################
physical reads cache.................... | 2,494
physical reads direct................... | 0
index fast full scans (full)............ | 1
index fast full scans (direct read)..... | 0
##########################################################
-
logical reads...........................2496
physical reads..........................2494
physical read requests..................95
segment scans...........................1
PL/SQL procedure successfully completed.
Formating and Exporting a Report Column
apex_application.g_excel_format = FALSE
to says not to export or the opposite one:
apex_application.g_excel_format = TRUE
to say to export.This solution may work with a standard report but it can't be used efficiently with Interactive Reports.
Just in case you didn't know, you can solve that problem easily. See this example at apex.oracle.com. There, I am using a function to format a string and use the value of the current request to see if the report is simply getting rendered on the page or exported. If the report is exported, I will not use the formatting tags and return the values as it was parsed.

Scrabble Puzzle
I like words. I like to write. I like to read.
But one of the things I like best about Scrabble is how it is so much like life.
Every time I play a game of Scrabble, something totally amazing about the pattern of play in that game will strike me:
"Check it out! Ten turns and not a single letter E was used!"
"Whoa, that's amazing. Every word that contains an A also contains the letter I!"
and so on.
So why do I say Scrabble is like life? Because both Scrabble and life are filled with patterns that seem to be very amazing and profound, but in fact are meaningless, nothing more than coincidences to which our brain happens to be attuned.
OK, time for the Scrabble Puzzle:
A week ago, I played a game of Scabble with Veva and scored 36 points by placing the word "SEEN" on the board.
Now, every letter in "SEEN" is worth 1 point, making it very difficult to get to 36 points. But I attached "SEEN" to a word on the board (of course!) and I got my 36 points by using a total of 8 letters, two of which were worth 3 points and the other 5 were worth 1 point each.
Still not a whole lot of points to work from.
So how was I able to get to 36 points? I will post the answer next week.
APEX Collections and Joins
ORA-01722: invalid number
The strange thing with this error is that it may occur sporadic and you can't realy understand why it happens. If you operate on different systems (development, testing, production) you may receive this error on one of the instances and never on the other one. I asked Patrick Wolf about the reasons and he answered that this has to do with the way the cost based optimizer works. If it uses a different plan from the one you would expect, it may find non numeric values in the same column comming from a different collection. In that case the solution is to use the numeric columns in the collection (n0xx) for storing numeric values used in later joins.
Unresolved quiz: Avoiding in-list iterator
A couple days ago i had very interesting quiz, which is not resolved yet.
Look at this simplified query:
select *
from xt1,xt2
where
xt1.b=10
and xt1.a=xt2.a
and xt2.b in (1,2);
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------
Plan hash value: 2715236140
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 36900 | 501 (0)| 00:00:07 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 100 | 36900 | 501 (0)| 00:00:07 |
| 3 | TABLE ACCESS BY INDEX ROWID| XT1 | 100 | 31000 | 101 (0)| 00:00:02 |
|* 4 | INDEX RANGE SCAN | IX_XT1 | 100 | | 1 (0)| 00:00:01 |
| 5 | INLIST ITERATOR | | | | | |
|* 6 | INDEX RANGE SCAN | IX_XT2 | 1 | | 3 (0)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID | XT2 | 1 | 59 | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("XT1"."B"=10)
6 - access("XT1"."A"="XT2"."A" AND ("XT2"."B"=1 OR "XT2"."B"=2))
Spoiler:: Full test case
SelectShow
But how we can do it? I know 5 options: But unfortunately all of them are inapplicable for the my real problem, because i cannot for some reasons rewrite query or change query with advanced rewrite, cannot recreate/add index, and can’t change optimizer_mode, because execution plan for the real query will become worst than plan generated with CBO with inlist iterator(some operations aren’t exists in RBO). Could anybody suggest any another solution?
create table xt1 as
select
level a
, mod(level,1000) b
, lpad(1,300,1) padding
from dual
connect by level<=1e5;
create index ix_xt1 on xt1(b);
create table xt2 as
select
level a
, mod(level,5) b
, lpad(1,50,1) padding
from dual
connect by level<=1e6;
alter table xt2
add constraint uq_xt2
unique (a)
using index(create index ix_xt2 on xt2(a,b));
exec dbms_stats.gather_table_stats('','XT1',cascade=>true);
exec dbms_stats.gather_table_stats('','XT2',cascade=>true);
explain plan for
select *
from xt1,xt2
where
xt1.b=10
and xt1.a=xt2.a
and xt2.b in (1,2);
@?/rdbms/admin/utlxpls.sql
As you see, in such queries cbo always generating plans with INLIST ITERATOR, and it is reasonably in cases when there are many rows with different values of field B for most values of A, and this number is much larger than number of values in the “INLIST”. But in such case as shown, will be better to use index range scan with access by A and filter by B:
SQL> select *
2 from xt1,xt2
3 where
4 xt1.b=10
5 and xt1.a=xt2.a
6 and xt2.b in (1,2);
no rows selected
Statistics
----------------------------------------------------------
...
505 consistent gets
SQL> -- without inlist iterator:
SQL> select *
2 from xt1,xt2
3 where
4 xt1.b=10
5 and xt1.a=xt2.a
6 and xt2.b+0 in (1,2);
no rows selected
Statistics
----------------------------------------------------------
...
305 consistent gets
1. Trace event 10157
2. Rewrite code. for example replacing “b in (1,2)” to “b+0 in (1,2)”
3. Changing query with “Advanced query rewrite” (DBMS_ADVANCED_REWRITE.DECLARE_REWRITE_EQUIVALENCE)
4. Recreating index from xt2(a,b) to xt2(a,1,b)
5. Changing optimizer_mode to “rule” through hint or SQL profile/baseline


