Adsense Ad

Thursday 15 February 2018

Create Custom split() function in Oracle

Some programming and scripting languages provide built-in functions that split a string around a given delimiter. Oracle SQL does not offer similar functionality out of the box, but this following function can help fill the gap.



create or replace function split(input_list varchar2, ret_this_one number, delimiter varchar2) 
return varchar2 is 
        v_list varchar2(32767) := delimiter || input_list; 
        start_position number; 
        end_position number; 
begin 
        start_position := instr(v_list, delimiter, 1, ret_this_one); 
        if start_position > 0 then 
                end_position := instr( v_list, delimiter, 1, ret_this_one + 1); 
                if end_position = 0 then 
                        end_position := length(v_list) + 1; 
                end if; 
return(substr(v_list, start_position + 1, end_position - start_position - 1)); 
        else 
        return NULL; 
        end if; 
end split; 

/ show errors;


In the previous listed example, we would run this function as follows.

select split('AAA,BBB',1,','); -- Returns AAA 
select split('AAA,BBB',2,','); -- Returns BBB


Please note that the first index is 1, not 0.

The following function will take in a list, let's say "AAA,BBB", split them up to "AAA" and "BBB", and allow the user to specify which one to return.

Functions to join and split strings in Oracle SQL

A common task when selecting data from a database is to take a set of values a query returns and format it as a comma delimited list. Another task that's almost as common is the need to do the reverse: Take a comma delimited list of values in a single string and use it as a table of values.

Many scripting languages, such as Perl and Python, provide functions that do this with their own language-specific list of values; so it's surprising that, as of yet, this functionality isn't a standard part of SQL functions. I've seen some pretty ugly looking code that involved complex declarations with MAX and DECODE, but that solution usually only returns a limited set of values. With some of the new Oracle9i and above features, it's possible to do this yourself.

I'd like to use a "join" functionality to specify a query that returns a single column and a delimiter, and then receive a simple string that contains a list of those values separated by my delimiter. The query part can be passed to the function as a REF CURSOR using the new SQL CURSOR function. The delimiter should default to a comma, since that is the most commonly used delimiter. So, the syntax should be:

SQL> select join(cursor(select ename from emp)) from dual;


SMITH,ALLEN,WARD,JONES,MARTIN,BLAKE,CLARK,SCOTT,KING,TURNER,ADAMS,
JAMES,FORD,MILLER

The following code will perform this function:

create or replace function join
(
p_cursor sys_refcursor,
p_del varchar2 := ','
) return varchar2
is
l_value varchar2(32767);
l_result varchar2(32767);
begin
loop
fetch p_cursor into l_value;
exit when p_cursor%notfound;
if l_result is not null then
l_result := l_result || p_del;
end if;
l_result := l_result || l_value;
end loop;
return l_result;
end join;
/
show errors;


The PL/SQL User's Guide says you always have to declare a package that defines a ref cursor; however, the database already defines this as SYS_REFCURSOR in the STANDARD package. The PL/SQL code should be fairly straightforward. There is a limit of 32,767 characters on the output string and the input column.

Since all datatypes can be automatically converted to character strings, you can use any datatype in the cursor—as long as it's one column. For example:

SQL> select join(cursor(select trunc(hiredate,'month') from emp),'|') from
dual;


01-DEC-80|01-FEB-81|01-FEB-81|01-APR-81|01-SEP-81|01-MAY-81|01-JUN-81|01-APR-87|01-NOV-81|01-SEP-81|01-MAY-87|01-DEC-81|
01-DEC-81|01-JAN-82

There's another extra benefit. Since the cursor is part of the SQL statement, you can easily join the query inside the join with the outer query. Here is a query that returns each table and a list of the columns that make up its primary key:

SQL> select table_name,join(cursor(select column_name from user_cons_columns
where constraint_name = user_constraints.constraint_name
order by position)) columns
from user_constraints where constraint_type = 'P';

View the output in Table A.

You can also use this "join" function to compare two sets of ordered data. For example, the following query will check that an index has been created on a foreign key (which helps prevent locking the table and aids master-detail queries):

column status format a7
column table_name format a30
column columns format a40 word_wrapped

select decode(indexes.table_name,null,'missing','ok') status,
constraints.table_name,
constraints.columns
from
(select table_name,
constraint_name,
join(cursor
(
select column_name
from user_cons_columns
where constraint_name = user_constraints.constraint_name
)) columns
from user_constraints
where constraint_type = 'R'
) constraints,
(select table_name, index_name,
join(cursor
(
select column_name
from user_ind_columns
where index_name = user_indexes.index_name
)) columns
from user_indexes) indexes
where constraints.table_name = indexes.table_name (+)
and constraints.columns = indexes.columns (+);


This query works by executing two subqueries: one that queries foreign keys and another that queries indexes. The join between these two queries is on the table name and the list of columns used in creating the foreign key and the index, taken as an ordered list of values.


We'd also like the reverse functionality: to have the ability to take a single comma-delimited value and treat it as if it were a column in a table. We can take advantage of the TABLE SQL function and PL/SQL function tables to do this quite easily, but first, we must define the result type to be a TABLE type of the largest possible string.

create or replace type split_tbl as table of varchar2(32767);
/
show errors;

create or replace function split
(
p_list varchar2,
p_del varchar2 := ','
) return split_tbl pipelined
is
l_idx pls_integer;
l_list varchar2(32767) := p_list;
AA
l_value varchar2(32767);
begin
loop
l_idx := instr(l_list,p_del);
if l_idx > 0 then
pipe row(substr(l_list,1,l_idx-1));
l_list := substr(l_list,l_idx+length(p_del));

else
pipe row(l_list);
exit;
end if;
end loop;
return;
end split;
/
show errors;

With this function, I can run a query like this:

SQL> select * from table(split('one,two,three'));

one
two
three


The PL/SQL procedure will parse its argument and return each part through a PIPELINE; the TABLE function allows it to be used in the FROM statement, so it appears to SQL as if it is a table with one column and three rows. (Remember that the column being returned is named COLUMN_VALUE if you want to use the value elsewhere.)

Here's an example query, which shows a dynamic IN condition in a query. The split function generates a table of values, which can be used on a row-by-row basis.

SQL> select ename from emp
where to_char(hiredate,'YY')
in (select column_value from table(split('81,82')));



View the output in Table B.

If you want, you can join a column and then split it, too:

SQL> select * from table(split(join(cursor(select ename from emp))));

And, you can use this method to merge sets of values:

create table t(a varchar2(200));
insert into t values('81,82');
insert into t values('84,85');

SQL> select * from table(split(join(cursor(select a from t))));

81
82
84
85


These are just simple example functions. You could extend join to enclose values in quotes and escape quotes inside the values. You could extend split to allow a REF CURSOR parameter instead of a single VARCHAR2, so it could split up sets of columns as well.

Wednesday 14 February 2018

Oracle Forms: Progress Bar Solution - 5

Scenario to generate progress bar:
either it can be applied for processing time of a transaction/job/query or no. of records,procedures.
to simplify it i have taken no. of records.


Steps: here are the steps and below is the code of all the steps.
1>emp,dept table should be in scott/tiger schema and create emp_dept table to run this example successfully.


2>create a progress_bar package with 3 procedures Initialize_progress_bar,Show_progress_bar,Hide_progress_bar


3>create a form with 2 display item PERCENT and BAR with color you want to see it with and one START button (you can apply on any of the other trigger as well) to see the status bar on click of it.


4>write a when-button-pressed trigger to show and hide progress bar.


Source Codes:
1>CREATE TABLE EMP_DEPT
(  EMPNO     NUMBER(4),
  ENAME     VARCHAR2(10 BYTE),
  DEPTNO    NUMBER(2),
  DNAME VARCHAR2(14 BYTE)  
)
2>for Progress_bar package
PACKAGE progress_bar IS
  ----------------------------------------------- Public Variable
return_item VARCHAR2(32);
bar_id ITEM; -- id of the bar
view_id VIEWPORT ; -- id of the canvas
full_width NUMBER; -- full width before modifying
return_item VARCHAR2(32); -- return item
Initialized BOOLEAN;
--------------------------------------------- Public Procedures
PROCEDURE Initialize_progress_bar ;
PROCEDURE Show_progress_bar (percent IN NUMBER := 0);
PROCEDURE Hide_progress_bar ;
END progress_bar;
----------Package body----------------------- 
PACKAGE BODY progress_bar IS
    PROCEDURE Show_bar (percent IN NUMBER := 0) IS
     bar_width NUMBER;     IT_ID1 ITEM;
    BEGIN
        bar_width := ROUND((progress_bar.full_width)/ 100 * percent,2);
        SET_ITEM_PROPERTY(progress_bar.bar_id,WIDTH, bar_width);
        COPY(RTRIM(TO_CHAR(ROUND(percent,0)) || '%'),'progress_bar.percent');
        SYNCHRONIZE;
       -- IF percent >= 100 THEN -- job completed
         --   progress_bar.Hide_progress_bar;
       -- END IF;
    END Show_bar;

    PROCEDURE Initialize_progress_bar IS
        it_id varchar2(200);IT_ID1 ITEM;
    BEGIN
        IF progress_bar.Initialized THEN
            MESSAGE('A progress bar is already initialized');
            RAISE FORM_TRIGGER_FAILURE;
            ELSIF progress_bar.full_width IS NULL THEN
            progress_bar.bar_id :=FIND_ITEM('progress_bar.bar');
            progress_bar.full_width := TO_NUMBER(GET_ITEM_PROPERTY(progress_bar.bar_id, WIDTH));
            IT_ID1 := Find_Item('progress_bar.bar');
            SET_ITEM_PROPERTY(it_id1,VISIBLE,PROPERTY_TRUE);
            IT_ID1 := Find_Item('progress_bar.PERCENT');
            SET_ITEM_PROPERTY(it_id1,VISIBLE,PROPERTY_TRUE);
        END IF;
        IF progress_bar.Return_item IS NULL THEN
            it_id := :SYSTEM.TRIGGER_ITEM;
            progress_bar.Return_item :=NAME_IN('SYSTEM.TRIGGER_ITEM');
        END IF;
        progress_bar.Initialized := TRUE;
        Show_bar(0);
    END;

    PROCEDURE Show_progress_bar (percent IN NUMBER := 0) IS
        BEGIN
            IF NOT progress_bar.Initialized THEN
                MESSAGE('No progress bar has been initialized');
                RAISE FORM_TRIGGER_FAILURE;
            END IF;
            Show_bar(percent);
        END ;

    PROCEDURE Hide_progress_bar IS
        IT_ID1 ITEM;
        BEGIN
            IT_ID1 := Find_Item('progress_bar.bar');
            SET_ITEM_PROPERTY(it_id1,VISIBLE,PROPERTY_FALSE);
            IT_ID1 := Find_Item('progress_bar.PERCENT');
            SET_ITEM_PROPERTY(it_id1,VISIBLE,PROPERTY_FALSE);            -- SET_ITEM_PROPERTY(progress_bar.bar_id,WIDTH, 0);
            progress_bar.full_width := NULL;
            GO_ITEM(progress_bar.return_item);-- GO_ITEM(:SYSTEM.TRIGGER_ITEM);
            progress_bar.Return_item := NULL;
            progress_bar.Initialized := FALSE;--SET_WINDOW_PROPERTY('PROCESSING_bar',VISIBLE,PROPERTY_FALSE);
        END Hide_progress_bar;
    END;
----------End of Package body----------------------- 
3> Progress_bar datablock on form builder with 2 display item:PERCENT,BAR
                                                                              1 button :START
4>when-button-pressed trigger
DECLARE
CURSOR C1 IS select empno,ename,d.deptno,d.dname from emp e,dept d where d.deptno=e.deptno;
P_PERCENT NUMBER(3):=0;
C_DONE NUMBER:=1;
BEGIN
    select count(*) INTO :GLOBAL.C_TOT from emp e,dept d where d.deptno=e.deptno;
PROGRESS_BAR.INITIALIZE_PROGRESS_BAR;
FOR CL_REC IN C1 LOOP
-- :global.c_totis the total number of records to be processed.
--c_done total number of records processed.
insert into emp_dept(EMPNO,ENAME,DEPTNO,DNAME) values (CL_REC.EMPNO,CL_REC.ENAME,CL_REC.DEPTNO,CL_REC.DNAME);
C_DONE:=C_DONE+1;
P_PERCENT := TRUNC(C_DONE*100/:GLOBAL.C_TOT);
IF P_PERCENT >= 100 THEN
P_PERCENT := 100;
END IF;
--commit;
PROGRESS_BAR.SHOW_PROGRESS_BAR(P_PERCENT);
END LOOP;
message('Process has been completed successfully');
commit_form;
END;

What is BULK COLLECT? How and Why do we need to use it?

Before understanding about BULK COLLECT, lets see how a PL/SQL code is executed. Oracle uses two engines to process PL/SQL code. All procedural code is handled by the PL/SQL engine while all SQL is handled by the SQL engine. When ever there is a need to process an SQL statement, a context switch happens between PL/SQL and SQL engines.

Imagine a cursor with a SELECT statement which retrieves 1000 rows, in such scenario a context switch will happen for 1000 times which consumes lot of CPU resources and leads to a performance issue. BULK COLLECT is one of the way to solve this problem.
BULK COLLECT is one of the way of fetching bulk collection of data. With Oracle bulk collect, the PL/SQL engine tells the SQL engine to collect many rows at once and place them into a collection. During an Oracle bulk collect, the SQL engine retrieves all the rows and loads them into the collection and switches back to the PL/SQL engine. When rows are retrieved using Oracle bulk collect, context switch happens only onceThe larger the number of rows you would like to collect with Oracle bulk collect, the more performance improvement you will see using an Oracle bulk collect.

Example

In this example, lets use BULK COLLECT to fetch information of all the applications present in an EBS instance.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
DECLARE
   TYPE xx_rec IS RECORD (
      col1   fnd_application.APPLICATION_ID%TYPE,
      col2   fnd_application.APPLICATION_SHORT_NAME%TYPE,
      col3   fnd_application.PRODUCT_CODE%TYPE,
      col4   fnd_application.BASEPATH%TYPE
   ); --Record Type
 
   TYPE xx_tab IS TABLE OF xx_rec
      INDEX BY BINARY_INTEGER; --Table Type
 
   v_tab   xx_tab; --This is a type of Collection
 
   --
   --Cursor to list all applications in EBS instance
   --
   CURSOR cur
   IS
      SELECT application_id, application_short_name, product_code, basepath
        FROM fnd_application;
BEGIN
 
   OPEN cur;
 
   FETCH cur
   BULK COLLECT INTO v_tab; --BULK COLLECT usage
 
   CLOSE cur;
 
   FOR l_index IN v_tab.FIRST .. v_tab.COUNT
   LOOP
      DBMS_OUTPUT.put_line (   v_tab (l_index).col1
                            || ' '
                            || v_tab (l_index).col2
                            || ' '
                            || v_tab (l_index).col3
                            || ' '
                            || v_tab (l_index).col4
                           );
   END LOOP;
END;
Note: Remember that collections are held in memory, so doing a bulk collect from a large query could occupy most of your memory considerably leading to performance problem. Instead you can limit the rows returned using the LIMIT clause and move through the data processing smaller chunks. Below is an example of usage of LIMIT clause
   OPEN cur;
 
   FETCH cur
   BULK COLLECT INTO v_tab LIMIT 100; --limiting BULK COLLECT to 100 records each per a context switch 
 
   CLOSE cur;
More in detail of how memory consumption happens when Collections are used:
Memory for collections is stored in the program global area (PGA), not the system global area (SGA). SGA memory is shared by all sessions connected to Oracle Database, but PGA memory is allocated for each session. Thus, if a program requires 5MB of memory to populate a collection and there are 100 simultaneous connections, that program causes the consumption of 500MB of PGA memory, in addition to the memory allocated to the SGA.

Tuesday 13 February 2018

How to Convert Comma Separated Values into Table using 'REGEXP_SUBSTR'

WITH csv
     AS (SELECT 'AA,BB,CC,D33D,EE,FFDD.MM,GG' AS csvdata FROM DUAL)
    SELECT REGEXP_SUBSTR (csv.csvdata, '[^,]+', 1, LEVEL) pivot_char
      FROM DUAL, csv
CONNECT BY REGEXP_SUBSTR (csv.csvdata,'[^,]+', 1, LEVEL) IS NOT NULL;

Monday 12 February 2018

Oracle Forms: Progress Bar Solution - 4

1) create a display_item and give it a static length
that you wish.
2) In the procedure or function where you execute the
main proces that you want the users now that are
running, declare 3 variables as follows:
x number := 225; /* Size of the bar */
z number; /* Size of the step */
c number; /* Counter */

c := 0;
The variable "z" recievs the result of a
SELECT COUNT(*) into z from xxx where xxx; as
follows:

z := x/c;
x := 0;
3) Initialize the bar:

SET_ITEM_PROPERTY('STDTOOLBAR.PROGRESS2',WIDTH,x);
SET_ITEM_PROPERTY('STDTOOLBAR.PROGRESS2' ,VISIBLE,PROPERTY_TRUE);
synchronize;

4) Inside a loop or something put the following line:
The loop could be the main loop or your proces.
x := x + z;

5) Before the loop ends put the following lines:

SET_ITEM_PROPERTY('STDTOOLBAR.PROGRESS2',WIDTH,x);
synchronize;

6) After loop ends write this line:

:STDTOOLBAR.PROGRESS2 := '100%';
synchronize;
set_application_property(cursor_style,'Default');
SET_ITEM_PROPERTY('STDTOOLBAR.PROGRESS2' ,VISIBLE,PROPERTY_FALSE);

7) Separate of this in a WHEN-NEW-FORM-INSTANCE
Trigger put the next line:

SET_ITEM_PROPERTY('STDTOOLBAR.PROGRESS' ,VISIBLE,PROPERTY_FALSE);

8) In a WHEN-BUTTON-PRESSED Trigger put this line:

SET_ITEM_PROPERTY('STDTOOLBAR.PROGRESS' ,VISIBLE,PROPERTY_TRUE);

Oracle Forms: Progress Bar Solution - 3

Based on the percentage match

You can do this by using 2 nondatabase text fields. First - BACK- with lower border and white background, indicating range of bar, and second PROG, with i.e. red backgroud, without border, which drawing progress. In this simple example bar is growing on each button pressed till variable get 100%. Presented solution rely on covering backgroud item (BACK) by foregroud item (PROG) that width depending on percentage variable.

Progress bar block:

BAR

- PCG - text field, non canvas, number(4), default 0, Your percetage variable

- START - button, canvas

- BACK - text field, same canvas, non value, i.e. 229x29 (w x h)

- PROG - text field, same canvas, cover BACK item on layout, non value, i.e. 227 x 26 (w x h)

WHEN-NEW-FORM-INSTANCE:

Set_Item_Property('BAR.PROG',width,0);

(BAR.START) WHEN-BUTTON-PRESSED

declare
w_bar number(4);
begin 

--static increase by 20% on each button press till 100% 

if :BAR.PCG < 100 then
  :BAR.PCG := :BAR.PCG+20;
end if;

w_bar := trunc((:BAR.PCG*(Get_Item_Property('BAR.BACK',width)-2))/100);
Set_Item_Property('BAR.PROG',width,w_bar); 
end;

Oracle Forms: Progress Bar Solution - 2

One option you could use is to create a displayed, non-base table, text item in the form. Then in your when-button-pressed trigger, you can assign values like: "Step 1", "Step 2" etc. to this displayed field in your procedure in between each of the calls. You will also need to use the "Synchronize" command after you assign each value and before you call the next step.

For example, if your when-button-pressed trigger now is something like this:
begin
procedure_1;
procedure_2;
procedure_3;
-- (etc.)
end;

Create a displayed, but non-enterable field named: show_progress, then change your trigger to:
begin
:show_progress := 'Step 1';
synchronize;
procedure_1;
:show_progress := 'Step 2';
synchronize;
procedure_2;
:show_progress := 'Step 3';
synchronize;
procedure_3;
-- (etc.)
end;

Oracle Forms: Progress Bar Solution - 1

Here is a way to implement a Progress Bar using ORACLE FORMS.
------------------------------------------------------------------
-- Block5.Item8 is a white empty text item
-- Block5.Item9 is a blue empty text item that grows over Block5.Item8.
-- c1 is a cursor.  This piece of code has to perform a given action
-- (PROCESS) for every cursor's record.
DECLARE
   record_count         INTEGER;
   total_number_record  INTEGER;
   CURSOR c1 IS SELECT... ; --
   c1_rec  c1%ROWTYPE;
BEGIN
   OPEN c1;
   FETCH c1 INTO c1_rec;
   total_number_record := SELECT COUNT(*) FROM ...; --same table then
the cursor.
   WHILE c1%FOUND LOOP
      PROCESS(c1_rec);  -- The action to monitor;
      record_count := record_count + 1;
      set_item_property('Item9',width, 200 * record_count /
total_number_record );
        synchronize;
      FETCH C1 INTO c1_rec;
   END LOOP ;
END;
------------------------------------------------------------------

Saturday 10 February 2018

Oracle Forms 10g+ : Configure Forms Look&Feel and ColorScheme

There are two system profile options that can be used to change look and feel.
  • Java Look and Feel &
  • Java Color Scheme
Java Look and Feel:
Java Look and Feel can have two values:
  • GENERIC
  • ORACLE
If set to GENERIC, then set Java Color Scheme to blank.
Which will leave look and feel to generic.
The Generic look and feel adheres to the native interface and color scheme of the current operating system.
If set to ORACLE (or if the value is left blank), then the profile option ‘Java Color Scheme’ can be used to personalize the colors of your screen.
Java Color Scheme:
Profile option ‘Java Color Scheme’ has to be configured to change color of Oracle Forms.
‘Java Color Scheme’ can be set to the following values:
  • Blaf  (in R12: Swan)
  • Blue
  • Khaki
  • Olive
  • Purple
  • Red
  • Teal
  • Titanium

Steps to configure the profile option:

a. Login to Oracle Applications as the System Administator responsibility.
b. Navigate to Profile > System.
c. Ensure that the Site display is checked.
d. Query up ‘Java Color Scheme’ for the profile.
e. Select the appropriate color under the Site column.
f. Save the selection.
g. Sign off and clear cache to make the changes effect.
Note: Be aware that the ‘Java Color Scheme’ profile has no effect if ‘Java Look and Feel’ is set to GENERIC.

Friday 2 February 2018

Error Fixed:- Oracle Reports Developer 10g starts and stay minimized in taskbar

We came across a small annoyance with Oracle product Reports developer 10g. We are yet to pin point the exact reasons, however, if you are experiencing the same issue which is as elaborated below:

Start Reports Developer 10g

Flash Screen appears


Then gets automatically minimized to the taskbar (Windows XP/7/8/8.1), and pointing the mouse shows the preview, as in below image.


However, clicking the preview window doesn’t bring up the Reports Builder 10g to maximized mode and remains unresponsive

Solution:
Right click the Preview window
and select “Maximize”


Once maximized, exit the reports developer. Restart to confirm the builder starts in Maximized mode next time.

Hope it helps! 


If this not works for you try another solution mentioned below:


  1. Open the file named as "cauprefs.ora", located in "DevSuiteHome" directory.
  2. Search line
                                  "Reports.root_max = "
  3. Old value will be
                                  "Reports.root_max = No"
  4.  Change this to
                                  "Reports.root_max = Yes"




Error: when report builder opens minimized

1: Location of file

2: Search Line

3: Old Value

4: New Value

Thursday 1 February 2018

How to Create Browser / Open HTML file in same form in Oracle Forms 6i

In forms 6i, you can use an OCX object to navigate to an HTML page.
The following steps may help you in doing so:

in your form, insert an OCX element

right-click on this object and click on "Insert Object"

choose "Microsoft Web Browser" from the list. The object is now inserted.

in forms menu bar, go to Programs --> Import OLE Library Interfaes.

from the list, under OLE classes, choose Shell.Explorer.2 and under method packages, choose IWebBrowser2 then click OK. Now, a new package is added to your program units.

in the appropriate trigger, write the following code:

declare 
 handlehtml ole2.obj_type;
BEGIN 
 synchronize;
 handlehtml := ole2.create_obj('Shell.Explorer.2');
 handlehtml := forms_ole.get_interface_pointer ('<block_name>.<ocx_name>'); Shell_IWebBrowser2.NAVIGATE(handlehtml,'<URL>'); 
END;


run your form, and fire the trigger where you have written the code, the page specified in <URL> will be displayed in the OCX element.