Examples for Creating Oracle Functions

Examples for Creating Oracle Functions

Mika Wendelius, 1 Apr 2012 CPOL

   4.46 (11 votes)
 
Rate:
vote 1vote 2vote 3vote 4vote 5
 

The article starts with building a simple toolkit package using basic PL/SQL but also covers some more advanced techniques, such as pipeline.

http://www.codeproject.com/Articles/168136/Examples-for-Creating-Oracle-Functions

Introduction

After contributing few articles on Microsoft SQL Server, I thought it would be a good time to do something Oracle specific. This first Oracle article of mine describes some basic tasks but extends to more advanced features.

So the purpose of this article is two-fold: To create a basis for a toolkit collection for developers and administrators to extend and use and also to describe some other techniques how Oracle functionality can be extended. The main areas covered are:

  • Creation of a package
  • Defining constants and publishing them
  • Creating deterministic functions
  • Using AUTHID
  • Creating a function that returns a result set
  • Creating a pipelined function

Creating the Schema for Utilities

The first thing is to create a new schema for the utility package. I decided to create a new schema because of few reasons:

  • Security: The owner of the utilities can be granted such permissions which wouldn’t normally be granted to end-users.
  • Administration: When all the utilities are in a single, separated schema, administrative tasks such as schema compilations, exports etc. become easier.

Creating the user is basically simple. In your installation, just check that the default and temporary tablespaces are correct. However, since this new user will be granted privileges to sys user’s tables, SYSDBA privileges are required.

Hide   Copy Code

CREATE USER ToolkitUser
   IDENTIFIED BY Hammer
   DEFAULT TABLESPACE USERS
   TEMPORARY TABLESPACE TEMP;
/

GRANT CREATE SESSION TO ToolkitUser;
/

GRANT RESOURCE TO ToolkitUser;
/

GRANT SELECT ON sys.v_$session TO ToolkitUser;
/ 

Creating the Package

The downloadable scripts include the package header and body in a single file along with the types needed in this example. For convenience reasons, a public synonym is created for the package at the end of the script. The public synonym guarantees that the package can be called without specifying the schema as long as necessary privileges are granted to the caller.

The package contains several simple examples of functions just to get to know the basics. These functions won’t be discussed in more detail but few things could be pointed out.

  • Even though some of the functions are just short ‘aliases’ to native Oracle functions, I wanted to create a bit more intuitive versions.
  • Most of the functions are defined as DETERMINISTIC meaning that the function always produces the same result if the input values are the same. So these functions can be used in queries, but also in indexes.

The simple functions included are listed below. I decided to create mostly date-related functions since they often seem to cause headache.

  • Day: Returns the number of the day for the given date.
  • Month: Returns the number of the month for the given date.
  • Year: Returns the number of the month for the given date.
  • FirstDayOfMonth: Returns the first day of month for the given date.
  • FirstDayOfYear: Returns the first day of year for the given date.
  • LastDayOfMonth: Returns the last day of month for the given date.
  • DaysInMonth: Returns the number of days in month for the given date.
  • DaysLeftInYear: Returns the number of days left in year for the given date.
  • WorkingDaysLeftInYear: Returns the number of working days left in the year for the given date.

Both Holiday and Toolkit-package scripts are meant to be run under ToolkitUser so after creating the user, get a new connection using ToolkitUser‘s credentials and execute the scripts. If you use SQL*Plus (or equivalent), add the necessary slashes (/) at the end of package header and body.

Publishing Constants

Constants can be added to the package header and then be used throughout the package. However, a constant cannot be referenced in a SQL statement from outside the package. In the Toolkit-package, I’ve defined a constant for PI and I could use it inside a function or a stored procedure, but in order to use it I have to create a small wrapper function for the constant. In this example, the PI_CONSTANT is published using the PI-function.

The constant declaration is:

Hide   Copy Code

PI_CONSTANT CONSTANT NUMBER := 3.14159265358979323846264338327950288419;

and the publishing function:

Hide   Copy Code

FUNCTION Pi RETURN NUMBER DETERMINISTIC IS
BEGIN
   RETURN ( PI_CONSTANT );
END;

Now I can use the value of the PI to calculate for example the area of a 30 centimeter pizza:

Hide   Copy Code

SELECT Toolkit.PI * POWER(30/2, 2) FROM Dual;

Security, AUTHID DEFINER Example

Since the package can be defined to run either using callers or definers privileges, it can be used to encapsulate operations which otherwise wouldn’t be allowed to the caller. This is the same situation as with stand-alone procedures and functions.

Let’s create a small function that returns if a transaction is active for the caller. In order to do this, we’ll need access to v$session to get the address of the transaction state object (if any). Earlier in this article, when the user was created, it was granted SELECT access to sys.v_$session. This is the actual dynamic view containing the information about sessions. Now, since the user owning the package (ToolkitUser) has access to the view, we can define the function in the package as:

Hide   Copy Code

FUNCTION TransactionActive RETURN NUMBER IS
   nActive NUMBER;
BEGIN
   SELECT DECODE( vs.TADDR, NULL, 0, 1)
   INTO nActive
   FROM v$session vs
   WHERE vs.SID = SYS_CONTEXT(‘USERENV‘, ‘SID‘);

   RETURN ( nActive );
END;

If the TADDR is NULL, then there’s currently no transaction. In the function, we need to know from what session we’re querying the state so we need to get the SID for the caller. This is done using SYS_CONTEXT –function.

The package itself was created using definers rights:

Hide   Copy Code

CREATE OR REPLACE PACKAGE Toolkit
AUTHID DEFINER
 ...

Now the caller doesn’t have to have access to sys.v_$session, just to the Toolkit –package in order to know if there’s an active transaction for the session. To get the result, just try:

Hide   Copy Code

SELECT Toolkit.TransactionActive FROM Dual;

Function Returning a Result Set

Functions can also be used to return result sets. To test this, let’s create a function that returns the numbers between given limits. First, we have to define a type for a single row and then a table type containing the data. Both of these statements are included in the package script.

Hide   Copy Code

---------------------------------------------------------------------
-- Type and table for number values
---------------------------------------------------------------------
CREATE OR REPLACE TYPE numberRow AS OBJECT (
   value NUMBER
);
CREATE OR REPLACE TYPE numberTable IS TABLE OF numberRow;

After these are defined, we can create the actual function in the package:

Hide   Copy Code

FUNCTION NumbersBetween(startValue NUMBER, endValue NUMBER ) RETURN numberTable IS
   tabNumbers numberTable := numberTable();
   nCounter NUMBER;
BEGIN
   FOR nCounter IN startValue..endValue LOOP
      tabNumbers.EXTEND;
      tabNumbers(tabNumbers.COUNT) := numberRow(nCounter);
   END LOOP;

   RETURN tabNumbers;
END;

The function uses a FOR loop to generate the numbers. In every iteration, the number table is extended by 1 element and a new row containing the number is added to the table. When the function exits, the table of numbers is returned to the caller.

In order to use the function, TABLE statement has to be used. So getting for example numbers between 1 and 5, the statement would be:

Hide   Copy Code

SELECT * FROM TABLE( Toolkit.NumbersBetween(1, 5));

And the result is:

Hide   Copy Code

VALUE
----------
1
2
3
4
5

Creating a Pipelined Function

Pipelined function also returns result sets much like we saw in the previous example. The difference is that a pipelined function doesn’t return the whole set at the end of the execution of the function. In a pipelined function,PIPE ROW statement is used to send the result to the caller immediately. The concept is quite similar to yield return structure in C#.

Let’s create the same NumbersBetween –function, but this time pipelined. The package has a table type definition which is used as the return type for the pipelined function. The type is named numberTable2 so that it won’t get mixed with the previously defined type.

Hide   Copy Code

TYPE numberTable2 IS TABLE OF NUMBER;

Now the function looks like:

Hide   Copy Code

FUNCTION NumbersBetweenPiped(startValue NUMBER, endValue NUMBER ) RETURN numberTable2
   PIPELINED IS
   nCounter NUMBER;
BEGIN
   FOR nCounter IN startValue..endValue LOOP
      PIPE ROW ( nCounter );
   END LOOP;
END;

The idea is the same as before, but this time we’re not gathering the result to a single set and returning it. Instead, each value is returned separately at the PIPE ROW statement. Using the function looks exactly the same as before:

Hide   Copy Code

SELECT * FROM TABLE( Toolkit.NumbersBetweenPiped(1, 5) );

One big advantage of using pipelined functions is that the result set doesn’t have to be stored until it‘s returned because the results are returned to the caller as they ready. This will have a positive memory impact and also may improve performance for example because parallel operations can be done earlier.

Other Examples of Pipelined Functions

To demonstrate few more examples for functions returning result sets, I’ve included the following (pipelined) functions to the package.

DatesUntil

The function is much like the NumbersBetween –function but operates on dates. The parameters are in different order, first the end date and then the start date. This is because the start date is optional. So, if the start date is not defined in the call, SYSDATE is used. The function implementation is:

Hide   Copy Code

FUNCTION DatesUntil(endDate DATE, startDate DATE DEFAULT SYSDATE ) RETURN dateTable
   PIPELINED IS
   dCounter DATE := TRUNC( startDate );
BEGIN
   WHILE TRUNC( endDate ) >= dCounter LOOP
      PIPE ROW( dCounter );
      dCounter := dCounter + 1;
   END LOOP;
END;

Getting for example this day and the next 5 days would look like:

Hide   Copy Code

SELECT * FROM TABLE( Toolkit.DatesUntil( SYSDATE + 5 ) );

And the results:

Hide   Copy Code

COLUMN_VAL
----------
13.03.2011
14.03.2011
15.03.2011
16.03.2011
17.03.2011
18.03.2011

WorkingDaysLeftInYear

Actually, this isn‘t a pipelined function since it returns only one number. However, a pipelined function is used to demonstrate different usage possibilities. The scripts contains a table called Holiday and the package has a function called WorkingDaysLeftInYear. The idea of the function is that it calculates how many working dates are left in the year for the given date (defaults to current date). Holidays are excluded based on the information inHoliday -table.

The Holiday -table is created as follows (note that in the scripts the Holiday -table is created before the package in order to prevent compilation errors).

Hide   Copy Code

CREATE TABLE ToolkitUser.Holiday (
   Holiday     DATE          NOT NULL PRIMARY KEY,
   Description VARCHAR2(100)
);
/

CREATE PUBLIC SYNONYM Holiday FOR ToolkitUser.Holiday;
/ 

Now, we can use the DatesUntil –function to populate the Holiday-table. Let’s add some weekends to holidays.

Hide   Copy Code

INSERT INTO ToolkitUser.Holiday (Holiday, Description)
SELECT days.Column_Value,
       TO_CHAR(days.Column_Value, ‘DAY‘ )
FROM  TABLE( Toolkit.DatesUntil( SYSDATE + 100)) days
WHERE TO_CHAR(days.Column_Value, ‘DY‘, ‘NLS_DATE_LANGUAGE=AMERICAN‘) IN (‘SAT‘,‘SUN‘);

COMMIT; 

Now, we can select the amount of working days left this year (2011) excluding weekends:

Hide   Copy Code

SELECT Toolkit.WorkingDaysLeftInYear FROM Dual;

StringSplit

One more example is a splitter function which receives a string and a delimiter for values. It breaks the stringinto parts based on the delimiter and returns each element. The functionality is much like .NET Framework‘sString.Split method. The implementation:

Hide   Copy Code

FUNCTION StringSplit(string VARCHAR2, delimiter VARCHAR2 DEFAULT ‘;‘) RETURN stringTable
   PIPELINED IS
   nStartPosition NUMBER := 1;
BEGIN
   FOR endPosition IN (SELECT Column_Value
                       FROM TABLE( StringOccurences( string, delimiter ))) LOOP
      PIPE ROW( SUBSTR( string, nStartPosition, endposition.Column_Value-nStartPosition));
      nStartPosition := endposition.Column_Value + 1;
   END LOOP;
   PIPE ROW( SUBSTR( string, nStartPosition ));
END;

This function uses another pipelined function, StringOccurences, which returns all the indexes from the originalstring where the delimiter is found and then based on those results, this function splits the string. Calling the function:

Hide   Copy Code

SELECT NVL(Column_Value, ’— NULL –’) AS Result
FROM TABLE(Toolkit.StringSplit( ’;Sunday;Afternoon;’, ’;’));

Would return:

Hide   Copy Code

RESULT
-------------
- NULL -
Sunday
Afternoon
- NULL –

Final Words

Hopefully this article has demonstrated some techniques to create packaged functions in Oracle environment. I’d be grateful if you would have the extra time for comments and votes. Thank you.

History

  • March 13, 2011: Article created.
  • April 1, 2012: Slight modification + examples added to scripts.
时间: 2024-10-15 09:35:13

Examples for Creating Oracle Functions的相关文章

Oracle Functions转成Ms-Sql procedure

最近公司的一些Oracle项目要转到Ms_sql上,在把Oracle Functions改成MS-Sql的Procedure时,遇到了翻译的问题. 罗列出这些问题: 一.Oracle 基本类型 oracle mssql varchar2 varchar number int number(15,2) decimal(15,2) 二.序列. Oracle中的序列,我本来打算用自增列加一些小手段处理的,后来在同事的提醒下,才知道ms-sql2012的新特性也包含序列. 附上简单的sql语句 crea

8 Most Required Examples Reference For Oracle Forms

Check the following 8 Links for best Oracle Forms examples with source code (Fmb files), which will work in any version of Oracle Forms. The examples are given for "Oracle Form's Triggers", "Hierarchical Trees", "Stacked Canvases&

Oracle corrupt block(坏块) 详解

转自:http://blog.csdn.net/tianlesoftware/article/details/5024966 一. 坏块说明 1.1 相关链接 在看坏块之前,先看几个相关的链接,在后面的说明中,会用到链接中的一些内容. ORA-600 各个参数含义说明 http://blog.csdn.net/tianlesoftware/article/details/6645809 Oracle 不同故障的恢复方案 http://blog.csdn.net/tianlesoftware/ar

java之Secure hash functions

java之Secure hash functions A secure hash function will generate a large number, called the hash value, when given a document of some sort. This document can be of almost any type. We will be using simple strings in our examples. The function is a one

Life of an Oracle I/O: tracing logical and physical I/O with systemtap

https://db-blog.web.cern.ch/blog/luca-canali/2014-12-life-oracle-io-tracing-logical-and-physical-io-systemtap Luca Canali on 01 Dec 2014 Topic: This post is about tracing logical and physical reads in Oracle using SystemTap. You find here a few examp

Linux Perf Probes for Oracle Tracing

Luca Canali on 21 Jan 2016 Topic: this post is about Linux perf and uprobes for tracing and profiling Oracle workloads for advanced troubleshooting. Context The recent progress and maturity of some of the Linux dynamic tracing tools has raised intere

Scheduling Jobs with Oracle Scheduler

In this chapter: About Scheduler Objects and Their Naming Creating, Running, and Managing Jobs Creating and Managing Programs to Define Jobs Creating and Managing Schedules to Define Jobs Using Events to Start Jobs Creating and Managing Job Chains Pr

HOWTO install Oracle 11g on Ubuntu Linux 12.04 (Precise Pangolin) 64bits

安装了Ubuntu 12.04 64bit, 想在上面安装Oracle 11gr2,网上找了好多文档都没成功,最后完全参考了MordicusEtCubitus的文章. 成功安装的关键点:install additional packages, 在link过程中处理错误的方法. 原文:http://www.makina-corpus.org/blog/howto-install-oracle-11g-ubuntu-linux-1204-precise-pangolin-64bits Before

Oracle内存详解之二 Library cache 库缓冲-转载

Library cache是Shared pool的一部分,它几乎是Oracle内存结构中最复杂的一部分,主要存放shared curosr(SQL)和PLSQL对象(function,procedure,trigger)的信息,以及这些对象所依赖的table,index,view等对象的信息. Library cache需要解决三个问题: 1.快速定位的问题:Library cache中对象众多,Oracle如何管理这些对象,以便服务进程可以迅速找到他们需要的信息.比如某个服务进程需要迅速定位