Archive

Archive for the ‘Oracle PL-SQL’ Category

How to load Data very fast using Partition Exchange

Overview

One of the great features about partitioning, and most specifically range-based partitioning, is the ability to load data quickly and easily with minimal impact on the current users using:

alter table call exchange partition data_2007 with table call_temp;

This command swaps over the definitions of the named partition and the CALL table, so that the data suddenly exists in the right place in the partitioned table. Moreover, with the inclusion of the two optional extra clauses, index definitions will be swapped and Oracle will not check whether the data actually belongs in the partition – so the exchange is very quick.

Example with no Parent Child Relation

In this example, the primary key and the partition key in the partitioned table CALL both are build on the same column «id». Therefore the primary key on CALL can be a LOCAL index and the indexes doesn’t become UNUSABLE (must not be rebuild) after the EXCHANGE.

– Create the Partition Table (Destination Table)

CREATE TABLE call (
id NUMBER(12,6),
v1    VARCHAR2(10),
data  VARCHAR2(100)
)
PARTITION BY RANGE(id) ( – Partion Key = Primary Key
PARTITION call_partition VALUES LESS THAN (MAXVALUE)
);

– Next, create the temporary Table which is used to load the Data offline

CREATE TABLE call_temp (
id    NUMBER(12,6),
v1    VARCHAR2(10),
data  VARCHAR2(100)
);

– Load 1′000′000 Rows into the offline Table

INSERT /*+ append ordered full(s1) use_nl(s2) */
INTO call_temp
SELECT
TRUNC((ROWNUM-1)/500,6),
TO_CHAR(ROWNUM),
RPAD(‘X’,100,’X')
FROM
all_tables s1,
all_tables s2
WHERE
ROWNUM <= 1000000;

– Add LOCAL Primary Key to the Partition Table as a local Index

ALTER TABLE call
ADD CONSTRAINT pk_call PRIMARY KEY(id)
USING INDEX (CREATE INDEX pk_call ON CALL(id) NOLOGGING LOCAL);

– Add Primary Key to the offline Table

ALTER TABLE call_temp
ADD CONSTRAINT pk_call_temp PRIMARY KEY(id)
USING INDEX (CREATE INDEX pk_call_temp ON call_temp(id) NOLOGGING);

– Now swap the offline Table into the Partition

ALTER TABLE CALL
EXCHANGE PARTITION call_partition WITH TABLE call_temp
INCLUDING INDEXES
WITHOUT VALIDATION;

Elapsed: 00:00:00.01

Oracle is checking that the exchange won’t cause a uniqueness problem. The query is searching the entire CALL table (excluding the partition we are exchanging) to see if there are any duplicates of the rows which we are loading. This is particularly daft, since the unique constraint is maintained through a local index, so it must include the partitioning key ID, which means there is only one legal partition in which a row can be, and we have already promised (through the without validation clause) that all the rows belong where we are putting them.

– For the next load, first truncate the CALL Table, then process the next load.

TRUNCATE TABLE call;

ALTER TABLE CALL
EXCHANGE PARTITION call_partition WITH TABLE call_temp
INCLUDING INDEXES
WITHOUT VALIDATION;

Example with Parent Child Relation

In this example, the primary key and the partition key in the partitioned table CALL are NOT the same. The primary key is build on the column «id», but the partition key is build on «created_date». Therefore the primary key on CALL must be a GLOBAL index and the indexes must be maintained using the clause UPDATE GLOBAL INDEXES in the EXCHANGE.

– Create and populate a small lookup table

CREATE TABLE lookup (
id            NUMBER(10),
description   VARCHAR2(50)
);

ALTER TABLE lookup ADD (
CONSTRAINT pk_lookup PRIMARY KEY (id)
);

INSERT INTO lookup (id, description) VALUES (1, ‘ONE’);
INSERT INTO lookup (id, description) VALUES (2, ‘TWO’);
INSERT INTO lookup (id, description) VALUES (3, ‘THREE’);
COMMIT;

– Create and populate a temporary table to load the data

CREATE TABLE call_temp (
id            NUMBER(10),
created_date  DATE,
lookup_id     NUMBER(10),
data          VARCHAR2(50)
);

– Load the temporary table

DECLARE
l_lookup_id    lookup.id%TYPE;
l_create_date  DATE;
BEGIN
FOR i IN 1 .. 1000000 LOOP
IF MOD(i, 3) = 0 THEN
l_create_date := ADD_MONTHS(SYSDATE, -24);
l_lookup_id   := 2;
ELSIF MOD(i, 2) = 0 THEN
l_create_date := ADD_MONTHS(SYSDATE, -12);
l_lookup_id   := 1;
ELSE
l_create_date := SYSDATE;
l_lookup_id   := 3;
END IF;
INSERT INTO call_temp (id, created_date, lookup_id, data)
VALUES (i, l_create_date, l_lookup_id, ‘Loaded Data for ‘ || i);
END LOOP;
COMMIT;
END;
/

– Apply Primary Key to the CALL_TEMP table.

ALTER TABLE call_temp ADD (
CONSTRAINT pk_call_temp PRIMARY KEY (id)
);

– Add Key and Foreign Key Constraint to the CALL_TEMP table.

CREATE INDEX idx_call_temp_created_date ON call_temp(created_date);
CREATE INDEX idx_call_temp_lookup_id ON call_temp(lookup_id);

ALTER TABLE call_temp ADD (
CONSTRAINT fk_call_temp_lookup_id
FOREIGN KEY (lookup_id)
REFERENCES lookup(id)
);

– Next we create a new table with the appropriate partition structure
– to act as the destination table. The destination must have the
– same constraints and indexes defined.

CREATE TABLE call (
id NUMBER(10),
created_date DATE,
lookup_id     NUMBER(10),
data          VARCHAR2(50)
)
PARTITION BY RANGE (created_date)
(PARTITION call_temp_created_date VALUES LESS THAN (MAXVALUE));

– Add Primary Key to CALL which must be GLOBAL

ALTER TABLE call ADD
CONSTRAINT pk_call PRIMARY KEY (id)
USING INDEX (CREATE INDEX pk_call ON CALL(id) GLOBAL
);

– Add Keys and Foreign Key Constraint to the CALL table
– which can be LOCAL

CREATE INDEX idx_call_created_date ON call(created_date) LOCAL;
CREATE INDEX idx_call_lookup_id ON call(lookup_id) LOCAL;

ALTER TABLE call ADD (
CONSTRAINT fk_call_lookup_id
FOREIGN KEY (lookup_id)
REFERENCES lookup(id)
);

– With this destination table in place we can start the conversion.
– We now switch the segments associated with the source table and the
– partition in the destination table using EXCHANGE PARTITION

SET TIMING ON;
ALTER TABLE call
EXCHANGE PARTITION call_temp_created_date
WITH TABLE call_temp
WITHOUT VALIDATION
UPDATE GLOBAL INDEXES;

Elapsed: 00:00:27.19

This is significantly slower than in the previous example!

The UPDATE GLOBAL INDEXES is needed because without it that would leave the global indexes associated with the partition in an UNUSABLE state. If the UPDATE GLOBAL INDEXES clause is added, the performance is reduced since the index rebuild is part of the issued DDL. The index updates are logged and it should only be used when the number of rows is low and data must stay available. For larger numbers of rows index rebuilds are more efficient and allow index reorganization.

Categories: Oracle PL-SQL

Bulk Insert

The Old Fashioned Way

A quick glance at the following Code should make one point very clear: This is straightforward code; unfortunately, it takes a lot of time to run – it is “old-fashioned” code, so let’s improve it using collections and bulk processing.

CREATE OR REPLACE PROCEDURE test_proc IS
BEGIN
FOR x IN (SELECT * FROM all_objects)
LOOP

INSERT INTO t1
(owner, object_name, subobject_name, object_id,
data_object_id, object_type, created, last_ddl_time,
timestamp, status, temporary, generated, secondary)
VALUES
(x.owner, x.object_name, x.subobject_name, x.object_id,
x.data_object_id, x.object_type, x.created,
x.last_ddl_time, x.timestamp, x.status, x.temporary,
x.generated, x.secondary);
END LOOP;
COMMIT;
END test_proc;
/

CREATE TABLE t1 AS SELECT * FROM all_objects WHERE 1 = 2;

SQL> set timing on;
SQL> exec test_proc;

PL/SQL procedure successfully completed.

Elapsed: 00:00:12.84
SQL> exec test_proc;

PL/SQL procedure successfully completed.

Elapsed: 00:00:15.03
SQL> exec test_proc;

PL/SQL procedure successfully completed.

Elapsed: 00:00:12.54

Very slow – do not use it in that way!

USING Bulk Collect

Converting to collections and bulk processing can increase the volume and complexity of your code. If you need a serious boost in performance, however, that increase is well-justified.

Collections, an evolution of PL/SQL tables that allows us to manipulate many variables at once, as a unit. Collections, coupled with two new features introduced with Oracle 8i, BULK_COLLECT and FORALL, can dramatically increase the performance of data manipulation code within PL/SQL.

CREATE OR REPLACE PROCEDURE test_proc (p_array_size IN PLS_INTEGER DEFAULT 100)
IS
TYPE ARRAY IS TABLE OF all_objects%ROWTYPE;
l_data ARRAY;

CURSOR c IS SELECT * FROM all_objects;

BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO l_data LIMIT p_array_size;

FORALL i IN 1..l_data.COUNT
INSERT INTO t1 VALUES l_data(i);

EXIT WHEN c%NOTFOUND;
END LOOP;
CLOSE c;
END test_proc;
/

SQL> exec test_proc;

PL/SQL procedure successfully completed.

Elapsed: 00:00:03.34
SQL> exec test_proc;

PL/SQL procedure successfully completed.

Elapsed: 00:00:03.20
SQL> exec test_proc;

PL/SQL procedure successfully completed.

Elapsed: 00:00:03.90

Eliminate CURSOR LOOP at all

You may eliminate the CURSOR Loop at all, the resulting Procedure is compacter and the performance is more or less the same.

CREATE OR REPLACE PROCEDURE test_proc
IS
TYPE TObjectTable IS TABLE OF ALL_OBJECTS%ROWTYPE;
ObjectTable$ TObjectTable;

BEGIN
SELECT * BULK COLLECT INTO ObjectTable$
FROM ALL_OBJECTS;

FORALL x in ObjectTable$.First..ObjectTable$.Last
INSERT INTO t1 VALUES ObjectTable$(x) ;

END;
/

SQL> exec test_proc;

PL/SQL procedure successfully completed.

Elapsed: 00:00:03.51
SQL> exec test_proc;

PL/SQL procedure successfully completed.

Elapsed: 00:00:03.35
SQL> exec test_proc;

PL/SQL procedure successfully completed.

Elapsed: 00:00:04.46

Categories: Oracle PL-SQL

Shell Commands From PL/SQL

Using a Java stored procedure it is possible to perform shell commands from PL/SQL:

Create the Java Stored Procedure

First we need to create the Java class to perform the shell command.

CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "Host" AS
import java.io.*;
public class Host {
  public static void executeCommand(String command) {
    try {
      String[] finalCommand;
      if (isWindows()) {
        finalCommand = new String[4];
        // Use the appropriate path for your windows version.
        finalCommand[0] = "C:\\windows\\system32\\cmd.exe";  // Windows XP/2003
        //finalCommand[0] = "C:\\winnt\\system32\\cmd.exe";  // Windows NT/2000
        finalCommand[1] = "/y";
        finalCommand[2] = "/c";
        finalCommand[3] = command;
      }
      else {
        finalCommand = new String[3];
        finalCommand[0] = "/bin/sh";
        finalCommand[1] = "-c";
        finalCommand[2] = command;
      }

      final Process pr = Runtime.getRuntime().exec(finalCommand);
      pr.waitFor();

      new Thread(new Runnable(){
        public void run() {
          BufferedReader br_in = null;
          try {
            br_in = new BufferedReader(new InputStreamReader(pr.getInputStream()));
            String buff = null;
            while ((buff = br_in.readLine()) != null) {
              System.out.println("Process out :" + buff);
              try {Thread.sleep(100); } catch(Exception e) {}
            }
            br_in.close();
          }
          catch (IOException ioe) {
            System.out.println("Exception caught printing process output.");
            ioe.printStackTrace();
          }
          finally {
            try {
              br_in.close();
            } catch (Exception ex) {}
          }
        }
      }).start();

      new Thread(new Runnable(){
        public void run() {
          BufferedReader br_err = null;
          try {
            br_err = new BufferedReader(new InputStreamReader(pr.getErrorStream()));
            String buff = null;
            while ((buff = br_err.readLine()) != null) {
              System.out.println("Process err :" + buff);
              try {Thread.sleep(100); } catch(Exception e) {}
            }
            br_err.close();
          }
          catch (IOException ioe) {
            System.out.println("Exception caught printing process error.");
            ioe.printStackTrace();
          }
          finally {
            try {
              br_err.close();
            } catch (Exception ex) {}
          }
        }
      }).start();
    }
    catch (Exception ex) {
      System.out.println(ex.getLocalizedMessage());
    }
  }

  public static boolean isWindows() {
    if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1)
      return true;
    else
      return false;
  }

};
/

show errors java source "Host"

Publish the Java call specification

Next we publish the call specification using a PL/SQL “wrapper” PL/SQL procedure.

CREATE OR REPLACE PROCEDURE host_command (p_command  IN  VARCHAR2)
AS LANGUAGE JAVA
NAME 'Host.executeCommand (java.lang.String)';
/

Grant Privileges

The relevant permissions must be granted from SYS for JServer to access the file system.

EXEC DBMS_JAVA.grant_permission('SCHEMA-NAME', 'java.io.FilePermission', '<<ALL FILES>>', 'read ,write, execute, delete');
EXEC Dbms_Java.Grant_Permission('SCHEMA-NAME', 'SYS:java.lang.RuntimePermission', 'writeFileDescriptor', '');
EXEC Dbms_Java.Grant_Permission('SCHEMA-NAME', 'SYS:java.lang.RuntimePermission', 'readFileDescriptor', '');

The affects of the grant will not be noticed until the grantee reconnects.

Test It

Finally we call the PL/SQL procedure with our command text.

SET SERVEROUTPUT ON SIZE 1000000
CALL DBMS_JAVA.SET_OUTPUT(1000000);
BEGIN
  host_command (p_command => 'move C:\test1.txt C:\test2.txt');
END;
/

The same result could be achieved with COM Automation but in my opinion this method is much neater.

The output from the host command can be captured using the DBMS_OUTPUT.get_lines procedure.

DECLARE
  l_output DBMS_OUTPUT.chararr;
  l_lines  INTEGER := 1000;
BEGIN
  DBMS_OUTPUT.enable(1000000);
  DBMS_JAVA.set_output(1000000);

  host_command('dir C:\');

  DBMS_OUTPUT.get_lines(l_output, l_lines);

  FOR i IN 1 .. l_lines LOOP
    -- Do something with the line.
    -- Data in the collection - l_output(i)
    NULL;
  END LOOP;
END;
/

Known Issues

Depending on the environment, the process may continue running as a zombie after the command has been executed, even if the destroy() method
is called manually. If this happens the process is only cleaned up when the session ends. Under normal circumstances this doesn’t represent
a problem, but when called as part of a job the zombie processes will only die when the Job Queue Coordinator is stopped.

Hope this helps. Regards Tim…

Back to the Top.

 

//

 

 

Categories: Oracle PL-SQL

Execute Immediate with Parameters

Example 1 : Running Execute immediate with an OUT parameter.

I wanted to output a count of tables to see if there is a data difference between Live/Test. I would have created a SQL script but it is hard to share with other developers so I found creating a procedure handy.

Create or replace procedure update_row_count as
l_count number;
l_sql_error_message varchar2(100) :=NULL;
SQL_STMT varchar2(200) :=NULL;
TYPE table_name_type IS VARRAY(7) OF VARCHAR2(30);
table_names table_name_type
:= table_name_type
(‘EMP’,
‘DEPT’,
‘SALARY’);
BEGIN
FOR i IN Table_Names.FIRST..Table_Names.LAST
LOOP
SQL_STMT := ‘SELECT COUNT(*) FROM SCOTT.’||table_names(i);
EXECUTE IMMEDIATE sql_stmt INTO l_count;
l_sql_error_message := SUBSTR(SQLERRM, 1, 100);
DBMS_OUTPUT.PUT_LINE(‘TABLE : ‘||upper(table_names(i)) || ‘ ‘|| l_count);
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(‘Others Error Message : ‘||l_sql_error_message);
END;
set serverout on size 10000;
/
exec update_row_count;

For an IN and OUT parameters you would do

EXECUTE IMMEDIATE ‘BEGIN SCOTT.’|| Table_Names(i)||’(:1,:2,:3);END;’

USING parameter1,OUT parameter2,OUT parameter3;

Example 2 : Running Execute immediate with number of IN parameters. In an example below I am sending IN parameters Emp_id , Dept_id, Dept_name,location and retrieving Salary & Emp_rec

Create or replace procedure update_row_count as
sql_stmt VARCHAR2(200);
plsql_block VARCHAR2(500);
emp_id NUMBER(4) := 7566;
salary NUMBER(7,2);
dept_id NUMBER(2) := 50;
dept_name VARCHAR2(14) := ‘PERSONNEL’;
location VARCHAR2(13) := ‘DALLAS’;
emp_rec emp%ROWTYPE;
BEGIN
EXECUTE IMMEDIATE ‘CREATE TABLE bonus (id NUMBER, amt NUMBER)’;
sql_stmt := ‘INSERT INTO dept VALUES (:1, :2, :3)’;
EXECUTE IMMEDIATE sql_stmt USING dept_id, dept_name, location;
sql_stmt := ‘SELECT * FROM emp WHERE empno = :id’;
EXECUTE IMMEDIATE sql_stmt INTO emp_rec USING emp_id;
plsql_block := ‘BEGIN emp_pkg.raise_salary(:id, :amt); END;’;
EXECUTE IMMEDIATE plsql_block USING 7788, 500;
sql_stmt := ‘UPDATE emp SET sal = 2000 WHERE empno = :1
RETURNING sal INTO :2′;
EXECUTE IMMEDIATE sql_stmt USING emp_id RETURNING INTO salary;
EXECUTE IMMEDIATE ‘DELETE FROM dept WHERE deptno = :num’
USING dept_id;
EXECUTE IMMEDIATE ‘ALTER SESSION SET SQL_TRACE TRUE’;
END;

Categories: Oracle PL-SQL

Function Digit To Word

Following the function for convert from digit to Word :

Create or Replace Function Terbilang(Digit varchar2) Return Varchar2 is
Tampung99 varchar2(100);

# (Author : Ida Bagus Enderajana)
kel_1    varchar2(30);
kel_2    varchar2(30);
kel_3    varchar2(30);
kel_4    varchar2(30);
kel_5    varchar2(30);
hasil   varchar2(3000);
cekdigit number;
sisabagi number;
Begin
tampung99 :=”;
kel_1:=”;
kel_2:=”;
kel_3:=”;
kel_4:=”;
kel_5:=”;
cekdigit :=length(digit);
sisabagi :=mod(cekdigit,3);

if sisabagi=0 then
tampung99:=digit;
elsif sisabagi=1 then
tampung99:=’00′||digit;
elsif sisabagi=2 then
tampung99:=’0′||digit;
end if;

if length(tampung99)=3 then
kel_1:=substr(tampung99,1,3);
hasil:=basecurr_triple_number_words(kel_1);

elsif length(tampung99)=6 then
kel_1:=substr(tampung99,1,3);
kel_2:=substr(tampung99,4,3);
if kel_1=’001′ and kel_2=’000′ then
hasil:=’seribu’;
elsif kel_1=(‘001′) and kel_2 <>(‘000′) then
hasil:=’seribu ‘||basecurr_triple_number_words(to_number(kel_2));
elsif kel_1 <>(‘001′) and kel_2 <>(‘000′) then
hasil:=basecurr_triple_number_words(to_number(kel_1))||’ ribu ‘||basecurr_triple_number_words(to_number(kel_2));
elsif kel_1 <>’001′ and kel_2=’000′ then
hasil:=basecurr_triple_number_words(to_number(kel_1))||’ ribu ‘;
end if;

elsif length(tampung99)=9 then
begin
kel_1:=substr(tampung99,1,3);
kel_2:=substr(tampung99,4,3);
kel_3:=substr(tampung99,7,3);
if kel_1 <>’000′ then
begin
if kel_2 <>’000′ and kel_2 <>’001′ and kel_3 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ juta ‘||basecurr_triple_number_words(kel_2)||’ ribu ‘||basecurr_triple_number_words(kel_3);
elsif kel_2 =’000′ and kel_3=’000′  then
hasil:=basecurr_triple_number_words(kel_1)||’ juta ‘||basecurr_triple_number_words(kel_3);
elsif kel_2=’000′ and kel_3 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ juta ‘||basecurr_triple_number_words(kel_3);
elsif kel_2=’001′ and kel_3=’000′ then
hasil:=basecurr_triple_number_words(to_number(kel_1))||’ juta ‘||’ seribu’;
elsif kel_2=’001′ and kel_3 <>’000′ then
hasil:=basecurr_triple_number_words(to_number(kel_1))||’ juta ‘||’ seribu ‘||basecurr_triple_number_words(to_number(kel_3));
elsif kel_2 =’001′ and kel_3 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ juta ‘||’ seribu ‘||basecurr_triple_number_words(kel_3);
elsif kel_2 <>’000′ and kel_2 <>’001′ and kel_3 <>’000′ or kel_3=’000′   then
hasil:=basecurr_triple_number_words(kel_1)||’ juta ‘||basecurr_triple_number_words(kel_2)||’ ribu ‘||basecurr_triple_number_words(kel_3);
end if;
end;
end if;
end;

elsif length(tampung99)=12 then
begin
kel_1:=substr(tampung99,1,3);
kel_2:=substr(tampung99,4,3);
kel_3:=substr(tampung99,7,3);
kel_4:=substr(tampung99,10,3);
if kel_1 <>’000′ and kel_2 <>’000′ then
begin
if kel_3 <>’000′ and kel_4 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ milyar ‘||basecurr_triple_number_words(kel_2)||’ juta ‘||basecurr_triple_number_words(kel_3)||’ ribu ‘||basecurr_triple_number_words(kel_4);
elsif kel_3 =’000′ and kel_4 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ milyar ‘||basecurr_triple_number_words(kel_2)||’ juta ‘||basecurr_triple_number_words(kel_4);
elsif kel_3 =’000′ and kel_4=’000′  then
hasil:=basecurr_triple_number_words(kel_1)||’ milyar ‘||basecurr_triple_number_words(kel_2)||’ juta ‘||basecurr_triple_number_words(kel_4);
elsif kel_3=’001′ and kel_4=’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ milyar ‘||basecurr_triple_number_words(kel_2)||’ juta ‘||’ seribu ‘;
elsif kel_3=’001′ and kel_4 <>’000′ then
hasil :=basecurr_triple_number_words(kel_1)||’ milyar ‘||basecurr_triple_number_words(kel_2)||’ juta ‘||’ seribu ‘||basecurr_triple_number_words(kel_4);
elsif kel_3 <>’000′ and kel_3 =’001′ and kel_4 <>’000′ or kel_4=’000′   then
hasil :=basecurr_triple_number_words(kel_1)||’ milyar ‘||basecurr_triple_number_words(kel_2)||’ juta ‘||basecurr_triple_number_words(kel_3)||’ ribu ‘||basecurr_triple_number_words(kel_4);
end if;
end;
elsif kel_1 <>’000′ and kel_2=’000′ then
begin
if kel_3 =’000′ and kel_4=’000′ or kel_4 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ milyar ‘||basecurr_triple_number_words(kel_4);
elsif kel_3 <>’001′ or kel_3 <>’000′ and kel_4 =’000′ or kel_4 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ milyar ‘||basecurr_triple_number_words(kel_3)||’ ribu ‘||basecurr_triple_number_words(kel_4);
end if;
end;
end if;
end;
elsif length(tampung99)=15 then
begin
kel_1:=substr(tampung99,1,3);
kel_2:=substr(tampung99,4,3);
kel_3:=substr(tampung99,7,3);
kel_4:=substr(tampung99,10,3);
kel_5:=substr(tampung99,13,3);
if kel_1 <>’000′ and kel_2 <>’000′ and kel_3 <>’000′ then
begin
if kel_4 <>’000′ and kel_5 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||basecurr_triple_number_words(kel_4)||’ ribu ‘||basecurr_triple_number_words(kel_5);
elsif kel_4 =’000′ and kel_5 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||basecurr_triple_number_words(kel_5);
elsif kel_4 =’000′ and kel_5=’000′  then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||basecurr_triple_number_words(kel_5);
elsif kel_4=’001′ and kel_5=’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||’ seribu ‘;
elsif kel_4=’001′ and kel_5 <>’000′ then
hasil :=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||’ seribu ‘||basecurr_triple_number_words(kel_5);
elsif kel_4 <>’000′ and kel_4 =’001′ and kel_5 <>’000′ or kel_5=’000′   then
hasil :=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||basecurr_triple_number_words(kel_4)||’ ribu ‘||basecurr_triple_number_words(kel_5);
end if;
end;
elsif kel_1 <>’000′ and kel_2 <>’000′ and kel_3 =’000′ then
begin
if kel_4 <>’000′ and kel_5 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||basecurr_triple_number_words(kel_4)||’ ribu ‘||basecurr_triple_number_words(kel_5);
elsif kel_4 =’000′ and kel_5 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||basecurr_triple_number_words(kel_5);
elsif kel_4 =’000′ and kel_5=’000′  then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||basecurr_triple_number_words(kel_5);
elsif kel_4=’001′ and kel_5=’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||’ seribu ‘;
elsif kel_4=’001′ and kel_5 <>’000′ then
hasil :=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||’ seribu ‘||basecurr_triple_number_words(kel_5);
elsif kel_4 <>’000′ and kel_4 =’001′ and kel_5 <>’000′ or kel_5=’000′   then
hasil :=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_2)||’ milyar ‘||basecurr_triple_number_words(kel_4)||’ ribu ‘||basecurr_triple_number_words(kel_5);
end if;
end;
elsif kel_1 <>’000′ and kel_2 =’000′ and kel_3 <>’000′ then
begin
if kel_4 <>’000′ and kel_5 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||basecurr_triple_number_words(kel_4)||’ ribu ‘||basecurr_triple_number_words(kel_5);
elsif kel_4 =’000′ and kel_5 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||basecurr_triple_number_words(kel_5);
elsif kel_4 =’000′ and kel_5=’000′  then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||basecurr_triple_number_words(kel_5);
elsif kel_4=’001′ and kel_5=’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||’ seribu ‘;
elsif kel_4=’001′ and kel_5 <>’000′ then
hasil :=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||’ seribu ‘||basecurr_triple_number_words(kel_5);
elsif kel_4 <>’000′ and kel_4 =’001′ and kel_5 <>’000′ or kel_5=’000′   then
hasil :=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_3)||’ juta ‘||basecurr_triple_number_words(kel_4)||’ ribu ‘||basecurr_triple_number_words(kel_5);
end if;
end;
elsif kel_1 <>’000′ and kel_2=’000′ and kel_3=’000′ then
begin
if kel_3 =’000′ and kel_4=’000′ or kel_4 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_5);
elsif kel_3 <>’001′ or kel_3 <>’000′ and kel_4 =’000′ or kel_4 <>’000′ then
hasil:=basecurr_triple_number_words(kel_1)||’ trilyun ‘||basecurr_triple_number_words(kel_4)||’ ribu ‘||basecurr_triple_number_words(kel_5);
end if;
end;
end if;
end;
end if;
return(hasil);
end;
/

Categories: Oracle PL-SQL

Function Decrypt and Encrypt

This function is useful for encrypt  the secret data,an example thif function can be used to encrypt the “sensitive” filed in the table :

CREATE OR REPLACE function f_decrypt( p_str in raw ) return
varchar2
as
begin
return utl_raw.cast_to_varchar2(
dbms_obfuscation_toolkit.DESdecrypt
( input => p_str,
key   => utl_raw.cast_to_raw(‘MagicKey’) ) );
end;
/

CREATE OR REPLACE function F_Encrypt( p_str in varchar2 )
return raw
as
l_data  varchar2(255);
begin
l_data := rpad( p_str, (trunc(length(p_str)/8)+1)*8, chr(0) );
return dbms_obfuscation_toolkit.DESEncrypt
( input => utl_raw.cast_to_raw(l_data),
key => utl_raw.cast_to_raw(‘MagicKey’) );
end;
/

Categories: Oracle PL-SQL

Calculate Time

Calculate time scripts is used to get differrent value between two datetime ,here the folowing scripts :

create or replace procedure ConversiTime(VInput in Number,VOutput out Varchar2) as
Months    Number;
Days    Number;
Hours   Number;
Minutes Number;
Seconds  Number;
MSeconds Number;
Diff    Number;
Diff0   Number;
Diff1   Number;
Diff2   Number;
Diff3   Number;
Begin
Diff :=VInput;
Days:=FLOOR(Diff);
Diff0:=Mod(Diff,1);
—dbms_output.put_line ( ‘Days – ‘||Days||’  Mod is ‘||Diff0);
Hours:=FLOOR(Diff0*24);
Diff1:=Mod(Diff0*24,1);
—dbms_output.put_line ( ‘Hours – ‘||Hours||’  Mod 1 is ‘||Diff1);
Minutes:=FLOOR(Diff1*60);
Diff2:=MOD(Diff1*60,1);
Seconds:=Floor(Diff2*60);
Diff3 :=Mod(Diff2 * 1000,1);
MSeconds:=Floor(Diff3 * 1000);
VOutput := To_Char(Days) ||’ Days ‘|| To_Char(Hours)||’ Hours ‘||To_Char(Minutes)||’ Minutes ‘||To_Char(Seconds)||’ Seconds ‘ ||To_Char(MSeconds)||’ MilliSeconds’;
end;

Categories: Oracle PL-SQL

Get an oracle error info

Here folowing script to get oracle error info :

———————————————————-

CREATE OR REPLACE PACKAGE oracle_error_info
IS
FUNCTION is_app_error (code_in IN INTEGER)
RETURN BOOLEAN;

FUNCTION is_valid_oracle_error (
code_in            IN   INTEGER
, app_errors_ok_in   IN   BOOLEAN DEFAULT TRUE
, user_error_ok_in   IN   BOOLEAN DEFAULT TRUE
)
RETURN BOOLEAN;

PROCEDURE validate_oracle_error (
code_in            IN       INTEGER
, message_out        OUT      VARCHAR2
, is_valid_out       OUT      BOOLEAN
, app_errors_ok_in   IN       BOOLEAN DEFAULT TRUE
, user_error_ok_in   IN       BOOLEAN DEFAULT TRUE
);
END oracle_error_info;
/

CREATE OR REPLACE PACKAGE BODY oracle_error_info
IS
FUNCTION is_app_error (code_in IN INTEGER)
RETURN BOOLEAN
IS
BEGIN
RETURN code_in BETWEEN -20999 AND -20000;
END is_app_error;

PROCEDURE validate_oracle_error (
code_in            IN       INTEGER
, message_out        OUT      VARCHAR2
, is_valid_out       OUT      BOOLEAN
, app_errors_ok_in   IN       BOOLEAN DEFAULT TRUE
, user_error_ok_in   IN       BOOLEAN DEFAULT TRUE
)
IS
l_message   VARCHAR2 (32767);

PROCEDURE set_failure
IS
BEGIN
message_out := NULL;
is_valid_out := FALSE;
END set_failure;
BEGIN
l_message := SQLERRM (code_in);

– If SQLERRM does not find an entry, it return a string like one of these:
– If the number is negative…
– ORA-NNNNN: Message NNNN not found;  product=RDBMS; facility=ORA
– If the number is positive…
–  -13000: non-ORACLE exception
– If the positive number is too big, we get numeric overflow.
IF is_app_error (code_in) AND NOT app_errors_ok_in
THEN
set_failure;
ELSIF code_in = 1 AND NOT user_error_ok_in
THEN
set_failure;
ELSIF    l_message LIKE ‘ORA-_____: Message%not found;%’
OR l_message LIKE ‘%: non-ORACLE exception%’
THEN
set_failure;
ELSE
message_out := l_message;
is_valid_out := TRUE;
END IF;
EXCEPTION
WHEN OTHERS
THEN
–numeric overflow
IF SQLCODE = -1426
THEN
set_failure;
ELSE
RAISE;
END IF;
END validate_oracle_error;

FUNCTION is_valid_oracle_error (
code_in            IN   INTEGER
, app_errors_ok_in   IN   BOOLEAN DEFAULT TRUE
, user_error_ok_in   IN   BOOLEAN DEFAULT TRUE
)
RETURN BOOLEAN
IS
l_message   VARCHAR2 (32767);
retval      BOOLEAN;
BEGIN
validate_oracle_error (code_in
, l_message
, retval
, app_errors_ok_in
, user_error_ok_in
);
RETURN retval;
END is_valid_oracle_error;
END oracle_error_info;
/

Categories: Oracle PL-SQL

Package Debuging

Debugging BP – Debugging Using Pre-Processor Directives

In Oracle 10g Release 2, Oracle introduced a new feature called conditional compilation. Using conditional compilation makes debugging statements a breeze. For instance, here is an example of the debugging statements placed in the source code:

CREATE OR REPLACE PACKAGE mypkg
IS
   PROCEDURE wrapper;

   gv_debug   CONSTANT BOOLEAN := FALSE;
END;
/

CREATE OR REPLACE PACKAGE BODY mypkg
AS
   v_empid   NUMBER (3) := 100;

   PROCEDURE myproc
   IS
      v_empid   NUMBER (3);
   BEGIN
      v_empid := 10;

      IF (gv_debug)
      THEN
         DBMS_OUTPUT.put_line ('Inside myproc(). v_empid = ' || v_empid);
      END IF;
   END;

   PROCEDURE wrapper
   IS
   BEGIN
      myproc;

      IF (gv_debug)
      THEN
         DBMS_OUTPUT.put_line ('Outside myproc(). v_empid = ' || v_empid);
      END IF;
   END;
END;

When the code is run in production, the debugging statements do not get executed because the condition “if (gv_debug)” evaluates to false. When needed, you can turn on the debugging by changing the value of gv_debug to TRUE. This works, but the variable is always evaluated at the runtime, affecting performance. A better approach would be to use conditional compilation, as shown below:

CREATE OR REPLACE PACKAGE mypkg
IS
   PROCEDURE wrapper;
END;
/

CREATE OR REPLACE PACKAGE BODY mypkg
AS
   g_empid   NUMBER (3) := 100;

   PROCEDURE myproc
   IS
      l_empid   NUMBER (3);
   BEGIN
      l_empid := 10;
      $if $$debug $then
      DBMS_OUTPUT.put_line ('Inside myproc(). l_empid = ' || l_empid);
      $end
   END;

   PROCEDURE wrapper
   IS
   BEGIN
      myproc;
      $if $$debug $then
      DBMS_OUTPUT.put_line ('Outside myproc(). g_empid = ' || g_empid);
      $end
   END;
END;

After the package is created, you can alter the debug level by simply issuing:

alter package mypkg compile plsql_ccflags = 'debug:true'

This turns on the debugging statements. So, why it is better that the variables approach? In the variable approach, here is a portion of the code where the condition is evaluated:

  BEGIN
      v_empid := 10;

      IF (gv_debug)
      THEN
         DBMS_OUTPUT.put_line ('Inside myproc(). v_empid = ' || v_empid);
      END IF;
   END;

In the post-processing example, if the value of the variable debug is false, the condition “$if $$debug” evaluates to false and hence the actual code that gets executed in the database is:

  BEGIN
      v_empid := 10;

   END;
Categories: Oracle PL-SQL

Package Best Practice

Package Best Practice – Hide Package DataBy placing data inside the package body, it is protected from direct access by any programs outside of the package.

Recommendation:

Never put your variables and other data structures in the package specification.

  • Always put them in the body.
  • Then build programs to change values in the data structures and retrieve the current values.
  • Make these “get and set” programs available in the package specification.

The benefits include:

  • Tighter control over data structures.
  • Flexibility to change implementation of data structure.

Hiding Package Data Examples

Package specification with an “exposed” variable:

PACKAGE P_and_L
IS
   last_stmt_dt DATE;
END P_and_L;

Package body which hides the variable and also asserts a rule about last statement date:

PACKAGE BODY P_and_L
IS
   last_stmt_dt DATE;               i NOW IN BODY

   FUNCTION last_date RETURN DATE IS
   BEGIN
      RETURN last_stmt_dt;
   END;

   PROCEDURE set_last_date (date_in IN DATE)
   IS
   BEGIN
      last_stmt_dt := 	          Business Rule!
         LEAST (date_in, SYSDATE);
   END;
END P_and_L;

This following version fully exposes the variable and allows for violation of the business rule: The last statement sets the date into the future.

IF P_and_L.last_stmt_dt <
   ADD_MONTHS (SYSDATE, -3)
THEN
   P_and_L.last_stmt_dt := SYSDATE + 12;
END IF;

This next version of same code hides the variable completely and protects against violations of the rules.

IF P_and_L.last_date <
   ADD_MONTHS (SYSDATE, -3)
THEN
   P_and_L.set_last_date (SYSDATE + 12);
END IF;

It’s recommended that you set standards for elements and their names in your “get and set” programs. For example, if the data structure is “maximum length,” then create the following elements:

  • A public procedure named “set_max_length”
  • A public function named “max_length”
  • A private variable named “v_max_length”.

This way, you can use the most natural names (max_length) for the public elements. If the variable has a fixed number of distinct values, such as a Boolean, then you might take a different approach and create multiple procedures and functions to “set and get.”

PACKAGE PLVtrc
IS
   PROCEDURE turn_on;
   PROCEDURE turn_off;

   FUNCTION tracing RETURN BOOLEAN;
   FUNCTION not_tracing RETURN BOOLEAN;
END PLVtrc;
Categories: Oracle PL-SQL