It's All About ORACLE

Oracle - The number one Database Management System. Hope this Blog will teach a lot about oracle.

Showing posts with label PL/SQL. Show all posts
Showing posts with label PL/SQL. Show all posts

Oracle Object Type

Object Types

Oracle implements Objects through the use of TYPEs, defined in a similar way to packages. Unlike packages where the instance of the package is limited to the current session, an instance of an object type can be stored in the database for later use. The definition of the type contains a comma separated list of attributes/properties, defined in the same way as package variables, and member functions/procedures. If the a type contains member functions/procedures, the procedural work for these elements is defined in the TYPE BODY.
  • Define A TYPE
  • Define A TYPE BODY
  • Defining A Table
  • Constructors
  • Data Access
  • Issues


Define A TYPE

First we define a type PersonObj to represent a person. In this example, a person is defined by three attributes (first_name, last_name, date_of_birth) and one member function (getAge).
CREATE OR REPLACE TYPE PersonObj AS OBJECT (
  first_name  VARCHAR2(50),
  last_name   VARCHAR2(50),
  date_of_birth  DATE,
  MEMBER FUNCTION getAge RETURN NUMBER
);
/

Define A TYPE BODY

Next we define a TYPE BODY to add functionality to the getAge member function.
CREATE OR REPLACE TYPE BODY PersonObj AS
  MEMBER FUNCTION getAge RETURN NUMBER AS
  BEGIN
    RETURN Trunc(Months_Between(Sysdate, date_of_birth)/12);
  END getAge;
END;
/

Define A TYPE

First we define a type PersonObj to represent a person. In this example, a person is defined by three attributes (first_name, last_name, date_of_birth) and one member function (getAge).
CREATE OR REPLACE TYPE PersonObj AS OBJECT (
  first_name  VARCHAR2(50),
  last_name   VARCHAR2(50),
  date_of_birth  DATE,
  MEMBER FUNCTION getAge RETURN NUMBER
);
/

Define A TYPE BODY

Next we define a TYPE BODY to add functionality to the getAge member function.
CREATE OR REPLACE TYPE BODY PersonObj AS
MEMBER FUNCTION getAge RETURN NUMBER AS
BEGIN
RETURN Trunc(Months_Between(Sysdate, date_of_birth)/12);
END getAge; END;
/

Defining A Table

Now the object type is defined we can use it as a datatype in a table.
CREATE TABLE people (
  id      NUMBER(10) NOT NULL,
  person  PersonObj
);

Constructors

To insert data into the PEOPLE table we must use the PersonObj() constructor. This can be done as part of a regular DML statement, or using PL/SQL.
INSERT INTO people
VALUES (1, PersonObj('John','Doe',
        TO_DATE('01/01/1999','DD/MM/YYYY')));
COMMIT;

DECLARE
  v_person  PersonObj;
BEGIN
  v_person := PersonObj('Jane','Doe',
              TO_DATE('01/01/1999','DD/MM/YYYY'));
  INSERT INTO people VALUES (2, v_person);
  COMMIT;
END;
/


Data Access

Once the data is loaded it can be accessed using the dot notation.
  • alias.column.attribute
  • alias.column.function()
SELECT p.id,
       p.person.first_name,
       p.person.getAge() age
FROM   people p;

        ID PERSON.FIRST_NAME                  AGE
---------- --------------------------- ----------
         1 John                                 2
         2 Jane                                 2

2 row selected.

SQL>

Issues

  • Once an object is used to define a table only it's BODY cannot be altered. To alter the type all table references to it must be dropped. This means the type definition should be very stable before it is used in a table. Note. Later releases of the database allow Type Evolution.
  • The Export & Import often have difficulties with Object Types.
  • The SQL*Plus COPY command does not work with Object Types.
  • There are a number of issues with database links in conjunction with Object Types. This makes the use of object tables in distributed systems impossible without the use of views to hide the object functionality. This defeats the "object" of Objects somewhat.


Oracle MERGE statement - Features, Enhancements and Logging

The MERGE statement was introduced in Oracle 9i to conditionally insert or update data depending on its presence, a process also known as an "upsert". The MERGE statement reduces table scans and can perform the operation in parallel if required. With different releases of Oracle MERGE has also got enhancements. 

Use the MERGE statement to select rows from one or more sources for update or insertion into a table or view. You can specify conditions to determine whether to update or insert into the target table or view. 

This statement is a convenient way to combine multiple operations. It lets you avoid multiple INSERT, UPDATE, and DELETE DML statements.
MERGE is a deterministic statement. You cannot update the same row of the target table multiple times in the same MERGE statement.
Oracle Database does not implement fine-grained access control during MERGE statements. If you are using the fine-grained access control feature on the target table or tables, then use equivalent INSERT and UPDATE statements instead of MERGE to avoid error messages and to ensure correct access control.

Prerequisite:
You must have the INSERT and UPDATE object privileges on the target table and the SELECT object privilege on the source table. To specify the DELETE clause of the merge_update_clause, you must also have the DELETE object privilege on the target table.

Syntax:
Description of merge.gif follows

MERGE [ hint ]
   INTO [ schema. ] { table | view } [ t_alias ]
   USING { [ schema. ] { table | view }
         | subquery 
         } [ t_alias ]
   ON ( condition )
   [ merge_update_clause ]
   [ merge_insert_clause ]
   [ error_logging_clause ] ;


merge_update_clause ::=
Description of merge_update_clause.gif follows

merge_insert_clause ::=
Description of merge_insert_clause.gif follows


error_logging_clause ::=
Description of error_logging_clause.gif follows

LOG ERRORS 
  [ INTO [schema.] table ]
  [ (simple_expression) ]
  [ REJECT LIMIT { integer | UNLIMITED } ]

Initially when MERGE statement was introduced it has the following syntax:
Consider the following example where data from the HR_RECORDS table is merged into the EMPLOYEES table.
MERGE INTO employees e
    USING hr_records h
    ON (e.id = h.emp_id)
  WHEN MATCHED THEN
    UPDATE SET e.address = h.address
  WHEN NOT MATCHED THEN
    INSERT (id, address)
    VALUES (h.emp_id, h.address);
The source can also be a query.
MERGE INTO employees e
    USING (SELECT * FROM hr_records WHERE start_date > ADD_MONTHS(SYSDATE, -1)) h
    ON (e.id = h.emp_id)
  WHEN MATCHED THEN
    UPDATE SET e.address = h.address
  WHEN NOT MATCHED THEN
    INSERT (id, address)
    VALUES (h.emp_id, h.address);

With further Oracle release there was a tremendous enhancement in the way MERGE works.

MERGE Statement Enhancements in Oracle Database 10g

We will be using a test table to explain the Enhancement with example. 

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

1. Optional Clauses
The merge_insert_clause specifies values to insert into the column of the target table if the condition of the ON clause is false. If the insert clause is executed, then all insert triggers defined on the target table are activated. If you omit the column list after the INSERT keyword, then the number of columns in the target table must match the number of values in the VALUES clause.
The merge_update_clause specifies the new column values of the target table. Oracle performs this update if the condition of the ON clause is true. If the update clause is executed, then all update triggers defined on the target table are activated.
In previous oracle version these two clauses were used together. From 10g the MATCHED and NOT MATCHED clauses are now optional ( can be used without other) making all of the following examples valid.
-- Both clauses present.
MERGE INTO test1 a
  USING all_objects b
    ON (a.object_id = b.object_id)
  WHEN MATCHED THEN
    UPDATE SET a.status = b.status
  WHEN NOT MATCHED THEN
    INSERT (object_id, status)
    VALUES (b.object_id, b.status);

-- No matched clause, insert only.
MERGE INTO test1 a
  USING all_objects b
    ON (a.object_id = b.object_id)
  WHEN NOT MATCHED THEN
    INSERT (object_id, status)
    VALUES (b.object_id, b.status);

-- No not-matched clause, update only.
MERGE INTO test1 a
  USING all_objects b
    ON (a.object_id = b.object_id)
  WHEN MATCHED THEN
    UPDATE SET a.status = b.status;

Conditional Operations

Conditional inserts and updates are now possible by using a WHERE clause on these statements.
Specify the where_clause if you want the database to execute the insert/update operation only if the specified condition is true. The condition can refer to either the data source or the target table. If the condition is not true, then the database skips the insert/update operation when merging the row into the table.
-- Both clauses present.
MERGE INTO test1 a
  USING all_objects b
    ON (a.object_id = b.object_id)
  WHEN MATCHED THEN
    UPDATE SET a.status = b.status
    WHERE  b.status != 'VALID'
  WHEN NOT MATCHED THEN
    INSERT (object_id, status)
    VALUES (b.object_id, b.status)
    WHERE  b.status != 'VALID';

-- No matched clause, insert only.
MERGE INTO test1 a
  USING all_objects b
    ON (a.object_id = b.object_id)
  WHEN NOT MATCHED THEN
    INSERT (object_id, status)
    VALUES (b.object_id, b.status)
    WHERE  b.status != 'VALID';

-- No not-matched clause, update only.
MERGE INTO test1 a
  USING all_objects b
    ON (a.object_id = b.object_id)
  WHEN MATCHED THEN
    UPDATE SET a.status = b.status
    WHERE  b.status != 'VALID';

DELETE Clause

An optional DELETE WHERE clause can be added to the MATCHED clause to clean up after a merge operation. Only those rows in the destination table that match both the ON clause and the DELETE WHERE are deleted. Depending on which table the DELETE WHERE references, it can target the rows prior or post update. The following examples clarify this.
Create a source table with 5 rows as follows.
CREATE TABLE source AS
SELECT level AS id,
       CASE
         WHEN MOD(level, 2) = 0 THEN 10
         ELSE 20
       END AS status,
       'Description of level ' || level AS description
FROM   dual
CONNECT BY level <= 5;

SELECT * FROM source;

        ID     STATUS DESCRIPTION
---------- ---------- -----------------------
         1         20 Description of level 1
         2         10 Description of level 2
         3         20 Description of level 3
         4         10 Description of level 4
         5         20 Description of level 5

5 rows selected.

SQL>
Create the destination table using a similar query, but this time with 10 rows.
CREATE TABLE destination AS
SELECT level AS id,
       CASE
         WHEN MOD(level, 2) = 0 THEN 10
         ELSE 20
       END AS status,
       'Description of level ' || level AS description
FROM   dual
CONNECT BY level <= 10;

SELECT * FROM destination;

         1         20 Description of level 1
         2         10 Description of level 2
         3         20 Description of level 3
         4         10 Description of level 4
         5         20 Description of level 5
         6         10 Description of level 6
         7         20 Description of level 7
         8         10 Description of level 8
         9         20 Description of level 9
        10         10 Description of level 10

10 rows selected.

SQL>
The following MERGE statement will update all the rows in the destination table that have a matching row in the source table. The additional DELETE WHERE clause will delete only those rows that were matched, already in the destination table, and meet the criteria of the DELETE WHERE clause.
MERGE INTO destination d
  USING source s
    ON (s.id = d.id)
  WHEN MATCHED THEN
    UPDATE SET d.description = 'Updated'
    DELETE WHERE d.status = 10;

5 rows merged.

SQL>

SELECT * FROM destination;

        ID     STATUS DESCRIPTION
---------- ---------- -----------------------
         1         20 Updated
         3         20 Updated
         5         20 Updated
         6         10 Description of level 6
         7         20 Description of level 7
         8         10 Description of level 8
         9         20 Description of level 9
        10         10 Description of level 10

8 rows selected.

SQL>
Notice there are rows with a status of "10" that were not deleted. This is because there was no match between the source and destination for these rows, so the delete was not applicable.
The following example shows the DELETE WHERE can be made to match against values of the rows before the update operation, not after. In this case, all matching rows have their status changed to "10", but the DELETE WHERE references the source data, so the status is checked against the source, not the updated values.
ROLLBACK;

MERGE INTO destination d
  USING source s
    ON (s.id = d.id)
  WHEN MATCHED THEN
    UPDATE SET  d.description = 'Updated',
                d.status = 10
    DELETE WHERE s.status = 10;

5 rows merged.

SQL>
  
SELECT * FROM destination;

        ID     STATUS DESCRIPTION
---------- ---------- -----------------------
         1         10 Updated
         3         10 Updated
         5         10 Updated
         6         10 Description of level 6
         7         20 Description of level 7
         8         10 Description of level 8
         9         20 Description of level 9
        10         10 Description of level 10

8 rows selected.

SQL>
Notice, no extra rows were deleted compared to the previous example.
By switching the DELETE WHERE to reference the destination table, the extra updated rows can be deleted also.
ROLLBACK;

MERGE INTO destination d
  USING source s
    ON (s.id = d.id)
  WHEN MATCHED THEN
    UPDATE SET  d.description = 'Updated',
                d.status = 10
    DELETE WHERE d.status = 10;

5 rows merged.

SQL>
  
SELECT * FROM destination;

        ID     STATUS DESCRIPTION
---------- ---------- -----------------------
         6         10 Description of level 6
         7         20 Description of level 7
         8         10 Description of level 8
         9         20 Description of level 9
        10         10 Description of level 10

5 rows selected.

SQL>


Restrictions on the merge_update_clause 
This clause is subject to the following restrictions:
  • You cannot update a column that is referenced in the ON condition clause.
  • You cannot specify DEFAULT when updating a view.
Restriction on Merging into a View 
  • You cannot specify DEFAULT when updating a view.

Advantages of Using MERGE 

Oracle Flashback Query (SELECT AS OF)

To use Oracle Flashback Query, use a SELECT statement with an AS OF clause. Oracle Flashback Query retrieves data as it existed at an earlier time. The query explicitly references a past time through a time stamp or System Change Number (SCN). It returns committed data that was current at that point in time.

Uses of Oracle Flashback Query include:

  •  Recovering lost data or undoing incorrect, committed changes. For example, if you mistakenly delete or update rows, and then commit them, you can immediately undo the mistake.
  •  Comparing current data with the corresponding data at an earlier time. For example, you can run a daily report that shows the change in data from yesterday. You can compare individual rows of table data or find intersections or unions of sets of rows.
  •  Checking the state of transactional data at a particular time. For example, you can verify the account balance of a certain day.
  •  Simplifying application design by removing the need to store some kinds of temporal data. Oracle Flashback Query lets you retrieve past data directly from the database.
  •  Applying packaged applications, such as report generation tools, to past data.
  •  Providing self-service error correction for an application, thereby enabling users to undo and correct their errors.

Example of Examining and Restoring Past Data
Suppose that you discover at 12:30 PM that the row for employee Chung was deleted from the employees table, and you know that at 9:30AM the data for Chung was correctly stored in the database. You can use Oracle Flashback Query to examine the contents of the table at 9:30 AM to find out what data was lost. If appropriate, you can restore the lost data.

Example 1 retrieves the state of the record for Chung at 9:30AM, April 4, 2004:

Example 1 Retrieving a Lost Row with Oracle Flashback Query
SELECT * FROM employees
AS OF TIMESTAMP
TO_TIMESTAMP('2004-04-04 09:30:00', 'YYYY-MM-DD HH:MI:SS')
WHERE last_name = 'Chung';
Example 2 restores Chung's information to the employees table:
Example 2 Restoring a Lost Row After Oracle Flashback Query
INSERT INTO employees (
  SELECT * FROM employees
  AS OF TIMESTAMP
  TO_TIMESTAMP('2004-04-04 09:30:00', 'YYYY-MM-DD HH:MI:SS')
  WHERE last_name = 'Chung'
);

Guidelines for Oracle Flashback Query
  • You can use the AS OF clause in queries to perform data definition language (DDL) operations (such as creating and truncating tables) or data manipulation language (DML) statements (such as INSERT and DELETE) in the same session as Oracle Flashback Query.
  • To use the result of Oracle Flashback Query in a DDL or DML statement that affects the current state of the database, use an AS OF clause inside an INSERT or CREATE TABLE AS SELECT statement.
  • If a possible 3-second error (maximum) is important to Oracle Flashback Query in your application, use an SCN instead of a time stamp. 
  • You can create a view that refers to past data by using the AS OF clause in the SELECT statement that defines the view.
    If you specify a relative time by subtracting from the current time on the database host, the past time is recalculated for each query. For example:
    CREATE VIEW hour_ago AS
      SELECT * FROM employees
        AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '60' MINUTE);
    
    SYSTIMESTAMP refers to the time zone of the database host environment.
  • You can use the AS OF clause in self-joins, or in set operations such as INTERSECT and MINUS, to extract or compare data from two different times.
    You can store the results by preceding Oracle Flashback Query with a CREATE TABLE AS SELECT or INSERT INTO TABLE SELECT statement. For example, this query reinserts into table employees the rows that existed an hour ago:
    INSERT INTO employees
        (SELECT * FROM employees
         AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '60' MINUTE)
        MINUS SELECT * FROM employees;
    
    SYSTIMESTAMP refers to the time zone of the database host environment.
We can use either AS OF TIMESTAMP or AS OF SCN. For example.
CREATE TABLE flashback_query_test (
  id  NUMBER(10)
);

SELECT current_scn, TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS') FROM v$database;

CURRENT_SCN TO_CHAR(SYSTIMESTAM
----------- -------------------
     722452 2004-03-29 13:34:12

INSERT INTO flashback_query_test (id) VALUES (1);
COMMIT;

SELECT COUNT(*) FROM flashback_query_test;

  COUNT(*)
----------
         1

SELECT COUNT(*)
FROM   flashback_query_test AS OF TIMESTAMP TO_TIMESTAMP('2004-03-29 13:34:12', 'YYYY-MM-DD HH24:MI:SS');

  COUNT(*)
----------
         0

SELECT COUNT(*)
FROM   flashback_query_test AS OF SCN 722452;

  COUNT(*)
----------
         0

Oracle Optimizer's Join Method

A join method is the mechanism for joining two row sources. Depending on the statistics, the optimizer chooses the method with the lowest estimated cost.

This section contains the following topics:
  • Nested Loops Joins
  • Hash Joins
  • Sort Merge Joins
  • Cluster Joins



Nested Loop Join

Suppose somebody gave you a telephone book and a list of 20 names to look up, and then asked you to write down each person’s name and corresponding telephone number. You would probably go down the list of names, looking up each one in the telephone book one at a time. This task would be pretty easy because the telephone book is alphabetized by name. Moreover, somebody looking over your shoulder could begin calling the first few numbers you write down while you are still looking up the rest. This scene describes a NESTED LOOPS join.
In a NESTED LOOPS join, Oracle reads the first row from the first row source and then checks the second row source for matches. All matches are then placed in the result set and Oracle goes on to the next row from the first row source. This continues until all rows in the first row source have been processed. The first row source is often called the outer or driving table, whereas the second row source is called the inner table. Using a NESTED LOOPS join is one of the fastest methods of receiving the first records back from a join.
NESTED LOOPS joins are ideal when the driving row source (the records you are looking for) is small and the joined columns of the inner row source are uniquely indexed or have a highly selective nonunique index. NESTED LOOPS joins have an advantage over other join methods in that they can quickly retrieve the first few rows of the result set without having to wait for the entire result set to be determined. This situation is ideal for query screens where an end user can read the first few records retrieved while the rest are being fetched. NESTED LOOPS joins are also flexible in that any two-row sources can always be joined by NESTED LOOPS—regardless of join condition and schema definition.
However, NESTED LOOPS joins can be very inefficient if the inner row source (second table accessed) does not have an index on the joined columns or if the index is not highly selective. If the driving row source (the records retrieved from the driving table) is quite large, other join methods may be more efficient.
Figure 1 below illustrates the method of executing the query shown next where the DEPT table is accessed first and the result is then looped through the EMP table with a NESTED LOOPS join. The type of join performed can be forced with a hint and will vary due to different variables on your system.

0465_001


0466_001










Oracle SORT-MERGE Joins
Suppose two salespeople attend a conference and each collect over 100 business cards from potential new customers. They now each have a pile of cards in random order, and they want to see how many cards are duplicated in both piles. The salespeople alphabetize their piles, and then they call off names one at a time. Because both piles of cards have been sorted, it becomes much easier to find the names that appear in both piles. This example describes a SORT-MERGE join.

In a SORT-MERGE join, Oracle sorts the first row source by its join columns, sorts the second row source by its join columns, and then merges the sorted row sources together. As matches are found, they are put into the result set. SORT-MERGE joins can be effective when lack of data selectivity or useful indexes render a NESTED LOOPS join inefficient, or when both of the row sources are quite large (greater than 5 percent of the blocks accessed).
However, SORT-MERGE joins can be used only for equijoins (WHERE D.deptno = E.deptno, as opposed to WHERE D.deptno >= E.deptno). SORT-MERGE joins require temporary segments for sorting (if SORT_AREA_SIZE or the automatic memory parameters like MEMORY_TARGET are set too small). This can lead to extra memory utilization and/or extra disk I/O in the temporary tablespace. Table 1 below illustrates the method of executing the query shown next when a SORT-MERGE join is performed.
0466_002


 0467_001
Table 1. SORT-MERGE join
Oracle HASH Joins
HASH joins are the usual choice of the Oracle optimizer when the memory is set up to accommodate them. In a HASH join, Oracle accesses one table (usually the smaller of the joined results) and builds a hash table on the join key in memory. It then scans the other table in the join (usually the larger one) and probes the hash table for matches to it. Oracle uses a HASH join efficiently only if the parameter PGA_AGGREGATE_TARGET is set to a large enough value. If MEMORY_TARGET is used, the PGA_AGGREGATE_TARGET is included in the MEMORY_TARGET, but you may still want to set a minimum.
If you set the SGA_TARGET, you must set the PGA_AGGREGATE_TARGET as the SGA_TARGET does not include the PGA (unless you use MEMORY_TARGET as just described). The HASH join is similar to a NESTED LOOPS join in the sense that there is a nested loop that occurs—Oracle first builds a hash table to facilitate the operation and then loops through the hash table. When using an ORDERED hint, the first table in the FROM clause is the table used to build the hash table.
HASH joins can be effective when the lack of a useful index renders NESTED LOOPS joins inefficient. The HASH join might be faster than a SORT-MERGE join, in this case, because only one row source needs to be sorted, and it could possibly be faster than a NESTED LOOPS join because probing a hash table in memory can be faster than traversing a b-tree index.
As with SORT-MERGE joins and CLUSTER joins, HASH joins work only on equijoins. As with SORT-MERGE joins, HASH joins use memory resources and can drive up I/O in the temporary tablespace if the sort memory is not sufficient (which can cause this join method to be extremely slow).
Finally, HASH joins are available only when cost-based optimization is used (which should be 100 percent of the time for your application running on Oracle 11g).
Table 1 illustrates the method of executing the query shown in the listing that follows when a HASH join is used.
0468_001

Table 1. HASH join
0469_001

 
Oracle CLUSTER Joins

A CLUSTER join in Oracle is really just a special case of the NESTED LOOPS join that is not used very often. If the two row sources being joined are actually tables that are part of a cluster, and if the join is an equijoin between the cluster keys of the two tables, then Oracle can use a CLUSTER join. In this case, Oracle reads each row from the first row source and finds all matches in the second row source by using the CLUSTER index.

CLUSTER joins are extremely efficient because the joining rows in the two row sources will actually be located in the same physical data block. However, clusters carry certain caveats of their own, and you cannot have a CLUSTER join without a cluster. Therefore, CLUSTER joins are not very commonly used.

ORA-04068: existing state of packages has been discarded

Error ORA-04068: existing state of packages has been discarded comes with package having variables defined in package specification or body. Whenever there are variables/constants defined in package specification or body that package is said to have a state. Upon first calling the package, the "state" is created in memory to hold the values of those variables etc.

This error is received when following changes are performed on package which has state :
  •  If an object that the package depends upon e.g. a table is altered in some way e.g. dropped and recreated, then because of the database dependencies, the package takes on an INVALID status.
  • If there is some compilation error in package specification or body due to which it's state goes invalid.
  •  Value of any variable in the package specification or body is changed and package is compiled, in a different session.
  • If we make any modification in package specification or body in a different session. Even if the change doesn't make the state INVALID.
If we compile such a package specification then all the procedures/packages referring procedure/function/variable of that procedure will give this error.

When you next make a call to the package, Oracle looks at the status and sees that it is invalid, then determines that the package has a "state". Because something has altered that the package depended upon, the state is taken as being out of date and is discarded, thus causing the "Package state has been discarded" error message.


NOTE: We will never receive this error if we do not have any variable/constant defined in a package.

Solution:

  • Move all constants and variables into a stand-alone package specification and reference those from your initial package. Thus when the status of your original package is invalidated for whatever reason, it has no package state and can be recompiled automatically, however the package containing the variables/constants will not become invalidated as it has no dependencies, so the state that is in memory for that package will remain and can continue to be used.
  • Make the package SERIALLY_REUSABLE. No state will be saved for the package and oracle will create new state of the package for every call

Simulation of ORA-04068: existing state of packages has been discarded error:

SQL> set serveroutput on
SQL> create table dependonme (x number)
  2  / 

Table created.

SQL>
SQL> insert into dependonme values (5)
  2  / 

1 row created.

SQL>
SQL> create or replace package mypkg is
 v_statevar number := 5;
procedure myproc;
    end mypkg;
    / 

Package created.

SQL>
SQL> create or replace package body mypkg is
     -- this means my package has a state
  
      procedure myproc is
        myval number;
      begin
        select x
        into myval
        from dependonme;

     -- myval := myval * v_statevar;
      DBMS_OUTPUT.PUT_LINE('My Result is: '||myval);
    end;
   end mypkg;
 / 

Package body created.

SQL>
SQL> exec mypkg.myproc
My Result is: 25

PL/SQL procedure successfully completed.

SQL> select object_name, object_type, status from user_objects where object_name = 'MYPKG';

OBJECT_NAME                    OBJECT_TYPE         STATUS
------------------------------ ------------------- -------
MYPKG                          PACKAGE             VALID
MYPKG                          PACKAGE BODY        VALID

1. Changing the Value of a variable declared in package specification:

I have a variable declared in package specification. This variable is not being used anywhere.
a). In same session: 
SQL>  create or replace package mypkg is
  2      v_statevar number := 15;
  3      procedure myproc;
  4      end mypkg;
  5      /

Package created.

SQL>  exec mypkg.myproc;
My Result is: 5

Change of value in same session didn't make any error.

b). In a second session.
Chaning the value in session 2:
create or replace package mypkg is
   v_statevar number := 25;
   procedure myproc;
   end mypkg;
/

Executing procedure in previos session 1:
SQL>  exec mypkg.myproc;
BEGIN mypkg.myproc; END;

*
ERROR at line 1:
ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package "SCOTT.MYPKG" has been invalidated
ORA-04065: not executed, altered or dropped package "SCOTT.MYPKG"
ORA-06508: PL/SQL: could not find program unit being called: "SCOTT.MYPKG"
ORA-06512: at line 1

NOTE: Just compiling and not changing the value will not give this error. If value of variable is changed in session 2 only then we will receive this error.

2. Changing  the Value of a variable declared in package body:
Similarly if the variable is declared in Package Body rather than in specification and if we make changes to that variable in package body in a different session, then again we will receive this error.

SQL> create or replace package mypkg is
  2
  3         procedure myproc;
  4          end mypkg;
  5      /

Package created.

SQL> create or replace package body mypkg is
  2        v_statevar number := 105;
  3
  4        procedure myproc is
  5          myval number;
  6        begin
  7          select x
  8          into myval
  9          from dependonme;
 10
 11        myval := myval * v_statevar;
 12        DBMS_OUTPUT.PUT_LINE('My Result is: '||myval);
 13      end;
 14     end mypkg;
 15   /

Package body created.

SQL>  exec mypkg.myproc;
My Result is: 525

PL/SQL procedure successfully completed.

SQL> create or replace package body mypkg is
  2        v_statevar number := 205;
  3
  4        procedure myproc is
  5          myval number;
  6        begin
  7          select x
  8          into myval
  9          from dependonme;
 10
 11        myval := myval * v_statevar;
 12        DBMS_OUTPUT.PUT_LINE('My Result is: '||myval);
 13      end;
 14     end mypkg;
 15   /

Package body created.

SQL> exec mypkg.myproc;
My Result is: 1025

PL/SQL procedure successfully completed.

Session 2: Compiling the package body with different value set for variable in it, in session 2 will generate this error.

SQL>    create or replace package body mypkg is
  2        v_statevar number := 305;
  3
  4        procedure myproc is
  5          myval number;
  6        begin
  7          select x
  8          into myval
  9          from dependonme;
 10
 11        myval := myval * v_statevar;
 12        DBMS_OUTPUT.PUT_LINE('My Result is: '||myval);
 13      end;
 14     end mypkg;
 15   /

Package body created.

Session 1: Executing the dependent procedure again.

SQL> exec mypkg.myproc;
BEGIN mypkg.myproc; END;

*
ERROR at line 1:
ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package body "SCOTT.MYPKG" has been invalidated
ORA-04065: not executed, altered or dropped package body "SCOTT.MYPKG"
ORA-06508: PL/SQL: could not find program unit being called: "SCOTT.MYPKG"
ORA-06512: at line 1


3. Making changes to object on which procedure in package body is dependend

a) Droping the table used in procedure body which maked the package body INVALID. These operation can be performed in same session.

SQL> select object_name, object_type, status from user_objects where object_name = 'MYPKG';

OBJECT_NAME                    OBJECT_TYPE         STATUS
------------------------------ ------------------- -------
MYPKG                          PACKAGE             VALID
MYPKG                          PACKAGE BODY        VALID

SQL> drop table dependonme;

Table dropped.

SQL> select object_name, object_type, status from user_objects where object_name = 'MYPKG';

OBJECT_NAME                    OBJECT_TYPE         STATUS
------------------------------ ------------------- -------
MYPKG                          PACKAGE             VALID
MYPKG                          PACKAGE BODY        INVALID

SQL> set serveroutput on
SQL> exec mypkg.myproc;
BEGIN mypkg.myproc; END;

*
ERROR at line 1:
ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package body "SCOTT.MYPKG" has been invalidated
ORA-04065: not executed, altered or dropped package body "SCOTT.MYPKG"
ORA-06508: PL/SQL: could not find program unit being called: "SCOTT.MYPKG"
ORA-06512: at line 1

However if we did not have any variable defined then we would got the same error which is recieved on second call of this procedure.

SQL> exec mypkg.myproc;
BEGIN mypkg.myproc; END;

*
ERROR at line 1:
ORA-04063: package body "SCOTT.MYPKG" has errors
ORA-06508: PL/SQL: could not find program unit being called: "SCOTT.MYPKG"
ORA-06512: at line 1


NOTE: 
 Testing this same scenario but by declaring the variable in specificaiton rather then body would give the same result. So variable defined anywhere in package specification or body, when state of body get invalid, we will receieve this error.


4. Making changes to logic of procedure in package body 

b). In Same session. No error will be generated if we change the logic in procedure.

SQL>  create or replace package body mypkg is
  2           procedure myproc is
  3          myval number;
  4        begin
  5          select sal
  6          into myval
  7          from emp
  8     where empno = 7369;
  9
 10         myval := myval * mypkg.v_statevar;
 11         DBMS_OUTPUT.PUT_LINE('My Result is: '||myval);
 12       end;
 13     end mypkg;
 14   /

Package body created.

SQL> set serveroutput on
SQL> exec mypkg.myproc;
My Result is: 80000

PL/SQL procedure successfully completed.

SQL>  create or replace package body mypkg is
  2           procedure myproc is
  3          myval number;
  4        begin
  5          select x
  6          into myval
  7          from dependonme;
  8
  9         myval := myval * mypkg.v_statevar;
 10         DBMS_OUTPUT.PUT_LINE('My Result is: '||myval);
 11       end;
 12     end mypkg;
 13   /

Package body created.

SQL> set serveroutput on
SQL> exec mypkg.myproc;
My Result is: 500

PL/SQL procedure successfully completed.

b). Changing the package logic in a different session will generate this error.

Session 1:
-----------
SQL> exec mypkg.myproc;
My Result is: 500

PL/SQL procedure successfully completed.

Session 2:
-----------
SQL>  create or replace package body mypkg is
  2           procedure myproc is
  3          myval number;
  4        begin
  5          select sal
  6          into myval
  7          from emp
  8     where empno = 7369;
  9
 10         myval := myval * mypkg.v_statevar;
 11         DBMS_OUTPUT.PUT_LINE('My Result is: '||myval);
 12       end;
 13     end mypkg;
 14   /

Package body created.

Session 1:
-----------
SQL> exec mypkg.myproc;
BEGIN mypkg.myproc; END;

*
ERROR at line 1:
ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package body "SCOTT.MYPKG" has been invalidated
ORA-04065: not executed, altered or dropped package body "SCOTT.MYPKG"
ORA-06508: PL/SQL: could not find program unit being called: "SCOTT.MYPKG"
ORA-06512: at line 1

SQL> set serveroutput on
SQL> exec mypkg.myproc;
My Result is: 80000

PL/SQL procedure successfully completed.


5. Checking the procedure specification variable change with two session
It has been observed that whenever there is a change in variable value in package specification, the package body got invalid and all the session who calls the procedure in this package starts showing "ORA-04068: existing state of packages has been discarded" error.

Session 1:
-----------
SQL> create or replace package mypkg is
  2   v_statevar number := 40;
  3   procedure myproc;
  4   end mypkg;
  5  /

Package created.

SQL> create or replace package body mypkg is
  2        procedure myproc is
  3          myval number;
  4        begin
  5          select x
  6          into myval
  7          from dependonme;
  8
  9       --  myval := myval * mypkg.v_statevar;
 10         DBMS_OUTPUT.PUT_LINE('My Result is: '||myval);
 11       end;
 12     end mypkg;
 13   /

Package body created.

SQL> select object_name, object_type, status from user_objects where object_name = 'MYPKG';

OBJECT_NAME                    OBJECT_TYPE         STATUS
------------------------------ ------------------- -------
MYPKG                          PACKAGE             VALID
MYPKG                          PACKAGE BODY        VALID

Session 2:
-----------
SQL> set serveroutput on
SQL> exec mypkg.myproc;
My Result is: 5

PL/SQL procedure successfully completed.

Session 3:
-----------
SQL> set serveroutput on
SQL> exec mypkg.myproc;
My Result is: 5

PL/SQL procedure successfully completed.

Session 1:
----------
SQL>  create or replace package mypkg is
  2    v_statevar number := 40;
  3    procedure myproc;
  4    end mypkg;
  5   /

Package created.

SQL> select object_name, object_type, status from user_objects where object_name = 'MYPKG';

OBJECT_NAME                    OBJECT_TYPE         STATUS
------------------------------ ------------------- -------
MYPKG                          PACKAGE             VALID
MYPKG                          PACKAGE BODY        INVALID

Session 2
----------
SQL> set serveroutput on
SQL> exec mypkg.myproc;
BEGIN mypkg.myproc; END;

*
ERROR at line 1:
ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package "SCOTT.MYPKG" has been invalidated
ORA-04065: not executed, altered or dropped package "SCOTT.MYPKG"
ORA-06508: PL/SQL: could not find program unit being called: "SCOTT.MYPKG"
ORA-06512: at line 1

Session 3
----------
SQL> set serveroutput on
SQL> exec mypkg.myproc;
BEGIN mypkg.myproc; END;

*
ERROR at line 1:
ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package "SCOTT.MYPKG" has been invalidated
ORA-04065: not executed, altered or dropped package "SCOTT.MYPKG"
ORA-06508: PL/SQL: could not find program unit being called: "SCOTT.MYPKG"
ORA-06512: at line 1

6. Package Body Depended on Package Specification
This example shows package body is dependent on package specification and any change in it will make package body invalid. However we do not receive error while referring that package body procedure in same session because Package body is complied when we call the procedure.

SQL>  create or replace package mypkg is
  2       v_statevar number := 40;
  3        end mypkg;
  4      /

Package created.

SQL>  create or replace package body mypkg is
  2            procedure myproc is
  3              myval number;
  4            begin
  5              select x
  6              into myval
  7              from dependonme;
  8  
  9           --  myval := myval * mypkg.v_statevar;
 10          DBMS_OUTPUT.PUT_LINE('My Result is: '||myval);
 11        end;
 12      end mypkg;
 13    /

Package body created.

SQL> select object_name, status from user_objects where object_name='MYPKG';

OBJECT_NAME                    STATUS
----------                    ------- 
MYPKG                          VALID                               MYPKG                          VALID                                            
SQL>  create or replace package mypkg is
  2       v_statevar number := 140;
  3        end mypkg;
  4      /

Package created.

SQL> select object_name, status from user_objects where object_name='MYPKG';

OBJECT_NAME                    STATUS                               
--------------                -------  
MYPKG                          VALID     
MYPKG                          INVALID                                          
SQL>  create or replace package body mypkg is
  2            procedure myproc is
  3  
  4            begin
  5                 DBMS_OUTPUT.PUT_LINE('My Result is: ');
  6        end;
  7      end mypkg;
  8    /

Package body created.

SQL> select object_name, status from user_objects where object_name='MYPKG';

OBJECT_NAME                    STATUS                               
-----------------             -------                               
MYPKG                          VALID                               
MYPKG                          VALID                                            
SQL>  create or replace package mypkg is
  2       v_statevar number := 240;
  3        end mypkg;
  4      /

Package created.

SQL> select object_name, status from user_objects where object_name='MYPKG';

OBJECT_NAME                    STATUS                             
---------------               -------                               
MYPKG                          VALID                             MYPKG                          INVALID                                          

Conclusion:
"ORA-04068: existing state of packages has been discarded" error is received only on a package which has variable defined in its package specification or body. We will get this error in following situaions:

  •  If value of variable defined in specification or body, is modified in a different session.
  •  Package specification or body got INVALID.
  •  Package specification or body is modified in a different session.

Solution:

With pragma SERIALLY_REUSABLE package

Making the package SERIALLY_REUSABLE will avoid this error.
Session 1:
-----------
SQL> create or replace package mypkg is
  2  pragma SERIALLY_REUSABLE;
  3  v_statevar number := 5;
  4  procedure myproc;
  5  end mypkg;
  6  /

Package created.

SQL> create or replace package body mypkg is
  2  pragma SERIALLY_REUSABLE;
  3           procedure myproc is
  4          myval number;
  5        begin
  6          select x
  7          into myval
  8          from dependonme;
  9
 10         myval := myval * mypkg.v_statevar;
 11         DBMS_OUTPUT.PUT_LINE('My Result is: '||myval);
 12       end;
 13     end mypkg;
 14   /

Package body created.

SQL> exec mypkg.myproc;
My Result is: 25

PL/SQL procedure successfully completed.

Session 2: Modifying the variable value

SQL>  create or replace package mypkg is
  2   pragma SERIALLY_REUSABLE;
  3   v_statevar number := 50;
  4   procedure myproc;
  5   end mypkg;
  6   /

Package created.

Session 1:

SQL> exec mypkg.myproc;
My Result is: 250

PL/SQL procedure successfully completed.

You Might Also Like

Related Posts with Thumbnails

Pages