Understanding Package Level Caching In Oracle Database.

pkg_caching

A package-based cache consists of one or more variables declared at the package level,rather than in any subprogram of the package.Package-level data is a candidate for caching, because this kind of data persists throughout a session,even if programs in that session are not currently using the data or calling any of the subprograms in the package.In other words,if you declare a variable at the package level,once you assign a value to that variable it keeps that value until you disconnect,recompile the package,or change the value.

SQL> CREATE OR REPLACE PACKAGE thisuser
  2  IS
  3   cname CONSTANT VARCHAR2(30) := USER;
  4   FUNCTION name RETURN VARCHAR2;
  5  END;
  6  /

Package created.

SQL> CREATE OR REPLACE PACKAGE BODY thisuser
  2  IS
  3   g_user VARCHAR2(30) := USER;
  4   FUNCTION name RETURN VARCHAR2
  5   IS
  6    BEGIN
  7      RETURN g_user;
  8    END;
  9  END;
 10  /

Package body created.

SQL> CREATE OR REPLACE PROCEDURE test_thisuser(count_in IN PLS_INTEGER)
  2  IS
  3   l_name       all_users.username%TYPE;
  4   l_start_time NUMBER;
  5   l_end_time   NUMBER;
  6   l_diff       NUMBER;
  7  BEGIN
  8   l_start_time := DBMS_UTILITY.GET_TIME;
  9
 10   FOR indx IN 1 .. count_in
 11     LOOP
 12       l_name := thisuser.name;
 13     END LOOP;
 14
 15   l_end_time := DBMS_UTILITY.GET_TIME;
 16
 17   l_diff := l_end_time - l_start_time;
 18
 19   DBMS_OUTPUT.PUT_LINE('Package Function (BODY) Elapsed ::: '||l_diff);
 20
 21   l_start_time := 0;
 22   l_end_time := 0;
 23   l_diff := 0;
 24
 25   l_start_time := DBMS_UTILITY.GET_TIME;
 26
 27   FOR indx IN 1 .. count_in
 28     LOOP
 29       l_name := thisuser.cname;
 30     END LOOP;
 31
 32   l_end_time := DBMS_UTILITY.GET_TIME;
 33
 34   l_diff := l_end_time - l_start_time;
 35
 36   DBMS_OUTPUT.PUT_LINE('Package Constant (SPEC) Elapsed ::: '||l_diff);
 37
 38   l_start_time := 0;
 39   l_end_time := 0;
 40   l_diff := 0;
 41
 42   l_start_time := DBMS_UTILITY.GET_TIME;
 43
 44   FOR indx IN 1 .. count_in
 45     LOOP
 46       l_name := USER;
 47     END LOOP;
 48
 49   l_end_time := DBMS_UTILITY.GET_TIME;
 50
 51   l_diff := l_end_time - l_start_time;
 52
 53   DBMS_OUTPUT.PUT_LINE('USER Function Elapsed ::: '||l_diff);
 54  END test_thisuser;
 55  /

Procedure created.

SQL> EXEC test_thisuser(100);
Package Function (BODY) Elapsed ::: 0
Package Constant (SPEC) Elapsed ::: 0
USER Function Elapsed ::: 0

PL/SQL procedure successfully completed.

SQL> EXEC test_thisuser(1000000);
Package Function (BODY) Elapsed ::: 14
Package Constant (SPEC) Elapsed ::: 2
USER Function Elapsed ::: 129

PL/SQL procedure successfully completed.

--For small numbers of iterations,the advantage of caching is not apparent.However,for large numbers of iterations,the package-based cache is dramatically faster than going through the SQL layer and the SGA.

Accessing the constant is faster than calling a function that returns the value.So why use a function? The function version offers this advantage over the constant : it hides the value.So,if for any reason the value must be changed,you can do so without recompiling the package specification,which would force recompilation of all programs dependent on this package.

SQL> CREATE TABLE products
  2  (
  3   product_number INTEGER PRIMARY KEY,
  4   description VARCHAR2(1000)
  5  );

Table created.

SQL> DECLARE
  2     TYPE products_t IS TABLE OF products%ROWTYPE INDEX BY PLS_INTEGER;
  3     l_rows   products_t;
  4  BEGIN
  5     FOR indx IN 1 .. 10000
  6     LOOP
  7        l_rows (indx).product_number := indx;
  8        l_rows (indx).description := 'Product ' || TO_CHAR (indx);
  9     END LOOP;
 10
 11     FORALL indx IN 1 .. 10000
 12        INSERT INTO products
 13            VALUES l_rows (indx);
 14     COMMIT;
 15  END;
 16  /

PL/SQL procedure successfully completed.

SQL> CREATE OR REPLACE PACKAGE products_cache
  2  IS
  3     FUNCTION with_sql (product_number_in IN products.product_number%TYPE) RETURN products%ROWTYPE;
  4     FUNCTION from_cache (product_number_in IN products.product_number%TYPE) RETURN products%ROWTYPE;
  5     FUNCTION jit_from_cache (product_number_in IN products.product_number%TYPE) RETURN products%ROWTYPE;
  6  END products_cache;
  7  /

Package created.

SQL> CREATE OR REPLACE PACKAGE BODY products_cache
  2  IS
  3     TYPE cache_t IS TABLE OF products%ROWTYPE INDEX BY PLS_INTEGER;
  4     g_cache   cache_t;
  5
  6     FUNCTION with_sql (product_number_in IN products.product_number%TYPE)
  7        RETURN products%ROWTYPE
  8     IS
  9        l_row   products%ROWTYPE;
 10     BEGIN
 11        SELECT *
 12          INTO l_row
 13          FROM products
 14         WHERE product_number = product_number_in;
 15
 16        RETURN l_row;
 17     END with_sql;
 18
 19     FUNCTION from_cache (product_number_in IN products.product_number%TYPE)
 20        RETURN products%ROWTYPE
 21     IS
 22     BEGIN
 23        RETURN g_cache (product_number_in);
 24     END from_cache;
 25
 26     FUNCTION jit_from_cache (product_number_in IN products.product_number%TYPE
 27                             )
 28        RETURN products%ROWTYPE
 29     IS
 30        l_row   products%ROWTYPE;
 31     BEGIN
 32        IF g_cache.EXISTS (product_number_in)
 33        THEN
 34           l_row := g_cache (product_number_in);
 35        ELSE
 36           l_row := with_sql (product_number_in);
 37           g_cache (product_number_in) := l_row;
 38        END IF;
 39
 40        RETURN l_row;
 41     END jit_from_cache;
 42  BEGIN
 43     SELECT *
 44       BULK COLLECT
 45       INTO g_cache
 46       FROM products;
 47  END products_cache;
 48  /

Package body created.

SQL> DECLARE
  2     l_row        products%ROWTYPE;
  3     l_start_time NUMBER;
  4     l_end_time   NUMBER;
  5     l_diff       NUMBER;
  6  BEGIN
  7     l_start_time := DBMS_UTILITY.GET_TIME;
  8
  9     FOR indx IN 1 .. 100000
 10     LOOP
 11        l_row := products_cache.from_cache (5000);
 12     END LOOP;
 13
 14     l_end_time := DBMS_UTILITY.GET_TIME;
 15
 16     l_diff := l_end_time - l_start_time;
 17
 18     DBMS_OUTPUT.PUT_LINE('FROM_CACHE ::: '||l_diff);
 19
 20     l_start_time := 0;
 21     l_end_time := 0;
 22     l_diff := 0;
 23
 24     l_start_time := DBMS_UTILITY.GET_TIME;
 25
 26     FOR indx IN 1 .. 100000
 27     LOOP
 28        l_row := products_cache.with_sql (5000);
 29     END LOOP;
 30
 31     l_end_time := DBMS_UTILITY.GET_TIME;
 32
 33     l_diff := l_end_time - l_start_time;
 34
 35     DBMS_OUTPUT.PUT_LINE('WITH_SQL ::: '||l_diff);
 36
 37     l_start_time := 0;
 38     l_end_time := 0;
 39     l_diff := 0;
 40
 41     l_start_time := DBMS_UTILITY.GET_TIME;
 42
 43     FOR indx IN 1 .. 100000
 44     LOOP
 45        l_row := products_cache.jit_from_cache (5000);
 46     END LOOP;
 47
 48     l_end_time := DBMS_UTILITY.GET_TIME;
 49
 50     l_diff := l_end_time - l_start_time;
 51
 52     DBMS_OUTPUT.PUT_LINE('JIT_FROM_CACHE ::: '||l_diff);
 53  END;
 54  /
 
--OUTPUT :::
FROM_CACHE ::: 9
WITH_SQL ::: 120
JIT_FROM_CACHE ::: 13

Shoumadip Das

Hi Folks, I am Shoumadip Das hailing from Kolkata, India welcoming you to explore my blog www.oraclemasterpiece.com. I am a self motivated and successful professional working in a leading IT Giant for more than 10 years.My area of expertise includes –

  • Oracle Database Programming (SQL & PL/SQL)
  • Perl Programming
  • Unix Programming

Read More

Leave a Reply

Your email address will not be published. Required fields are marked *