An Introduction to Oracle DBMS Architecture and Server-Side Programming Victor Matos Department of Computer and Information Science Cleveland State University 1 CONTENTS Introduction Oracle Architecture PL/SQL Programming Supplied Packages Collections and Records Objects Cursors Batch Processing Exceptions Packages Triggers Mutating Tables Example Package (OLD) 3 18 127 213 222 272 309 333 344 357 367 397 423 CREDITS. This document is a collage of notes taken from 2 1. Oracle Documentation Files (www.oracle.com) 2. Fundamentals of Database Systems by Elmasri & Navathe. Ed. Addison Wesley 3. Oracle – The Complete Reference by Koch & Loney. Ed. Oracle Press 4. Microsoft On-Line Help files (www.microsoft.com) 5. V. Matos lecture notes What is ORACLE ? • Very robust implementation of a Relational Database Management System (RDBMS) • The relational model of data is simple to understand and has been extensively scrutinized since 1970’s 3 What else is good about ORACLE ? • The company is solid and successful, since 1994 revenues in the order of $2+ billion/year (~ $3BD in 2005) • Very popular software, worldwide distribution (≈ 41% world market in 2007) • Runs in different type of computers. 4 Reasons for the Oracle Success • Security Mechanisms users are given specific rights to operate on the data. • Backup and Recovery minimizes data loss and idle time in the presence of failures • Space Management flexible allocation • Open Connectivity uninterrupted services reachable from heterogeneous clients • Development Tools many to choose from 5 Software Support on the Oracle Server • Procedural Option: built-in programming capabilities based on the PL/SQL language • Stored Procedures: programs stored in the server-side could be invoked using an ADA-like interface • Triggers: dormant server-side routines which are ‘fired’ when specific maintenance conditions are met 6 Parts of the Oracle Server cont • Packages: libraries of server-side routines including proc, functions, triggers, user defined data types, constants, variables, etc. ORACLE PACKAGES Various Clients ORACLE SERV ER Same set of Proc, functions, datatype, const, etc 7 Parts of the Oracle Server cont Distributed Option: • Data could be stored in different machines and placed in different cities. • Oracle Distributed option provides location transparency, the user does not need to be aware of where the data is kept. 8 Oracle Distributed Option Cleveland, Ohio Ohio Inventory Workstation SELECT PartNumber, Price FROM Ohio_inventory, suppliers WHERE inv_sup = Sup_Numb; Indians95 Oraccle Server NETWORK New York, NY. Suppliers Yankees96 ORACLE Server 9 Oracle Distributed Option Homogenous Distributed Database Systems A homogenous distributed database system is a network of two or more Oracle Databases that reside on one or more machines Heterogeneous Distributed Database Systems - In a heterogeneous distributed database system, at least one of the databases is a non-Oracle Database system. - To the application, the heterogeneous distributed database system appears as a single, local, Oracle Database. - The local Oracle Database server hides the distribution and heterogeneity of the data. 10 Oracle Distributed Option Location Transparency • For a client application, the location and platform of the databases are transparent. • You can also create synonyms for remote objects in the distributed system so that users can access them with the same syntax as local objects. • For example, if you are connected to local database MFG but want to access data on remote database HQ, creating a synonym on mfg for the remote emp table enables you to issue this query: SELECT * FROM emp; same as SELECT * FROM emp@HQ.ACME.COM; • In this way, a distributed system gives the appearance of native data access. • Users on mfg do not have to know that the data they access resides on remote databases. 11 Parts of the Oracle Server cont Parallel Server Option: • Independent machines (nodes) which their own memory and disk space could tap into a common disk database. • This loosely coupled architecture provides data sharing and message exchange between processes 12 Parallel Server Option NODE 1 Cleveland, Ohio NODE 2 New York, NY. NY data Ohio data Shared Database Indians95 Oraccle Server Yankees96 ORACLE Server 13 Parts of the Oracle Server cont Parallel Query Option: • Computers holding more than one CPU (such as multiPentium Windows NT, XT, …) could use those processors in solving independent portions of a query. • An Oracle control unit breaks the query into pieces, and re-assembles the partial results. 14 Parallel Query Option Cleveland, Ohio Original SQL statement SELECT LastName, Dno FROM Employee WHERE (salary > 70000) or (sex = 'F') Ohio data Pentium 1 SELECT LastName, Dno FROM Employee WHERE (salary > 70000) Indians95 Oraccle Server Dual Pentium Window s NT4.0 Pentium 2 OR SELECT LastName, Dno FROM Employee WHERE (sex = 'F') Results 15 Links to ORACLE Products & Services www.oracle.com TECHNOLOGY PRODUCTS Database and Grids Middleware | Data Hubs Developer Tools Security | Search Enterprise Management Business Intelligence Collaboration | Content Data Warehousing Standards | Java | Linux Downloads APPLICATION PRODUCTS Customer Relationship Management Financial Accounting Human Resources Supply Chain Public Sector Applications Banking Applications Retail Applications Telecom Applications Higher Education Applications Other Industries ORACLE SERVICES Support | Education On Demand Financing INSIGHT | Consulting 16 Links to ORACLE Products & Services www.oracle.com INDUSTRIES Oracle has solutions for many industry segments such as. Automotive Banking Engineering & Construction Download (PDF) Download (PDF) Download (PDF) Government Healthcare Human Capital Management Download (PDF) Download (PDF) Download (PDF) Government Download (PDF) Manufacturing Download (PDF) Government Download (PDF) Retail Download (PDF) Telecommunications Download (PDF) 17 Components of Typical Oracle Server Data Dictionary Permanent Storage Memory & Processes 18 Oracle Server – Database Dictionary The Database dictionary is a read-only set of tables and views that provides information about the database. A data dictionary contains: ■ The definitions of all schema objects in the database (tables, views, indexes, clusters, synonyms, sequences, procedures, functions, packages, triggers, and so on) ■ How much space has been allocated for, and is currently used by, the schema objects ■ Default values for columns ■ Integrity constraint information ■ The names of Oracle users ■ Privileges and roles each user has been granted ■ Auditing information, such as who has accessed or updated various schema objects ■ Other general database information The data dictionary tables and views for a given database are stored in that database’s SYSTEM tablespace. 19 Oracle Server – Database Dictionary How the Data Dictionary Is Used The data dictionary has three primary uses: ■ Oracle accesses the data dictionary to find information about users, schema objects, and storage structures. ■ Oracle modifies the data dictionary every time that a data definition language (DDL) statement is issued. ■ Any Oracle user can use the data dictionary as a read-only reference for information about the database. Data Dictionary View Prefixes Prefix Scope USER User’s view (what is in the user’s schema) ALL Expanded user’s view (what the user can access) DBA Database administrator’s view (what is in all users’ schemas) 20 ORACLE Architecture A. What is inside of the database dictionary? SYSTEM SQL> column COMMENTS format a35 wrap SYSTEM SQL> select * from DICTIONARY; TABLE_NAME -----------------------------DBA_ROLES DBA_PROFILES USER_RESOURCE_LIMITS USER_PASSWORD_LIMITS USER_CATALOG ALL_CATALOG DBA_CATALOG USER_CLUSTERS ALL_CLUSTERS DBA_CLUSTERS … DBA_SQLSET_DEFINITIONS USER_SQLSET_DEFINITIONS 1821 rows selected. COMMENTS --------------------------------------All Roles which exist in the database Display all profiles and their limits Display resource limit of the user Display password limits of the user Tables, Views, Synonyms and Sequences owned by the user All tables, views, synonyms, sequences accessible to the user All database Tables, Views, Synonyms, Sequences Descriptions of user's own clusters Description of clusters accessible to the user Description of all clusters in the database Synonym for DBA_SQLSET Synonym for USER_SQLSET 21 ORACLE Architecture B. What is inside of a table? SCOTT_SQL> describe Name ------------------FNAME MINIT LNAME SSN BDATE ADDRESS SEX SALARY SUPERSSN DNO Employee Null? Type -------- ------------------------VARCHAR2(10) CHAR(1) NOT NULL VARCHAR2(15) NOT NULL CHAR(9) DATE VARCHAR2(28) CHAR(1) NUMBER(10,2) CHAR(9) NUMBER(38) NOTE: You could use desc instead of describe 22 ORACLE Architecture B. What tables do I have? SCOTT_SQL> select table_name, blocks, num_rows from user_tables; TABLE_NAME BLOCKS NUM_ROWS ------------------------------ ---------- ---------DEPENDENT 5 7 DEPARTMENT 5 3 DEPT_LOCATIONS 5 5 PROJECT 5 6 JUNK 5 1 PAY_CHECK_TABLE 5 3 WORKS_ON 5 17 EMPLOYEE 5 8 7 rows selected. 23 Physical Database Structures Datafiles • Every Oracle database has one or more physical datafiles. • The datafiles contain all the database data. • The data of logical database structures, such as tables and indexes, is physically stored in the datafiles allocated for a database. The characteristics of datafiles are: ■ A datafile can be associated with only one database. ■ Datafiles can have certain characteristics set to let them automatically extend when the database runs out of space. ■ One or more datafiles form a logical unit of database storage called a tablespace. ■ Data in a datafile is read, as needed, during normal database operation and stored in the memory cache of Oracle. Example. Assume that a user wants to access some data in a table of a database. If the requested information is not already in the memory cache for the database, then it is read from the appropriate datafiles and stored in memory. Modified or new data is not necessarily written to a datafile immediately. To reduce the amount of disk access and to increase performance, data is pooled in memory and written to the appropriate datafiles all at once, as determined by the database writer process (DBWn) background process. 24 ORACLE Architecture •A database consists of one or more logical storage units called tablespaces, which collectively store all of the database’s data. •Each tablespace consists of one or more files called datafiles. •Datafiles are physical structures that conform to the operating system in which Oracle is running. 25 ORACLE Architecture Exploring TABLESPACE definitions SYSTEM SQL> desc v$tablespace Name -----------------------------------TS# NAME INCLUDED_IN_DATABASE_BACKUP BIGFILE FLASHBACK_ON ENCRYPT_IN_BACKUP Type ------------NUMBER VARCHAR2(30) VARCHAR2(3) VARCHAR2(3) VARCHAR2(3) VARCHAR2(3) This DATABASE instance is called XE SYSTEM SQL> select * from v$tablespace; TS# ---------0 1 2 4 3 NAME -----------------SYSTEM UNDO SYSAUX USERS TEMP 5 rows selected. INC --YES YES YES YES NO BIG --NO NO NO NO NO FLA ENC --- --YES YES YES YES YES 26 ORACLE Architecture Enlarging a Database by Adding a Datafile to a Tablespace Enlarging a Database by Adding a New Tablespace 27 ORACLE Architecture Enlarging a Database by Dynamically Sizing Datafiles 28 ORACLE Architecture Physical Storage and its relationship to Oracle logical components [Database, TableSpace, Schema Objects (Tables, Indices, Clusters,…)] 29 ORACLE Architecture The logical organization and operation of Oracle DBMS is very similar to the IBM370 Virtual Memory, Multitasking computers of the late 70’s. 30 ORACLE Architecture LOGICAL DATA STRUCTURES • Oracle stores data in data blocks. One data block corresponds to a specific number of bytes of physical database space on disk. • The next level of logical database space is an extent. An extent is a specific number of contiguous data blocks allocated for storing a specific type of information. • The level of logical database storage greater than an extent is called a segment. A segment is a set of extents, each of which has been allocated for a specific data structure and all of which are stored in the same tablespace. • For example, each table’s data is stored in its own data segment, while each index’s data is stored in its own index segment. If the table or index is partitioned, each partition is stored in its own segment. 31 ORACLE Architecture Data Block Format The Oracle data block format is similar regardless of whether the data block contains table, index, or clustered data. 32 Physical Database Structures Control Files Every Oracle database has a control file. A control file contains entries that specify the physical structure of the database. For example, it contains the following information: ■ Database name ■ Names and locations of datafiles and redo log files ■ Time stamp of database creation Oracle can multiplex the control file, that is, simultaneously maintain a number of identical control file copies, to protect against a failure involving the control file. Every time an instance of an Oracle database is started, its control file identifies the database and redo log files that must be opened for database operation to proceed. If the physical makeup of the database is altered (for example, if a new datafile or redo log file is created), then the control file is automatically modified by Oracle to reflect the change. A control file is also used in database recovery. 33 Physical Database Structures Control Files Create controlfile reuse set database "XE" MAXINSTANCES 8 MAXLOGHISTORY 1 MAXLOGFILES 16 MAXLOGMEMBERS 3 MAXDATAFILES 100 Datafile 'C:\oraclexe\oradata\XE\system.dbf', 'C:\oraclexe\oradata\XE\undo.dbf', 'C:\oraclexe\oradata\XE\sysaux.dbf', 'C:\oraclexe\oradata\XE\users.dbf' LOGFILE GROUP 1 SIZE 51200K, GROUP 2 SIZE 51200K, RESETLOGS; 34 Physical Database Structures Redo Log Files • Every Oracle database has a set of two or more redo log files. • The primary function of the redo log is to record all changes made to data. If a failure prevents modified data from being permanently written to the datafiles, then the changes can be obtained from the redo log, so work is never lost. • To protect against a failure involving the redo log itself, Oracle allows a multiplexed redo log so that two or more copies of the redo log can be maintained on different disks. • The information in a redo log file is used only to recover the database from a system or media failure that prevents database data from being written to the datafiles. Example. If an unexpected power outage terminates database operation, then data in memory cannot be written to the datafiles, and the data is lost. However, lost data can be recovered when the database is opened, after power is restored. By applying the information in the most recent redo log files to the database datafiles, Oracle restores the database to the time at which the power failure occurred. The process of applying the redo log during a recovery operation is called rolling forward. 35 Physical Database Structures Other Control Files Archive Log Files Permanent (disk) images of REDO logs. Parameter Files Parameter files contain a list of configuration parameters for that instance and database. Alert and Trace Log Files The alert file, or alert log, is a special trace file. The alert log of a database is a chronological log of messages and errors. Each server and background process can write to an associated trace file. When an internal error is detected by a process, it dumps information about the error to its trace file. Backup Files To restore a file is to replace it with a backup file. Typically, you restore a file when a media failure or user error has damaged or deleted the original file. 36 Oracle Memory & Transactions Oracle uses memory to store information such as the following: ■ Program code ■ Information about a connected session, even if it is not currently active ■ Information needed during program execution (for example, the current state of a query from which rows are being fetched) ■ Information that is shared and communicated among Oracle processes (for example, locking information) ■ Cached data that is also permanently stored on peripheral memory (for example, data blocks and redo log entries) 37 Oracle Memory & Transactions The basic memory structures associated with Oracle include: ■ System Global Area (SGA), which is shared by all server and background processes. ■ Program Global Areas (PGA), which is private to each server and background process; there is one PGA for each process. 38 Oracle Memory & Transactions System Global Area • The System Global Area (SGA) is a shared memory region that contains data and control information for one Oracle instance. • Oracle allocates the SGA when an instance starts and deallocates it when the instance shuts down. • Each instance has its own SGA. • Users currently connected to an Oracle database share the data in the SGA. • For optimal performance, the entire SGA should be as large as possible (while still fitting in real memory) to store as much data in memory as possible and to minimize disk I/O. 39 Oracle Memory & Transactions System Global Area The information stored in the SGA is divided into several types of memory structures • Database Buffer Cache of the SGA Database buffers store the most recently used blocks of data. The set of database buffers in an instance is the database buffer cache. The buffer cache contains modified as well as unmodified blocks. Because the most recently (and often, the most frequently) used data is kept in memory, less disk I/O is necessary, and performance is improved. • Redo Log Buffer of the SGA The redo log buffer stores redo entries—a log of changes made to the database. The redo entries stored in the redo log buffers are written to an online redo log table, which is used if database recovery is necessary. • Shared Pool of the SGA The shared pool contains shared memory constructs, such as shared SQL areas. A shared SQL area is required to process every unique SQL statement submitted to a database. A shared SQL area contains information such as the parse tree and execution plan for the corresponding statement. A single shared SQL area is used by multiple applications that issue the same statement, leaving more shared memory for other uses. 40 Oracle Memory & Transactions Database Buffer Cache The database buffer cache is the portion of the SGA that holds copies of data blocks read from datafiles. Organization of the Database Buffer Cache. The buffers in the cache are organized in two lists: • The WRITE list holds dirty buffers, which contain data that has been modified but has not yet been written to disk. • The LRU list holds free buffers, pinned buffers, and dirty buffers that have not yet been moved to the write list. Free buffers do not contain any useful data and are available for use. Pinned buffers are currently being accessed. When an Oracle process accesses a buffer, the process moves the buffer to the most recently used (MRU or HOT SIDE) end of the LRU list. As more buffers are continually moved to the MRU end of the LRU list, dirty buffers age toward the 41 LRU end (COLD SIDE) of the LRU list. Oracle Memory & Transactions Database Buffer Cache The WRITE List holds dirty buffers, which contain data that has been modified but has not yet been written to disk. Dirty Ready for disk Dirty Ready for disk The LRU list holds free buffers, pinned buffers, and dirty buffers that have not yet been moved to the write list. Dirty Moving to WL Dirty Moving to WL Pinned (in use) Pinned (in use) Pinned (in use) Free Free COLD side HOT side (least rec. used) (most rec. used) Free 42 Oracle Memory & Transactions Least Recently Used (LRU) Method - Memory Management The first time an Oracle user process requires a particular piece of data, it searches for the data in the database buffer cache. 1. If the process finds the data already in the cache (a cache hit), it can read the data directly from memory. 2. If the process cannot find the data in the cache (a cache miss), it must copy the data block from a datafile on disk into a buffer in the cache before accessing the data (accessing data through a cache hit is faster than data access through a cache miss). 3. Before writing a data block into the cache, the process must first find a free buffer. 1. The process searches the LRU list, starting at the least recently used end of the list. The process searches either until it finds a free buffer or until it has searched the threshold limit of buffers. 2. If the user process finds a dirty buffer as it searches the LRU list, it moves that buffer to the write list and continues to search. 3. When the process finds a free buffer, it reads the data block from disk into the buffer and moves the buffer to the MRU end (HOT SIDE) of the LRU list. 4. If an Oracle user process searches the threshold limit of buffers without finding a free buffer, the process stops searching the LRU list and signals the DBW0 background process to write some of the dirty buffers to disk. 43 ORACLE Transactions Example. Transfer $500 from savings to checking account. • A transaction is a logical unit of work that contains one or more SQL statements. • A transaction is an atomic unit. The effects of all the SQL statements in a transaction can be either all committed (applied to the database) or all rolled back (undone from the database). • A transaction begins with the first executable SQL statement. • A transaction ends when it is committed or rolled back, either explicitly with a COMMIT or ROLLBACK statement or implicitly when a DDL statement is issued. •Commit Write Immediate NoWait •Commit Work •Rollback ToSavePointName 44 ORACLE Processes Introduction to Processes All connected Oracle users must run two modules of code to access an Oracle database instance. ■ Application or Oracle tool: A database user runs a database application (such as a precompiler program) or an Oracle tool (such as SQL*Plus), which issues SQL statements to an Oracle database. ■ Oracle database server code: Each user has some Oracle database code executing on his or her behalf, which interprets and processes the application’s SQL statements. 45 ORACLE Processes An instance is a portion of the computers memory and a collection of processes required by Oracle to operate 46 ORACLE Processes Background Process Acronym Definition Archiver ARCH Copies online REDO LOG files to a designated storage device once it has become full Checkpoint CKPT Updates datafile headers and control files on behalf of the LGWR when a checkpoint is completed Database Writer DBWR Writes data from the database buffer cache to the datafiles Lock Process LCKn Performs inter-instance locking in a parallel server system Log Writer LGWR Writes data from the redo log buffer to the redo log files. Updates datafile headers and control files if CKPT is not enabled Process Monitor PMON Performs process recovery when a user process fails Recoverer RECO Recovers failures involving distributed transactions System Monitor SMON Performs instance recovery at instance startup, does cleanup, and reclaims temporary segments Parallel Query Qnnn Performs parallel queries Snapshot SNPn Performs automatic refreshes of snapshots and handles the server job queues 47 ORACLE Processes An Example of How ORACLE DBMS works The following example is a simple illustration of the dedicated server architecture of Oracle. Assume a database server machine is currently running Oracle using multiple background processes. 1. A client workstation runs a database application (in a user process) such as SQL*Plus. The client application attempts to establish a connection to the server using a SQL*Net driver. 2. The database server is currently running the proper SQL*Net driver. The Listener process on the database server detects the connection request from the client database application and creates a dedicated server process on the database server on behalf of the user process. 3. The user executes a single SQL statement. For example, the user inserts a row into a table. 48 ORACLE Processes An Example of How ORACLE DBMS works (continuation) 4. The dedicated server process receives the statement. At this point, two paths can be followed to continue processing the SQL statement: 4.1 If the shared pool contains a shared SQL area for an identical SQL statement, the server process can use the existing shared SQL area to execute the client's SQL statement. 4.2 If the shared pool does not contain a shared SQL area for an identical SQL statement, a new shared SQL area is allocated for the statement in the shared pool. 5. In either case, a private SQL area is created in the session's PGA and the dedicated server process checks the user's access privileges to the requested data. 6. The server process retrieves data blocks from the actual datafile, if necessary, or uses data blocks already stored in the buffer cache in the SGA of the instance. 49 ORACLE Processes An Example of How ORACLE DBMS works (continuation) 7. The server process executes the SQL statement stored in the shared SQL area. 8. Data is first changed in the SGA. It is permanently written to disk when the DBWR process determines it is most efficient to do so. 9. The LGWR process records the transaction in the online redo log file only on a subsequent commit request from the user. If the request is successful, the server sends a message across the network to the user. If it is not successful, an appropriate error message is transmitted. 10. Throughout this entire procedure, the other background processes are running and watching for any conditions that require intervention. In addition, Oracle is managing other transactions and preventing contention between different transactions that request the same data. 50 These steps show only the most basic level of operations that Oracle performs. ORACLE Management Tool Explore the Oracle architecture using the ENTERPRISE MANAGER (EM) web tool (10g v10.1) 51 ORACLE Management Tool 52 ORACLE Management Tool Explore the Oracle architecture using the ENTERPRISE MANAGER (EM) web tool. 53 ORACLE Management Tool 54 ORACLE Management Tool 55 ORACLE Tools Explore/manage database using Oracle 10g Express Edition (v 10.2) http://127.0.0.1:8080/apex/ 56 ORACLE Tools Explore/manage database using Oracle 10g Express Edition (v 10.2) 57 ORACLE Tools Explore/manage database using Oracle 10g Express Edition (v 10.2) 58 ORACLE Tools Explore/manage database using Oracle 10g Express Edition (v 10.2) 59 ORACLE Tools Explore/manage database using Oracle 10g Express Edition (v 10.2) 60 ORACLE Tools SQL*Plus is a simple text editor (notepad-like) connected to the Oracle Server. It is useful for simple development tasks. 61 ORACLE Tools SQL*Plus Connection String for Reaching a Remote Server. Assume remote Oracle server instance named ORCL is hosted at sancho.csuohio.edu. The following is a valid (although terribly inconvenient) connection string for the user identified as: csuperson / euclid. SQL> connect csuperson @ (DESCRIPTION= (ADDRESS=(PROTOCOL=tcp) (HOST=sancho.csuohio.edu)(PORT=1521) ) (CONNECT_DATA=(SERVICE_NAME=ORCL) ) ) / euclid NOTE. In Oracle 11g credentials (UserName/Password) are case sensitive. It is recommended to use UPPER CASE values. 62 ORACLE Tools SQL*Plus Connection String for Reaching a Remote Server. A better way to connect to the remote Oracle server instance named ORCL hosted at sancho.csuohio.edu requires us to add the following entry to the tnsnames.ora file located in the path c:\…\server\NETWORK\ADMIN SANCHO = (DESCRIPTION= (ADDRESS=(PROTOCOL=tcp) (HOST=sancho.csuohio.edu)(PORT=1521) ) (CONNECT_DATA=(SERVICE_NAME=ORCL) ) ) To use this definition with SQL*Plus the csuperson user should enter SQL> connect CSUPERSON/EUCLID@sancho case sensitive 63 See note on: Database Links ORACLE Tools SQL Developer is a sophisticated IDE connected to the Oracle Server. Useful for complex server-side tasks. Originally part of JDeveloper. 64 Making .sql scripts with SQL*Plus How to know what Objects are already there? --myObjects.sql --useful script showing user’s objects column object_name format a40 wrap select from where and order object_name, object_type obj object_name not like 'BIN$%' object_name not like 'SYS_%' by 2, 1 SCOTT SQL> desc obj Name Type ------------------- -------------OBJECT_NAME VARCHAR2(128) SUBOBJECT_NAME VARCHAR2(30) OBJECT_ID NUMBER DATA_OBJECT_ID NUMBER OBJECT_TYPE VARCHAR2(19) CREATED DATE LAST_DDL_TIME DATE TIMESTAMP VARCHAR2(19) STATUS VARCHAR2(7) TEMPORARY VARCHAR2(1) GENERATED VARCHAR2(1) SECONDARY VARCHAR2(1) 65 SQL * Plus Tools for creating Oracle Objects SQL*Plus is a command-line tool that provides access to the Oracle RDBMS. SQL*Plus enables you to: ■ Enter SQL*Plus commands to configure the SQL*Plus environment ■ Startup and shutdown an Oracle database ■ Connect to an Oracle database ■ Enter and execute SQL commands and PL/SQL blocks ■ Format and print query results ■ SQL*Plus is available on several platforms. In addition, it has a web-based user interface, iSQL*Plus. 66 SQL * Plus Basic SQL*Plus Commands Log in to SQL*Plus SQLPLUS [ { username[/passward][@connect_identifier] | / } [ AS { SYSDBA | SYSOPER } ] | /NOLOG ] List help topics available in SQL*Plus HELP [ INDEX | topic ] Execute host commands HOST [ command ] Show SQL*Plus system variables or environment settings SHOW { ALL | ERRORS | USER | system_variable [, system_ variable] ...} Alter SQL*Plus system variables or environment settings SET system_variable value Start up a database STARTUP [ PFILE = filename ] [ MOUNT [ dbname ] | NOMOUNT ] Connect to a database CONNECT [{username[/password] [@connect_identifier] | /} [AS {SYSOPER | SYSDBA} |{proxy_user [ username ] [/password] [@connect_identifier]} ] List column definitions for a table, view, or synonym, or specifications for a function or procedure DESCRIBE [ schema. ] object Edit contents of the SQL buffer or a file EDIT [ filename [ .ext ] ] Get a file and load its contents into the SQL buffer GET filename [ .ext ] [ LIST | NOLLIST ] Save contents of the SQL buffer to a file SAVE filename [ .ext ] [ CREATE | REPLACE | APPEND ] List contents of the SQL buffer LIST [ n | n m | n LAST ] Delete contents of the SQL buffer DEL [ n | n m | n LAST ] Add new lines following current line in the SQL buffer INPUT [ text ] Append text to end of current line in the SQL buffer APPEND text Find and replace first occurrence of a text string in current line of the SQL buffer CHANGE sepchar old [ sepchar [ new [ sepchar ] ] ] sepchar can be any nonalphanumeric ASCII character such as "/" or "!“ 67 SQL * Plus Basic SQL*Plus Commands Capture query results in a file and, optionally, send contents of file to default printer SPOOL [ filename [ .ext ] [ CREATE | REPLACE | APPEND | OFF | OUT ] Run SQL*Plus statements stored in a file @ { url | filename [ .ext ] } [ arg ... ] START { url | filename [ .ext ] } [ arg ... ] ext can be omitted if the filename extension is .sql Execute commands stored in the SQL buffer / List and execute commands stored in the SQL buffer RUN Execute a single PL/SQL statement or run a stored procedure EXECUTE statement Disconnect from a database DISCONNECT Shut down a database SHUTDOWN [ ABORT | IMMEDIATE | NORMAL ] Log out of SQL*Plus { EXIT | QUIT } [ SUCCESS | FAILURE | WARNING ] [ COMMIT | ROLLBACK ] 68 Basic SQL * Plus Commands Remark Set headsep Ttitle Btitle Column Break on Compute sum Set linesize Set pagesize Set newpage Spool /* */ -Set pause Line comments The heading separator is a single char used to split a title into two or more lines Sets the top page-title Sets the bottom page-title Formatting, heading of a column. Put spaces between sections of a report Calculates subtotal Number of characters per line. Nuber of lines per page. Number of blank lines between pages. Re-route report from screen to file. Comment delimiters. (Minus Minus) In-line comment. Stop screen displaying. 69 Basic SQL * Plus Commands Save Host Start @ Edit Define_Editor Saves current SQL query to disk. Send any command to the host operating system. Execute the instructions saved on file Call editor Customize editor selection. NOTE: The editor is not part of Oracle. For instance, use NOTEPAD.EXE in windows. Example Define_editor = “Notepad” Define_editor = “c:\Program Files\Notepad++\notepad++.exe -/sql“ 70 Basic SQL * Plus Commands SET HEADSEP set headsep ! The symbol ! indicates where you want to break a page title or a column heading that runs longer than a line. 71 Basic SQL * Plus Commands COLUMN column column column Item Item Item heading 'What Was!Sold' format a18 truncated • Heading indicates the column’s title. Instead of Item, the column’s name is a two-line header displaying “What was” (line1), “Sold” (line2). • Format indicates the layout of the output value. A18 indicated alphanumeric, 18 characters wide. A999.99 is an example of a numeric format. • Truncated tells the report facility to cut strings longer than 18. Other option is WRAP. 72 Basic SQL * Plus Commands Numeric Format column Rate format 90.99 • Format 90.99 reserves space for a numeric field of at most four digits. • For numbers less than 1 - such as 1/2 - , the output should be printed using a zero rather than a blank. In our example output should be _0.50 instead of _.50 73 Basic SQL * Plus Commands SET PAGESIZE, LINESIZE set linesize 79 set pagesize 50 set newpage 0 • Linesize Total number of characters per line is set to 79. • Pagesize Total number of lines -icluding top/bottom titles- is set to 50. • NewPage Sets the number of blank lines printed of top of the report. 74 Basic SQL * Plus Commands SPOOLING spool c:\activity.lst spool off • Spool <filename> All of the SQL output that normally is delivered to the screen will also be routed to a disk file -in the example Activity.lst. • Spool Off Stops the routing of data to the file. Next output goes to the screen only. • Use the disk file as a common ASCII text file. 75 Basic SQL * Plus Commands HOST $ Pass any valid command to your operating system (WIN, DOS, Unix…) SQL> host copy a:\x.sql c:\y.sql SQL> $ xcopy a:\*.* c:\Junk /s SQL> host mv aaa bbb 76 Basic SQL * Plus Commands Checking the SQL*PLUS Environment Use the SHOW command to see settings SQL> show ttitle SQL> show headsep SQL> show pagesize SQL> show all Reset the different environment variables SQL> SQL> SQL> SQL> ttitle off btitle off clear columns clear breaks Note: Use the ‘column’ command to see column formatting. 77 Basic SQL * Plus Commands How to create SQL scripts – Call your editor: SQL> EDIT myQuery1.sql – Type in the SQL command(s) – File | Close the editing session – Execute the script with the SQL*PLUS command: SQL> @myQuery1 or RUN myQuery1 – Repeat the process if necessary 78 Basic SQL * Plus Commands Example: Login.sql - A script to setup current session set set set set feedback echo verify pagesize OFF OFF ON 50, linesize 120, ServerOutput ON set SqlPrompt "&_user &_connect_identifier>" SELECT * FROM V$VERSION; prompt prompt prompt ============================================== This is my ORACLE 10g (10.2) Xpress Edition ============================================== column F1 format A39, F2 format A39 set feedback ON 79 Basic SQL * Plus Commands Example: Whoami.sql - Exploring current session ------ ----------------------------------------------------------------------Filename: Whoami.sql Purpose: information about your current database session Author: Victor Matos Date: 8-Feb-2008 ----------------------------------------------------------------------- set feedback OFF column global_name format a60 wrap select USER, TO_CHAR(SYSDATE, 'DD MON YYYY HH24:MI:SS') Now, USERENV('Terminal') as Terminal, USERENV('SESSIONID') as "Session ID" from Dual; select * from global_name; set feedback ON 80 Basic SQL * Plus Commands Example: Options.sql - Showing Version and Installed Options ------- ----------------------------------------------------------------------Filename: Options.sql Purpose: Show database version with options intalled/unistalled Date: 8-Feb-2008 Author: Victor Matos ----------------------------------------------------------------------- clear screen set head ON, feedback OFF, verify ON set linesize 100, pagesize 25 column banner format a70 wrap, parameter format a40 wrap, value select banner as Version from format a10 sys.v_$version; select Decode(value,'TRUE','ON', 'OFF from sys.v_$option order by 1, 2; ') Status, parameter set feedback ON 81 Basic SQL * Plus Commands Links to Useful Oracle On-Line Resources http://www.oracle.com/pls/db102/homepage http://www.orafaq.com/scripts/index.htm#GENSQL http://www.dbasupport.com/oracle/ http://www.oracle-base.com/dba/DBACategories.php http://www.oraclepower.com/WebPortal/webportal http://www.oradev.com 82 Basic SQL * Plus Commands Using other editors • NOTEPAD is the Windows default editor. • To change the editor -for instance- to NotePad++ you enter the following command Define_Editor = “c:\Program Files\Notepad++\Notepad++.exe -/sql “ Define_Editor = “vi” __________ Notepad++ is a free GNU source code editor and Notepad replacement, which supports several programming languages, running under the MS Windows environment. 83 Objects reachable from the Oracle Enterprise Manager Tool Oracle Objects Instance Storage Security Memory Parameters All Initialization Parameters Controlfiles Tablespaces Datafiles Rollback Segments Redo Log Groups Archive Logs Temporary Tablespace Groups Users Roles Profiles Schema Tables Indexes Views Synonyms Sequences Database Links Warehouse Packages Package Bodies Procedures Functions Triggers Java Sources Java Classes Array Types Object Types Table Types Cubes OLAP Dimensions Measure Folders Dimensions Materialized Views Materialized View Logs Oracle Objects Objects seen from the Oracle’s JDeveloper and SQLDeveloper Tools ORACLE Objects How to create objects in ORACLE Objects in an ORACLE database are typically created in the following order 1. create database 2. create tablespace(s) 3. create rollback segment (s) 4. create user(s) 5. create table(s) 6. create index 7. create other objects such as: sequence, cluster, procedure, function, package, trigger, link, profile, synonym, schema, snapshot, view, etc. 86 ORACLE Objects 1. CREATE DATABASE Command Creates, initialize, and mounts a database, making it available for general use. The following actions are performed Creates the datafiles for the database Creates the control files Creates the redo log files and establishes the ARCHIVELOG mode. Creates the SYSTEM tablespace Creates the SYSAUX tablespace Creates the data dictionary Sets the character set that stores data in the database Sets the database time zone Mounts and opens the database for use Warning: This command prepares a database for initial use and erases any data currently in the specified files. Only use this command when you understand its ramifications. 87 ORACLE Objects CREATE DATABASE Command (Example 2) CREATE DATABASE myNewDB USER SYS IDENTIFIED BY mypassword1 USER SYSTEM IDENTIFIED BY mypassword2 LOGFILE GROUP 1 ('c:/oradata/mynewdb/redo01.log') SIZE 100M, GROUP 2 ('c:/oradata/mynewdb/redo02.log') SIZE 100M, GROUP 3 ('c:/oradata/mynewdb/redo03.log') SIZE 100M MAXLOGFILES 5 MAXLOGMEMBERS 5 MAXLOGHISTORY 1 MAXDATAFILES 100 MAXINSTANCES 1 CHARACTER SET US7ASCII NATIONAL CHARACTER SET AL16UTF16 CREATE TABLESPACE SYSTEM DATAFILE 'c:/oradata/mynewdb/system01.dbf' CREATE TABLESPACE SYSAUX DATAFILE 'c:/oradata/mynewdb/sysaux01.dbf' CREATE TABLESPACE TEMP DATAFILE 'c:/oradata/mynewdb/temp01.dbf' CREATE TABLESPACE USERS DATAFILE 'c:/oradata/mynewdb/users01.dbf' SIZE 325M REUSE SIZE 325M REUSE SIZE 20M REUSE SIZE 20M REUSE DEFAULT TABLESPACE Users DEFAULT TEMPORARY TABLESPACE Temp 88 ORACLE Objects CREATE DATABASE Command (Example 2) A database is created with the following characteristics: 1. The database is named mynewdb. Its global database name is mynewdb.us.oracle.com. 2. The password for user SYS is mypassword1 and the password for SYSTEM is mypassword2. The two clauses that specify the passwords for SYS and SYSTEM are not mandatory in Express Oracle. However, if you specify either clause, you must specify both clauses. 3. The new database has three redo log files as specified in the LOGFILE clause. MAXLOGFILES, MAXLOGMEMBERS, and MAXLOGHISTORY define limits for the redo log. 4. MAXDATAFILES specifies the maximum number of datafiles that can be opened in the database. This number affects the initial sizing of the control file. 5. MAXINSTANCES specifies that only one instance can have this database mounted and opened. 89 ORACLE Objects CREATE DATABASE Command (Example 2) • The US7ASCII character set is used to store data in this database. • The AL16UTF16 character set is specified as the NATIONAL CHARACTER SET, used to store data in columns specifically defined as NCHAR, NCLOB, or NVARCHAR2. • The SYSTEM tablespace, consisting of the operating system file c:/oradata/mynewdb/ system01.dbf is created as specified by the DATAFILE clause. If a file with that name already exists, it is overwritten. • A SYSAUX tablespace is created, consisting of the operating system file c:/oradata/ mynewdb/sysaux01.dbf as specified in the SYSAUX DATAFILE clause. • The DEFAULT TABLESPACE clause creates and names a default permanent tablespace for this database. • The DEFAULT TEMPORARY TABLESPACE clause creates and names a default temporary tablespace for this database. 90 ORACLE Objects CREATE DATABASE Command (Example 2) Redo log files will not initially be archived, because the ARCHIVELOG clause is not specified in this CREATE DATABASE statement. 1. This is customary during database creation. 2. You can later use an ALTER DATABASE statement to switch to ARCHIVELOG mode. 91 ORACLE Objects CREATE DATABASE Command (Example 3) create table MyTable (col1 varchar2 primary key) tablespace testdata storage (initial 50M next 100M pctincrease 20 minextents 1 maxextents 200); Tracing the growth of the database: Base Original size: After Extend 1: After Extend 2: After Extend 3: After Extend 4: After Extend 5: ... <Extend> Fixed Add Pct% Add [---50---] [---100---] [---10---] [---160---] [---100---] [---32---] [---292---] [---100---] [---58---] [---450---] [---100---] [---90---] [---640---] [---100---] [---128---] [---868---] [---100---] [---173---] Total Size 160MB 292MB 450MB 640MB 868MB 1131MB 92 ORACLE Objects 2.1 How to create a TableSpace CREATE TABLESPACE PayRoll DATAFILE 'C:\ORADATA\PayRoll.dat' SIZE 10M DEFAULT STORAGE ( INITIAL 10K Associate applications (such as PayRoll, NEXT 50K HR, Inventory, CustomerAccounts, etc) to MINEXTENTS 1 individual and separated tablespaces MAXEXTENTS 999 PCTINCREASE 10) ONLINE; 93 ORACLE Objects 2.2 Exploring a TableSpace Asking for the datafiles, and tablespaces in the current database SYSTEM SQL> SELECT file_name, tablespace_name, bytes FROM dba_data_files; FILE_NAME ---------------------------------------C:\ORACLEXE\ORADATA\XE\USERS.DBF C:\ORACLEXE\ORADATA\XE\SYSAUX.DBF C:\ORACLEXE\ORADATA\XE\UNDO.DBF C:\ORACLEXE\ORADATA\XE\SYSTEM.DBF C:\ORADATA\PAYROLL.DAT TABLESPACE_NAME --------------USERS SYSAUX UNDO SYSTEM PAYROLL BYTES ---------104857600 461373440 94371840 356515840 10485760 94 ORACLE Objects 2.3 How to eliminate a tablespace DROP TABLESPACE PayRoll INCLUDING CONTENTS CASCADE CONSTRAINTS; HOST del C:\ORADATA\PayRoll.dat; 95 ORACLE Objects CREATE USER Purpose • Use the CREATE USER statement to create and configure a database user. • A user is an access account through which you can log in to the database. Prerequisites • You must have the CREATE USER system privilege. • To log on to Oracle Database, a user must have the CREATE SESSION system privilege. Example Create user Macarena identified by salsa; Grant connect, resource to Macarena; Note: 1. Macarena’s workarea is not explicitly indicated, therefore she is implicitly associated to the default table space. 2. To eliminate permanently a user issue the command 96 drop user Macarena [Cascade] ORACLE Objects Create User 97 ORACLE Objects Create User SCOTT / TIGER • During the Oracle setup process a demo user SCOTT/TIGER is usually created • The tables visible to scott/tiger are TABLE_NAME -----------------DEPT EMP BONUS SALGRADE DUMMY CUSTOMER ORD ITEM PRODUCT PRICE 98 ORACLE Objects Useful script to CREATE USER --- MAKEUSER Victor Matos 15-Oct-94. (SYSTEM/JEFE) --- ------------------------------------------------------------------------ Supervisor must be connected to the server==> ORCL (or XE for XEdition) --- Use this script to create a single user. The script should be executed --- by the SYSTEM_DBA. It will prompt for LOGIN and PASSWORD values. --- ---------------------------------------------------------------------set verify OFF prompt Creating a single user at XE (<<--database name) prompt accept login_name char PROMPT ' LOGIN_NAME: ' accept password char PROMPT ' PASSWORD: ' drop user &&login_name CASCADE; Change to PAYROLL (if needed) create user &&login_name identified by &&password default tablespace USERS – temporary tablespace SYSAUX quota unlimited on SYSTEM; grant connect, resource, create session, create view to &&login_name identified by &&password; 99 ORACLE Objects Useful script to CREATE USER (cont.) --- Sending a copy of the SCOTT.EMP and SCOTT.DEPT tables to the new user drop drop table &&login_name..EMP; table &&login_name..DEPT; COPY FROM scott/tiger@XE TO &&login_name/&&password@XE CREATE emp USING select * from SCOTT.EMPLOYEE; Change to ORCL (if needed) COMMIT; COPY FROM scott/tiger@XE TO &&login_name/&&password@XE CREATE dept USING select * from SCOTT.DEPARTMENT; COMMIT; --- Verify tables created for new user select table_name, owner from all_tables where owner = UPPER ('&&login_name'); 100 ORACLE Objects Useful script to DROP USER --- DROPUSER Victor Matos (30-JAN-2008). (SYSTEM/JEFE) --- ----------------------------------------------------------------------------------- Must be issued by Supervisor or Creator --- Use this script to eliminate a single user. The script should be executed --- by the SYSTEM_DBA. It will prompt for LOGIN and PASSWORD values. --- --------------------------------------------------------------------------------set verify OFF prompt Dropping a single user at XE (<<--database name) prompt accept login_name char PROMPT ' LOGIN_NAME: ' accept password char PROMPT ' PASSWORD: ' drop user &&login_name CASCADE; 101 ORACLE Objects 3. VIEWS • A view is a tailored presentation of the data contained in one or more tables or other views. • A view takes the output of a query and treats it as a table. • A view can be thought of as a stored query or a virtual table. • You can use views in most places where a table can be used. For example the employees table has several columns and numerous rows of information. If you want users to see only some of these columns or only specific rows, then you can create a view of that table for other users to access. 102 ORACLE Objects VIEWS SCOTT SQL>create view LADIES as select Fname, Lname, Salary, Dno from Employee where sex = 'F'; SCOTT SQL> select * from LADIES; FNAME ---------Alicia Jennifer Joyce LNAME SALARY DNO --------------- ---------- ---------Zelaya 25000 4 Wallace 43000 4 English 25000 5 SCOTT SQL> 103 ORACLE Objects VIEWS are often used to: Provide an additional level of table security by restricting access to a predetermined set of rows or columns of a table. Hide data complexity For example, a single view can be defined with a join, which is a collection of related columns or rows in multiple tables. However, the view hides the fact that this information actually originates from several tables. Simplify statements for the user For example, views allow users to select information from multiple tables without actually knowing how to perform a join. Present the data in a different perspective from that of the base table For example, the columns of a view can be renamed without affecting the tables on which the view is based. Isolate applications from changes in definitions of base tables For example, if a view’s defining query references three columns of a four column table, and a fifth column is added to the table, then the view’s definition is not affected, and all applications using the view are not affected. Express a query that cannot be expressed without using a view For example, a view can be defined that joins a GROUP BY view with a table, or a view can be defined that joins a UNION view with a table. Save complex queries For example, a query can perform extensive calculations with table information. By saving this query as a view, you can perform the calculations each time the view is queried. 104 ORACLE Objects 4. Sequence Generator (Autonumbers) The sequence generator provides a sequential series of numbers. CREATE SEQUENCE customers_ID START WITH 1000 INCREMENT BY 1; • The sequence generator is especially useful in multiuser environments for generating unique sequential numbers without the overhead of disk I/O or transaction locking. •The first reference to customers_ID.nextval returns 1000. • The second returns 1001. • Each subsequent reference will return a value 1 greater than the previous reference. •Any reference to .currval always returns the current value of the sequence 105 ORACLE Objects EXAMPLE. Creating/Using a Sequence CREATE SEQUENCE customers_ID START WITH 1000 INCREMENT BY 1; drop table junk; create table junk (col1 integer, col2 varchar2(20)); insert into junk values (customers_id.nextval, 'AAA'); insert into junk values (customers_id.nextval, 'BBB'); insert into junk values (customers_id.nextval, 'CCC'); select * from junk; select customers_id.CURRVAL from DUAL; CURRVAL -------1002 COL1 COL2 ------ ----1000 AAA 1001 BBB 1002 CCC 106 ORACLE Objects 5. Indexes Indexes are optional structures associated with tables and clusters. You can create indexes on one or more columns of a table to speed SQL statement execution on that table. CREATE INDEX employees_idx1 ON employee (Fname, Lname); CREATE INDEX employees_idx2 ON employee (SSN); Oracle provides several indexing schemes, which provide complementary performance functionality: • B-tree indexes • B-tree cluster indexes • Hash cluster indexes • Bitmap indexes • Bitmap join indexes 107 ORACLE Objects Internal Structure of a B-Tree Index 108 ORACLE Objects CLUSTERS A cluster is a schema object that contains data from one or more tables, all of which have one or more columns in common. For better performance Oracle stores together all the rows from all the tables that share the same cluster key. In the example Dept# is used to create an articulation point between EMPLOYEE and DEPARTMENT tables. 109 ORACLE Objects --Create Cluster area based on common Dept# key CREATE CLUSTER EmpDeptCluster (Department INTEGER) SIZE 512 STORAGE (initial 100K next 50K); --Create an Index for the Cluster CREATE INDEX idx_EmpDeptCluster ON CLUSTER EmpDeptCluster ; --Adding Tables to the Cluster CREATE TABLE departmentC CLUSTER EmpDeptCluster (Dnumber) AS SELECT * FROM department; Joining field(s) Dnumber = Dno CREATE TABLE employeeC CLUSTER EmpDeptCluster (Dno) AS SELECT * FROM employee; 110 ORACLE Objects CLUSTERS make efficient computation of JOIN operations relating tables tied by a common index. SELECT fname, dno, dnumber, dname FROM employeeC e JOIN departmentC d ON e.dno = d.dnumber; FNAME DNO DNUMBER DNAME ---------- ---------- ---------- --------------James 1 1 Headquarters Alicia 4 4 Administration Jennifer 4 4 Administration Ahmad 4 4 Administration John 5 5 Research Franklin 5 5 Research Ramesh 5 5 Research Joyce 5 5 Research drop drop drop drop INDEX idx_EmpDeptCluster; TABLE deptartmentC; TABLE employeeC; CLUSTER EmpDeptCluster ; 111 ORACLE Objects Hash Clusters Hashing is a technique for storing table data to improve the performance of data retrieval. To use hashing, create a hash cluster and load tables into the cluster. Oracle physically stores the rows of a table in a hash cluster and retrieves them according to the results of a hash function. Hash clusters are a better choice than using an indexed table or index cluster when a table is queried frequently with equality queries (for example, return all rows for department 10). 112 ORACLE Objects Hash Clusters (cont.) Oracle uses a hash function to generate a distribution of numeric values, called hash values, which are based on specific cluster key values. The key of a hash cluster, like the key of an index cluster, can be a single column or composite key (multiple column key). To find or store a row in a hash cluster, Oracle applies the hash function to the row's cluster key value. The resulting hash value corresponds to a data block in the cluster, which Oracle then reads or writes on behalf of the issued statement. 113 ORACLE Objects Hash Clusters (cont.) drop table demoemp; Creating a cluster drop cluster demo_cluster; CREATE CLUSTER demo_cluster (DNO NUMBER(3)) TABLESPACE users STORAGE (INITIAL 250K MINEXTENTS 1 NEXT 50K MAXEXTENTS 3 PCTINCREASE 0) HASH IS Mod(DNO,10) HASHKEYS 10; CREATE TABLE DemoEmp( SSN NUMBER(10) PRIMARY KEY, Fname varchar2(20), Lname varchar2(20), Dno NUMBER(3) ) CLUSTER demo_cluster (DNO); 114 ORACLE Objects Hash Clusters (cont.) Using a Cluster insert into demoEmp (ssn, fname, lname, DNO) select ssn, fname, lname, DNO from employee; select * from demoEmp where dno = 5; Demo_Cluster 0: 1: PointerTo (1) 2: Key = Employee.dno 3: Loc = MOD(dno,10) 4: PointerTo (2,3,4) 5: PointerTo (5,6,7,8) 6: 7: 8: 9: 115 ORACLE Objects Bitmap Indexes In a bitmap index, a binary string (or bitmap) is made to represent the actual key value held by each record. Each bit in the bitmap corresponds to a possible rowid. If the bit is set, then it means that the row with the corresponding rowid contains the key value. Bitmap indexing efficiently merges indexes that correspond to several conditions in a WHERE clause. Rows that satisfy some, but not all, conditions are filtered out before the table itself is accessed. This improves response time, often dramatically. 116 ORACLE Objects Bitmap Indexes Evaluate SELECT COUNT(*) FROM CUSTOMER WHERE MARITAL_STATUS = 'married' AND REGION IN ('central','west'); 117 ORACLE Objects Bitmap Index Example Create a bitmap on Department values for the Employee table. CREATE BITMAP INDEX idx_Bmp_Employee ON Employee(dno) TABLESPACE Users; 118 Using SQL * Plus to create Objects Create User Command Creates a database user, or an account through which you can log in to the database. You can optionally assign the following properties to the user: • default tablespace • temporary tablespace • quotas for allocating space in tablespaces • profile containing resource limits Example: You can create the user MARIA by issuing the following statement: CREATE USER Maria IDENTIFIED BY Macarena DEFAULT TABLESPACE payroll_ts QUOTA 10M ON payroll_ts QUOTA 5M ON temp_ts QUOTA 5M ON system PROFILE accountant; Grant Connect, Resource to Maria; 119 Using SQL * Plus to create Objects CREATE USER Command 120 ORACLE Objects PROFILE A profile is used to set user limits on database resources. If you assign the profile to a user, then that user cannot exceed these limits. CREATE PROFILE CONSULTANT LIMIT SESSIONS_PER_USER CPU_PER_SESSION CPU_PER_CALL CONNECT_TIME LOGICAL_READS_PER_SESSION LOGICAL_READS_PER_CALL PRIVATE_SGA COMPOSITE_LIMIT UNLIMITED UNLIMITED 3000 45 DEFAULT 1000 15K 5000000; CREATE USER Maria IDENTIFIED BY Macarena DEFAULT TABLESPACE example QUOTA 10M ON example QUOTA 5M ON system TEMPORARY TABLESPACE temp PROFILE consultant PASSWORD EXPIRE; 121 Using SQL * Plus to create Objects 6. TABLES • • • Tables are the basic unit of data storage in an Oracle database. Data is stored in rows and columns. Typical datatypes are: CHAR, VARCHAR2, NUMBER, DATE • Each row has a unique (invisible) database-wide identifier called RowID • Each row has a unique serial (invisible) autonumber called RowNum. A row is a collection of column information corresponding to a single record. You can optionally specify rules for each column of a table. These rules are called integrity constraints. Example: NOT NULL, PRIMARY KEY, CHECK… Once you create a table, you insert rows of data using SQL statements. Table data can then be queried, deleted, or updated using SQL. 122 Using SQL * Plus to create Objects 6. Record Identifiers CSUPERSON SQL> SELECT rownum, rowid, p.* FROM Project p; NOTE • The fields RowNum and RowID must be explicitly requested in a query. • RowNum changes when rows are inserted/deleted. • However RowID is permanently attached to each row until the record is deleted. 123 Using SQL * Plus to create Objects CREATE TABLE Command To define the EMP2 table you could issue the following statement. CREATE TABLE emp2 ( empno NUMBER CONSTRAINT pk_emp PRIMARY KEY, ename VARCHAR2(10) CONSTRAINT nn_ename NOT NULL CONSTRAINT upper_ename CHECK (ename = UPPER(ename)), CONSTRAINT unique_ename UNIQUE(ename), job VARCHAR2(9), mgr NUMBER CONSTRAINT fk_mgr REFERENCES emp2(empno), hiredate DATE DEFAULT SYSDATE, sal NUMBER(10,2) CONSTRAINT ck_sal CHECK (sal > 500), comm NUMBER(9,0) DEFAULT NULL, deptno NUMBER(2) DEFAULT 20 CONSTRAINT nn_deptno NOT NULL CONSTRAINT fk_deptno REFERENCES dept(deptno) ) PCTFREE 5 124 PCTUSED 75 ; Using SQL * Plus to create Objects Create Table Command Notes 1- This table contains 8 columns. Ex: EMPNO column is of datatype NUMBER and has an associated integrity constraint named PK_EMP. 2- HIREDDATE column is of datatype DATE and has a default value of SYSDATE. 3- PCTFREE of 5 indicates that a minimum 5% of space is reserved for future updates 4- PCTUSED says that a maximum of 75% of the table should be used on each data block 5- The constraint on the mgr field indicates that such a value (mgr’s id) must also be found in the emp2 table (a recursive reference) 125 Using SQL * Plus to create Objects Create Table Command Notes 1. The PCTFREE parameter sets the minimum percentage of a data block to be reserved as free space for possible updates to rows that already exist in that block. 2. The PCTUSED parameter sets the maximum percentage of a block that can be used for row data plus overhead. After a data block is filled to the limit determined by PCTFREE, Oracle considers the block unavailable for the insertion of new rows until the percentage of that block falls beneath the parameter PCTUSED. Until this value is achieved, Oracle uses the free space of the data block only for updates to rows already contained in the data block. . 126 Using SQL * Plus to create Objects Create Table Command. Typical data types Data Type Use CHAR Alpha-numeric, Fixed data length Alphanumeric, variable-length Numeric data: Integers an float Date data such as: ‘01-APR-97’ VARCHAR2 NUMBER DATE 127 Using SQL * Plus to create Objects DEFINING A COMPOUND KEY CREATE TABLE works_on ( essn char(9), pno number, hours number(5,1), PRIMARY KEY (essn, pno), FOREIGN KEY (Essn) REFERENCES Employee(Ssn), FOREIGN KEY (Pno) REFERENCES Project(Pnumber)) NOTE. You may also use the ALTER TABLE command. ALTER TABLE Works_On ADD PRIMARY KEY (essn, pno); ALTER TABLE Works_On ADD CONSTRAINT pk_works_on PRIMARY KEY (essn, pno); 128 Using SQL * Plus to create Objects Create Table Command. Example II. Assuming you have the parallel query option, then the fastest method to create a table that has the same columns as the EMP table, but only for those employees in department 10, is to issue a command similar to the following: CREATE TABLE emp_tmp UNRECOVERABLE PARALLEL (DEGREE 3) AS SELECT * FROM employee WHERE dno = 5; Notes 1- The UNRECOVERABLE keyword speeds up table creation because there is no overhead in generating and logging redo information. 2- Parallelism speeds up the creation of the table. 3- After the table is created, querying the table is also faster because the same degree of parallelism is used to access the table. 129 Using SQL * Plus to create Objects Database Links A database link is a schema object in one database that enables you to access objects on another database. Named Link: You specify the username and password used to connect to the remote database. drop database link SanchoLink; create database link SANCHOLINK connect to csuperson identified by euclid using '(DESCRIPTION= (ADDRESS=(PROTOCOL=tcp) (HOST=sancho.csuohio.edu)(PORT=1521) ) (CONNECT_DATA=(SERVICE_NAME=ORCL) ) )' ; Anonymous Link: The current session’s username/password is used to enter in the remote database. drop database link SanchoLink; create database link SANCHOLINK using 'sancho‘; Note: In this example ‘SANCHO’ is an entry already defined in the control file tnsnames.ora. Example: select * from employee@SanchoLink; CAUTION: Credentials for Oracle11g are case sensitive! If your account in the remote computer is 130 CSUPERSON/EUCLID you must log in the current with exactly the same userName/password. Using SQL * Plus to create Objects Summary – What objects do I have? ----- AUTHOR V. Matos 8-Feb-2008 FILE obj.sql PURPOSE List all objects accessible to the user. ----------------------------------------------------------------------- set verify OFF, feedback OFF column owner format a12, object_name format a35 wrap, object_type format a12 clear screen select o.owner ,o.object_name ,o.object_type from all_objects o where o.owner like upper (nvl('&v_Owner',user)) and o.object_name like upper ('%&v_Object_Name%') and o.object_name not like 'BIN$%' and o.object_name not like 'SYS_%' and o.object_type like upper ('%&v_Object_Type%') order by o.owner ,o.object_type ,o.object_name; set feedback on 131 PL/SQL PROGRAMMING 132 ORACLE: PL/SQL What is PL/SQL ? • PL/SQL is a portable, high-performance transaction processing language.PL/SQL stands for Procedural extensions to SQL. • It adds programming capabilities to Oracle-SQL. It is a block-structured language with a syntax similar to classical programming languages such as C, ADA, O-Pascal. • It is not a stand-alone language (requires Oracle DBMS server) 133 PL/SQL Programming PL/SQL = Oracle SQL1 + Standard programming features Support for typical programming features including • • • • • • • • • • • procedures, functions, flow-control/iterative statements, declared variables, constants, primitive and user-defined types, exception handling, triggers, packages, objects, collections, integration of SQL statements interaction with the local OS, anonymous blocks, etc. Notes 1. Oracle SQL is a superset of the American National Standards Institute (ANSI) and the International Standards Organization (ISO) standard. 2. Anonymous blocks are nameless fragments of code executed from script files 134 PL/SQL Architecture CLIENT SIDE CODE • .NET languages Java C, C++ COBOL MS-Access Delphi PowerBuilder People’s Soft SAP HTML + scripts (JavaScript, Perl, VBS) Oracle Forms/Reports ORACLE SERVER CODE ODBC .NET JDBC Native Comm PL-SQL (Oracle 7, 8, 9i, 10g…) Java (Oracle8i, …) ... 135 PL/SQL Programming Advantages of PL/SQL ■ Tight Integration with SQL ■ Thin client programming model with business logic on the server ■ Better enforcement of business rules, and standards ■ Better Performance (however creates bottleneck syndrome) ■ Higher Productivity ■ Full Portability ■ Tight Security ■ Access to Pre-defined Packages ■ Support for Object-Oriented Programming ■ Support for Developing Web Applications and Pages 136 PL/SQL Programming Block-Structure Declaration Section Execution Section Exception Section 137 PL/SQL Programming Block-Structure Procedure / Function / Trigger ObjectName (Arguments) …. IS Variable declaration; ... BEGIN Code to execute; ... EXCEPTION Error handling code; ... END; 138 PL/SQL Programming Structure of an ANONYMOUS block DECLARE Variable declaration; ... BEGIN Code to execute; ... EXCEPTION Error handling code; ... END; -- Smallest Anonymous block. DECLARE BEGIN Null; END; 139 PL/SQL Programming PL/SQL Syntax Assignment Null statement Blocks x := x + 1; Null; Begin ... End; Condition Testing if (condition) then [ elsif (condition) then ] [ else ] end if; Loops For v in -1..10 Loop ... End Loop; While (condition) Loop ... End Loop; Loop ... End Loop Conditional op. < ,<>, >, <= , >= , = and, or, not Logical op. 140 PL/SQL: Example-1 1. Use SQL*Plus. At the Oracle prompt enter the following line: csuperson> Edit c:\sql\hello create or replace Procedure HELLO Is Begin -- my first PL/SQL program Dbms_Output.Put_Line ('Hello wonderful world'); End; Note Make sure your DBMS_OUTPUT environment variable has been set. Invoke SQL*Plus and enter: SET SERVEROUTPUT ON 141 PL/SQL: Example-1 TESTING THE PROGRAM … CSUPERSON SQL> set serverOutput on CSUPERSON SQL> exec hello Hello world PL/SQL procedure successfully completed. CSUPERSON SQL> HINT You may add the statement set serverOutput on to your BIN\login.sql script 142 PL/SQL: Example-2 A function to retrieve the employee’s full name create or replace FUNCTION getName ( theSSN IS theTotal NUMBER; theName VARCHAR2(40); IN NUMBER ) RETURN VARCHAR2 BEGIN select count(*) into theTotal from employee where SSN = theSSN; if (theTotal = 0) then RETURN(''); else select (FName || ' ' || Lname) into theName from employee where SSN = theSSN; RETURN (theName); end if; EXCEPTION when others then return ('***ERROR***'); END; 143 PL/SQL: Example-2 1- Testing the function CSUPERSON SQL> variable xxx varchar2(40); CSUPERSON SQL> exec :xxx := getName(123456789); PL/SQL procedure successfully completed. CSUPERSON SQL> print xxx XXX -------------------------------------------John Smith CSUPERSON SQL> 144 PL/SQL: Example-2 2- Testing the function SELECT getName(123456789) FROM DUAL; GETNAME(123456789) ------------------------------------John Smith 145 PL/SQL: Example-2 2- Creating a Procedure create or replace PROCEDURE pGetName ( theSSN IN NUMBER, theName OUT VARCHAR2 ) IS theTotal NUMBER; BEGIN select count(*) into theTotal from employee where SSN = theSSN; if (theTotal = 0) then theName := 'Nada---'; else select (FName || ' ' || Lname) into theName from employee where SSN = theSSN; end if; EXCEPTION when others then theName := '***ERROR***'; END; 146 PL/SQL: Example-2 2- Testing a Procedure CSUPERSON SQL> variable xxx varchar2(40); CSUPERSON SQL> exec pGetName(123456789, :xxx); PL/SQL procedure successfully completed. CSUPERSON SQL> print xxx XXX ----------------------------------------------John Smith 147 PL/SQL: Example-2 2- Testing a Procedure (again) -- Anonymous block: Testing pGetName(…) DECLARE THESSN NUMBER; THENAME VARCHAR2(200); BEGIN THESSN := 123456789; PGETNAME ( THESSN, THENAME ); DBMS_OUTPUT.PUT_LINE('THENAME = ' || THENAME); END; 148 PL/SQL: Example-3 3- Example of Procedure -- Goal: -- Author: -- Date: Find an employee’s salary as well as his/her direct supervisor’s SSN and salary V. Matos 20-Feb-2008 create or replace PROCEDURE PGETEMPSUPERSALARIES ( eSSN IN VARCHAR2 , eSalary OUT NUMBER , sSSN OUT VARCHAR2 , sSalary OUT NUMBER ) AS vCounter Number; BEGIN --set default values for output variables eSalary := 0; sSSN := '***'; sSalary := 0; --is there such an employee? select count(*) into vCounter from employee where ssn = eSSN; --if not just exit now if (vCounter = 0) then return; end if; 149 PL/SQL: Example-3 3- Example of Procedure cont… --get the employee's salary and supervisor's ssn select salary, superSsn into eSalary, sSSN from employee where ssn = eSSN; --if the employee has no supervisor the routine is terminated if (sSSN is NULL) then return; end if; --is there such an employee acting as a supervisor? select count(*) into vCounter from employee where ssn = sSSN; --if not just exit now if (vCounter = 0) then return; end if; --get the supervisor's salary select salary into sSalary from employee where ssn = sSSN; --in case of troubles do nothing EXCEPTION when others then return; END PGETEMPSUPERSALARIES; 150 PL/SQL: Example-3 3- Testing the Procedure cont… --get the employee's salary and supervisor's ssn and salary DECLARE ESSN VARCHAR2(200); --employee’s SSN and salary ESALARY NUMBER; SSSN VARCHAR2(200); -- supervisor’s SSB and salary SSALARY NUMBER; BEGIN ESSN := 123456789; PGetEmpSuperSalaries(ESSN, ESALARY, SSSN, SSALARY ); DBMS_OUTPUT.PUT_LINE('ESALARY = ' || ESALARY); DBMS_OUTPUT.PUT_LINE('SSSN = ' || SSSN); DBMS_OUTPUT.PUT_LINE('SSALARY = ' || SSALARY); END; 151 PL/SQL Programming Creating/Debugging a Stored PL/SQL Function This example shows how to create an debug a PL/SQL stored function using SQL*Plus SQL> EDIT c:\sql\IsFile.sql CREATE or REPLACE FUNCTION IsFile ( MyTable IN VARCHAR2 ) RETURN ( boolean ) IS result user_tables.table_name%TYPE; BEGIN SELECT table_name INTO result Intentional FROM user_tables error WHERE table_name = UPPER(MyTable); RETURN true; EXCEPTION WHEN No_Data_Found THEN RETURN false; END; / 152 PL/SQL Programming Creating/Debugging a Stored PL/SQL Function SQL> @c:\sql\isfile Warning: Function created with compilation errors. SQL> show errors Errors for FUNCTION ISFILE: Line and column where error was found LINE/COL ERROR -------- -------------------------------------------------1/47 PLS-00103: Encountered the symbol "(" when expecting one of the following:<an identifier> <a double-quoted delimited-identifier> LONG_ double SQL> EDIT c:\sql\IsFile.sql Use the editor to correct each error in the script file 153 PL/SQL Programming Testing /Debugging a Stored PL/SQL Function Using an Anonymous Block --Assume all errors of IsFile(…) are corrected DECLARE myResult boolean; BEGIN myResult := IsFile('Employee'); if (myResult) then dbms_output.put_line('YES!'); else dbms_output.put_line('NO'); end if; END; / 154 PL/SQL Programming Anonymous Procedures The following ANONYMOUS performs the following tasks: • executes stored proc. GetEmpNameSal to obtain the name and salary of employee whose SSN is 123456789. Results are saved into the table MYLOG (predefined) • invokes the function RaiseSalary to increase the employee’s salary by 10%. Again a new line is added to MYLOG reporting the new salary value. ----- Name: Test.Sql Anonymous Procedure to Test STORED routines Uses: proc. GetEmpName, func. RaiseSalary table Mylog (already created with two columns) DECLARE EmpName varchar(20); mSalary number; BEGIN DELETE FROM Mylog; Local memory variables defined to store the employee’s name and salary retrieved using the stored procedure GetEmpNameSal (123456789, EmpName, mSalary); INSERT INTO MyLog VALUES (mSalary, EmpName); mSalary:= RaiseSalary(123456789, 0.10); INSERT INTO MyLog VALUES (mSalary, EmpName); END; 155 PL/SQL Programming Testing the Anonymous Procedure Running the anonymous script SQL> @c:\sql\TEST procedure successfully executed SQL> select * from MYLOG; COL1 --------30000 33000 COL2 ----------------Smith, John Smith, John Results are placed into table MYLOG 156 PL/SQL Programming PROCEDURE returning several results -- GETEMPNAMESAL CREATE or REPLACE PROCEDURE GetEmpNameSal ( EmpSSN IN number, EmpName OUT varchar, EmpSalary OUT number ) IS BEGIN SELECT Lname ||', '|| Fname, Salary INTO EmpName, EmpSalary FROM Employee OUT variables carry results WHERE SSN = EmpSSN; outside of the procedure EXCEPTION WHEN No_Data_Found THEN INSERT INTO MyLog VALUES (EmpSSN, 'ERROR. Emp not found'); EmpName:= '***'; EmpSalary:= -1; END; 157 PL/SQL Programming -- RaiseSalary: Function increases salary of certain employee by PctRaise CREATE or REPLACE FUNCTION RaiseSalary ( EmpSsn number, PctRaise number ) RETURN number IS Default type is IN (put) NewSalary number; OldSalary number; BEGIN SELECT Salary INTO OldSalary FROM Employee WHERE SSN = EmpSSN; NewSalary:= OldSalary * (1 + PctRaise); UPDATE Employee SET Salary = NewSalary WHERE SSN = EmpSsn; RETURN NewSalary; Returned value EXCEPTION WHEN No_Data_Found THEN INSERT INTO MyLog VALUES (EmpSSN, 'Not found'); RETURN -1; 158 END; PL/SQL Programming NOTE l We assume the general purpose table MYLOG (Col1, col2) has already been defined. Drop table MyLog; Create Table MyLog ( Col1 varchar2(30), Col2 varchar2(30) ); 159 PL/SQL Programming Anonymous Procedures The following ANONYMOUS performs the following tasks: • executes stored proc. GetEmpNameSal to obtain the name and salary of employee whose SSN is 123456789. Results are saved into the table MYLOG (predefined) • invokes the function RaiseSalary to increase the employee’s salary by 10%. Again a new line is added to MYLOG reporting the new salary value. ----- Name: Test.Sql Anonymous Procedure to Test STORED routines Uses: proc. GetEmpName, func. RaiseSalary table Mylog (already created with two columns) DECLARE EmpName varchar(20); mSalary number; BEGIN DELETE FROM Mylog; Local memory variables defined to store the employee’s name and salary retrieved using the stored procedure GetEmpNameSal (123456789, EmpName, mSalary); INSERT INTO MyLog VALUES (mSalary, EmpName); mSalary:= RaiseSalary(123456789, 0.10); INSERT INTO MyLog VALUES (mSalary, EmpName); END; 160 160 PL/SQL Programming -- Version2: Testing Function using anonymous procedure -- multiple parameters are referenced using named vars. => Notation (name, value)-pairs DECLARE allows passing of parameters in EMPSSN NUMBER; any order PCTRAISE NUMBER; v_Return NUMBER; BEGIN EMPSSN := 123456789; PCTRAISE := 10; v_Return := RAISESALARY(EMPSSN => EMPSSN, PCTRAISE => PCTRAISE ); DBMS_OUTPUT.PUT_LINE('v_Return = ' || v_Return); END; 161 PL/SQL Programming A Guide for Learning PL/SQL • • • • • • • • • • • • • Delimiters Identifiers Reserved words Quoted Identifiers Literals Booleans DateTime Variable Declaration Built-In DataTypes Scope and Visibility Assignments Precedence of Operators Logical Operators • • • • • Comparison Operators NULLs Built-In Functions DateTime Flow Control Structures 162 PL/SQL Programming Identifiers An identifier consists of a letter optionally followed by more letters, numerals, dollar signs, underscores, and number signs. Other characters such as hyphens, slashes, and spaces are not allowed (max. 30 chars). Example: The following are VALID identifiers custName, Cust_name, Cust_Number, CUST001, CUST#, CUST-NUMBER, CustBalance$ Quoted Identifiers PL/SQL lets you enclose identifiers within double quotes (not a very good idea!). Quoted identifiers are seldom needed. They can contain any sequence of printable characters including spaces but excluding double quotes. Example: The following identifiers are valid: "X+Y" “Last Name” (similar to [Last Name] notation used in MS-Access, SQL-Server) 163 PL/SQL Programming Reserved Words Some identifiers, called reserved words, have a special syntactic meaning to PL/SQL. The following are reserved words BEGIN, END, LOOP, IF, PROCEDURE, THEN, ELSE, ELSIF, FUNCTION, DECLARE, TRIGGER, … Often, reserved words are written in upper case for readability. 164 PL/SQL Programming Literals A literal is an explicit numeric, character, string, or Boolean value not represented by an identifier. Example: 123, 3.141592, 5E3 (same as 5000), ‘ABC’, ‘XYZ Corp.’, ‘Smith’, ‘SMITH’, ‘smith’, ‘’. BOOLEAN Literals BOOLEAN literals are the predefined values TRUE, FALSE, and NULL (NULL stands for a missing, unknown, or inapplicable value). Datetime Literals Datetime literals have various formats depending on the datatype. Example: '1998-12-25'; '1997-10-22 13:01:01'; ’19-Sep-2006’ Comments Single-line comments begin with a double hyphen (--) anywhere on a line and extend to the end of the line.Multi-line comments begin with a slash-asterisk (/*), end with an asterisk-slash (*/), and can span multiple lines. 165 PL/SQL Programming Defining/Initializing Variables Salary DeptNumber LastName Sex JobType BirthDate Age PI NUMBER(10,2); NUMBER (3) := 17; VARCHAR2(20); CHAR(1) := ‘F’; CHAR(15); DATE := ‘15-APR-96’; SMALLINT; REAL := 3.141592 Honorary EmpRec Employee.Salary%TYPE; Employee%ROWTYPE; Note. Most common data types are: NUMBER, VARCHAR2, DATE, CHAR. 166 PL/SQL Programming Built-in Datatypes 167 PL/SQL Programming DECLARE a CHAR; b REAL; BEGIN -- identifiers available here: a (CHAR), b DECLARE a INTEGER; c REAL; BEGIN -- identifiers available here: a (INTEGER), b, c NULL; END; DECLARE d REAL; BEGIN -- identifiers available here: a (CHAR), b, d NULL; END; -- identifiers available here: a (CHAR), b END; / 168 PL/SQL Programming Assigning Values to Variables You can use assignment statements to assign values to variables using the := operator. bonus := salary * 0.15; • Variables and constants are initialized every time a block or subprogram is entered. • By default, variables are initialized to NULL. • Unless you expressly initialize a variable, its value is undefined (NULL) Example DECLARE counter INTEGER; BEGIN -- COUNTER is initially NULL, so 'COUNTER + 1' is also null. counter := counter + 1; IF counter IS NULL THEN DBMS_OUTPUT.PUT_LINE('COUNTER is NULL not 1.'); END IF; END; / Note. To avoid unexpected results, never reference a variable before you assign it a value. 169 PL/SQL Programming Operator Precedence 170 PL/SQL Programming Logical Operators The logical operators are: AND, OR, and NOT. Comparison Operators IF (variable IS NULL) THEN ... IF (last_name LIKE 'J%S_N‘) THEN ... --( % wild char any symbol(s), _ one char. This example finds values like Johnson, Jensen, …) IF appointment BETWEEN 900 AND 1700 THEN ... IF letter IN (‘A’, ‘b’, ‘m’) THEN ... 171 PL/SQL Programming NULLs as Arguments to Built-In Functions If a null argument is passed to a built-in function, a null is returned except in the case of the NVL and DECODE functions. The function DECODE compares its first argument to one or more search expressions, which are paired with result expressions. Any search or result expression can be null. DECODE(superSsn, NULL, 'nobody', 'somebody') DECODE(commission,NULL,0,Commission) The function NVL returns the value of its second argument if its first argument is null. NVL(superSsn, 'Nobody') NVL(commission, 0) 172 PL/SQL Programming NULLs as Arguments to Built-In Functions Using Decode select ssn, decode(superSsn, null, ‘N.A.’, superSsn) from employee; SSN --------123456789 333445555 999887777 987654321 666884444 453453453 987987987 888665555 DECODE(SU --------333445555 888665555 987654321 888665555 333445555 333445555 987654321 N.A. 173 PL/SQL Programming NULLs as Arguments to Built-In Functions Using Decode select e.ssn, decode(superssn,null, 'NA', (select lname from employee where ssn=e.superssn)) from employee e; SSN --------123456789 333445555 999887777 987654321 666884444 453453453 987987987 888665555 DECODE(SUPERSSN --------------Wong Borg Wallace Borg Wong Wong Wallace NA 174 PL/SQL Built-in Functions 175 PL/SQL Programming Character functions Character functions that return character values are: CHR CONCAT INITCAP LOWER LPAD LTRIM NLS_INITCAP NLS_LOWER NLSSORT NLS_UPPER REGEXP_REPLACE REGEXP_SUBSTR REPLACE RPAD RTRIM SOUNDEX SUBSTR TRANSLATE TREAT TRIM UPPER Character functions that return number values are: ASCII INSTR LENGTH REGEXP_INSTR 176 PL/SQL Programming Character functions -- testing primitive string functions DECLARE v1 varchar(100) := 'Hola mundo'; BEGIN dbms_output.put_line (LENGTH(v1)); dbms_output.put_line (SUBSTR(v1,2,3)); dbms_output.put_line (INSTR(v1, 'ola')); dbms_output.put_line (REPLACE(v1, 'mundo', 'world')); dbms_output.put_line (UPPER(v1)); END; / 10 ola 2 Hola world HOLA MUNDO 177 PL/SQL Programming The datetime functions are: ADD_MONTHS CURRENT_DATE CURRENT_TIMESTAMP DBTIMEZONE EXTRACT (datetime) FROM_TZ LAST_DAY LOCALTIMESTAMP MONTHS_BETWEEN NEW_TIME NEXT_DAY NUMTODSINTERVAL NUMTOYMINTERVAL ROUND (date) SESSIONTIMEZONE SYS_EXTRACT_UTC SYSDATE SYSTIMESTAMP TO_CHAR (datetime) TO_TIMESTAMP TO_TIMESTAMP_TZ TO_DSINTERVAL TO_YMINTERVAL TRUNC (date) TZ_OFFSET 178 PL/SQL Programming Example of datetime functions -- testing primitive date functions DECLARE v0 date := sysdate; v1 timeStamp:= sysdate; BEGIN dbms_output.put_line dbms_output.put_line dbms_output.put_line dbms_output.put_line dbms_output.put_line dbms_output.put_line dbms_output.put_line dbms_output.put_line dbms_output.put_line dbms_output.put_line END; / (v0); (v1); (TRUNC(v1)); (NEXT_DAY(v1, 'Friday')); (LAST_DAY(v1)); (ADD_MONTHS(v1, 3)); (MONTHS_BETWEEN(v1, TO_DATE('01-01-2000','MM-DD-YYYY'))); (TO_CHAR(v1,'J')); ( TO_DATE(2454145,'J')); 25-FEB-08 (SESSIONTIMEZONE); 25-FEB-08 12.08.46.000000 AM 25-FEB-08 29-FEB-08 29-FEB-08 25-MAY-08 97.77438993428912783751493428912783751493 2454522 13-FEB-07 -05:00 Consult: http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements004.htm#sthref408 179 PL/SQL Programming Example of date formatting -- testing primitive date formatting(cont.) DECLARE v0 date := sysdate; v1 timeStamp:= sysdate; BEGIN dbms_output.put_line ('Timestamp ' || v1 ); dbms_output.put_line ('Date ' || v0 ); dbms_output.put_line ('DL ' || to_char(v0, dbms_output.put_line ('DS ' || to_char(v0, dbms_output.put_line ('D ' || to_char(v0, dbms_output.put_line ('DD ' || to_char(v0, dbms_output.put_line ('DDD ' || to_char(v0, dbms_output.put_line ('DAY ' || to_char(v0, dbms_output.put_line ('DY ' || to_char(v0, dbms_output.put_line ('day ' || to_char(v0, dbms_output.put_line ('MM ' || to_char(v0, dbms_output.put_line ('MONTH ' || to_char(v0, dbms_output.put_line ('YY ' || to_char(v0, dbms_output.put_line ('YYYY ' || to_char(v0, dbms_output.put_line ('Y,YYY ' || to_char(v0, dbms_output.put_line ('YEAR ' || to_char(v0, dbms_output.put_line ('HH ' || to_char(v0, dbms_output.put_line ('HH AM ' || to_char(v0, dbms_output.put_line ('HH24 ' || to_char(v0, dbms_output.put_line ('MI ' || to_char(v0, dbms_output.put_line ('SS ' || to_char(v0, dbms_output.put_line ('SSSSS ' || to_char(v0, dbms_output.put_line ('W ' || to_char(v0, dbms_output.put_line ('WW ' || to_char(v0, dbms_output.put_line ('Q ' || to_char(v0, END; Timestamp 25-FEB-08 12.07.17.000000 AM Date 25-FEB-08 DL Monday, February 25, 2008 DS 2/25/2008 D 2 DD 25 DDD 056 DAY MONDAY DY MON day monday MM 02 MONTH February YY 08 YYYY 2008 Y,YYY 2,008 YEAR TWO THOUSAND EIGHT HH 12 HH AM 12 AM HH24 00 MI 07 SS 17 SSSSS 00437 sec midnight W 4 week of month WW 08 week of year Q 1 quarter 'DL')); 'DS')); 'D')); 'DD')); 'DDD')); 'DAY')); 'DY')); 'day')); 'MM')); 'Month')); 'YY')); 'YYYY')); 'Y,YYY')); 'YEAR')); 'HH')); 'HH AM')); 'HH24')); 'MI')); 'SS')); 'SSSSS') || ' sec midnight'); 'W') || ' week of month'); 'WW') || ' week of year'); 'Q') || ' quarter'); 180 PL/SQL Programming Date Format Element Suffixes Suffix TH SP SPTH or THSP Meaning Ordinal Number Spelled Number Spelled, ordinal number Example Element Example Value DDTH 4TH DDSP FOUR DDSPTH FOURTH Example of date formatting (cont.) -- testing primitive date formatting (cont.) DECLARE V0 date := sysdate; BEGIN dbms_output.put_line (v0); dbms_output.put_line ('DD ' || to_char(v0, dbms_output.put_line ('DDTH ' || to_char(v0, dbms_output.put_line ('DDSP ' || to_char(v0, dbms_output.put_line ('DDTHSP ' || to_char(v0, END; / 'DD')); 'DDTH')); 'DDSP')); 'DDTHSP')); 25-FEB-08 DD 25 DDTH 25TH DDSP TWENTY-FIVE DDTHSP TWENTY-FIFTH 181 PL/SQL Programming Conversion Functions Conversion functions convert a value from one datatype to another. Generally, the form of the function names follows the convention datatype TO datatype. The first datatype is the input datatype. The second datatype is the output datatype. The SQL conversion functions are: ASCIISTR BIN_TO_NUM CAST CHARTOROWID COMPOSE CONVERT DECOMPOSE HEXTORAW NUMTODSINTERVAL NUMTOYMINTERVAL RAWTOHEX RAWTONHEX ROWIDTOCHAR ROWIDTONCHAR SCN_TO_TIMESTAMP TIMESTAMP_TO_SCN TO_BINARY_DOUBLE TO_BINARY_FLOAT TO_CHAR (character) TO_CHAR (datetime) TO_CHAR (number) TO_CLOB TO_DATE TO_DSINTERVAL TO_LOB TO_MULTI_BYTE TO_NCHAR (character) TO_NCHAR (datetime) TO_NCHAR (number) TO_NCLOB TO_NUMBER TO_DSINTERVAL TO_SINGLE_BYTE TO_TIMESTAMP TO_TIMESTAMP_TZ TO_YMINTERVAL TO_YMINTERVAL TRANSLATE ... USING UNISTR 182 PL/SQL Programming Numeric Functions Numeric functions accept numeric input and return numeric values. Most numeric functions that return NUMBER values that are accurate to 38 decimal digits. The transcendental functions COS, COSH, EXP, LN, LOG, SIN, SINH, SQRT, TAN, and TANH are accurate to 36 decimal digits. The transcendental functions ACOS, ASIN, ATAN, and ATAN2 are accurate to 30 decimal digits. The numeric functions are: ABS ACOS ASIN ATAN ATAN2 BITAND CEIL COS COSH EXP FLOOR LN LOG MOD NANVL POWER REMAINDER ROUND (number) SIGN SIN SINH SQRT TAN TANH TRUNC (number) WIDTH_BUCKET 183 PL/SQL Programming Environment and Identifier Functions The environment and identifier functions provide information about the instance and session. These functions are: SYS_CONTEXT SYS_GUID SYS_TYPEID UID USER USERENV 184 PL/SQL Programming The aggregate functions are: AVG COLLECT CORR CORR_* COUNT COVAR_POP COVAR_SAMP CUME_DIST DENSE_RANK FIRST GROUP_ID GROUPING GROUPING_ID LAST MAX MEDIAN MIN PERCENTILE_CONT PERCENTILE_DISC PERCENT_RANK RANK VARIANCE REGR_ (Linear Regression) Functions STATS_BINOMIAL_TEST STATS_CROSSTAB STATS_F_TEST STATS_KS_TEST STATS_MODE STATS_MW_TEST STATS_ONE_WAY_ANOVA STATS_T_TEST_* STATS_WSR_TEST STDDEV STDDEV_POP STDDEV_SAMP SUM VAR_POP VAR_SAMP 185 PL/SQL Programming PL/SQL Date Datatype You use the DATE datatype to store fixed-length datetimes, which include the time of day in seconds since midnight (eg. '22-JUN-2004 07:48:53.275'; ). ■ The date portion (22-JUN-2004) defaults to the first day of the current month; the time portion (07:48:53.275'; ) defaults to midnight. ■ The date function SYSDATE returns the current date and time. ■ To compare dates for equality, regardless of the time portion of each date, use the function result TRUNC(date_variable) in comparisons, GROUP BY operations, and so on. ■ To find just the time portion of a DATE variable, subtract the date portion: dateVariable - TRUNC(dateVariable). ■ Valid dates range from January 1, 4712 BC to December 31, 9999 AD. ■ A Julian date is the number of days since January 1, 4712 BC. Julian dates allow continuous dating from a common reference. ■ You can use the date format model 'J' with the date functions TO_DATE and TO_CHAR to convert between DATE values and their Julian equivalents. ■ In date expressions, PL/SQL automatically converts character values in the default date format to DATE values. 186 Julian Day System • The Julian Day number system was invented by Joseph Justus Scaliger (born in France in 1540). The system was named after the inventor's father, Julius Caesar Scaliger (1484-1558). • A Julian date is the number of days since January 1, 4712 BC. • A Scalinger cycle lasts 7980 years (= 28 * 19 * 15). • The first Scaliger cycle began with Year 1 on -4712-01-01 (Julian) and will end after 7980 years on 3267-12-31 (Julian), which is 3268-01-22 (Gregorian). • The Julian day (jd) is computed from Gregorian day, month and year (d, m, y) as follows: jd = ( 1461 * ( y + 4800 + ( m - 14 ) / 12 ) ) / 4 + ( 367 * ( m - 2 - 12 * ( ( m - 14 ) / 12 ) ) ) / 12 ( 3 * ( ( y + 4900 + ( m - 14 ) / 12 ) / 100 ) ) / 4 + d - 32075 187 PL/SQL Programming Date/Time Examples The following example returns the system timestamp: SELECT SYSTIMESTAMP FROM DUAL; SYSTIMESTAMP -----------------------------------------------------------------28-MAR-00 12.38.55.538741 PM -04:00 The following example shows how to explicitly specify fractional seconds: SELECT TO_CHAR(SYSTIMESTAMP, 'SSSSS.FF') FROM DUAL; TO_CHAR(SYSTIME --------------55615.449255 Expressing today’s date (18-Sep-2006) in JULIAN format SELECT TO_CHAR(SYSTIMESTAMP, 'J') FROM DUAL; TO_CHAR ------2453997 Number of days since 1-JAN-4712 BC 188 PL/SQL Programming Date/Time Examples How many days since 01-Jan-2000 (assume today is 18-Sep-2006)? SELECT (TO_CHAR(SYSTIMESTAMP, 'J') – TO_CHAR(TO_TIMESTAMP('01-JAN-2000'),'J')) as MillenniaDays FROM DUAL MILLENNIADAYS ------------2452 189 PL/SQL Programming Character Functions - Examples The following example returns several specified substrings of "ABCDEFG": SELECT SUBSTR('ABCDEFG',3,4) FROM DUAL; Substring --------CDEF SELECT SUBSTR('ABCDEFG',-5,4) FROM DUAL; Substring --------CDEF SELECT REPLACE('JACK and JUE','J','BL')FROM DUAL; Changes -------------BLACK and BLUE 190 PL/SQL Programming Overview of PL/SQL Control Structures IF (condition) THEN statements; ELSE statements; END IF; DECLARE sales NUMBER(8,2) := 12100; bonus NUMBER(8,2); empSSN NUMBER := 123456789; BEGIN IF (sales > 12000) THEN bonus := 0.10 * sales + 500 ; ELSE bonus := 1; END IF; UPDATE employee SET salary = salary + bonus WHERE SSN = empSSN; END; / 191 PL/SQL Programming IF (condition) THEN statements; ELSIF (condition) THEN statements; ELSE statements; END IF; DECLARE grade NUMBER(3) := 87; letter CHAR(1); BEGIN IF (grade > 90) THEN letter := 'A'; ELSIF (grade >80) THEN letter := 'B'; ELSIF (grade >70) THEN letter := 'C'; ELSE letter := 'F'; END IF; dbms_output.put_line('Grade: ' || to_char(Grade) || ' ' || letter END; ); 192 PL/SQL Programming DECLARE gpa NUMBER(1); letter CHAR(1) := 'B'; BEGIN CASE letter WHEN 'A' WHEN 'B' WHEN 'C' ELSE gpa END CASE; CASE varSelector WHEN val1 THEN stmt1; WHEN val2 THEN stmt2; WHEN val3 THEN stmt3; … ELSE stmt-n; END CASE; THEN gpa := 4; THEN gpa := 3; THEN gpa := 2; := 0; dbms_output.put_line('GPA: ' || to_char(gpa) || ' ' || letter END; / ); 193 PL/SQL Programming Using the EXIT and LOOP Statements The EXIT statement forces a loop to complete unconditionally. When an EXIT statement is encountered, the loop completes immediately and control passes to the next statement as shown in Example below. DECLARE credit_rating NUMBER := 0; BEGIN LOOP credit_rating := credit_rating + 1; IF credit_rating > 3 THEN EXIT; -- exit loop immediately END IF; END LOOP; -- control resumes here DBMS_OUTPUT.PUT_LINE ('Credit rating: ' || TO_CHAR(credit_rating)); IF credit_rating > 3 THEN DBMS_OUTPUT.PUT_LINE ('Credit rating is more than 3'); END IF; DBMS_OUTPUT.PUT_LINE ('Adios '); END; / 194 PL/SQL Programming Using the EXIT-WHEN Statement The EXIT-WHEN statement lets a loop complete conditionally. When the EXIT statement is encountered, the condition in the WHEN clause is evaluated. If the condition is true, the loop completes and control passes to the next statement after the loop. LOOP IF count > 100 THEN EXIT; ENDIF; ... EXIT WHEN count > 100; ... END LOOP; These statements are logically equivalent, but the EXIT-WHEN statement is (perhaps) easier to read and understand. 195 PL/SQL Programming Using the RETURN Statement • The RETURN statement immediately completes the execution of a subprogram (Function or Procedure) and returns control to the caller. • Execution resumes with the statement following the subprogram call. • In a function, the RETURN statement also sets the function identifier to the return value. 196 PL/SQL Programming Labeling a PL/SQL Loop Like PL/SQL blocks, loops can be labeled. The optional label, an undeclared identifier enclosed by double angle brackets, must appear at the beginning of the LOOP statement. i 1 j 1 s 20 DECLARE i 1 j 2 s 30 i 1 j 3 s 40 s INTEGER := 0; i 1 j 4 s 50 i INTEGER := 0; Outer --------------i 2 j 1 s 30 j INTEGER; i 2 j 2 s 40 BEGIN i 2 j 3 s 50 <<outer_loop>> i 2 j 4 s 60 Outer --------------LOOP i 3 j 1 s 40 i := i + 1; i 3 j 2 s 50 i 3 j 3 s 60 j := 0; i 3 j 4 s 70 <<inner_loop>> Outer --------------LOOP Adios j := j + 1; s := 10*(i + j); -- do some work here DBMS_OUTPUT.PUT_LINE(' i ' || to_char(i) || ' j ' || to_char(j) || ' s ' || to_char(s) ); EXIT inner_loop WHEN (j > 3); END LOOP inner_loop; DBMS_OUTPUT.PUT_LINE('Outer ---------------' ); EXIT outer_loop WHEN (i > 2); END LOOP outer_loop; DBMS_OUTPUT.PUT_LINE('Adios' ); END; 197 PL/SQL Programming Using the WHILE-LOOP Statement The WHILE-LOOP statement executes the statements in the loop body as long as a condition is true: WHILE condition LOOP sequence_of_statements END LOOP; Before each iteration of the loop, the condition is evaluated. If it is TRUE, the sequence of statements is executed, then control resumes at the top of the loop. If it is FALSE or NULL, the loop is skipped and control passes to the next statement. Example done := FALSE; WHILE NOT done LOOP sequence_of_statements done := boolean_expression; END LOOP; 198 PL/SQL Programming Using the FOR-LOOP Statement Simple FOR loops iterate over a specified range of integers. The number of iterations is known before the loop is entered. A double dot (..) serves as the range operator. The range is evaluated when the FOR loop is first entered and is never re-evaluated. If the lower bound equals the higher bound, the loop body is executed once. Example --Using a Simple FOR..LOOP Statement DECLARE p NUMBER := 0; BEGIN FOR k IN 15..87 LOOP -- add numbers in the range p := p + k; END LOOP; DBMS_OUTPUT.Put_Line ( to_char(p) ); END; 199 PL/SQL Programming Using a Reverse FOR..LOOP Statement DECLARE n NUMBER(1) := 3; BEGIN FOR i IN REVERSE 1..n LOOP -- assign the values 3,2,1 to i DBMS_OUTPUT.PUT_LINE (TO_CHAR(i)); END LOOP; END; / Note. Inside a FOR loop, the counter can be read but cannot be changed. Change STEP of a FOR..LOOP Statement Some languages provide a STEP clause, which allows you to specify a different increment. Unfortunately PL/SQL has no such structure. 200 PL/SQL Programming Scope of the Loop Counter Variable The loop counter is defined only within the loop. You cannot reference that variable name outside the loop. After the loop exits, the loop counter is undefined: Example DECLARE -- Scope of the LOOP Counter Variable BEGIN FOR i IN 1..3 LOOP -- assign the values 1,2,3 to i DBMS_OUTPUT.PUT_LINE (i); END LOOP; DBMS_OUTPUT.PUT_LINE (i); -- raises an error END; / Note You do not need to declare the loop counter (i) because it is implicitly defined as a local variable of type INTEGER. 201 PL/SQL Programming Using the EXIT Statement in a FOR Loop The EXIT statement lets a FOR loop complete early. Example --Using EXIT in a LOOP DECLARE x NUMBER; BEGIN FOR i IN 1..10 LOOP DBMS_OUTPUT.Put_Line (i); EXIT WHEN i > 7; -- do some work here DBMS_OUTPUT.Put_Line ('Busy Working …'); END LOOP; END; / 202 PL/SQL Programming Using a Simple GOTO Statement ( what ? A GO TO ? You must be kidding!!! ) DECLARE p VARCHAR2(30); n INTEGER := 37; -- test any integer > 2 for prime BEGIN FOR j in 2..ROUND(SQRT(n)) LOOP IF n MOD j = 0 THEN -- test for prime p := ' is not a prime number'; -- not a prime number GOTO print_now; END IF; END LOOP; p := ' is a prime number'; <<print_now>> DBMS_OUTPUT.PUT_LINE(TO_CHAR(n) || p); END; / 203 PL/SQL: Example-3 -- ToCELCIUS (Lowest temp, Highest Temp) Converts temperatures from Fahrenheit to Celcius. -- Results are saved into TEMP, a predefined table to hold (text, text, text) CREATE OR REPLACE PROCEDURE ToCelcius (Low IN NUMBER, High IN HUMBER) IS Celcius Fahren Msg BEGIN number(12); number(12); varchar2(40); Intentional error DELETE FROM temp; FOR Fahren IN Low..High LOOP Celcius:= ROUND ( (Fahren -32)*5/9 ); IF (Fahren < 30 ) THEN Msg := 'Too cold'; ELSIF (Fahren < 70 ) THEN Msg := 'Cold'; ELSIF (Fahren <80) THEN Msg := 'Very nice'; ELSE Msg := 'Hot'; END IF; INSERT INTO Temp VALUES (Fahren, Celcius, Msg); Dbms_Output.Put_Line (to_char(Fahren) || ' ' || to_char(Celcius) || ' ' || Msg); END LOOP; END; / show errors / 204 PL/SQL: Example-3 Testing the procedure CSUPERSON SQL> exec toCelcius(60,77); 60 16 Cold … 76 24 Very nice 77 25 Very nice Values generated with DBMS_OUTPUT.PUT_LINE… CSUPERSON SQL> select * from temp; F1 ----60 61 … 77 F2 ----16 16 F3 ----------Cold Cold 25 Very nice 205 PL/SQL: Example-4 -- ASSIGN or REMOVE employees from/to projects -- shows: input/optional parameters create or replace PROCEDURE AdjustWorkLoad ( p_action p_ssn p_pno p_hours VARCHAR2, VARCHAR2, NUMBER, NUMBER:= 40 ) IS BEGIN Optional argument & Default value IF (p_action = 'ASSIGN' ) THEN INSERT INTO works_on VALUES (p_ssn, p_pno, p_hours); ELSIF (p_action = 'REMOVE') THEN DELETE FROM works_on WHERE (essn = p_ssn) AND (pno = p_pno); ELSE dbms_output.put_line ('Invalid Action ' || p_action); END IF; EXCEPTION when others then dbms_output.put_line END; / ('Problems: '); 206 PL/SQL: Example-4 -- ASSIGN or REMOVE employees from/to projects create or replace PROCEDURE AdjustWorkLoad ( p_action p_ssn p_pno p_hours VARCHAR2, VARCHAR2, NUMBER := 7, NUMBER:= 40 ) IS BEGIN -- ASSIGN / REMOVE employee to project for some hours IF (p_action = 'ASSIGN' ) THEN INSERT INTO works_on VALUES (p_ssn, p_pno, p_hours); TWO Optional arguments & Default values ELSIF (p_action = 'REMOVE') THEN DELETE FROM works_on WHERE (essn = p_ssn) AND (pno = p_pno); ELSE dbms_output.put_line ('Invalid Action ' || p_action); END IF; EXCEPTION when others then dbms_output.put_line END; / ('Problems: '); 207 PL/SQL: Example-4 Testing the procedure CSUPERSON SQL> exec AdjustWorkLoad('ASSIGN',1,1,22); CSUPERSON SQL> exec AdjustWorkLoad('ASSIGN',1,2); CSUPERSON SQL> exec AdjustWorkLoad('ASSIGN',1); CSUPERSON SQL> select * from works_on; ESSN PNO HOURS --------- ---------- ---------123456789 1 32.5 … 1 1 22 1 2 40 … 1 7 40 CSUPERSON SQL> exec AdjustWorkLoad('REMOVE',1,2); 208 PL/SQL Programming Defining Variables %TYPE Declares a variable with respect to a Table.Column Bonus EMP.Sal%TYPE; EMP EMPNO ----7369 7499 7521 ENAME ---------SMITH ALLEN WARD JOB MGR HIREDATE SAL COMM DEPTNO --------- --------- --------- --------- --------- --------CLERK 7902 17-DEC-80 800 20 SALESMAN 7698 20-FEB-81 1600 300 30 SALESMAN 7698 22-FEB-81 1250 500 30 Number(7,2) 209 PL/SQL Programming Defining Variables %RowType Defines a record with respect to an existing table definition TempEmp EMP%RowType Refer to fields using: TempEmp.Salary EMP EMPNO ----7369 7499 7521 ENAME ---------SMITH ALLEN WARD JOB MGR HIREDATE SAL COMM DEPTNO --------- --------- --------- --------- --------- --------CLERK 7902 17-DEC-80 800 20 SALESMAN 7698 20-FEB-81 1600 300 30 SALESMAN 7698 22-FEB-81 1250 500 30 210 PL/SQL Example 5 Using %Type and %RowType (and Cursor construction) create or replace PROCEDURE Example5 IS vEmpRecTemp Employee%RowType; vSSN Employee.SSN%Type; The scope of the variable extends to the entire procedure The scope of the (undeclared) variable is limited to the for-loop BEGIN for vEmpRec in (select * from employee) loop vSSN := vEmpRec.SSN; dbms_output.put_line (to_Char(vSSN) || ' ' || vEmpRec.Fname ); end loop; dbms_output.new_line; dbms_output.put_line ('Adios'); END; 211 PL/SQL Example 5 Testing Example5 CSUPERSON SQL> exec example5 123456789 John 333445555 999887777 987654321 666884444 453453453 987987987 888665555 Adios Franklin Alicia Jennifer Ramesh Joyce Ahmad James 212 Jdeveloper 10g (10.1.3) – SQL Developer Using better tools for managing Oracle objects. • • • • • • • • • • Donwload the Jdeveloper / SQLdeveloper tool from www.oracle.com Create the subdirectory c:\Jdeveloper Unzip the downloaded package into the subdirectory (there is no setup.exe program) Execute the program Jdeveloper.exe From the main menu choose: View | Conection Navigator | Database Run the Connection Wizard. Enter OracleDBConnection at the “Connection Name” box Enter CSUPERSON, EUCLID in the UserName & Password boxes Enter XE (for Express Edition) in the SID box (replace ORCL) Test the connection. 213 Jdeveloper – SQL Developer 214 Jdeveloper – SQL Developer 215 Jdeveloper – SQL Developer NOTE In order for user SCOTT to create/compile/debug stored Oracle code when using the Jdeveloper tool you need to do this • Log into the system as a DBA (either connect system/manager or ) ( connect system/manager as sysdba ) • Enter the commands Grant DEBUG CONNECT SESSION to scott; Grant DEBUG ANY PROCEDURE to scott; 216 Jdeveloper Resources that can help you get started with Oracle JDeveloper. Online Demos: http://www.oracle.com/technology/products/jdev/viewlets/viewlet.html Step by Step Getting Started Tutorials http://www.oracle.com/technology/obe/obe1013jdev/index.htm JDeveloper discussion forum: http://www.oracle.com/technology/discussionforums/jdev.html For more information on Oracle JDeveloper visit OTN: http://www.oracle.com/technology/products/jdev/ JDeveloper RSS news feed: http://www.oracle.com/technology/products/jdev/temp/whatisrss.html 217 PL/SQL Programming SUPPLIED PACKAGES 218 PL/SQL Programming Supplied packages • DBMS_OUTPUT routes output from routines – Put_Line(…) – Put (…) – New_Line • DBMS_RANDOM generates random numbers/string – String(…) – Seed(…) – Value( ) • writes line + CrLf writes token writes CrLf returns STRING starting point returns NUMBER [0, 1) UTL_FILE programs can read and write operating system text files – Fclose – Put_Line – Get_Line Fopen Put … Fflush Is_Open New_line 219 PL/SQL Example 6 Using the DBMS_OUTPUT package create or replace PROCEDURE Example6 IS BEGIN for i in 0 .. 6 loop if MOD(i,2) = 0 then dbms_output.put ('Blip '); else dbms_output.put ('Blop '); end if; end loop; dbms_output.New_Line; dbms_output.put_line ('Adios'); dbms_output.put_line ('amigos'); END; 220 PL/SQL Example 6 Testing Example 6 CSUPERSON SQL> exec example6 Blip Blop Blip Blop Blip Blop Blip Adios amigos PL/SQL procedure successfully completed. 221 PL/SQL Example-7 Using the DBMS_RANDOM package --Create 10 random numbers and strings PROCEDURE Example7 IS myRandomNumber1 NUMBER(10); myRandomNumber2 NUMBER(10); myRandomString VARCHAR2(20); BEGIN for i in 0..9 loop DBMS_OUTPUT.New_Line; --make a random num. between 50 and 100 myRandomNumber1 := DBMS_RANDOM.Value(50, 101); --make a random num. between 0 and 100 myRandomNumber2 := 101 * DBMS_RANDOM.Value(); --X (alphanumeric) A (alpha) P (printable) myRandomString := DBMS_RANDOM.String('X', 10); DBMS_OUTPUT.Put ( to_char(i) || ' '); DBMS_OUTPUT.Put ( myRandomString || ' ' ); DBMS_OUTPUT.Put ( to_char(myRandomNumber1) || ' ' ); DBMS_OUTPUT.Put ( to_char(myRandomNumber2) || ' ' ); DBMS_OUTPUT.New_Line; end loop; END; 222 PL/SQL Example-7 Testing DBMS_RANDOM CSUPERSON SQL> Exec Example7 0 1 2 3 4 5 6 7 8 9 HCOGHALDI0 8M6S2DKG6D J6XGTPR3T5 NFRO7KFZZU YLX9C620JM 0CGB6QBW2X Y0G57QNIRV VT4CSCKNS8 BRV1S4PWLE WQJAHNHV5L 54 65 54 71 83 96 73 87 99 72 84 8 21 25 45 33 26 17 39 56 PL/SQL procedure successfully completed 223 PL/SQL Example-8 Using the UTL_FILE package Create a bridge between Oracle and the local OS using IO routines to operate on local OS (disk) text files. Preparation • • Log as DBA using SYS (connect system/manager as SYSDBA) Install the UTL_FILE package by executing (@) the script C:\oracle\product\10.1.0\db_1\RDBMS\ADMIN\utlFile.sql or Oracle 10g Express Edition (v10.2) appears not to support this feature. C:\oraclexe\app\oracle\product\10.2.0\server\RDBMS\ADMIN\utlFile.sql • Issue appropriate GRANT commands, finally create a (logical) working directory alias CREATE OR REPLACE PUBLIC SYNONYM utl_file FOR sys.utl_file; GRANT EXECUTE ON UTL_FILE TO PUBLIC; CREATE DIRECTORY WIN_DIR AS 'C:/Temp'; GRANT READ ON DIRECTORY WIN_DIR TO PUBLIC; GRANT WRITE ON DIRECTORY WIN_DIR TO PUBLIC; 224 PL/SQL Example-8 Log as CSUPerson/Euclid. Enter the following anonymous procedure DECLARE myFile UTL_FILE.File_Type; myBuffer varchar2(80); BEGIN myFile := UTL_FILE.Fopen('WIN_DIR','XYZ.TXT','W'); UTL_FILE.Put_Line(myFile, 'Uno'); UTL_FILE.Put_Line(myFile, 'Dos'); UTL_FILE.Put_Line(myFile, 'Tres'); UTL_FILE.Fclose(myFile); BEGIN myFile := UTL_FILE.Fopen('WIN_DIR','XYZ.TXT','R'); Loop UTL_FILE.Get_Line (myFile, myBuffer); DBMS_OUTPUT.Put_Line(myBuffer); End Loop; EXCEPTION when NO_DATA_FOUND then UTL_FILE.Fclose(myFile); DBMS_OUTPUT.Put_Line ('Adios ...'); END; DBMS_OUTPUT.Put_Line ('Amigos'); END; 225 PL/SQL Example-8 Testing anonymous procedure. Write then read some lines CSUPERSON SQL> @Code/Example8 Uno Dos Tres Adios ... Amigos PL/SQL procedure successfully completed. Note: File c:/Temp/XYZ.TXT contains Uno Dos Tres 226 PL/SQL Programming COLLECTIONS and RECORDS 227 PL/SQL Programming What are PL/SQL Collections and Records? • A collection is an ordered group of elements, all of the same type. It is a general concept that includes lists, arrays, tables and other datatypes used in classic programming algorithms. Each element is addressed by a unique subscript. • A record is a group of related data items stored in fields, each with its own name and datatype. You can think of a record as a variable that can hold a table row, or some columns from a table row. The fields (usually) correspond to table columns. 228 PL/SQL Example-9 Defining and Using a User-Defined Record Type DECLARE EmpName TYPE BadgeT IS RECORD( EmpName varchar2(40), DeptName varchar2(40) ); HelloTag BEGIN select into from where and BadgeT; (Fname || ' ' || Lname), (Dname) HelloTag.EmpName, HelloTag.DeptName Employee, Department SSN= 123456789 Dno = Dnumber; DBMS_OUTPUT.Put_Line ('--- HELLO ---'); DBMS_OUTPUT.Put_Line (HelloTag.EmpName); DBMS_OUTPUT.Put_Line (HelloTag.DeptName); EXCEPTION when others then DBMS_OUTPUT.Put_Line ('Problems'); END; DeptName RECORD TYPE Print a HELLO tag for employee 123456789 showing name and dept. --- HELLO --John Smith Research 229 PL/SQL Programming Understanding PL/SQL Collections 1. Associative arrays 2. Nested tables 3. Varrays 230 PL/SQL Programming Understanding PL/SQL Collections • Associative arrays (using integer & string indices) TYPE aa1T IS TABLE INDEX TYPE aa2T IS TABLE INDEX • Varchar2(10) BINARY_INTEGER; EmployeeRec Varchar2(20); Nested tables TYPE ntT IS • OF BY OF BY TABLE OF Varchar2(10); Varrays TYPE vaT IS VARRAY(20) OF VARCHAR2(30); 231 PL/SQL Programming Understanding PL/SQL Memory Collections Associative arrays, also known as index-by tables, let you look up elements using arbitrary numbers and strings for subscript values. These are similar to hash tables in other programming languages. Favorite City Key Ohio Spain Value Cleveland Madrid India ... Bangalore France ... Paris 232 PL/SQL Programming Understanding PL/SQL Collections • • • Nested tables hold an arbitrary number of row elements. They use sequential numbers as subscripts. You can define equivalent SQL types, allowing nested tables to be stored in database tables and manipulated through SQL. Key 1 Ohio Cleveland 2 Spain Madrid 3 India Bangalore Venezuela Pampatar … Unlimited number of rows n 233 PL/SQL Programming Understanding PL/SQL Collections • • • • VARRAYS (short for variable-size arrays) hold a fixed number of elements (although you can change the number of elements at runtime). They use sequential numbers as subscripts. You can define equivalent SQL types, allowing varrays to be stored in database tables. They can be stored and retrieved through SQL, but with less flexibility than nested tables. Favorite City Key 1 2 3 4 5 6 Value Cleveland Madrid San Francisco Bangalore Montreal Paris Limited (but dynamic) number of cells 234 PL/SQL Programming Understanding Associative Arrays (Index-By Tables) • Associative arrays are sets of key-value pairs, where each key is unique and is used to locate a corresponding value in the array. • The key can be an integer or a string. • Assigning a value using a key for the first time adds that key to the associative array. Subsequent assignments using the same key update the same entry. • Can not be inserted in a (SQL) table (as Varrays do) • Sparse (could have null segments: data + nulls) • Has methods/attributes: – Delete(…), First(...), Next(…), Prior(...), Last(…), Exists(…), Count… 235 PL/SQL – Example 10 • An associative array using integer indices (stores employee records). • There are gaps in the collection (entry 3 does not exist) 236 PL/SQL – Example 10B • An associative array using STRING indices 237 PL/SQL – Example11 PROCEDURE Example11 IS TYPE BadgeT IS RECORD( EmpName varchar2(40), DeptName varchar2(40) ); TYPE SimpleTableT IS TABLE OF Varchar2(10) INDEX BY BINARY_INTEGER; TYPE WorkerT IS TABLE OF Employee%ROWTYPE INDEX BY BINARY_INTEGER; TYPE BadgeTColl IS TABLE OF BadgeT INDEX BY BINARY_INTEGER; BadgeTable SimpleTable Worker BadgeTColl; SimpleTableT; WorkerT; Associative Array used to Store a user-defined record type TYPE definitions INSTANCE definition Table uses NUMERIC indices BEGIN -- Associative array BadgeTable(0).EmpName := '1- John Smith'; BadgeTable(0).DeptName := 'Intl. Sales'; BadgeTable(1).EmpName := '2- Maria Macarena'; BadgeTable(1).DeptName := 'Intl. Salsa'; BadgeTable(2).EmpName := '3- Sam Dynamite'; BadgeTable(2).DeptName := 'Intl. Bang'; 1- John Smith 2- Maria Macarena 3- Sam Dynamite for i in 0..2 loop dbms_output.put_line (BadgeTable(i).EmpName); end loop; END; 238 PL/SQL – Example11 Associative Arrays KEY VALUES EmpName DeptName 0 1- John Smith Int. Sales 1 2- Maria Macarena Int. Salsa 2 3- Sam Dynamite Int. Bang 239 PL/SQL Example-12 Associative Arrays PROCEDURE Example12 IS TYPE SimpleTableT IS TABLE OF Varchar2(10) INDEX BY BINARY_INTEGER; TYPE WorkerT IS TABLE OF Employee%ROWTYPE INDEX BY BINARY_INTEGER; SimpleTable SimpleTableT; Worker WorkerT; Data-5 BEGIN Data-4 for i in 1..5 loop Data-3 SimpleTable(i) := 'Data-' || i; Data-2 end loop; Data-1 for i in reverse 1..5 loop 3 Harry DBMS_OUTPUT.Put_Line (SimpleTable(i)); 5 Hermione end loop; worker(3).Fname := 'Harry'; worker(3).Lname := 'Potter'; worker(5).Fname := 'Hermione'; worker(5).Lname := 'Wranger'; for i in 1..6 loop begin DBMS_OUTPUT.Put_Line (to_char(i) || ' ' || worker(i).Fname); exception when others then null; BEGIN…EXCEPTION…END end; block deals with the problem of end loop; sparse data (null data gaps) END; 240 PL/SQL Example-12 Associative Arrays KEY VALUES 1 Data-1 2 Data-2 3 Data-3 4 Data-4 5 Data-5 KEY SimpleTable Worker VALUES Fname 3 Harry 5 Hermione Lname … Dno 241 Associative Arrays PL/SQL Example-12 WATCH WINDOW displayed by the Jdeveloper tool when tracing the Procedure. Observe the (Key, Value) structure. SimpleTable Worker 242 PL/SQL Programming Collection Methods A collection method is a built-in function or procedure that operates on collections and is called using dot notation. You can use the methods EXISTS, COUNT, LIMIT, EXTEND, TRIM, DELETE, FIRST, PRIOR, LAST, NEXT to manage collections whose size is unknown or varies. 243 PL/SQL Programming Collection Methods COUNT Returns the number of elements that a collection currently contains. • For varrays, COUNT always equals LAST. • For nested tables, normally, COUNT equals LAST. But, if you delete elements from the middle of a nested table, COUNT is smaller than LAST. DELETE Eliminate (without shifting) entries from the collection (must be an associative array or nested table). This procedure has three forms. • DELETE • DELETE(n) removes all elements from a collection. removes the n-th element from an associative array or nested table. If n is null, DELETE(n) does nothing. • DELETE(m,n) removes all elements in the range m..n from an associative array or nested table. If m is larger than n or if m or n is null, DELETE(m,n) does nothing. 244 PL/SQL Programming Collection Methods EXTEND Enlarges the size of a collection. This procedure has three forms. EXTEND appends one null element to a collection. EXTEND(n) appends n null elements to a collection. EXTEND(n,i) appends n copies of the i-th element to a collection. You cannot use EXTEND with associative arrays. TRIM TRIM removes one element from the end of a collection. TRIM(n) removes n elements from the end of a collection. If n is greater than COUNT, TRIM(n) raises SUBSCRIPT_BEYOND_COUNT. You cannot use TRIM with index-by tables. If TRIM encounters deleted elements, it includes them in its tally. 245 PL/SQL Programming Collection Methods FIRST, LAST FIRST and LAST return the first and last (smallest and largest) subscript values in a collection. The subscript values are usually integers, but can also be strings for associative arrays. • If the collection is empty, FIRST and LAST return NULL. • If the collection contains only one element, FIRST and LAST return the same subscript value. • For varrays, FIRST always returns 1 and LAST always equals COUNT. • For nested tables, normally, LAST equals COUNT. But, if you delete elements from the middle of a nested table, LAST is larger than COUNT. 246 PL/SQL Programming Collection Methods LIMIT For nested tables, which have no maximum size, LIMIT returns NULL. For varrays, LIMIT returns the maximum number of elements that a varray can contain (which you must specify in its type definition). NEXT, PRIOR PRIOR(n) returns the subscript that precedes index n in a collection. NEXT(n) returns the subscript that succeeds index n. If n has no predecessor, PRIOR(n) returns NULL. Likewise, if n has no successor, NEXT(n) returns NULL. 247 PL/SQL Programming Collection Methods EXISTS EXISTS(n) returns TRUE if the n-th element in a collection exists. Otherwise returns FALSE. Mainly, you use EXISTS with DELETE to maintain sparse nested tables. You can also use EXISTS to avoid raising an exception when you reference a nonexistent element. When passed an out-of-range subscript, EXISTS returns FALSE instead of raising SUBSCRIPT_OUTSIDE_LIMIT. 248 PL/SQL Programming Collection Methods collection_name.{ COUNT | DELETE [ (index [, index]) ] | EXISTS (index) | EXTEND [ (number [, index] ] | FIRST | LAST | LIMIT | NEXT (index) | PRIOR (index) | TRIM [ (number) ] } 249 PL/SQL Example-13 Associative Arrays Navigation Methods: First(), Next(), Last() create or replace PROCEDURE Example13 IS TYPE SimpleTableT IS TABLE OF Varchar2(10) INDEX BY BINARY_INTEGER; SimpleTable SimpleTableT; i NUMBER; BEGIN SimpleTable(0) := 'DHL'; SimpleTable(1) := 'Fedex'; SimpleTable(2) := 'UPS'; SimpleTable(3) := 'USPS'; for i in 0..3 loop dbms_output.put_line (simpleTable(i)); end loop; DHL Fedex UPS USPS 0 1 2 3 DHL Fedex UPS USPS i := SimpleTable.First; Loop exit when i is null; dbms_output.put_line (to_char(i) || ' ' || simpleTable(i)); i := SimpleTable.Next(i); End Loop; END; 250 PL/SQL Example-14 Deleting rows from a table. create or replace PROCEDURE Example14 IS TYPE SimpleTableT IS TABLE OF Varchar2(10) INDEX BY BINARY_INTEGER; SimpleTable SimpleTableT; i NUMBER; BEGIN SimpleTable(0) := 'DHL'; SimpleTable(1) := 'Fedex'; SimpleTable(2) := 'UPS'; SimpleTable(3) := 'USPS'; 0 1 2 3 Associative Arrays DHL Fedex UPS USPS SimpleTable.Delete(2); --removing UPS from the table 0 DHL 1 Fedex 3 USPS i := SimpleTable.First; Loop exit when i is null; dbms_output.put_line (to_char(i) || ' ' || simpleTable(i)); i := SimpleTable.Next(i); End Loop; END; 251 PL/SQL Example-15A Associative Arrays PROCEDURE Example15A AS TYPE BaseBallType Home City Team BaseBallType; VARCHAR2(64); VARCHAR2(64); IS TABLE OF VARCHAR2(64) INDEX BY VARCHAR2(64); BEGIN -- Creates new entries Home ('Indians') := 'Cleveland' ; Home ('Yankees') := 'New York' ; Home ('Dodgers') := 'Los Angeles' ; Indians Cleveland Yankees New York Dodgers Los Angeles -- Looks up value associated with a baseball team (string type) city := Home('Yankees'); dbms_output.put_line('The [Yankees] home is: ' || City); The [Yankees] home is: New York 252 PL/SQL Example-15A Associative Arrays cont. -- Look into the collection -- use: FIRST, NEXT, COUNT, PRIOR, LAST,... Team := Home.FIRST; Dodgers --> Los Angeles loop Indians --> Cleveland exit when Team is NULL; Yankees --> New York city := Home(Team); dbms_output.put_line( Team || ' --> ' || City); Team := Home.NEXT(Team); end loop; -- Looks up value associated with a football team (string type) BEGIN Team := 'Browns'; city := Home(team); dbms_output.put_line(Team || ' --> ' || City); EXCEPTION when others then dbms_output.put_line('What? Check your baseball'); END; What? Check your baseball END; 253 PL/SQL Example-15B DECLARE TYPE population_type country_population continent_population howmany which IS TABLE OF NUMBER INDEX BY VARCHAR2(64); population_type; population_type; NUMBER; VARCHAR2(64); Associative Arrays KEY is a string. The structure is a HASH table BEGIN country_population('Greenland') := 100000; -- Creates new entry country_population('Iceland') := 750000; -- Creates new entry -- Looks up value associated with a string howmany := country_population('Greenland'); dbms_output.put_line('Greenland pop. is ' || howmany ); continent_population('Australia') := 30000000; continent_population('Antarctica') := 1000; -- Creates new entry continent_population('Antarctica') := 1001; -- Replaces previous value -- Returns 'Antarctica' as that comes first alphabetically. which := continent_population.FIRST; dbms_output.put_line ('First KEY is: ' || which); -- Returns 'Australia' as that comes last alphabetically. which := continent_population.LAST; dbms_output.put_line ('First LAST is: ' || which); Greenland pop. is 100000 First KEY is: Antarctica First LAST is: Australia Population of Australia is 30000000 -- Returns the value corresponding to the last key, in this -- case the population of Australia. howmany := continent_population(which); dbms_output.put_line('Population of ' || which || ' is ' || howmany); END; / 254 PL/SQL: VARRAY • VARRAYs are one-dimensional arrays that can be stored in columns of a database table. • They are dense data objects (behave like arrays, a delete operation doesn’t shift rows) • A varray has a maximum size, which you specify in its type definition. Its index has a fixed lower bound of 1 and an extensible upper bound. TYPE dnames_var IS VARRAY(20) OF VARCHAR2(30); • A varray can contain a varying number of elements, from zero (when empty) to the maximum specified in its type definition. 255 PL/SQL: VARRAY Invoking the VARRAY Primitive Constructor - Extending DECLARE -- In the varray, we put an upper limit (20) on the number of elements TYPE dnamesType IS VARRAY(20) OF VARCHAR2(30); dept_names dnamesType; BEGIN -- Because dnames is declared as VARRAY(20), we can put up to 20 -- elements in the constructor dept_names := dnamesType('Shipping','Sales','Finance','Payroll'); -- after calling constructor COUNT is set to four elements dbms_output.put_line('LAST ' || dept_names.LAST); dbms_output.put_line('COUNT ' || dept_names.COUNT); --adding a new element dept_names.EXTEND(1); dept_names(dept_names.LAST) := 'Research'; dbms_output.put_line('LAST ' || dept_names.LAST); for i in 1..dept_names.LAST loop dbms_output.put_line(i || ' --> ' || dept_names(i)); end loop; END; / LAST 4 COUNT 4 1 --> Shipping 2 --> Sales 3 --> Finance 4 --> Payroll 5 --> Research 256 PL/SQL: VARRAY Expanding and Shrinking VARRAYs PROCEDURE Example16A IS TYPE VA is varray(3) of varchar2(10); A va; theCount number; Count: 0 Count: 2 aaa bbb ccc BEGIN -- instantiate varray (no data) a := VA(); theCount := a.COUNT; dbms_output.put_line('Count: ' || theCount); -- place a pair of values using varray constructor a := VA('aaa','bbb'); theCount := a.COUNT; dbms_output.put_line('Count: ' || theCount); --add one more element using .EXTEND(…) [already at the limit] a.extend(1); a(3) := 'ccc'; for i in 1..a.LAST loop dbms_output.put_line (a(i)); end loop; END; 257 PL/SQL: VARRAY Expanding and Shrinking VARRAYs cont. theCount := a.COUNT; dbms_output.put_line('Count: ' || theCount); dbms_output.put_line(' Limit: ' || a.LIMIT); --try to grow beyond the limit (it is not going to work!) BEGIN a.extend(1); a(4) := 'ddd'; EXCEPTI when others then dbms_output.put_line('Could NOT grow'); END; --eliminate right-most element a.trim; theCount := a.COUNT; dbms_output.put_line('Count: ' || theCount); dbms_output.put_line(' Limit: ' || a.LIMIT); Count: 3 Limit: 3 for i in 1..a.LAST loop Could NOT growth dbms_output.put_line (a(i)); Count: 2 end loop; Limit: 3 END; aaa .DELETE(…) can not be used with varrays bbb 258 PL/SQL: VARRAY Expanding and Shrinking NESTED TABLES Repeat previous example with the following modification for array a: TYPE NTType is TABLE of varchar2(10); a NTType; Try also .DELETE(…) operation on the LAST (OK!) and FIRST (will fail) 259 PL/SQL: VARRAY Creating a Table with a Varray Column (sets of atomic values) -- Each project has a 16-character code name. -- We will store up to 50 projects at a time -- in a single database column. CREATE TYPE ProjectList AS VARRAY(50) OF VARCHAR2(16); -- create database table: dept_projects -- Each department can have up to 50 projects. CREATE TABLE dept_projects ( dept_id NUMBER(2), name VARCHAR2(15), budget NUMBER(11,2), projects ProjectList); NOTE. To force the destruction of the table and type use the commands drop table dept_projects cascade constraints purge drop TYPE ProjectList 260 PL/SQL: VARRAY Creating a Table with a Varray Column continuation CREATE TYPE ProjectList AS VARRAY(50) OF VARCHAR2(16); 1 2 3 … 50 261 PL/SQL: VARRAY Creating a Table with a Varray Column continuation CREATE TABLE dept_projects ( dept_id NUMBER(2), name VARCHAR2(15), budget NUMBER(11,2), projects ProjectList); Projects Position Dept_id Name Budget 1 2 3 4 … 50 1 2 3 … 262 PL/SQL: VARRAY Varray Constructor Within a SQL Statement cont. BEGIN INSERT INTO dept_projects VALUES (60, 'Security', 750400, ProjectList('New Badges', 'Track Computers', 'Check Exits')); END; / Dept_id Name Budget Projects 60 Security 750400 PROJECTLIST('New Badges', 'Track Computers', 'Check Exits’) NOTE. To force column formatting you may have to issue the SQL-Plus commands column name format a20 column dept_id format 9999 column budget format 999,999,999.99 column projects format a80 Select * from dept_projects; 263 PL/SQL: VARRAY Creating a Table with a Varray Column cont. DECLARE some_proj ProjectList; BEGIN SELECT projects INTO some_proj FROM dept_projects WHERE dept_id = 60; Projects = New Badges Projects = Track Computers Projects = Check Exits FOR i IN some_proj.FIRST .. some_proj.LAST LOOP DBMS_OUTPUT.PUT_LINE('Projects = ' || some_proj(i)); END LOOP; END; / 264 PL/SQL Varray Non-First Normal Form Table holding a varray of a non-atomic data type create or replace TYPE PersonT AS OBJECT ( A single person OBJECT definition EmpSSN varchar2(10), EmpName varchar2(40) ); Defining a LIST of Person OBJECTS / (Collection Object) create or replace TYPE PersonListT AS VARRAY(50) of PersonT; / drop table CoWorker; / create table CoWorker ( A SQL table using a Collection of ProjectNumber number, OBJECTS ProjectName varchar2(20), (List of People) People PersonListT ) / insert into CoWorker values (10, 'Project X', PersonListT( PersonT(123, 'Harry Potter' ), PersonT(456, 'Hermione Wranger'), PersonT(777, 'Prof. Dumbledore') ) ) / insert into CoWorker values (20, 'Project Y', PersonListT( PersonT(789, 'Roy Malfoy' ), PersonT(999, 'Lord Voldemor' ) ) ) / select * from CoWorker;/ 265 PL/SQL Varray (cont.) Non-First Normal Form Table holding a varray of a non-atomic data type CSUPERSON SQL> select * from coworker; PROJECTNUMBER PROJECTNAME PEOPLE(EMPSSN, EMPNAME) ------------- ----------- ----------------------10 Project X PERSONLISTT( PERSONT('123', 'Harry Potter'), PERSONT('456', 'Hermione Wranger'), PERSONT('777', 'Prof. Dumbledore')) 20 Project Y PERSONLISTT( PERSONT('789', 'Roy Malfoy'), PERSONT('999', 'Lord Voldemor')) Complex object: A LIST (collection) of PersonT objects. NOTE. To force column formatting you may have to issue the SQL-Plus commands column ProjectName format a20 column ProjectNumber format 9999 column People format a40 WRAP 266 PL/SQL Varray (cont.) Non-First Normal Form Table holding a varray of a non-atomic data type CSUPERSON SQL> select * from coworker; ProjectNumber ProjectName People EmpSSN 10 Project X 123 456 777 20 Project Y 789 999 EmpName Harry Potter Hermione Wranger Prof. Dumbledore Roy Malfoy Lord Voldemor 267 PL/SQL: Nested Tables • PL/SQL nested tables represent sets of values. • You can think of them as one-dimensional arrays with no declared number of elements. • You can model multi-dimensional arrays by creating nested tables whose elements are also nested tables. • Within the database, nested tables are column types that hold sets of values. Oracle stores the rows of a nested table in no particular order. • When you retrieve a nested table from the database into a PL/SQL variable, the rows are given consecutive subscripts starting at 1. That gives you array-like access to individual rows. 268 PL/SQL: Nested Tables DECLARE TYPE nested_type IS TABLE OF VARCHAR2(30); i number := 0; n nested_type; aValue varchar2(20); No INDEX BY clause No size / storage is predefined BEGIN -- an arbitrary number of strings can be inserted in n n := nested_type('Shipping','Sales','Finance','Payroll'); n.extend(1); n(n.LAST) := 'Research'; i := n.FIRST; -- same as i:=1; Loop exit when i is null; dbms_output.put_line (i || ' ' || n(i)); i := n.Next(i); End Loop; 1 Shipping 2 Sales 3 Finance 4 Payroll 5 Research END; / 269 PL/SQL: Nested Tables Nested tables differ from arrays in two important ways: • Nested tables do not have a declared number of elements, while arrays have a predefined number. The size of a nested table can increase dynamically; however, a maximum limit is imposed. • Nested tables might not have consecutive subscripts, while arrays are always dense (have consecutive subscripts). Initially, nested tables are dense, but they can become sparse (have nonconsecutive subscripts). You can delete elements from a nested table using the built-in procedure DELETE. The built-in function NEXT lets you iterate over all the subscripts of a nested table, even if the sequence has gaps. 270 PL/SQL: Nested Tables PROCEDURE Example16B AS TYPE nested_type IS TABLE OF VARCHAR2(30); i number := 0; n nested_type; BEGIN -- call constructor, extend, and insert consecutive data cells n := nested_type(); n.extend(4); n(1):= '111'; n(2):= '222'; n(3):= '333'; n(4):= '444'; --show consecutive cells for i in 1..n.LAST Loop dbms_output.put_line (i || ' ' || n(i)); End Loop; --add one more cell at the end, delete the one whose key is 2 n.extend(1); n(n.LAST) := '555'; n.delete(2); --traverse the collection (there is a hole on position 2) i := n.FIRST; -- same as i:=1; Loop exit when i is null; dbms_output.put_line (i || ' ' || n(i)); i := n.Next(i); End Loop; 1 2 3 4 111 222 333 444 1 3 4 5 111 333 444 555 END; 271 PL/SQL: Nested Tables Consider making a nested NF2 database table holding • Dept. Name & Number. • All Projects controlled by the department (size unknown). • All Employees working in those project (size unknown). DeptNum DeptName Projects Employees {Pro-X, Pro-Y} {Maria, Joe, Pete} 10 Research 20 Marketing {Pro-Z} {Pete, Paul} Ozsoyoglu G., Matos V. “Extending Relational Algebra and Relational Calculus with Set-Valued Attributes and Aggregate Functions”. ACM Transactions on Database Systems. Vol. 12 (4): 566-592 (1987). http://www.informatik.uni-trier.de/~ley/db/indices/a-tree/m/Matos:Victor.html http://doi.acm.org/10.1145/32204.32219 272 PL/SQL: Nested Tables DROP DROP DROP DROP DROP DROP DROP TABLE ntE FORCE; TABLE ntP FORCE; TABLE DeptInfo FORCE; TYPE ntProjectT FORCE; TYPE ProjectT FORCE; TYPE ntEmployeeT FORCE; TYPE EmployeeT FORCE; /* -------------------------------------------------------------------- */ /* Define Object Type for storing project & employee information */ /* -------------------------------------------------------------------- */ CREATE or REPLACE TYPE ProjectT AS OBJECT ( PNumb VARCHAR2(10), PName VARCHAR2(20)); / CREATE or REPLACE TYPE EmployeeT AS OBJECT ( EmpSSN VARCHAR2(10), EmpName VARCHAR2(40)); / SHOW ERRORS 273 PL/SQL: Nested Tables /* -------------------------------------------------------------------- */ /* Define Nested Table Type of ProjectT & Employee object type */ /* -------------------------------------------------------------------- */ CREATE TYPE ntProjectT AS TABLE OF ProjectT; / CREATE TYPE ntEmployeeT AS TABLE OF EmployeeT; / /* -------------------------------------------------------------------- */ /* Table for storing Department related information */ /* -------------------------------------------------------------------- */ CREATE TABLE DeptInfo( DNumber VARCHAR2(10), Dname VARCHAR2(50), Projects ntProjectT, Employees ntEmployeeT) NESTED TABLE Projects STORE AS ntP, NESTED TABLE Employees STORE AS ntE; 274 PL/SQL: Nested Tables INSERT INTO DeptInfo VALUES ( 10, 'Research', ntProjectT( ProjectT(1, 'Project X'), ProjectT(2, 'Project Y') ), ntEmployeeT( EmployeeT(111, 'Joe'), EmployeeT(222, 'Maria'), EmployeeT(333, 'Pete') ) ); INSERT INTO DeptInfo VALUES ( 20, 'Marketing', ntProjectT( ProjectT(1, 'Project Z') ), ntEmployeeT( EmployeeT(333, 'Pete'), EmployeeT(444, 'Paul') ) ); 275 PL/SQL: Nested Tables SQL > select * from DeptInfo; DNUMBER DNAME PROJECTS(PNUMB, PNAME) EMPLOYEES(EMPSSN, EMPNAME) 10 Research NTPROJECTT( PROJECTT('1', 'Project X'), PROJECTT('2', 'Project Y')) NTEMPLOYEET( EMPLOYEET('111', 'Joe'), EMPLOYEET('222', 'Maria'), EMPLOYEET('333', 'Pete')) 20 Marketing NTPROJECTT( PROJECTT('1', 'Project Z')) NTEMPLOYEET( EMPLOYEET('333', 'Pete'), EMPLOYEET('444', 'Paul')) NOTE. To force column formatting you may have to issue the SQL-Plus commands column dname format a15 column Projects format a40 wrap column Employees format a40 wrap 276 PL/SQL Programming Objects 277 Oracle Objects Oracle OBJECTS Advantages of Objects • In general, the Oracle object-type model is similar to the class mechanism found in C++ and Java. • Objects consists of attributes and methods • Oracle objects features: encapsulation, single inheritance, polymorphism, and type evolution.. • No mapping layer is required between client-side objects and the relational database columns and tables that contain the object data. 278 Oracle Objects Oracle OBJECTS 279 Oracle Objects Creating/Using a Simple Oracle OBJECT drop type t_person_name; / CREATE TYPE t_Person_Name AS OBJECT ( first_name VARCHAR2(20), last_name VARCHAR2(20), middle_initial CHAR, INTERFACE Public Attributes Public Methods MEMBER FUNCTION Get_Name RETURN VARCHAR2, MEMBER PROCEDURE Capitalize ); / T_PERSON_NAME + first_name + last_name + middle_initial + Get_Name ( ) + Capitalize ( ) drop table emp_boss purge; drop type emp_boss_type; drop type t_person_name; 280 Oracle Objects Creating/Using an Oracle OBJECT CREATE TYPE BODY t_Person_Name AS IMPLEMENTATION MEMBER FUNCTION Get_Name RETURN VARCHAR2 IS BEGIN RETURN InitCap(Lower(first_name)) || ' ' || Upper(middle_initial) || ' ' || InitCap(Lower(last_name)); END; MEMBER PROCEDURE Capitalize IS BEGIN first_name := Upper(first_name); last_name := Upper(last_name); middle_initial := Upper(middle_initial); END; END; -- end create type -----------------------------/ 281 Oracle Objects Creating/Using an Oracle OBJECT >Desc t_person_name Name Type --------------------- -----------------------FIRST_NAME VARCHAR2(20) LAST_NAME VARCHAR2(20) MIDDLE_INITIAL CHAR(1) T_PERSON_NAME + first_name + last_name + middle_initial + Get_Name ( ) + Capitalize ( ) METHOD -----MEMBER FUNCTION GET_NAME RETURNS VARCHAR2 MEMBER PROCEDURE CAPITALIZE 282 Oracle Objects Oracle OBJECTS - Constructor • • As in Java, the language provides an implicit (default) constructor for each object type. Arguments of the default constructor are not defaulted, therefore you must supply a value for each parameter (even if that value is null) . EXAMPLES P t_person_type; … P := t_person_name(); -- ERROR invalid definition P := t_person_type(null, null, null); P := t_person_type('Maria', 'Macarena', 'S'); P := t_person_type('Jose', 'Cuervo', null); 283 Oracle Objects Creating/Using a simple Oracle OBJECT declare p t_person_name; begin p := t_person_name(null,null,null); select into from where T_PERSON_NAME + first_name + last_name + middle_initial + Get_Name ( ) + Capitalize ( ) fname, lname, minit p.first_name, p.last_name, p.middle_initial employee ssn=123456789; dbms_output.put_line('>>> ' || p.Get_Name() ); p.Capitalize(); dbms_output.put_line('>>> ' || p.last_name ); end; / >>> John B Smith >>> SMITH 284 Oracle Objects Creating a Table of Oracle OBJECTS • Object persistency could be achieved by defining Object-Tables (objects + optional primitive types) Create table MyAmigos of t_person_name; 285 Oracle Objects Creating a Table of Oracle OBJECTS T_PERSON_NAME + first_name + last_name + middle_initial CREATE TABLE objPeople OF t_Person_Name + Get_Name ( ) + Capitalize ( ) / INSERT INTO objPeople VALUES (t_Person_Name ('Maria','Macarena', 'S')); INSERT INTO objPeople VALUES (t_Person_Name ('Carmen','SanDiego', 'L')); INSERT INTO objPeople VALUES (t_Person_Name ('Harry','Potter', null) ); INSERT INTO objPeople VALUES (t_Person_Name ('Porky', 'Pig', null)) / 286 Oracle Objects Using a Table of Oracle OBJECTS T_PERSON_NAME >desc objPeople Name ----------------------------------FIRST_NAME LAST_NAME MIDDLE_INITIAL Type -----------VARCHAR2(20) VARCHAR2(20) CHAR(1) + first_name + last_name + middle_initial + Get_Name ( ) + Capitalize ( ) >select * from objPeople; FIRST_NAME -------------------Maria Carmen Harry Porky LAST_NAME -------------------Macarena SanDiego Potter Pig M S L 287 Oracle Objects Using a Table of Oracle OBJECTS > select p.*, p.get_name() from objPeople p where p.first_name like '_a%' FIRST_NAME ------------Maria Carmen Harry LAST_NAME ----------Macarena SanDiego Potter M S L T_PERSON_NAME + first_name + last_name + middle_initial + Get_Name ( ) + Capitalize ( ) P.GET_NAME() -----------------------Maria S Macarena Carmen L SanDiego Harry Potter 288 Oracle Objects Using a Table of Oracle OBJECTS select VALUE(p) from objPeople p; T_PERSON_NAME + first_name + last_name + middle_initial VALUE(P)(FIRST_NAME, LAST_NAME, MIDDLE_INITIAL) -----------------------------------------------T_PERSON_NAME('Maria', 'Macarena', 'S') T_PERSON_NAME('Carmen', 'SanDiego', 'L') T_PERSON_NAME('Harry', 'Potter', NULL) T_PERSON_NAME('Porky', 'Pig', NULL) select VALUE(p).Get_Name() from objPeople p where p.Last_Name LIKE 'Mac%'; + Get_Name ( ) + Nullify ( ) • The VALUE function returns rows as object instances. • The argument MUST be a correlated variable. VALUE(P).GET_NAME() -----------------------------------Maria S Macarena 289 Oracle Objects Using a Table of Oracle OBJECTS References • A REF is a logical pointer to a row object that is constructed from the object identifier (OID) of the referenced object and is an Oracle built-in datatype. • Function REF(e) requires e to be a correlation variable (alias). It returns the OID of a persistent (table bound) object. • REFs and collections of REFs model associations among objects, particularly many-to-one relationships, reducing the need for foreign keys. • REFs provide an easy mechanism for navigating between objects. You can use the dot notation to follow the pointers. • The DEREF(e) functions takes a pointer e and returns the object instance referenced by e. 290 Oracle Objects Using a Table of Oracle OBJECTS select REF(p), VALUE(p).Get_Name() from objPeople p where p.Last_Name LIKE 'Mac%'; REF(P) --------------------------------------------------VALUE(P).GET_NAME() -------------------------------------------------0000280209F7283D94DA8B4A6894E6043F14D4E48 26BCDD86D79E64B49801F9B5ADCFD3032010001960000 Maria S Macarena 291 Oracle Objects Creating/Linking Tables of Oracle OBJECTS drop table emp_boss purge / drop type emp_boss_type / CREATE TYPE emp_boss_type AS OBJECT ( emp VARCHAR2(30), boss REF t_Person_Name ) / CREATE TABLE emp_boss OF emp_boss_type; INSERT INTO emp_boss SELECT emp_boss_type ('Paula Salsa1', REF(b)) FROM objPeople b WHERE b.last_name LIKE 'Mac%' / INSERT INTO emp_boss SELECT emp_boss_type ('Jose Salsa2', REF(b)) FROM objPeople b WHERE b.last_name LIKE 'Mac%' / INSERT INTO emp_boss SELECT emp_boss_type ('Hermione', REF(b)) FROM objPeople b WHERE b.last_name LIKE 'Potter' / 292 Oracle Objects Creating/Linking Tables of Oracle OBJECTS 293 Oracle Objects Creating/Linking Tables of Oracle OBJECTS select e.emp, e.boss, (e.boss).last_name, (e.boss).Get_Name() from emp_boss e; EMP BOSS (E.BOSS).LAST_NAME (E.BOSS).GET_NAME() --------------- ------------------------------------- -------------------- -------------------Paula Salsa1 0000220208F7283D94DA8B4A6894E6043F14D Macarena Maria S Macarena 4E4826BCDD86D79E64B49801F9B5ADCFD3032 Jose Salsa2 0000220208F7283D94DA8B4A6894E6043F14D Macarena 4E4826BCDD86D79E64B49801F9B5ADCFD3032 Maria S Macarena Hermione 0000220208983E6EEDF6CB402D9B7133727B8 Potter A458D6BCDD86D79E64B49801F9B5ADCFD3032 Harry Reference to POTTER Potter Reference to MACARENA 294 Oracle Objects Creating/Linking Tables of Oracle OBJECTS select e.*, DEREF(e.boss) from emp_boss e; EMP BOSS --------------- ------------------------------------DEREF(E.BOSS)(FIRST_NAME, LAST_NAME, MIDDLE_INITIAL) ----------------------------------------------------Paula Salsa1 0000220208F7283D94DA8B4A6894E6043F14D 4E4826BCDD86D79E64B49801F9B5ADCFD3032 T_PERSON_NAME('Maria', 'Macarena', 'S') Jose Salsa2 0000220208F7283D94DA8B4A6894E6043F14D 4E4826BCDD86D79E64B49801F9B5ADCFD3032 T_PERSON_NAME('Maria', 'Macarena', 'S') Dereferencing REFs • Accessing the object referred to by a REF is called dereferencing the REF. • Oracle provides the DEREF operator to do this. • Dereferencing a dangling REF returns a null object (the predicate IS DANGLING could be used to test null REFs. Hermione 0000220208983E6EEDF6CB402D9B7133727B8 A458D6BCDD86D79E64B49801F9B5ADCFD3032 T_PERSON_NAME('Harry', 'Potter', NULL) 295 Oracle Objects Tables holding Objects and Simple DataTypes drop table objPeople2; drop type t_person_name_phone / CREATE TYPE t_Person_Name_Phone AS OBJECT ( phone VARCHAR2(20), guy t_person_name, MEMBER FUNCTION Get_Phone RETURN VARCHAR2 ); / CREATE TYPE BODY t_Person_Name_Phone AS MEMBER FUNCTION Get_Phone RETURN VARCHAR2 IS BEGIN if LENGTH(phone) < 10 then return '(216)' || phone; else return phone; end if; END; END; / 296 Oracle Objects Tables holding Objects and Simple DataTypes cont. create table objPeople2 of t_person_name_phone / insert into objPeople2 values ( t_person_name_phone t_person_name ); insert into objPeople2 values ( t_person_name_phone t_person_name ); insert into objPeople2 values ( t_person_name_phone t_person_name ); / ('2165551234', ('F1F1','L1L1', null) ) ('333-1234', ('F2F2','L2L2', null) ) ('444-4321', ('F3F3','L3L3', null) ) 297 Oracle Objects Tables holding Objects and Simple DataTypes cont. objPeople2 Guy [t_person_name] Phone First_Name Last_Name Midle_Initial 216 555 1234 F1F1 L1L1 Null 333 1234 F2F2 L2L2 Null 444 4321 F3F3 L3L3 Null 298 Oracle Objects Tables holding Objects and Simple DataTypes cont. CSUPERSON SQL> set describe depth all CSUPERSON SQL> desc objPeople2 Name Null? Type --------------------------------------- -------- ------------PHONE VARCHAR2(20) GUY T_PERSON_NAME FIRST_NAME VARCHAR2(20) LAST_NAME VARCHAR2(20) MIDDLE_INITIAL CHAR(1) METHOD -----MEMBER FUNCTION GET_NAME RETURNS VARCHAR2 MEMBER PROCEDURE CAPITALIZE 299 Oracle Objects Tables holding Objects and Simple DataTypes cont. select p.*, REF(p), p.get_phone(), value(p), p.guy.last_name, p.guy.get_name() from objPeople2 p where p.guy.first_name = 'F1F1‘; PHONE GUY ----------- ----------------------------------------------------------------2165551234 T_PERSON_NAME('F1F1', 'L1L1', NULL) 0000280209FBFC690AFB4C434C859A9796D037 . . . E46BCDD7092ADF0100058C0000 2165551234 2165551234 T_PERSON_NAME_PHONE('2165551234', T_PERSON_NAME('F1F1', 'L1L1', NULL)) L1L1 F1F1 L1L1 300 Oracle Objects Nested Objects STUDENTT Create or replace Type STUDENTT as object ( stud t_person_name, gpa number, Stud : T_PERSON_NAME First_name Last_name GPA Middle_initial static function NEW (pPerson t_person_name, pGpa number) return STUDENTT ); 301 Oracle Objects Nested Objects cont. create or replace type body STUDENTT as static function NEW ( pStud t_person_name, pGpa number) return StudentT is begin return StudentT (pStud , pGpa); end; end; / 302 Oracle Objects Tables holding Objects and Simple DataTypes cont. CSUPERSON SQL> desc studentT Name Null? ------------------------------------------- -------STUD FIRST_NAME LAST_NAME MIDDLE_INITIAL Type ------------T_PERSON_NAME VARCHAR2(20) VARCHAR2(20) CHAR(1) METHOD -----MEMBER FUNCTION GET_NAME RETURNS VARCHAR2 MEMBER PROCEDURE CAPITALIZE GPA NUMBER METHOD -----STATIC FUNCTION NEW RETURNS STUDENTT Argument Name Type ------------------------------ ----------------------PPERSON T_PERSON_NAME PGPA NUMBER In/Out Default? ------ -------IN IN 303 Oracle Objects Nested Objects cont. declare s1 studentT; Begin s1 := studentT.new( t_person_name('Juan', 'Valdez', null), 3.98 ); dbms_output.put_line dbms_output.put_line dbms_output.put_line dbms_output.put_line ('GPA: ' || ('First: ' || ('Last: ' || ('Get_Name: ' s1.gpa); s1.stud.first_name ); s1.stud.last_name ); || s1.stud.Get_Name() ); end; / GPA: 3.98 First: Juan Last: Valdez Get_Name: Juan Valdez 304 Oracle Objects Comparing Objects. • The MAP method is used to compare two objects. • It returns a scalar-value which can be used to compare and order the object type. • MAP accepts as its parameter the SELF built-in parameter. • An object type can only have one MAP method defined. 305 Oracle Objects Comparing Objects. Create or replace Type STUDENTT as object ( stud t_person_name, gpa number, map member function Merits return number, member function ShowMe return varchar2; static function NEW ( pStud t_person_name, pGpa number) return STUDENTT ) NOT FINAL; / 306 Oracle Objects Comparing Objects. create or replace type body STUDENTT as static function NEW ( pStud t_person_name, pGpa number) return StudentT is begin return StudentT (pStud , pGpa); end; member function ShowMe return varchar2 is begin return ' Sut. Name: ' || self.stud.get_name() || ' Gpa: ' || self.gpa; end; map member function Merits return number is begin return self.gpa; end; end; / 307 Oracle Objects Comparing Objects. T_PERSON_NAME + first_name + last_name + middle_initial + Get_Name ( ) + Capitalize ( ) STUDENTT + Stud + GPA t_person_name number + Merits ( ) return number + ShowMe ( ) return varchar2 + NEW ( t_person_name, number) 308 Oracle Objects Comparing Objects. declare --testing the MAP/ORDER function s1 studentT; s2 studentT; begin s1 := studentT.new( t_person_name('Juan', 'Valdez', null), 3.98 ); s2 := studentT.new( t_person_name('Diego','DeLaVega',null), 4.00); if s1 > s2 then dbms_output.put_line dbms_output.put_line else dbms_output.put_line dbms_output.put_line end if; end; ('Numero UNO is : ' || s1.stud.Get_Name() ); ('Numero DOS is : ' || s2.stud.Get_Name() ); ('Numero UNO is : ' || s2.stud.Get_Name() ); ('Numero DOS is : ' || s1.stud.Get_Name() ); Numero UNO is : Diego Delavega Numero DOS is : Juan Valdez 309 Oracle Objects Object Inheritance. INHERITANCE Create or replace Type Graduate_STUDENTT UNDER StudentT ( Credits NUMBER, overriding member function ShowMe return varchar2 ) NOT FINAL; / create or replace type body Graduate_STUDENTT as POLYMORPHISM overriding member function ShowMe return varchar2 is begin return ' Sut. Name: ' || self.stud.get_name() || ' Gpa: ' || self.gpa ' Credits: ' || self.credits; end; end; / 310 Oracle Objects Inheritance. T_PERSON_NAME + first_name + last_name + middle_initial + Get_Name ( ) + Capitalize ( ) STUDENTT HAS_A + Stud + GPA t_person_name number + Merits ( ) return number + ShowMe ( ) return varchar2 + NEW ( t_person_name, number) IS_A GRADUATE_STUDENTT + Credits number + ShowMe ( ) return varchar2 311 Oracle Objects Object Inheritance. declare --exploring the base class: STUDENTT (reviewing) ugs StudentT; begin ugs := studentT( t_person_name('Juan', 'Valdez', null), 3.98 ); dbms_output.put_line dbms_output.put_line dbms_output.put_line dbms_output.put_line ('First: ' || ('Last: ' || ('GPA: ' || ('Get_Name: ' gs.stud.first_name ); gs.stud.last_name ); gs.gpa); || gs.stud.Get_Name() ); dbms_output.put_line ('ShowMe>>> ' || gs.ShowMe() ); end; / First: Juan Last: Valdez GPA: 3.98 Get_Name: Juan ShowMe>>> Valdez Sut. Name: Juan Valdez Gpa: 3.98 312 Oracle Objects Object Inheritance. declare --extending the base class: STUDENTT gs Graduate_StudentT; begin gs := graduate_studentT( t_person_name('Juan', 'Valdez', null), 3.98, 132 ); dbms_output.put_line dbms_output.put_line dbms_output.put_line dbms_output.put_line ('First: ' || ('Last: ' || ('GPA: ' || ('Get_Name: ' gs.stud.first_name ); gs.stud.last_name ); gs.gpa); || gs.stud.Get_Name() ); dbms_output.put_line ('Credits: ' || gs.credits); dbms_output.put_line ('ShowMe >> ' || gs.ShowMe() ); end; / First: Juan Last: Valdez GPA: 3.98 Get_Name: Juan Valdez Credits: 132 ShowMe >> Sut. Name: Juan Valdez Gpa: 3.98 Credits: 132 313 PL/SQL Programming CURSORS 314 PL/SQL Cursors • A cursor is a type of pointer built into PL/SQL to provide sequential (one row at a time) access to the rows produced by a SQL statement. • Depending on the cursor’s life cycle and management code there are two types Implicit cursors (controlled by PL/SQL on execution of DML-maintenance or select … into statements) Explicit cursors (created with a select statement) 315 PL/SQL Cursors cursor points to the current row in the present table. It also provides additional information such as the availability of data, number of visited records, etc. Position Column1 … Column n 1 2 … … N-1 N 316 PL/SQL Cursors Attributes Attribute Definition %Found True when last FETCH operation finds a record in the result set made by the cursor. %NotFound True when last FETCH operation does not find a record. %RowCount Returns the count of records that have been fetched from the cursor at the current point. %IsOpen True when the cursor has been opened. 317 PL/SQL Implicit Cursors • Implicit cursors are opened/closed automatically by Oracle. • Created as a result of some operation on your PL/SQL. However you dot explicitly create any CURSOR type variable. • They could be created in association to the execution of – DML SQL code (insert, delete, update) – A CURSOR-FOR-LOOP 318 PL/SQL Implicit Cursors --Implicit cursor in SQL code Records affected: 0 No project found with given Pnumber Records affected: 6 PROCEDURE EXAMPLE12 AS BEGIN update Project set Pname = Pname where Pnumber = -1; Implicit cursor’s name is SQL dbms_output.put_line('Records affected: ' || sql%RowCount ); if sql%NotFound then dbms_output.put_line ('No project found with given end if; Pnumber'); update Project set Pname = Pname; dbms_output.put_line('Records affected: ' || sql%RowCount ); END; 319 PL/SQL Implicit Cursors For_Loop Cursor PROCEDURE EXAMPLE13 AS BEGIN CSUPERSON SQL> exec example13; Emp: Emp: Emp: Emp: Emp: John Smith makes $30000 Franklin Wong makes $40000 Ramesh Narayan makes $38000 Ahmad Jabbar makes $25000 James Borg makes $55000 for guy in (select fname, lname, salary from employee where sex = 'M') loop dbms_output.put (' Emp: ' || guy.Fname || ' ' || guy.Lname ); dbms_output.put (' makes $' || guy.salary ); end loop; END; 320 PL/SQL Explicit Cursors Why & When to Use EXPLICIT CURSORS – For full control over query processing, you can use explicit cursors in combination with the OPEN, FETCH, and CLOSE statements. – You might want to specify a query in one place but retrieve the rows somewhere else, even in another subprogram. – Or you might want to choose very different query parameters, such as ORDER BY or GROUP BY clauses, depending on the situation. – Or you might want to process some rows differently than others, and so need more than a simple loop. 321 PL/SQL Explicit Cursors Used with select statements returning more than one row. Explicit Cursors are declared with the construct CURSOR cursorName [(parameter(s)] [RETURN RowType ] IS some_Sql_Select_Statement; The life-cycle of the cursor involves • Open the cursor • Fetch a row from the cursor • Close the cursor 322 PL/SQL Cursors Attributes Every explicit cursor has access to the four attributes: %FOUND, %NOTFOUND, %ROWCOUNT, %ISOPEN OPEN First FETCH Next FETCH(es) Last FETCH CLOSE %FOUND %ISOPEN %NOTFOUND %ROWCOUNT before exception FALSE exception exception after NULL TRUE NULL 0 before NULL TRUE NULL 0 after TRUE TRUE FALSE 1 before TRUE TRUE FALSE 1 after TRUE TRUE FALSE data dependent before TRUE TRUE FALSE data dependent after FALSE TRUE TRUE data dependent before FALSE TRUE TRUE data dependent after exception FALSE exception exception Notes: 1. 2. Referencing %FOUND, %NOTFOUND, or %ROWCOUNT before a cursor is opened or after it is closed raises INVALID_CURSOR. After the first FETCH, if the result set was empty, %FOUND yields FALSE, %NOTFOUND yields TRUE, and %ROWCOUNT yields 0. 323 PL/SQL Explicit Cursors -- Get SSN of male employees Explicit definition of the cursor variable DECLARE CURSOR C1 IS SELECT ssn FROM employee WHERE sex = BEGIN ' M '; OPEN, CLOSE, FETCH is taken care of by the FOR-LOOP. FOR person IN C1 LOOP DBMS_OUTPUT.PUT_LINE('SSN = ' || person.ssn); END LOOP; END; / 324 PL/SQL Explicit Cursors -- Get Fname & Salary of male employees Explicit definition of the cursor variable DECLARE CURSOR C1 RETURN Employee%rowtype IS SELECT * FROM employee WHERE sex = 'M'; BEGIN OPEN, CLOSE, FETCH is taken care of FOR person IN C1 by the FOR-LOOP. LOOP DBMS_OUTPUT.PUT_LINE(person.Fname '' || person.Lname); END LOOP; END; John Smith Franklin Wong Ramesh Narayan Ahmad Jabbar James Borg Harry Potter 325 PL/SQL Explicit Cursors -- Find the top three projects using the highest number of employees of a given gender. PROCEDURE Proj_Using_Most_People_ByGender (TheGender Employee.Sex%Type) IS ProjId Works_On.Pno%TYPE; Explicit cursor finding the project ProjCnt number; CURSOR number and count of employees by gender. Get_People_Involved(Gender IN Employee.Sex%Type) IS select w.Pno, count(*) as TotalPeople from Works_On w, Employee e where e.Sex = Gender and e.Ssn = w.Essn TheGender = {M, F} group by w.Pno Cursor finds the projects employing the most order by TotalPeople desc; female/male workers in the company BEGIN open Get_People_Involved(TheGender); Just in case we don’t for i in 1..3 loop have three projects fetch Get_People_Involved into ProjId, ProjCnt; exit when Get_People_Involved%NOTFOUND; dbms_output.put_line (' Project: ' || ProjId ||' Personnel: ' || ProjCnt ); end loop; close Get_People_Involved; EXCEPTION when others then OPEN, CLOSE, FETCH is explicitly dbms_output.PUT_LINE ( 'Problems' ); managed by the user’s code END; 326 PL/SQL Explicit Cursors Find the three projects using the most FEMALE employees. CSUPERSON Project: Project: Project: SQL> exec Proj_Using_Most_People_ByGender ('F'); 30 Personnel: 2 1 Personnel: 1 2 Personnel: 1 PNO TotalPeople 30 2 1 1 2 1 10 1 20 1 Temporary table is created after the command OPEN Get_People_Involved(TheGender) is executed This row will be retrieved the second time the FETCH operator is executed 327 PL/SQL Explicit Cursors Find top three salaries in the company PROCEDURE EXAMPLE19 AS cursor ThreePeople is Select * from employee where rowNum <= 3 order by salary desc; OneEmp employee%rowType; Looking at rec. no. 1 Name: Wong Salary: 40000 Looking at rec. no. 2 Name: Smith Salary: 30000 Looking at rec. no. 3 Name: Zelaya Salary: 25000 BEGIN open ThreePeople; fetch ThreePeople into OneEmp; loop if ThreePeople%FOUND then dbms_output.put_line('Looking at rec. no. ' || ThreePeople%RowCount || ' Name: ' || OneEmp.Lname || ' Salary: ' || OneEMp.Salary ); fetch ThreePeople into OneEmp; elsif ThreePeople%NOTFOUND then exit; --finish the loop end if; end loop; if ThreePeople%ISOPEN then close ThreePeople; end if; END; 328 PL/SQL Ref Cursors Using Cursor Variables (REF CURSORs) • Implicit and Explicit cursors are static. They are defined at compile time. • Ref Cursors are dynamic in the sense they are created and processed at run-time. • You use them when you want to perform a SQL query in one (producer) subprogram, and process the results in a different (consumer) subprogram (possibly one written in a different language). 329 PL/SQL Ref Cursors Using Cursor Variables (REF CURSORs) • A result set could be produced by a PL/SQL Function or Procedure and returned using a SYS_REFCURSOR variable. • The OPEN-FOR statement associates a cursor variable with a multi-row query, executes the query, and identifies the result set. 330 PL/SQL Ref Cursors Get the name of each department and its corresponding manager PROCEDURE EXAMPLE15(Managers IN OUT SYS_REFCURSOR) AS BEGIN open Managers for select (e.Fname || ' ' || e.Lname) FullName, d.Dname from Department d, Employee e where (d.MgrSsn = e.SSN) ; END; CSUPERSON SQL> var x refCursor; CSUPERSON SQL> exec example15( :x ); PL/SQL procedure successfully completed. CSUPERSON SQL> print x; FULLNAME -------------------------Franklin Wong James Borg Jennifer Wallace DNAME --------------Research Headquarters Administration 331 PL/SQL Ref Cursors (Dynamically) Return a Database table using a SYS_RefCursor variable PROCEDURE EXAMPLE16 (theTable IN varchar2, myResult IN OUT SYS_REFCURSOR) AS BEGIN CSUPERSON SQL> var x RefCursor; if theTable = 'E' then CSUPERSON SQL> exec Example16('D', :x ); open myResult for CSUPERSON SQL> print x; select * from employee; DNAME DNUMBER MGRSSN MGRSTARTD elsif theTable = 'D' then --------------- ---------- --------- --------open myResult for Research 5 333445555 22-MAY-78 select * from department; Administration 4 987654321 01-JAN-85 elsif theTable = 'P' then Headquarters 1 888665555 19-JUN-71 open myResult for CSUPERSON SQL> exec Example16('P', :x); select * from project; CSUPERSON SQL> print x; else open myResult for PNAME PNUMBER PLOCATION DNUM select * from works_on; ----------------- ---------- ---------- ---------end if; ProductX 1 Bellaire 5 ProductY 2 Sugarland 5 END; ProductZ Computerization Reorganization Newbenefits 3 10 20 30 Houston Stafford Houston Stafford 5 4 1 4 332 PL/SQL Ref Cursors Passing Cursor Variables As Parameters • You can declare cursor variables as the formal parameters of functions and procedures. • The following example defines a REF CURSOR type, then declares a cursor variable of that type as a formal parameter: 333 PL/SQL Ref Cursors DECLARE TYPE EmpCurTyp IS REF CURSOR RETURN Employee%ROWTYPE; emp EmpCurTyp; -- Once we have a result set, we can process all the rows -- inside a single procedure rather than calling a procedure -- for each row. PROCEDURE process_emp_cv (emp_cv IN EmpCurTyp) IS person Employee%ROWTYPE; BEGIN dbms_output.put_line('-----'); dbms_output.put_line('Here are the names from the result set:'); LOOP ----FETCH emp_cv INTO person; Here are the names from the EXIT WHEN emp_cv%NOTFOUND; result set: dbms_output.put_line('Name = ' || person.fname ||' ' || person.lname); Name = John Smith END LOOP; Name = Franklin Wong END; Name = Alicia Zelaya Name = Jennifer Wallace BEGIN Name = Ramesh Narayan -- First find 10 arbitrary employees. Name = Joyce English OPEN emp FOR SELECT * FROM employee WHERE ROWNUM < 11; Name = Ahmad Jabbar process_emp_cv(emp); Name = James Borg CLOSE emp; ------ Then find employees matching a condition. Here are the names from the OPEN emp FOR SELECT * FROM employee WHERE lname LIKE 'W%'; result set: process_emp_cv(emp); Name = Franklin Wong CLOSE emp; Name = Jennifer Wallace END; / 334 PL/SQL Nested Cursors A cursor could be opened inside the scope of another. This nesting of cursors provides a powerful programming tool for more complex queries. Example: A department number is given. Find all the employees working for this department and their corresponding assignments. Provide only one row for each employee (regardless of the total number of projects in which they work) For instance Emp SSN: 333445555 Project(s): {2, 3, 10, 20} 335 PL/SQL Example 16 Nesting loops. Employees and their SET of projects PROCEDURE Example17(DeptNumber number) IS EmpRec Employee%ROWTYPE; WorkRec Works_On%ROWTYPE; Message1 VARCHAR2(40); Message2 VARCHAR2(40); Comma VARCHAR2(3):= ' '; CURSOR Guys_in_Dept ( DeptNumber number ) IS select * from employee where Dno = DeptNumber order by ssn; CURSOR Working_On ( EmpSsn varchar ) IS select * from works_on where Essn = EmpSsn order by pno; 336 PL/SQL Example 16 cont. --Nesting loops. Employees and their SET of projects BEGIN FOR EmpRec IN Guys_in_Dept(DeptNumber) LOOP Message1:= ' Emp SSN: ' || EmpRec.Ssn; Message2:= ' Project(s): {'; Comma := ''; FOR WorkRec IN Working_On(EmpRec.SSN) LOOP Message2:= Message2 || Comma || WorkRec.Pno; Comma := ', '; END LOOP; DBMS_OUTPUT.PUT_LINE(Message1 || Message2 || '}' ); END LOOP; EXCEPTION WHEN Others THEN DBMS_OUTPUT.PUT_LINE('Problems. ssn ' || EmpRec.SSN); END; SQL> exec Example16(5) Emp SSN: 123456789 Project(s): Emp SSN: 333445555 Project(s): Emp SSN: 453453453 Project(s): Emp SSN: 666884444 Project(s): {1, 2} {2, 3, 10, 20} {1, 2} {3} 337 PL/SQL Programming BATCH Processing. 338 PL/SQL Cursors – Batch Operations Batch Processing • A group of transactions (insert, delete, and update) are stored in a transaction table. • Those operations will be applied to the master database at a later time. Read one Transaction until no more data INSERTION code UPDATE code DELETE code INVALID OPERATIONS 339 PL/SQL Cursors – Batch Operations Example: The following work-assignments will be used to change the WORKS_ON table. Transactions: (i) insert, (d) delete, (u) update SQL> select * from WorkAction; SSN ---------123456789 123456789 666884444 666884444 123456789 PNUM ---1 10 3 1 30 OPER NEW_HOURS STATUS TIMEMARK ---- --------- ---------- --------u 10 24-SEP-06 i 15 25-SEP-06 d 25-SEP-06 u 40 26-SEP-06 y 10 27-SEP-06 master file SQL> select * from Works_On; ESSN PNO HOURS --------- --------- --------123456789 1 32.5 123456789 2 7.5 666884444 3 40 453453453 1 20 453453453 2 20 333445555 2 10 333445555 3 10 333445555 10 10 333445555 20 10 999887777 30 30 999887777 10 10 987987987 10 35 987987987 30 5 987654321 30 20 987654321 20 15 888665555 20 0 16 rows selected. 340 PL/SQL Cursors – Batch Operations /* -----------------------------------------------------------------** This program modifies the WORKS_ON table based on instructions ** stored in the WorkAction table. Each row of the ACTION table ** contains a (SSN,ProjecNumber) key, an action to be taken (u,i,d = ** insert, update, or delete), a number of Hours by which to update the ** work load, and a time stamp. ** ** On an insert, if the assignment already exists, an update is ** performed instead. On an update, if the assignment does not exist, ** it is created by an insert. On a delete, if the row does not ** exist, no action is taken. ** ------------------------------------------------------------------*/ Create or replace PROCEDURE BatchUpdate IS CURSOR cTransaction IS SELECT ssn, pnumb, oper_type, new_hours FROM WorkAction ORDER BY TimeMark FOR UPDATE OF Status; 341 PL/SQL Cursors – Batch Operations BEGIN FOR wload IN cTransaction LOOP -- process each row from WorkAction wload.oper_type := upper(wload.oper_type); /*------------------------------------------------------------* * Process an UPDATE. If the assignment to be updated doesn't * * exist, create a new assignment. * *------------------------------------------------------------*/ IF wload.oper_type = 'U' THEN BEGIN UPDATE works_on SET hours = wload.new_Hours WHERE (essn = wload.ssn) AND (pno = wload.Pnumb); IF ( SQL%NOTFOUND )THEN /* work load didn't exist. Create it. */ INSERT INTO Works_On VALUES (wload.ssn, wload.pnumb, wload.new_Hours); UPDATE WorkAction SET status = 'Update: ID not found. Value inserted.' WHERE CURRENT OF cTransaction; ELSE UPDATE WorkAction SET status = 'Update: Success.' WHERE CURRENT OF cTransaction; END IF; END; 342 PL/SQL Cursors – Batch Operations /*------------------------------------------------------------* * Process an INSERT. If the assignment already exists, do an * * update of the assignment instead. * *-------------------------------------------------------------*/ ELSIF wload.oper_type = 'I' THEN BEGIN INSERT INTO Works_On VALUES (wload.ssn, wload.pnumb, wload.new_Hours); UPDATE WorkAction SET status = 'Insert: Success.' WHERE CURRENT OF cTransaction; EXCEPTION WHEN DUP_VAL_ON_INDEX THEN /* assignment already exists */ UPDATE Works_On SET Hours = wload.new_Hours WHERE (essn = wload.ssn) AND (pno = wload.Pnumb); UPDATE WorkAction SET status = 'Insert: exists. Updated instead.' WHERE CURRENT OF cTransaction; END; 343 PL/SQL Cursors – Batch Operations /*-------------------------------------------------------------* * Process a DELETE. If the assignment doesn't exist, set the * * status field to say that the assignment wasn't found. * *--------------------------------------------------------------*/ ELSIF wload.oper_type = 'D' THEN BEGIN DELETE FROM Works_On WHERE (essn = wload.ssn) AND (pno = wload.Pnumb); IF ( SQL%NOTFOUND ) THEN -- work assignment didn't exist. UPDATE WorkAction SET status = 'Delete:ID not found.' WHERE CURRENT OF cTransaction; ELSE UPDATE WorkAction SET status = 'Delete: Success.' WHERE CURRENT OF cTransaction; END IF; END; 344 PL/SQL Cursors – Batch Operations /*-----------------------------------------------------------* * The requested operation is invalid. * *------------------------------------------------------------*/ ELSE /* oper_type is invalid */ UPDATE workAction SET status = 'Error op. No action taken.' WHERE CURRENT OF cTransaction; END IF; END LOOP; COMMIT; END; 345 PL/SQL Cursors – Batch Operations WORKACTION: Transaction file BEFORE updating. SSN PNUMB O NEW_HOURS STATUS --------- ---------- - ---------- ---------------------------------------123456789 1 U 10 123456789 10 I 15 666884444 3 D 666884444 1 U 40 123456789 30 X 10 TIMEMARK --------24-JAN-05 25-JAN-05 25-JAN-05 26-JAN-05 27-JAN-05 WORKACTION: Transaction file AFTER update SSN PNUMB O NEW_HOURS STATUS --------- ---------- - ---------- ---------------------------------------123456789 1 U 10 Update: Success. 123456789 10 I 15 Insert: Success. 666884444 3 D Delete: Success. 666884444 1 U 40 Update: ID not found. Value inserted. 123456789 30 X 10 Error op. No action taken. TIMEMARK --------24-JAN-05 25-JAN-05 25-JAN-05 26-JAN-05 27-JAN-05 346 PL/SQL Cursors – Batch Operations Works_ON table BEFORE updating Works_On table AFTER updating ESSN PNO HOURS --------- ---------- ---------123456789 1 32.5 123456789 2 7.5 333445555 2 10 333445555 3 10 333445555 10 10 333445555 20 10 453453453 1 20 453453453 2 20 666884444 3 40 888665555 20 0 987654321 20 15 987654321 30 20 987987987 10 35 987987987 30 5 999887777 10 10 999887777 30 30 ESSN PNO HOURS --------- ---------- ---------123456789 1 10 123456789 2 7.5 123456789 10 15 333445555 2 10 333445555 3 10 333445555 10 10 333445555 20 10 453453453 1 20 453453453 2 20 666884444 1 40 888665555 20 0 987654321 20 15 987654321 30 20 987987987 10 35 987987987 30 5 999887777 10 10 999887777 30 30 347 PL/SQL Cursors – Batch Operations Try the example using the WorkAction table specification given below drop table WORKACTION; create table WORKACTION ( SSN VARCHAR2(9), PNUMB NUMBER(5), OPER_TYPE VARCHAR2(1), NEW_HOURS NUMBER(5,1), STATUS VARCHAR2(40), TIMEMARK DATE); INSERT INSERT INSERT INSERT INSERT INTO INTO INTO INTO INTO WORKACTION WORKACTION WORKACTION WORKACTION WORKACTION VALUES VALUES VALUES VALUES VALUES (123456789, (123456789, (666884444, (666884444, (123456789, 1 , 10, 3 , 1 , 30, 'U', 'I', 'D', 'U', 'X', 10, 15, NULL, 40, 10, NULL, NULL, NULL, NULL, NULL, '24-JAN-2005' '25-JAN-2005' '25-JAN-2005' '26-JAN-2005' '27-JAN-2005' ); ); ); ); ); COMMIT; 348 PL/SQL Programming EXCEPTIONS 349 PL/SQL Exceptions • PL/SQL implements error detection and processing with Exceptions and Exception Handlers. • Errors fall into two categories – Compile-Time errors (PL/SQL compiler is responsible) – Run-Time errors (programmer is on charge) • Undetected run-time errors produce abnormal program termination –and usually total loss of the job. 350 PL/SQL Exceptions The exception handler is similar to a CASE statement in which the programmer could detect what type of error has been risen Syntax is EXCEPTION [WHEN exceptionExpression1 THEN statement(s); [WHEN exceptionExpression2 THEN statement(s); [ . . . ] ] ] exceptionExpression could be – An Oracle pre-defined exception – An user-define condition – A PRAGMA EXCEPTION_INIT exception. 351 PL/SQL Exceptions • An internal exception is raised automatically if your PL/SQL program violates an Oracle rule or exceeds a system-dependent limit. • PL/SQL predefines some common Oracle errors as exceptions. – For example, PL/SQL raises the predefined exception NO_DATA_FOUND if a SELECT INTO statement returns no rows. 352 PL/SQL Exceptions PL/SQL declares predefined exceptions globally in package STANDARD (You don’t need to declare them yourself) Oracle Error Exception Description ORA-0001 DUP_VAL_ON_INDEX Unique constraint key violated ORA-0051 TIMEOUT_ON_RESOURCE Timeout occurred while waiting for a resource ORA-0904 INVALID_COLUMN_NAME Illegal name ORA-1001 INVALID_CURSOR Illegal cursor operation ORA_1403 NO_DATA_FOUND No data found ORA_1422 TOO_MANY_ROWS A SELEC INTO statement matches more than one row ORA-1722 INVALID_NUMBER Conversion to a number failed. See. User’s Guide and Reference. Handling Run-Time PL/SQL Errors. 353 PL/SQL Exceptions User-defined-Exceptions. Ordering a cup of coffee. PROCEDURE EXAMPLE30 (LiquidTemperature NUMBER) AS too_hot EXCEPTION; too_cold EXCEPTION; BEGIN --order a cup of coffee IF LiquidTemperature < 90 THEN RAISE too_cold; ELSIF LiquidTemperature > 104 THEN RAISE too_hot; ELSE dbms_output.put_line('Uhhh, good coffee!!!'); END IF; EXCEPTION WHEN too_cold THEN dbms_output.put_line('Problem - call manager and complaint'); WHEN too_hot THEN dbms_output.put_line('Problem - call a good lawyer and sue'); WHEN OTHERS THEN dbms_output.put_line('Problem - just complaint'); END; 354 PL/SQL Exceptions User-Defined-Cond. Raise exception if video (book, bill, …) is overdue. PROCEDURE EXAMPLE31 AS past_due EXCEPTION; due_date DATE := SYSDATE - 1; todays_date DATE := SYSDATE; acct_num NUMBER; BEGIN CSUPERSON SQL> exec example17 IF due_date < todays_date THEN RAISE past_due; Problem – (my) PAST_DUE exception. END IF; EXCEPTION WHEN past_due THEN --take care of the user-defined exception here dbms_output.put_line('Problem – (my) PAST_DUE exception.'); --due something here (if possible!) --to correct the problem WHEN OTHERS THEN dbms_output.put_line('Problem-something really bad happened'); END; 355 PL/SQL Exceptions PRAGMA EXCEPTION_INIT • Pragmas are pseudo-instructions telling the compiler to do something. • PRAGMA EXECTION_INIT is a mechanism the developer could use to trick Oracle into accepting that one of the developer’s exceptions is (temporarily) replacing a pre-defined Oracle condition. • You can use the pragma EXCEPTION_INIT to associate exception names with other Oracle error codes that you can anticipate. 356 PL/SQL Exceptions Write your own version of exception ORA-0904 “Invalid Column Name” PROCEDURE EXAMPLE32 (tableName varchar2, updColumnName varchar2, updColumnValue varchar2, keyName varchar2, keyValue varchar2) as MyInvalid_Column EXCEPTION; PRAGMA EXCEPTION_INIT (MyInvalid_Column, -904); cmd_Update_Text varchar2(200); begin cmd_Update_Text := 'update ' || tableName || ' set ' || updColumnName || ' = ''' || updColumnValue || '''' || ' where ' || keyName || ' = ' || keyValue ; execute immediate cmd_Update_Text; --dynamic SQL code exception when MyInvalid_Column then dbms_output.put_line('**MY ERROR** invalid column name in command '); dbms_output.put_line(cmd_Update_Text); dbms_output.put_line('SqlCode: ' || sqlcode); dbms_output.put_line('SqlErrm: ' || sqlErrm); end; exec Example32 ('project','Pnameeee','XYZ','Pnumber', '10'); **MY ERROR** invalid column name in command update project set Pnameeee = 'XYZ' where Pnumber = 10 SqlCode: -904 SqlErrm: ORA-00904: "PNAMEEEE": invalid identifier 357 PL/SQL Example19 cont. Observations • Dynamic PL/SQL. Some programs must build and process SQL statements where some information is not known in advance. • Dynamic SQL statements are made as strings at run time. The strings contain the text of a SQL statement or PL/SQL block. • To process most dynamic SQL statements, you use the EXECUTE IMMEDIATE statement. 358 PL/SQL Example19 cont. Apply the following changes on Example19 1. Change “Pnameeee” to “Pname” on line Exec Example32('project','Pnameeee','XYZ','Pnumber', '10'); 1. Change error code “904” to “7777” on line PRAGMA EXCEPTION_INIT (invalid_Column, -904); 359 PL/SQL Exceptions Defining Your Own Error Messages: Procedure RAISE_APPLICATION_ERROR The procedure RAISE_APPLICATION_ERROR lets you issue user-defined ORAerror messages from stored subprograms. That way, you can report errors to your application and avoid returning unhandled exceptions. Syntax raise_application_error (error_number, message[, {TRUE | FALSE}]); • error_number is a negative integer in the range -20000 .. -20999 • message is a character string up to 2048 bytes long. • If the optional third parameter is TRUE, the error is placed on the stack of previous errors. If the parameter is FALSE (the default), the error replaces all previous errors. • An application can call raise_application_error only from an executing stored subprogram (or method). • When called, raise_application_error ends the subprogram and returns a user-defined error number and message to the application. • The error number and message can be trapped like any Oracle error. 360 PL/SQL Exceptions Defining Your Own Error Messages: Procedure RAISE_APPLICATION_ERROR DECLARE num_tables NUMBER; BEGIN SELECT COUNT(*) INTO num_tables FROM USER_TABLES; -- Issue your own error code (ORA-20101) with your own error message. IF num_tables < 1000 THEN raise_application_error(-20101, 'You need to work harder -- ' || ' Expecting at least 1000 tables'); ELSE -- Do the rest of the processing (for the non-error case). NULL; END IF; END; / 361 PL/SQL Programming PACKAGES 362 PL/SQL Packages • A package is a schema object that groups logically related PL/SQL objects • Packages usually have two parts, a specification and a body (sometimes the body is unnecessary). • The specification is the interface to the package. It declares the – – – – – – • types, variables, constants, exceptions, cursors, and the signature of the subprograms that can be referenced from outside the package. The body defines the queries for the cursors and the code for the subprograms. 363 PL/SQL Packages Syntax Package Specification PACKAGE package_name IS [ declaration of public variables and user-defined-types] [ specification of constants, exceptions ] [ specification of cursors ] [ signatures of procedures and functions ] END [package_name ]; 364 PL/SQL Packages Package body definition PACKAGE BODY package_name IS [ private variables, constants, exceptions and types ] [ CURSOR specifications and SELECT stmts ] [ Specification of Procedures and Functions ] [ BEGIN ] [ Executable statements ] [ EXCEPTION ] [ Exception handlers ] END [ package_name ] 365 PL/SQL Packages Advantages of PL/SQL Packages • Modularity • • Easier Application Design • • With packages, you can specify which types, items, and subprograms are public (visible and accessible) or private (hidden and inaccessible). Added Functionality • • When designing an application, all you need initially is the interface information in the package specs. Information Hiding • • Packages let you encapsulate logically related types, items, and subprograms in a named PL/SQL module. Packaged public variables and cursors persist for the duration of a session. Better Performance • When you call a packaged subprogram for the first time, the whole package is loaded into memory. Later calls to related subprograms in the package require no disk I/O. 366 PL/SQL Packages Example of a PL/SQL Package • The example below shows the package way of how to support operations for the company database. • The package contains – A constant, a global variable, a cursor, an exception, and subs. • The procedures hire_employee and fire_employee – Check the existence of the person, verifies business rules and either executes or aborts execution by raising an exception. 367 PL/SQL Packages The Package Specification (Interface) PACKAGE Emp_Actions AS BAD_OPERATION myPI myStr TYPE EmpRecTyp CURSOR desc_salary EXCEPTION; NUMBER := 3.141592; VARCHAR2(120); IS RECORD (SSN VARCHAR2(9), salary NUMBER(10,2)); RETURN EmpRecTyp; PROCEDURE hire_employee ( pFNAME VARCHAR2 ,pMINIT VARCHAR2 ,pLNAME VARCHAR2 ,pSSN VARCHAR2 ,pBDATE DATE ,pADDRESS VARCHAR2 ,pSEX VARCHAR2 ,pSALARY NUMBER ,pSUPERSSN VARCHAR2 ,pDNO NUMBER ); PROCEDURE fire_Employee (pSSN NUMBER); END emp_actions; 368 PL/SQL Packages The Package Implementation PACKAGE BODY emp_actions AS CURSOR desc_salary RETURN SELECT FROM ORDER EmpRecTyp IS SSN, salary employee BY salary DESC; PROCEDURE fire_Employee (pSSN NUMBER) IS myCount NUMBER; BEGIN --here you add logic to make sure the employee could be --safely removed from the table (without violating any --business rules and/or referential integrity constraints) select count(*) into myCount from employee where ssn = pSSN; if (myCount = 0) then --employee not found RAISE emp_actions.BAD_OPERATION; end if; DELETE FROM employee WHERE SSN = pSSN; END fire_Employee; 369 PL/SQL Packages PROCEDURE hire_employee ( pFNAME VARCHAR2 ,pMINIT VARCHAR2 ,pLNAME VARCHAR2 ,pSSN VARCHAR2 ,pBDATE DATE ,pADDRESS VARCHAR2 ,pSEX VARCHAR2 ,pSALARY NUMBER ,pSUPERSSN VARCHAR2 ,pDNO NUMBER ) IS BEGIN --Here you add logic to validate the data and make sure --no business rules are violated INSERT INTO employee VALUES ( pFNAME ,pMINIT ,pLNAME ,pSSN ,pBDATE ,pADDRESS ,pSEX ,pSALARY ,pSUPERSSN ,pDNO ); EXCEPTION When others then raise emp_actions.BAD_OPERATION; END hire_employee; END emp_actions; 370 PL/SQL Packages Testing the package (assume you are CSUperson/euclid) 1. CSUPERSON SQL> grant execute on emp_actions to scott; 2. Connect scott/tiger 3. Scott SQL> select * from csuPerson.employee; … insuficient priviledges 4. SCOTT SQL> exec csuperson.emp_actions.fire_employee(123); BEGIN csuperson.emp_actions.fire_employee(123); END; ERROR at line 1: ORA-06510: PL/SQL: unhandled user-defined exception ORA-06512: at "CSUPERSON.EMP_ACTIONS", line 17 ORA-06512: at line 1 5. SCOTT SQL> var X number; 6. SCOTT SQL> exec :x := emp_actions.myPi; 7. SCOTT SQL> print x; 8. X 9. -----------10. 3.1415 371 PL/SQL Programming TRIGGERS 372 PL/SQL Triggers • Triggers are SERVER SIDE stored blocks of code that execute -or fire- automatically when some database events occur. • Triggers are similar to parameter-less procedures, however they can not be called directly. • Triggers can NOT (call code or) include the COMMIT / ROLLBACK statements. • Traditionally triggers are associated to maintenance of database tables and are invoked by the run-time Oracle processes on DELETE, UPDATE, INSERT operations. • User Event Triggers could fire on non-maintenance events such as: startup, shutdown, create, alter, drop, grant, revoke, ... operations. • In general, triggers appear to execute quietly without the user even knowing of their existence. 373 PL/SQL Triggers. Example Insert into works_on(Essn,Pno, Hours) values (123456789, 10, 40); WORKS_ON table ESSN PNO HOURS --------- ---------- ---------123456789 1 32.5 123456789 2 7.5 666884444 3 40 453453453 1 20 453453453 2 20 333445555 2 10 333445555 3 10 333445555 10 10 333445555 20 10 999887777 30 30 999887777 10 10 987987987 10 35 987987987 30 5 987654321 30 20 987654321 20 15 888665555 20 0 TRIGGER T008B BEFORE INSERT OR UPDATE OR DELETE ON WORKS_ON FOR EACH ROW . . . . . . TRIGGER T008A BEFORE INSERT OR UPDATE OR DELETE ON WORKS_ON BEGIN --Employees can not work more than 40 hours/week . . . . . . END; ERROR at line 1: ORA-20008: T008A - Excessive work load violation (PK_WORKS_ON) Employee 123456789, Project 10, Hours 4. Operation rejected. ORA-04088: error during execution of trigger 'CSUPERSON.T008A' 374 PL/SQL Triggers Uses of triggers • • • • • • Provide sophisticated auditing / logging Prevent invalid transactions Enforce referential integrity Enforce data integrity Enforce complex business rules Enforce complex security authorizations 375 PL/SQL Triggers Consulting Database Dictionary desc user_triggers; select trigger_name from user_triggers where table_name='WORKS_ON'; Enable / Disable Alter trigger T001 enable; Alter trigger T001 disable; ALTER TABLE Employee ENABLE ALL TRIGGERS; ALTER TABLE Employee DISABLE ALL TRIGGERS; DROP TRIGER triggerName; 376 PL/SQL Triggers Abbreviated Trigger syntax Create or replace trigger triggerName { before | after | instead of } eventClause on [ schema | database ] [ referencing new … old ] [ for each row ] [ when condition ] PL/SQL block | call_procedure_statement 377 PL/SQL Triggers Row Triggers and Statement Triggers When you define a trigger, you can specify the number of times the trigger action is to be run: • Once for every row affected by the triggering statement, such as a trigger fired by an UPDATE statement that updates many rows • Once for the triggering statement, no matter how many rows it affects. 378 PL/SQL Triggers Statement Triggers A statement trigger is fired once on behalf of the triggering statement (regardless of the number of rows in the table that the triggering statement affects, even if no rows are affected). For example, if a DELETE statement deletes several rows from a table, a statement-level DELETE trigger is fired only once. Statement triggers are useful if the code in the trigger action does not depend on the data provided by the triggering statement or the rows affected. For example, use a statement trigger to: – Make a complex security check on the current time or user – Generate a single audit record. 379 PL/SQL Triggers Row Triggers A row trigger is fired each time the table is affected by the triggering statement. For example, if an UPDATE statement modifies multiple rows of a table, a row trigger is fired once for each row affected by the UPDATE statement. – If a triggering statement affects no rows, a row trigger is not run. – Row triggers are useful if the code in the trigger action depends on data provided by the triggering statement or rows that are affected (New: Old: data). 380 PL/SQL Triggers TRIGGER "CSUPERSON"."T4S" AFTER UPDATE ON "CSUPERSON"."PROJECT" BEGIN dbms_output.put_line ('Greetings from T4S your humble STATEMENT LEVEL trigger'); END; TRIGGER "CSUPERSON"."T4ROW" AFTER UPDATE ON "CSUPERSON"."PROJECT" REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW BEGIN dbms_output.put_line ('Greetings from T4ROW your useful ROW LEVEL trigger'); END; CSUPERSON SQL>update PROJECT set pname=pname; Greetings from T4ROW your useful ROW LEVEL trigger Greetings from T4ROW your useful ROW LEVEL trigger Greetings from T4ROW your useful ROW LEVEL trigger Greetings from T4ROW your useful ROW LEVEL trigger Greetings from T4ROW your useful ROW LEVEL trigger Greetings from T4ROW your useful ROW LEVEL trigger Greetings from T4S your humble STATEMENT LEVEL trigger 381 PL/SQL Triggers BEFORE Triggers BEFORE triggers run the trigger action before the triggering statement is run. This type of trigger is commonly used in the following situations: – When the trigger action determines whether the triggering statement should be allowed to complete. Using a BEFORE trigger for this purpose, you can eliminate unnecessary processing of the triggering statement and its eventual rollback in cases where an exception is raised in the trigger action. – To derive specific column values before completing a triggering INSERT or UPDATE statement. AFTER Triggers AFTER triggers run the trigger action after the triggering statement is run. 382 PL/SQL Before-After Triggers TRIGGER T007A BEFORE INSERT OR UPDATE DECLARE oldSalary NUMBER; BEGIN select sum(salary) into oldSalary insert into MYLOG values (sysdate 'Before Salary: END; OR DELETE ON EMPLOYEE from employee; || ' ' || user, ' || oldsalary); TRIGGER T007B AFTER INSERT OR UPDATE OR DELETE ON EMPLOYEE DECLARE newSalary NUMBER; BEGIN select sum(salary) into newSalary from employee; insert into MYLOG values ( sysdate || ' ' || user, 'After Salary: ' || newsalary); END; 383 PL/SQL Before-After Triggers CSUPERSON SQL>select sum(salary) from employee; SUM(SALARY) ----------281000 1 row selected. CSUPERSON SQL>update employee set salary=salary+100000 where ssn=123456789; 1 row updated. CSUPERSON SQL>select * from mylog; C1 -------------------------------------------------04-OCT-06 CSUPERSON 04-OCT-06 CSUPERSON C2 -------------------------Before Salary: 281000 After Salary: 381000 384 PL/SQL Triggers – Row Level Example: --Business Rule 1: Job assignments to project 10 must be between 4 & 6 hours. TRIGGER CSUPERSON.T001 BEFORE INSERT OR UPDATE OF hours ON CSUPERSON.WORKS_ON REFERENCING OLD AS OLD_VALUE NEW AS NEW_VALUE FOR EACH ROW WHEN (new_value.pno = 10) BEGIN if :new_value.hours >= 6 or :new_value.hours <= 4 then raise_application_error (-20001, 'Proj. 10 load must be between 4-6 hours/week' ); end if; END; 385 PL/SQL Triggers Testing T001 Add employee 1 to project 10 for a total of 9 hours/week. CSUPERSON SQL> insert into works_on values (1,10,9); insert into works_on values (1,10,9) * ERROR at line 1: ORA-20001: Proj. 10 load must be between 4-6 hours/week ORA-06512: at "CSUPERSON.T001", line 4 ORA-04088: error during execution of trigger 'CSUPERSON.T001' create table myLog (C1 varchar2(40), C2 varchar2(40) ); 386 PL/SQL Triggers Observation Firing trigger on a particular column BEFORE INSERT OR UPDATE OF hours ON CSUPERSON.WORKS_ON Aliasing :NEW :OLD REFERENCING OLD AS OLD_VALUE NEW AS NEW_VALUE Note: Goal of [optional] referencing clause is improving readability ‘NEW’ could be referenced as: Nuevo, Kotha, Novo, Nouveau, Neu, … ‘OLD’ could be referenced as: Viejo, Patha, Velho, Vieux, Alt, … 387 PL/SQL Triggers EXAMPLE 2 • The following trigger is fired when a new dependent record is inserted. • If the record corresponds to a new born child ( < 1 year) several gifts are ordered for the baby. • If the new entry is not related to an existing employee the insertion is rejected (referential integrity). • Assume there is a logging table MYLOG(c1,c2) 388 PL/SQL Triggers TRIGGER T002 BEFORE INSERT on Dependent FOR EACH ROW DECLARE mName varchar(20):= NULL; BEGIN select Lname into mName from employee where ssn = :new.Essn; if (months_between(sysdate,:new.Bdate) < 12 ) then insert into Mylog values (sysdate || ' Personnel Dept-->', 'Send NEW_BABY greeting card to '|| mName); insert into Mylog values (sysdate || ' Marketing Dept.-->', 'Mail $100 company stock to '|| mName); insert into mylog values (sysdate || ' Purchasing Dpt.-->', 'Order one-year diapers for '|| mName); end if; EXCEPTION when others then raise_application_error(-20002,'Dependent problem: ' || :new.essn ); END; 389 PL/SQL Triggers Testing T002 -- Anonymous block to test T002 DECLARE BEGIN -- Intentionally done! this insertion will be rejected BEGIN insert into dependent values (777000777, 'Angel' , 'M', '03-SEP-96','Son' ); EXCEPTION when others then dbms_output.put_line (sqlErrm ); END; -- This second insertion is valid and will be accepted -- all the gifts will be ordered for the new baby (change bdate!). BEGIN insert into dependent values (123456789, 'Jose', 'M', SYSDATE + 30, 'Son' ); END; -- This third insertion is valid and will be accepted -- not a baby. BEGIN insert into dependent values (123456789, 'Joe', 'M', '04-JUL-2001', 'Son' ); END; END; 390 PL/SQL Triggers Testing T002 ORA-20002: Dependent problem: 777000777 ORA-06512: at "CSUPERSON.T002", line 24 ORA-04088: error during execution of trigger 'CSUPERSON.T002' MYLOG F1 --------------------------------------15-FEB-05 Personnel Dept--> 15-FEB-05 Marketing Dept.--> 15-FEB-05 Purchasing Dpt.--> F2 --------------------------------------Send NEW_BABY greeting card to Smith Mail $100 company stock to Smith Order one-year diapers for Smith DEPENDENT ESSN --------333445555 333445555 333445555 987654321 123456789 123456789 123456789 123456789 123456789 DEPENDENT_ ---------Alice Theodore Joy Abner Michael Alice Elizabeth Jose Joe S F M F M M F F M M BDATE --------05-APR-76 02-OCT-73 03-MAY-48 29-FEB-32 01-JAN-78 31-DEC-78 05-MAY-57 01-JAN-05 04-JUL-01 RELATIONSH ---------Daughter Son Spouse Spouse Son Daughter Spouse Son Son 391 PL/SQL Triggers Your turn → How to enforce REFERENTIAL INTEGRITY? Try a ROW LEVEL trigger fixing the CASCADE/NULLIFY/MANDATORY retention modes of EMPLOYEE and DEPENDENT. Extend the results for dealing with job assignments and other meaningful relationships in the database. 392 PL/SQL Triggers Your turn → How to enforce REFERENTIAL INTEGRITY? A typical way of defining referential integrity using DDL is given below ALTER TABLE table_name ADD CONSTRAINT constraint_name FOREIGN KEY (col1, col2) REFERENCES table_2 (cola,colb); To enforce/ignore the constraint we may use the statements ALTER TABLE table-name DISABLE constraint-specification; ALTER TABLE table-name ENABLE constraint-specification; You may choose to enforce referential integrity using a trigger. For instance DEPENDENT.ESSN → EMPLOYEE.SSN could be implemented as follows 393 PL/SQL Triggers Your turn → How to enforce REFERENTIAL INTEGRITY? --enforcing the rule: DEPENDENT.ESSN → EMPLOYEE.SSN TRIGGER T007C BEFORE INSERT ON DEPENDENT FOR EACH ROW DECLARE empCounter number; BEGIN -- is there a PARENT employee with the given Soc. Sec. Numb. ? select count(*) into empCounter from employee where ssn = :new.essn; -- if not, throw an EXCEPTION if empCounter = 0 then raise_application_error ('20004', 'REF. INTEGRITY VIOLATION ' || :new.essn); end if; END; 394 PL/SQL Statement-Level Triggers Example: Tracking the salary gap between male and female employees TRIGGER "CSUPERSON"."T007" AFTER INSERT or UPDATE ON "CSUPERSON"."EMPLOYEE" DECLARE LadiesSal NUMBER; GentSal NUMBER; BEGIN select avg(salary) into LadiesSal from employee where sex = 'F'; select avg(salary) into GentSal from employee where sex = 'M'; If (GentSal - LadiesSal) > 10000 then insert into myLOG values ( 'WARNING', 'Salary gap between ladies and gentlemen exceeds $10K'); end if; END; 395 PL/SQL Triggers Example: Statement Level trigger. Execute only once for the entire statement ( rather than on a row-by-row basis ). TRIGGER "CSUPERSON"."T003" BEFORE INSERT OR UPDATE OR DELETE ON "CSUPERSON"."WORKS_ON" BEGIN -- RULE: Only CSUPERSON is allowed to change the works_on table if user != 'CSUPERSON' then raise_application_error(-20004, 'T003. You are not authorized to make changes on Works_On' ); end if; -- RULE: changes to WORKS_ON are accepted only from Mon-Fri between -- working hours 8:00 AM. and 6:00 PM. Reject otherwise. if (to_char(sysdate,'HH24:MI') not between '08:00' and '18:00' ) or (to_char(sysdate,'DY') in ('SAT', 'SUN') ) then raise_application_error(-20005, 'T003. Changes are only accepted during normal office hours' ); end if; END; 396 PL/SQL Triggers Tell me more! What is going on? You may use the following predicates to exactly determine what is happening in the trigger’s code: • if INSERTING then … • if DELETING then … • if UPDATING then … 397 PL/SQL Triggers T004. Testing the action Predicates. TRIGGER "CSUPERSON"."T004" BEFORE INSERT OR UPDATE OR DELETE ON "CSUPERSON"."PROJECT" FOR EACH ROW BEGIN --tell us what is going on during the execution of the trigger /************************************************************* INSERTING (only :NEW data exists for the record) *************************************************************/ if INSERTING then dbms_output.put_line ('Inserting a new Project '); dbms_output.put_line (:NEW.Pname || '-' || :NEW.Pnumber|| '-' || :NEW.Plocation || '-' || :NEW.Dnum); return; end if; /************************************************************* DELETING (there is no :NEW image - ask only for :OLD) *************************************************************/ if DELETING then dbms_output.put_line ('Deleting an old Project '); dbms_output.put_line (:OLD.Pname || '-' || :OLD.Pnumber|| '-' || :OLD.Plocation || '-' || :OLD.Dnum); return; end if; 398 PL/SQL Triggers /******************************************************************** UPDATING (there are :NEW and :OLD images for the record *********************************************************************/ if UPDATING then -- do we have a new project name ? if (:NEW.Pname != :OLD.Pname ) then dbms_output.put_line ('Updating PNAME of an old Project '); dbms_output.put_line (:NEW.Pname || '-' || :NEW.Pnumber|| '-' || :NEW.Plocation || '-' || :NEW.Dnum); dbms_output.put_line ('Old Pname: ' || :OLD.Pname ); end if; -- do we have a new project location? if (:NEW.Plocation != :OLD.Plocation ) then dbms_output.put_line ('Updating PLOCATION of an old Project '); dbms_output.put_line (:NEW.Pname || '-' || :NEW.Pnumber|| '-' || :NEW.Plocation || '-' || :NEW.Dnum); dbms_output.put_line ('Old Plocation: ' || :OLD.Plocation ); end if; --do we have a new controlling department? if (:NEW.Dnum != :OLD.Dnum ) then dbms_output.put_line ('Updating DNUM of an old Project '); dbms_output.put_line (:NEW.Pname || '-' || :NEW.Pnumber|| '-' || :NEW.Plocation || '-' || :NEW.Dnum); dbms_output.put_line ('Old DNUM: ' || :OLD.Dnum ); end if; end if; -- updating END; 399 PL/SQL Triggers 1. Testing triggering predicates PROJECT Table PNAME PNUMBER PLOCATION DNUM ----------------- ---------- ---------- ---------ProductX 1 Bellaire 5 ProductY 2 Sugarland 5 ProductZ 3 Houston 5 Computerization 10 Stafford 4 Reorganization 20 Houston 1 Newbenefits 30 Stafford 4 SQL> insert into project values ('XYZ', 40, 'Cleveland', 4); Inserting a new Project XYZ-40-Cleveland-4 1 row created. CSUPERSON SQL> select * from project; PNAME PNUMBER PLOCATION DNUM ----------------- ---------- ---------- ---------ProductX 1 Bellaire 5 ProductY 2 Sugarland 5 ProductZ 3 Houston 5 Computerization 10 Stafford 4 Reorganization 20 Houston 1 Newbenefits 30 Stafford 4 XYZ 40 Cleveland 4 400 PL/SQL Triggers 2. Testing triggering predicates SQL> update project set pname='Rock and Roll', Dnum=1 where pnumber=40; Updating PNAME of an old Project Rock and Roll-40-Cleveland-1 Old Pname: XYZ Updating DNUM of an old Project Rock and Roll-40-Cleveland-1 Old DNUM: 4 1 row updated. PROJEC table PROJEC table PNAME PNUMBER PLOCATION DNUM ----------------- ---------- ---------- ---------ProductX 1 Bellaire 5 ProductY 2 Sugarland 5 ProductZ 3 Houston 5 Computerization 10 Stafford 4 Reorganization 20 Houston 1 Newbenefits 30 Stafford 4 XYZ 40 Cleveland 4 PNAME PNUMBER PLOCATION DNUM ----------------- ---------- ---------- ---------ProductX 1 Bellaire 5 ProductY 2 Sugarland 5 ProductZ 3 Houston 5 Computerization 10 Stafford 4 Reorganization 20 Houston 1 Newbenefits 30 Stafford 4 Rock and Roll 40 Cleveland 1 401 PL/SQL Triggers MUTATING TABLES 402 PL/SQL Triggers MUTATING TABLES • Oracle enforces a reading of data that guarantees logical consistency. • The mutation error is encountered when a row-level trigger accesses the same (changing, unfinished, transient) table on which it is based. • Mutation will not occur if a single record is inserted in the table (using VALUES clause). • If bulk insertion is done or data is inserted from another table mutation will occur. • STATEMENT LEVEL triggers are immune to mutating tables. 403 PL/SQL Triggers MUTATING Tables • Mutating table exceptions occur when we try to reference the triggering table in a query from within row-level trigger code. • A mutating table is a relation that is being modified by an UPDATE, DELETE, or INSERT statement, or • A table that might be updated by the effects of a DELETE CASCADE constraint. Consequences – The session that issued the triggering statement cannot query or modify a mutating table. – This restriction prevents a trigger from seeing an inconsistent set of data. – This restriction applies to all triggers that use the FOR EACH ROW clause. 404 PL/SQL Triggers Example: Limit assignments to a max of 4. TRIGGER "CSUPERSON"."T007" BEFORE INSERT OR UPDATE OR DELETE ON "CSUPERSON"."WORKS_ON" FOR EACH ROW DECLARE myCounter Number; BEGIN -- count on how many projects the employee is currently working. select count(*) into myCounter from works_on where essn = :NEW.essn; -- if four or more reject operation raising error condition 20007 if myCounter >= 4 then raise_application_error (-20007, 'Excessive work load violation ' ||'Employee: ' || :new.essn ); end if; END; NOTE: In this example Delete & Update will run into mutating-table conflict, however a single Insert will not. 405 PL/SQL Triggers Example: Limit assignments to a max of 4. CSUperson SQL> update works_on set Hours = hours; update works_on set Hours = hours * ERROR at line 1: ORA-04091: table CSUPERSON.WORKS_ON is mutating, trigger/function may not see it bb ORA-06512: at "CSUPERSON.T007", line 5 ORA-04088: error during execution of trigger 'CSUPERSON.T007' CSUPERSON SQL>insert into works_on values (333445555,7,1); insert into works_on values (333445555,7,1) * ERROR at line 1: ORA-20007: Excessive work load violation Employee: 333445555 ORA-06512: at "CSUPERSON.T007", line 9 ORA-04088: error during execution of trigger 'CSUPERSON.T007' NOTE: In this example Delete & Update will run into mutating-table conflict, however a single Insert will not. 406 PL/SQL Triggers Example: Limit assignments to a max of 4. CSUPERSON SQL> insert into works_on select * from works_on; insert into works_on select * from works_on * ERROR at line 1: ORA-04091: table CSUPERSON.WORKS_ON is mutating, trigger/function may not see it ORA-06512: at "CSUPERSON.T007", line 5 ORA-04088: error during execution of trigger 'CSUPERSON.T007‘ NOTE: The INSERT operation finds each new records has already a previous image having the same primary key and concludes they are OLD versions of the new data, therefore the table itself is mutating. 407 PL/SQL Triggers Example: Limit assignments to a max of 4. • Assign ladies to new project 77 for 1 hour/week SQL> insert into works_on select ssn, 77, 1 from employee where sex = ‘F’; insert into works_on * ERROR at line 1: ORA-04091: table CSUPERSON.WORKS_ON is mutating, trigger/function may not see it ORA-06512: at "CSUPERSON.T007", line 5 ORA-04088: error during execution of trigger 'CSUPERSON.T007‘ NOTE: The INSERT operation finds each new records has already a previous image having the same primary key and concludes they are OLD versions of the new data, therefore the table itself is mutating. 408 PL/SQL Triggers Dealing with mutating tables Order of DML trigger execution 1. BEFORE STATEMENT_LEVEL, if present (could include cursors, select, and maintenance SQL statements on any table). 2. For each ROW LEVEL trigger linked to the table 1. 2. 3. 3. Run BEFORE ROW LEVEL, if present. Execute the statement (insert/delete/update). Run AFTER ROW LEVEL, if present. AFTER STATEMENT_LEVEL, if present (could include cursors, select, and maintenance SQL statements on any table). NOTE: On Step2; SQL select and maintenance statements raise mutating table exception. 409 PL/SQL Triggers Dealing with mutating tables A common technique for solving the mutating table problem consists on writing two triggers around the – otherwise- mutating table. 1. Write a BEFORE statement-level trigger, saving in (a) DB tables, or (b) memory table(s) -defined in a package- the relevant data needed by the forthcoming row-level triggers. 2. Write a ROW LEVEL trigger that gets access to the :NEW rows and the data saved in table(s) by the previously executed statement-level trigger. 3. If needed, conclude with an AFTER statement-level trigger. 410 PL/SQL Triggers Example Use triggers to enforce the following rules 1. Employees can not work on more than 4 projects. 2. Employees can not work more than 40 hours/week. 411 PL/SQL Triggers Example. Avoiding Mutating Tables Rules to be enforced 1. Employees can not work on more than 4 projects. 2. Employees can not work more than 40 hours/week. 2. 1. Trigger-B Trigger-A Before Row-Level Trigger Before Statement Trigger For demonstration purposes Uses a cursor to reach WORKS_ON records and summarize data in memory table: EMPLOAD Displays memory table. 3. MEMORY TABLE SSN PROJ HOURS T008A 1 123456789 2 40 T008A 2 333445555 4 40 T008A 3 453453453 2 40 T008A 4 666884444 1 40 T008A 5 888665555 1 0 T008A 6 987654321 2 35 T008A 7 987987987 2 40 T008A 8 999887777 2 40 Trigger-C Before Row-Level Trigger Uses memory table EMPLOAD, as well as :OLD and :NEW images. Raises EXCEPTIONS. ORACLE database 412 PL/SQL Triggers Supporting PACKAGE PACKAGE "MYTRIGGERAID" AS TYPE EmpLoadT IS RECORD( EmpSSN varchar2(9), EmpProjectCount number, EmpHoursCount number ); TYPE EmpLoadArrayT IS TABLE OF EmpLoadT INDEX BY BINARY_INTEGER; EmpLoad EmpLoadArrayT; EmpLoadSize number; END; 413 PL/SQL Triggers -- T008A. STATEMENT LEVEL TRIGGER TRIGGER "CSUPERSON"."T008A" BEFORE INSERT OR UPDATE OR DELETE ON "CSUPERSON"."WORKS_ON" DECLARE i number:= 0; BEGIN -- DEMO/TESTING: (remove printing-code later!!!) -- Fill up the array "EmpLoad" defined in the utility package -- "myTriggerAid" with a list of records, each of the form -<essn, countProjects, sumHours> -- this data will be used by other ROW-LEVEL triggers myTriggerAid.EmpLoadSize := 0; for w in (select essn, count(*) as totalProjects, sum(hours) as totalHours from works_on group by essn) loop i := i + 1; myTriggerAid.EmpLoad(i).EmpSSN := w.essn; myTriggerAid.EmpLoad(i).EmpProjectCount := w.TotalProjects; myTriggerAid.EmpLoad(i).EmpHoursCount := w.TotalHours; dbms_output.put_line('T008A ' || i || ' ' || w.essn || ' ' || w.totalProjects || ' ' || w.totalHours ); end loop; myTriggerAid.EmpLoadSize := i; dbms_output.put_line(' ------------------------------------- '); END; 414 PL/SQL Triggers -- (Testing Purposes) ROW LEVEL trigger. TRIGGER "CSUPERSON"."T008B" BEFORE INSERT OR UPDATE OR DELETE ON "CSUPERSON"."WORKS_ON" FOR EACH ROW DECLARE w myTriggerAid.EmpLoadT; i number; n number := myTriggerAid.EMpLoadSize; keyESSN integer; BEGIN -- T008B is used here only for demonstration purposes (drop it later!). -- The T008B trigger uses an "indexed-by" table and variables created by a -- BEFORE statement-level (T008A) that also fires on this table. -- Each entry in the table contains <ESSN,CountProjects,SumHours> -- The table is sequentially scanned looking for data in the array associated -- to the current :NEW record. Try a better search (perhaps binary, hashing…) if deleting then keyESSN := :old.ESSN; else keyESSN := :new.ESSN; end if; for i in 1..myTriggerAid.EmpLoadSize loop w.EmpSSN := myTriggerAid.EmpLoad(i).EmpSSN; w.EmpProjectCount := myTriggerAid.EmpLoad(i).EmpProjectCount; w.EmpHoursCount := myTriggerAid.EmpLoad(i).EmpHoursCount; if (keyESSN = myTriggerAid.EmpLoad(i).EmpSSN) then dbms_output.put_line('T008B ' || i || ' ' || w.EmpSSN || ' ' || w.EmpProjectCount || ' ' || w.EmpHoursCount ); end if; end loop; END; 415 PL/SQL Triggers CSUPERSON SQL> update works_on set hours=hours; T008A 1 123456789 2 40 T008A 2 333445555 4 40 T008A 3 453453453 2 40 T008A 4 666884444 1 40 T008A 5 888665555 1 0 T008A 6 987654321 2 35 T008A 7 987987987 2 40 T008A 8 999887777 2 40 ------------------------------------T008B 1 123456789 2 40 T008B 1 123456789 2 40 T008B 4 666884444 1 40 T008B 3 453453453 2 40 T008B 3 453453453 2 40 T008B 2 333445555 4 40 T008B 2 333445555 4 40 T008B 2 333445555 4 40 T008B 2 333445555 4 40 T008B 8 999887777 2 40 T008B 8 999887777 2 40 T008B 7 987987987 2 40 T008B 7 987987987 2 40 T008B 6 987654321 2 35 T008B 6 987654321 2 35 T008B 5 888665555 1 0 Written by T008A BEFORE level statement Written by T008B FOR EACH ROW level 416 PL/SQL Triggers Part2: ROW-Level Trigger. -- T008C. Enforcing rule 40hours/week max. TRIGGER "CSUPERSON"."T008C" BEFORE INSERT OR UPDATE OR DELETE ON "CSUPERSON"."WORKS_ON" FOR EACH ROW DECLARE w i myPos myValue keyESSN myTriggerAid.EmpLoadT; number; number; number; integer; -- <EmpSSN, EmpProjectCount, EmpHoursCount> -- index for the table -- search index, -1 if not found -- ESSN coming from the OLD or NEW rec. BEGIN ------------- The T008C trigger uses an "indexed-by" table and variables created by a BEFORE statement-level (T008A) that also fires on this table. Each entry in the table contains <ESSN,CountProjects,SumHours> The table is sequentially scanned looking for data in the array associated to the current :NEW record. EXCEPTIONS TO BE CONSIDERED Condition -20007 will be raised if the employee already works on 4 projects. Condition -20008 will be raised if the employee is placed on 40+ hours/week. OBSERVATION if the SQL statement is a DELETE or UPDATE and it finds no matching record in the WORKS_ON database, this trigger is not executed. Please notice there is no image for the :OLD record. 417 PL/SQL Triggers /*************************************************************************** PREAMBLE: Get ready to FIND (myPos -1 signals not found) ****************************************************************************/ -- a location value of -1 indicates not found (not really possible!) myPos := -1; -- key (ESSN) comes from either :NEW or :OLD record’s image if deleting then keyESSN := :old.ESSN; else keyESSN := :new.ESSN; end if; -- scan sequentially the memory list looking for a key match for i in 1..myTriggerAid.EmpLoadSize loop if (keyESSN = myTriggerAid.EmpLoad(i).EmpSSN) then w.EmpSSN := myTriggerAid.EmpLoad(i).EmpSSN; w.EmpProjectCount := myTriggerAid.EmpLoad(i).EmpProjectCount; w.EmpHoursCount := myTriggerAid.EmpLoad(i).EmpHoursCount; myPos := i; exit; --consider using a modified Binary search (faster?) end if; end loop; --remember the position in the table in which employee rec. was found i:= myPos; 418 PL/SQL Triggers --display these lines only for demonstration purpose dbms_output.put_line('New record (ESSN,PNO,HOURS): :new.eSSN || ', ' || :new.pno || ', ' || :new.hours); dbms_output.put_line('Old record (ESSN,PNO,HOURS): :old.eSSN || ', ' || :old.pno || ', ' || :old.hours); ' || ' || dbms_output.put_line('Loc: ' || i || ' Contains (SSN, ProjectCount, TotalHours): w.EmpSSN || ', ' || W.EmpProjectCount || ', ' || W.EmpHoursCount); ' || 419 PL/SQL Triggers /****************************************************************** get ready to DELETE. ******************************************************************/ if deleting then dbms_output.put_line('Deleting ---'); -- update memory table EmpLoad (less assignments & hours). myTriggerAid.EmpLoad(i).EmpProjectCount := myTriggerAid.EmpLoad(i).EmpProjectCount - 1; myTriggerAid.EmpLoad(i).EmpHoursCount := myTriggerAid.EmpLoad(i).EmpHoursCount - :old.Hours; end if; --deleting 420 PL/SQL Triggers /****************************************************************** get ready to UPDATE an existing record. ******************************************************************/ if updating then dbms_output.put_line('updating ---'); --adjust sum of hours myValue := myTriggerAid.EmpLoad(i).EmpHoursCount + :new.Hours - :old.hours; if myValue > 40 then --if more than 40 hour/week raise the exception raise_application_error(-20007, 'Error. More than 40 hours/week'); end if; -- OK. update memory table EmpLoad using -- the current modification of hours myTriggerAid.EmpLoad(i).EmpHoursCount := myValue; end if; --updating 421 PL/SQL Triggers /****************************************************************** get ready to INSERT. Caution! this could be the FIRST record. ******************************************************************/ if myPos = -1 then -- New person. -- This is the first entry for this emp in the table. -- make room in the table for a new entry myTriggerAid.EmpLoadSize := myTriggerAid.EmpLoadSize + 1; i := myTriggerAid.EmpLoadSize; myTriggerAid.EmpLoad(i).EmpSSN := :new.Essn; myTriggerAid.EmpLoad(i).EmpHoursCount := 0; myTriggerAid.EmpLoad(i).EmpProjectCount := 0; myValue := :new.Hours; else --this employee has already received some assignments myValue := myTriggerAid.EmpLoad(i).EmpHoursCount + :new.Hours; end if; --testing if this is the FIRST time in the table 422 PL/SQL Triggers if inserting then dbms_output.put_line('Inserting ---'); -- make sure we do not exceed 40 hour/week --(TODO: Check too many assignments !!!) if myValue > 40 then raise_application_error(-20007, 'Error. More than 40 hours/week'); end if; -- OK. update memory table EmpLoad. -- Modify the existing emp. cell with NEW data. myTriggerAid.EmpLoad(i).EmpHoursCount := myValue; if myPos = -1 then --this is the first entry for the employee myTriggerAid.EmpLoad(i).EmpProjectCount := 1; else --the employee had already some assignments myTriggerAid.EmpLoad(i).EmpProjectCount := myTriggerAid.EmpLoad(i).EmpProjectCount + 1; end if; -- fixing the FIRST entry for this employee end if; END; -- inserting 423 PL/SQL Triggers 1. Testing: T008A and T008C on 40 hours/week rule. WORKS_ON table ESSN PNO HOURS --------- ---------- ---------123456789 1 31.5 123456789 2 7.5 666884444 3 40 453453453 1 20 453453453 2 20 333445555 2 10 333445555 3 10 333445555 10 10 333445555 20 10 999887777 30 30 999887777 10 10 987987987 10 35 987987987 30 5 987654321 30 20 987654321 20 15 888665555 20 0 SQL> update works_on set hours= hours+1 where essn=123456789; T008A 1 333445555 4 40 T008A 2 987987987 2 40 T008A 3 123456789 2 40 T008A 4 666884444 1 40 T008A 5 987654321 2 35 T008A 6 453453453 2 40 T008A 7 888665555 1 0 T008A 8 999887777 2 40 ------------------------------------New record (ESSN,PNO,HOURS): Old record (ESSN,PNO,HOURS): Written by T008A BEFORE statement level trigger T008C finds a possible change from 32.5 to 33.5 hours on project 1 123456789, 1, 33.5 123456789, 1, 32.5 Loc: 3 Contains (SSN, ProjectCount, TotalHours): 123456789 already works 40 hours/week 123456789, 2, 40 updating --update works_on set hours = hours+1 where essn = 123456789 ERROR at line 1: The UPDATE operation fails. Trigger T008C throws an Excepttion ORA-20007: Error. More than 40 hours/week ORA-06512: at "CSUPERSON.T008C", line 85 ORA-04088: error during execution of trigger 'CSUPERSON.T008C‘ 424 PL/SQL Triggers 2. Testing: T008A and T008C on 40 hours/week rule. WORKS_ON table ESSN PNO HOURS --------- ---------- ---------123456789 1 31.5 123456789 2 7.5 666884444 3 40 453453453 1 20 453453453 2 20 333445555 2 10 333445555 3 10 333445555 10 10 333445555 20 10 999887777 30 30 999887777 10 10 987987987 10 35 987987987 30 5 987654321 30 20 987654321 20 15 888665555 20 0 The INSERT operation succeeds. (Note: on this example we do not check Foreign – Primary key constraints) SQL> insert into works_on values (777777777, 1, 40); T008A 1 333445555 4 40 Written by T008A T008A 2 987987987 2 40 BEFORE statement level trigger T008A 3 123456789 2 40 T008A 4 666884444 1 40 T008C finds that 777777777 T008A 5 987654321 2 35 could be inserted. T008A 6 453453453 2 40 T008A 7 888665555 1 0 (it was not found in the memory T008A 8 999887777 2 40 table, but was added to it) ------------------------------------New record (ESSN,PNO,HOURS): 777777777, 1, 40 Old record (ESSN,PNO,HOURS): , , Loc: -1 Contains (SSN, ProjectCount, TotalHours): , , Inserting --1 row created. 425 PL/SQL Triggers 3. Testing: T008A and T008C on 40 hours/week rule. WORKS_ON table ESSN PNO HOURS --------- ---------- ---------123456789 1 31.5 123456789 2 7.5 666884444 3 40 453453453 1 20 453453453 2 20 333445555 2 10 333445555 3 10 333445555 10 10 333445555 20 10 999887777 30 30 999887777 10 10 987987987 10 35 987987987 30 5 987654321 30 20 987654321 20 15 888665555 20 0 777777777 1 40 The INSERT operation FAILS SQL> insert into works_on values (777777777,2,5); T008A 1 333445555 4 40 T008A 2 987987987 2 40 T008A 3 123456789 2 40 T008A 4 666884444 1 40 T008A 5 987654321 2 35 T008A 6 453453453 2 40 T008A 7 888665555 1 0 T008A 8 777777777 1 40 T008A 9 999887777 2 40 ------------------------------------- Written by T008A BEFORE statement level trigger T008C finds the record 777777777 could NOT be inserted. Emp already works 40 hours/week New record (ESSN,PNO,HOURS): 777777777, 2, 5 Old record (ESSN,PNO,HOURS): , , Loc: 8 Contains (SSN, ProjectCount, TotalHours): 777777777, 1, 40 Inserting --insert into works_on values (777777777,2,5) * ERROR at line 1: ORA-20007: Error. More than 40 hours/week ORA-06512: at "CSUPERSON.T008C", line 113 ORA-04088: error during execution of trigger 'CSUPERSON.T008C' 426 PL/SQL Triggers 4. Testing: T008A and T008C on 40 hours/week rule. WORKS_ON table ESSN PNO HOURS --------- ---------- ---------123456789 1 31.5 123456789 2 7.5 666884444 3 40 453453453 1 20 453453453 2 20 333445555 2 10 333445555 3 10 333445555 10 10 333445555 20 10 999887777 30 30 999887777 10 10 987987987 10 35 987987987 30 5 987654321 30 20 987654321 20 15 888665555 20 0 777777777 1 40 SQL> delete from works_on where essn = 777777777; T008A 1 123456789 2 39 T008A 2 333445555 4 40 Written by T008A T008A 3 453453453 2 40 BEFORE statement level trigger T008A 4 666884444 1 40 T008A 5 777777777 1 40 T008A 6 888665555 1 0 T008A 7 987654321 2 35 T008A 8 987987987 2 40 T008A 9 999887777 2 40 ------------------------------------NEW -T008C finds the rec. 777777777 Found: 5 ** 777777777-1-40 could be delete. Deleting --1 row deleted. The DELETE operation succeeds 427 OLD NOTES Example of a COMPANY personnel PACKAGE 428 PL/SQL Programming Invoking a Stored Function from a SQL Query The numbers in the BaseBall table are hard to read. A function BigBucks() is called to facilitated the reading of the big numbers in the table SQL> select * from baseball; PNAME PSALARY -------------------- -----------A. Belle 987654321 K. Griffey 1987654322 B. Bonds 1234567890 D. Cone 123456 P. Butterfingers 900 429 PL/SQL Programming Invoking a Stored Function from a SQL Query -- Playball.sql -- This is a small SCRIPT (not an anonymous proc.) -- The pretty function is called from the SQL comnd column psalary format 99999999999 column bigbucks(psalary) format a30 select pname, psalary, BigBucks(psalary) from baseball; SQL> @Playball PNAME PSALARY BIGBUCKS(PSALARY) -------------------- ------------ --------------------A. Belle 987654321 987.65 Millions K. Griffey 1987654322 1.99 Billions B. Bonds 1234567890 1.23 Billions D. Cone 123456 123.46 Thousands P. Butterfingers 900 900.00 Peanuts 430 PL/SQL Programming A Stored Function for Beautifying Big Numbers Create or Replace Function BIGBUCKS ( salary number ) Return varchar IS Billion number := 1000000000; Million number := 1000000; Thousand number := 1000; LPAD( ) adds n spaces on Message varchar(30); on left side of the string Dollars number(6,2); begin if (salary >= Billion) then Dollars:= salary / billion; message:= LPAD(to_char(dollars),8) || ' Billions'; return (message); elsif (salary >= Million) then Dollars:= salary / Million; message:= LPAD(to_char(dollars),8) || ' Millions'; return (message); elsif (salary >= Thousand) then Dollars:= salary / Thousand; message:= LPAD(to_char(dollars),8) || ' Thousands'; return (message); Numeric format else message:= to_char(Salary,'999.00'); message:= LPAD(message,8)||' Peanuts'; return (message); end if; end; 431 PL/SQL Programming Writing a Procedure to Securely Insert Works_On records The procedure Insert_WorksOn() is invoked to reliably add new records to the table Works_On. Each call to the function provides the programmers authentication code and data (SSN, ProjNumb, Hour). For example: Result:= Insert_WorkOn(‘JB007’,123456789,10,40) • If the programmer ‘JB007’ is not in the list of people authorized to make insertions, the operation is rejected and MYLOG file is update with a rejection message • If the SSN and PNO provided are already in the Works_On table the current insertion is rejected and the log file is updated • The function returns a numeric code 1, -1, -2, etc. to indicate the status of the execution. This number is stored into the variable Result (1: success, negative values: problems). 432 PL/SQL Programming create or replace FUNCTION Insert_WorksOn ( Programmer_Id EmployeeSsn ProjectNumber WeeklyHours number, number, number, number ) RETURN number IS Programmer_Name varchar(20); Message varchar(70); BEGIN -- First block: Is the programmer allowed to insert records? -- Note: All code goes into one BEGIN...END block. BEGIN select Operator_Name into Programmer_Name from Valid_Operators where Operator_Id = Programmer_Id; EXCEPTION when No_Data_Found then Insert into Mylog values (Programmer_Id, 'ERROR** unauthorized used of function'); RETURN -1; END; -- first block 433 PL/SQL Programming -- INSERTWORKSON cont. -- Second block: Here other conditions could be checked -- before accepting record into the works_on table BEGIN insert into Works_On values (EmployeeSsn, ProjectNumber, WeeklyHours); RETURN 1; EXCEPTION When Dup_Val_on_Index then Message:= ' Emp ' || to_char(EmployeeSsn) || ' already working in project ' || to_char(ProjectNumber); insert into Mylog values (NULL, Message); RETURN -2; When Others then Message:= ' Emp ' || to_char(EmployeeSsn) || ' can not be placed on project ' || to_char(ProjectNumber); insert into Mylog values (NULL, Message); RETURN -3; END; -- second block END; 434 PL/SQL Programming Writing a Secure Procedure to Delete a Works_On record The procedure Delete_WorksOn() is invoked to remove records from the Works_On table. In the function-call the programmers authentication code and the Works_On key field(s) are supplied. For example: in the following call programmer ‘JB007’ asks to remove employee 123456789 from proj. 1. Result:= Delete_WorkOn(‘JB007’,123456789,1) • If the programmer ‘JB007’ is not authorized to make such a deletion, the operation is rejected and MYLOG file is update with a rejection message • If employee 123456789 is not in the Works_On table the current deletion is rejected and the log file is updated otherwise, the record is eliminated • The function returns a numeric code 1, -1, -2, etc. to indicate the status of the execution. (1: success, negative values: problems). 435 PL/SQL Programming -- DELETE_WORKSON script create or replace FUNCTION Delete_WorksOn ( Programmer_Id EmployeeSsn number, number ) RETURN number IS Programmer_Name varchar(20); Message varchar(70); BEGIN -- First block: Verify that programmer is allowed to delete -- Note: all code goes in one BEGIN...END block BEGIN select Operator_Name into Programmer_Name from Valid_Operators where Operator_Id = Programmer_Id; EXCEPTION when No_Data_Found then Message:= 'Programmer not allowed to ' || 'delete Works_On rec.'; Insert into Mylog values(Programmer_Id, Message ); RETURN -1; END; -- first block 436 PL/SQL Programming -- DELETE_WORKSON script cont. -- Second block: Other conditions could be checked here -- before deleting the record BEGIN delete from Works_On where Essn= EmployeeSsn; RETURN 1; EXCEPTION When No_Data_Found then Message:= ' Emp ' || to_char(EmployeeSsn) || ' does not exist in Works_On table'; insert into Mylog values (NULL, Message); RETURN -2; When Others then Message:= ' Emp ' || to_char(EmployeeSsn) || ' can not be deleted from Works_On'; insert into Mylog values (NULL, Message); RETURN -3; END; -- second block END; / 437 PL/SQL Programming Writing a Secure Procedure to Update Project records The procedure Update_Project( ) is used to modify existing records in the Project table. Individual records are located using the Pnumber values as search-key. The updating of projects occurs in the context of a function call such as the example bellow. The arguments passed to the function are: Programmer_Id, project number, name, location; and number of responsible department. Result:= Update_Project(‘JB007’,1,‘Cerveza XXX’,‘Margarita’,NULL); • • • • • • • Each project record to be updated must include the project number More than one field could be updated in a single function call Project numbers are not changeable (instead delete whole rec. and re-insert with a new number) If the programmer ‘JB007’ is authorized to make deletion, the operation is rejected If there is no record for project number 1, the current update is rejected If a field is not going to be updated, the word NULL must be written in the proper column The function returns a numeric code 1, -1, -2, etc. to indicate the status of the execution. 438 PL/SQL Programming Writing a Secure Procedure to Update Project records create or replace FUNCTION Update_Project( Programmer_Id ProjectNumber ProjectName ProjectLocation DepartmentNumber RETURN number IS Programmer_Name varchar(20); Message varchar(70); VarPnumber number; number, number, varchar, varchar, number ) 439 PL/SQL Programming Writing a Secure Procedure to Update Project records cont. BEGIN -- First block: Is programmer allowed to update projects? -- NOTE: all code goes in one BEGIN...END block BEGIN select Operator_Name into Programmer_Name from Valid_Operators where Operator_Id = Programmer_Id; EXCEPTION when No_Data_Found then Message:= 'Programmer not allowed to ' || 'update Projects'; Insert into Mylog values (Programmer_Id, Message); RETURN -1; END; -- first block 440 PL/SQL Programming Writing a Secure Procedure to Update Project records cont. -- Second block: Verify there is a project record matching -- Pnumber key. If the field is not NULL, modify using given -- parameter BEGIN select Pnumber into VarPnumber from Project where Pnumber= ProjectNumber; if (ProjectName is not NULL) then update Project set Pname= ProjectName where Pnumber = ProjectNumber; end if; if (ProjectLocation is not NULL) then update Project set Plocation= ProjectLocation where Pnumber = ProjectNumber; End if; if (DepartmentNumber is not NULL) then update Project set Dnum = DepartmentNumber where Pnumber = ProjectNumber; End if; 441 RETURN 1 ; -- success ! PL/SQL Programming Writing a Secure Procedure to Update Project records cont. -- Deal with Update problems here EXCEPTION When No_Data_Found then Message:= ' Project ' || to_char(ProjectNumber) || ' does not exist '; insert into Mylog values (NULL, Message); RETURN -2; When Others then Message:= ' Project ' || to_char(ProjectNumber) || ' can not be updated '; insert into Mylog values (NULL, Message); RETURN -3; END; -- second block END; 442