You are on page 1of 57

6.

2 Cursors in PL/SQL
When you execute a SQL statement from PL/SQL, the Oracle RDBMS assigns a pri ate !or" area for that statement# $his !or" area contains information a%out the SQL statement an& the set of &ata returne& or affecte& %y that statement# $he PL/SQL cursor is a mechanism %y !hich you can name that !or" area an& manipulate the information !ithin it# 'n its simplest form, you can thin" of a cursor as a pointer into a ta%le in the &ata%ase# (or example, the follo!ing cursor &eclaration associates the entire employee ta%le !ith the cursor name& employee)cur* +,RSOR employee)cur 'S S-L-+$ . (ROM employee/ Once ' ha e &eclare& the cursor, ' can open it* OP-0 employee)cur/ 1n& then ' can fetch ro!s from it* (-$+2 employee)cur '0$O employee)rec/ an&, finally, ' can close the cursor* +LOS- employee)cur/ 'n this case, each recor& fetche& from this cursor represents an entire recor& in the employee ta%le# 3ou can, ho!e er, associate any ali& S-L-+$ statement !ith a cursor# 'n the next example ' ha e a 4oin of three ta%les in my cursor &eclaration* D-+L1R+,RSOR 4o"e)fee&%ac")cur 'S S-L-+$ 5#name, R#laugh) olume, +#name (ROM 4o"e 5, response R, come&ian + W2-R- 5#4o"e)i& 6 R#4o"e)i& 10D 5#4o"er)i& 6 +#4o"er)i&/ B-7'0 ### -0D/ 2ere, the cursor &oes not act as a pointer into any actual ta%le in the &ata%ase# 'nstea&, the cursor is a pointer into the irtual ta%le represente& %y the S-L-+$ statement 8S-L-+$ is calle& a irtual ta%le %ecause the &ata it pro&uces has the same structure as a ta%le 99 ro!s an& columns 99 %ut it exists only for the &uration of the execution of the SQL statement:# 'f the triple94oin returns ;< ro!s, each ro! containing the three columns in the prece&ing example, then the cursor functions as a pointer into those ;< ro!s#
=

>#;#= $ypes of +ursors 3ou ha e lots of options in PL/SQL for executing SQL, an& all of them occur as some type of cursor# 7enerally, there are t!o types of SQL that you can execute in PL/SQL* static an& &ynamic# SQL is static if the content of the SQL statement is &etermine& at compile time# 1 SQL statement is &ynamic if it is constructe& at runtime an& then execute&# Dynamic SQL is ma&e possi%le in PL/SQL only through the use of the DBMS)SQL %uilt9in pac"age 8see 1ppen&ix +, Built9'n Pac"ages:# 1ll other forms of SQL execute& insi&e a PL/SQL program represent static SQL/ these forms of cursors are the focus of the remain&er of this chapter# - en !ithin the category of static SQL, !e ha e further &ifferentiation# With the a& ent of PL/SQL Release ;#?, you can choose %et!een t!o &istinct types of cursor o%4ects* Static cursor o%4ects $hese are the really static cursors of PL/SQL# $he SQL is &etermine& at compile time, an& the cursor al!ays refers to one SQL statement, !hich is "no!n at compile time# $he examples sho!n earlier in this chapter are static cursors# ,nless other!ise note&, any reference to @static cursor@ refers to this su%9 category of static 8as oppose& to &ynamic: cursors# +ursor aria%les 3ou can &eclare a aria%le !hich references a cursor o%4ect in the &ata%ase# 3our aria%le may refer to &ifferent SQL statements at &ifferent times 8%ut that SQL is &efine& at compile time, not run time:# $he cursor aria%le is one of the ne!est enhancements to PL/SQL an& !ill %e unfamiliar to most programmers# +ursor aria%les act as references to cursor o%4ects# 1s a true aria%le, a cursor aria%le can change its alue as your program executes# $he aria%le can refer to &ifferent cursor o%4ects 8Aueries: at &ifferent times# 3ou can also pass a cursor aria%le as a parameter to a proce&ure or function# +ursor aria%les are &iscusse& later in this chapter# Static PL/SQL cursors ha e %een a aila%le since PL/SQL Bersion =# $he static ersion of cursors @har&co&es@ a lin" %et!een the cursor name an& a S-L-+$ statement# $he static cursor itself comes in t!o fla ors* implicit an& explicit# PL/SQL &eclares an& manages an implicit cursor e ery time you execute a SQL DML statement, such as an '0S-R$ or a S-L-+$ that returns a single ro!# 3ou, the programmer, &efine your o!n explicit cursors in your co&e# 3ou must use an explicit cursor !hen you nee& to retrie e more than one ro! of &ata at a time through a S-L-+$ statement# 3ou can then use the cursor to fetch these ro!s one at a time# $he set of ro!s returne& %y the Auery associate& !ith an explicit cursor is calle& the acti e set or result set of the cursor# $he ro! to !hich the explicit cursor points is calle& the current ro! of the result set#
;

$he %ul" of this chapter is &e ote& to the management of static, explicit cursors# 1ll information a%out cursor aria%les is localiCe& in Section >#=;, @+ursor Baria%les@# 1ny references to PL/SQL cursors an& cursor characteristics outsi&e of that section !ill pertain to static cursors# >#;#; +ursor Operations Regar&less of the type of cursor, PL/SQL performs the same operations to execute a SQL statement from !ithin your program* P1RS$he first step in processing an SQL statement is to parse it to ma"e sure it is ali& an& to &etermine the execution plan 8using either the rule9%ase& or cost9%ase& optimiCer:# B'0D When you %in&, you associate alues from your program 8host aria%les: !ith placehol&ers insi&e your SQL statement# (or static SQL, the SQL engine itself performs these %in&s# When you use &ynamic SQL, you explicitly reAuest a %in&ing of aria%le alues# OP-0 When you open a cursor, the %in& aria%les are use& to &etermine the result set for the SQL statement# $he pointer to the acti e or current ro! is set to the first ro!# Sometimes you !ill not explicitly open a cursor/ instea& the PL/SQL engine !ill perform this operation for you 8as !ith implicit cursors:# -D-+,$'n the execute phase, the statement is run !ithin the SQL engine# (-$+2 'f you are performing a Auery, the (-$+2 comman& retrie es the next ro! from the cursorEs result set# -ach time you fetch, PL/SQL mo es the pointer for!ar& in the result set# When !or"ing !ith explicit cursors, remem%er that if there are no more ro!s to retrie e, then (-$+2 &oes nothing 8it &oes not raise an error:# +LOS$he +LOS- statement closes the cursor an& releases all memory use& %y the cursor# Once close&, the cursor no longer has a result set# Sometimes you !ill not explicitly close a cursor/ instea& the PL/SQL engine !ill perform this operation for you 8as !ith implicit cursors:#
(igure >#= sho!s ho! some of these &ifferent operations are use& to fetch information

from the &ata%ase into your PL/SQL program#


?

(igure >#=* ,sing cursor operations to fetch &ata%ase information into your program

6.3 Implicit and Explicit Cursors

LetEs ta"e a closer loo" at implicit an& explicit cursors an& the !ays you can put them in your programs# >#?#= 'mplicit +ursors PL/SQL issues an implicit cursor !hene er you execute a SQL statement &irectly in your co&e, as long as that co&e &oes not employ an explicit cursor# 't is calle& an @implicit@ cursor %ecause you, the &e eloper, &o not explicitly &eclare a cursor for the SQL statement# 'f you use an implicit cursor, Oracle performs the open, fetches, an& close for you automatically/ these actions are outsi&e of your programmatic control# 3ou can, ho!e er, o%tain information a%out the most recently execute& SQL statement %y examining the alues in the implicit SQL cursor attri%utes, as explaine& later in this chapter# PL/SQL employs an implicit cursor for each ,PD1$-, D-L-$-, or '0S-R$ statement you execute in a program# 3ou cannot, in other !or&s, execute these statements !ithin an explicit cursor, e en if you !ant to# 3ou ha e a choice %et!een using an implicit or explicit cursor only !hen you execute a single9ro! S-L-+$ statement 8a S-L-+$ that returns only one ro!:# 'n the follo!ing ,PD1$- statement, !hich gi es e eryone in the company a =<F raise, PL/SQL creates an implicit cursor to i&entify the set of ro!s in the ta%le !hich !oul& %e affecte& %y the up&ate* ,PD1$- employee S-$ salary 6 salary . =#=/ $he follo!ing single9ro! Auery calculates an& returns the total salary for a &epartment# Once again, PL/SQL creates an implicit cursor for this statement* S-L-+$ S,M 8salary: '0$O &epartment)total (ROM employee W2-R- &epartment)num%er 6 =</ 'f you ha e a S-L-+$ statement that returns more than one ro!, you must use an explicit cursor for that Auery an& then process the ro!s returne& one at a time# PL/SQL &oes not yet support any "in& of array interface %et!een a &ata%ase ta%le an& a composite PL/SQL &atatype such as a PL/SQL ta%le# >#?#; Dra!%ac"s of 'mplicit +ursors - en if your Auery returns only a single ro!, you might still &eci&e to use an explicit
G

cursor# $he implicit cursor has the follo!ing &ra!%ac"s* 't is less efficient than an explicit cursor 8in PL/SQL Release ;#; an& earlier: 't is more ulnera%le to &ata errors 't gi es you less programmatic control $he follo!ing sections explore each of these limitations to the implicit cursor# >#?#;#= 'nefficiencies of implicit cursors 1n explicit cursor is, at least theoretically, more efficient than an implicit cursor 8in PL/SQL Release ;#; an& earlier:# 1n implicit cursor executes as a SQL statement an& OracleEs SQL is 10S'9stan&ar&# 10S' &ictates that a single9ro! Auery must not only fetch the first recor&, %ut must also perform a secon& fetch to &etermine if too many ro!s !ill %e returne& %y that Auery 8such a situation !ill R1'S- the $OO)M103)ROWS PL/SQL exception:# $hus, an implicit Auery al!ays performs a minimum of t!o fetches, !hile an explicit cursor only nee&s to perform a single fetch# $his a&&itional fetch is usually not noticea%le, an& you shoul&nEt %e neurotic a%out using an implicit cursor for a single9ro! Auery 8it ta"es less co&ing, so the temptation is al!ays there:# Loo" out for in&iscriminate use of the implicit cursor in the parts of your application !here that cursor !ill %e execute& repeate&ly# 1 goo& example is the Post9Query trigger in the Oracle (orms# Post9Query fires once for each recor& retrie e& %y the Auery 8create& from the %ase ta%le %loc" an& the criteria entere& %y the user:# 'f a Auery retrie es ten ro!s, then an a&&itional ten fetches are nee&e& !ith an implicit Auery# 'f you ha e ;H users on your system all performing a similar Auery, your ser er must process ;H< a&&itional 8unnecessary: fetches against the &ata%ase# So, !hile it might %e easier to !rite an implicit Auery, there are some places in your co&e !here you !ill !ant to ma"e that extra effort an& go !ith the explicit cursor# 0O$-* 'n PL/SQL Release ;#? an& a%o e, the implicit cursor has %een optimiCe& so that it may, in isolation, run faster than the correspon&ing explicit cursor# 7enerally, the &ifferences %et!een these t!o approaches from a performance stan&point are negligi%le# On the other han&, if you use an explicit cursor, you are more li"ely 8or at least a%le: to reuse that cursor, !hich increases the chance that it !ill %e pre9parse& in share& memory !hen nee&e& 99 there%y impro ing the performance of your application as a !hole# >#?#;#; Bulnera%ility to &ata errors 'f an implicit S-L-+$ statement returns more than one ro!, it raises the $OO)M103)ROWS exception# When this happens, execution in the current %loc" terminates an& control is passe& to the exception section# ,nless you &eli%erately plan to han&le this scenario, use of the implicit cursor is a &eclaration of faith# 3ou are saying, @' trust that Auery to al!ays return a single ro!I@ 't may !ell %e that to&ay, !ith the current &ata, the Auery !ill only return a single ro!#
H

'f the nature of the &ata e er changes, ho!e er, you may fin& that the S-L-+$ statement !hich formerly i&entifie& a single ro! no! returns se eral# 3our program !ill raise an exception# Perhaps this is !hat you !ill !ant# On the other han&, perhaps the presence of a&&itional recor&s is inconseAuential an& shoul& %e ignore&# With the implicit Auery, you cannot easily han&le these &ifferent possi%ilities# With an explicit Auery, your program !ill %e protecte& against changes in &ata an& !ill continue to fetch ro!s !ithout raising exceptions# >#?#;#? Diminishe& programmatic control $he implicit cursor ersion of a S-L-+$ statement is a %lac" %ox# 3ou pass the SQL statement to the SQL layer in the &ata%ase an& it returns 8you hope: a single ro!# 3ou canEt get insi&e the separate operations of the cursor, such as the open an& close stages# 3ou canEt examine the attri%utes of the cursor 99 to see !hether a ro! !as foun&, for example, or if the cursor has alrea&y %een opene&# 3ou canEt easily apply tra&itional programming control constructs, such as an '( statement, to your &ata access# Sometimes you &onEt nee& this le el of control# Sometimes you 4ust thin" you &onEt nee& this le el of control# ' ha e foun& that if ' am going to %uil& programs in PL/SQL, ' !ant as much control as ' can possi%ly get# 1l!ays ,se -xplicit +ursorsI My rule of thum% is al!ays to use an explicit cursor for all S-L-+$ statements in my applications, e en if an implicit cursor might run a little %it faster an& e en if, %y co&ing an explicit cursor, ' ha e to !rite more co&e 8&eclaration, open, fetch, close:# By setting an& follo!ing this clear9cut rule, ' gi e myself one less thing to thin" a%out# ' &o not ha e to &etermine if a particular S-L-+$ statement !ill return only one ro! an& therefore %e a can&i&ate for an implicit cursor# ' &o not ha e to !on&er a%out the con&itions un&er !hich a single9ro! Auery might su&&enly return more than one ro!, thus reAuiring a $OO)M103)ROWS exception han&ler# ' am guarantee& to get astly impro e& programmatic control o er that &ata access an& more finely9tune& exception han&ling for the cursor# >#?#? -xplicit +ursors 1n explicit cursor is a S-L-+$ statement that is explicitly &efine& in the &eclaration section of your co&e an&, in the process, assigne& a name# $here is no such thing as an explicit cursor for ,PD1$-, D-L-$-, an& '0S-R$ statements# With explicit cursors, you ha e complete control o er ho! to access information in the &ata%ase# 3ou &eci&e !hen to OP-0 the cursor, !hen to (-$+2 recor&s from the cursor 8an& therefore from the ta%le or ta%les in the S-L-+$ statement of the cursor: ho! many recor&s to fetch, an& !hen to +LOS- the cursor# 'nformation a%out the current state of your cursor is a aila%le through examination of the cursor attri%utes# $his granularity of control ma"es the explicit cursor an in alua%le tool for your &e elopment effort#
>

LetEs loo" at an example# $he follo!ing anonymous %loc" loo"s up the employee type &escription for an employee type co&e* = D-+L1R; /. -xplicit &eclaration of a cursor ./ ? +,RSOR emptyp)cur 'S G S-L-+$ emptyp#type)&esc H (ROM employees emp, employee)type emptyp > W2-R- emp#type)co&e 6 emptyp#type)co&e/ J B-7'0 K /. +hec" to see if cursor is alrea&y open# 'f not, open it# ./ L '( 0O$ emptyp)curF'SOP-0 =< $2-0 == OP-0 emptyp)cur/ =; -0D '(/ =? =G /. (etch ro! from cursor &irectly into an Oracle (orms item ./ =H (-$+2 emptyp)cur '0$O *emp#type)&esc/ => =J /. +lose the cursor ./ =K +LOS- emptyp)cur/ =L -0D/ $his PL/SQL %loc" performs the follo!ing cursor actions*
1ctionLine8s:Declare the cursor?Open the cursor 8if not alrea&y open:L, ==(etch one or more ro!s from the cursor=H+lose the cursor=K$he next fe! sections examine each of these steps in

more &etail# (or the remain&er of this chapter, unless note& other!ise, the !or& @cursor@ refers to the explicit cursor#

6.4 Declaring Cursors

$o use an explicit cursor, you must first &eclare it in the &eclaration section of your PL/SQL %loc" or in a pac"age, as sho!n here* +,RSOR cursor)name M 8 M parameter M, parameter ###N : N M R-$,R0 return)specification N 'S S-L-+$)statement/ !here cursor)name is the name of the cursor, return)specification is an optional R-$,R0 clause for the cursor, an& S-L-+$)statement is any ali& SQL S-L-+$ statement# 3ou can also pass arguments into a cursor through the optional parameter list &escri%e& in Section >#=<, @+ursor Parameters@# Once you ha e &eclare& a cursor you can then OP-0 it an& (-$+2 from it# 2ere are some examples of explicit cursor &eclarations*
J

1 cursor !ithout parameters# $he result set of this cursor is the set of company 'D num%ers for each recor& in the ta%le* +,RSOR company)cur 'S S-L-+$ company)i& (ROM company/
1 cursor !ith parameters# $he result set of this cursor is the name of the

company !hich matches the company 'D passe& to the cursor ia the parameter* +,RSOR name)cur 8company)i&)in '0 0,MB-R: 'S S-L-+$ name (ROM company W2-R- company)i& 6 company)i&)in/ 1 cursor !ith a R-$,R0 clause# $he result set of this cursor is all columns 8same structure as the un&erlying ta%le: from all employee recor&s in &epartment =<* +,RSOR emp)cur R-$,R0 employeeF 'S S-L-+$ . (ROM employee W2-R- &epartment)i& 6 =</ >#G#= $he +ursor 0ame $he name of an explicit cursor is not a PL/SQL aria%le# 'nstea&, it is an un&eclare& i&entifier use& to point to or refer to the Auery# 3ou cannot assign alues to a cursor, nor can you use it in an expression# 1s a result, %oth of the executa%le statements after the B-7'0 line %elo! are in ali&* D-+L1R+,RSOR company)cur 'S S-L-+$ company)i& (ROM company/ B-7'0 company)cur *6 =H<<L/ /. 'n ali& syntax ./ '( company)cur 'S 0O$ 0,LL $2-0 ### / /. 'n ali& syntax ./ 'n compiling either statement, you !ill recei e the follo!ing error* PLS9<?;=* expression E+OMP103)+,RE is inappropriate as the left9han& si&e of an assignment statement# $he name of a cursor can %e up to ?< characters in length an& follo!s the rules for any other i&entifier in PL/SQL# >#G#; PL/SQL Baria%les in a +ursor Because a cursor must %e associate& !ith a S-L-+$ statement, e ery cursor must
K

reference at least one ta%le from the &ata%ase an& &etermine from that 8an& from the W2-R- clause: !hich ro!s !ill %e returne& in the acti e set# $his &oes not mean, ho!e er, that a cursorEs S-L-+$ may only return &ata%ase information# $he list of expressions that appears after the S-L-+$ "ey!or& an& %efore the (ROM "ey!or& is calle& the select list# 'n nati e SQL, this select list may contain %oth columns an& expressions 8SQL functions on those columns, constants, etc#:# 'n PL/SQL, the select list of a S-L-+$ may contain PL/SQL aria%les, expressions, an& e en functions 8PL/SQL Release ;#= an& a%o e:# 'n the follo!ing cursor, the S-L-+$ statement retrie es ro!s %ase& on the employee ta%le, %ut the information returne& in the select list contains a com%ination of ta%le columns, a PL/SQL aria%le, an& a %in& aria%le from the host en ironment 8such as an Oracle (orms item:* D-+L1R/. 1 local PL/SQL aria%le ./ pro4ecte&)%onus 0,MB-R *6 =<<</ /. OO +ursor a&&s P=<<< to the salary of each employee OO hire& more than three years ago# ./ +,RSOR employee)cur 'S S-L-+$ employee)i&, salary Q pro4ecte&)%onus ne!)salary, /. +olumn alias ./ *re ie!#e aluation /. Bin& aria%le ./ (ROM employee W2-R- hire&ate R 1DD)MO0$2S 8S3SD1$-, 9?>:/ B-7'0 ### -0D/ 3ou can reference local PL/SQL program &ata 8PL/SQL aria%les an& constants:, as !ell as host language %in& aria%les in the W2-R-, 7RO,P, an& 21B'07 clauses of the cursorEs S-L-+$ statement# >#G#? '&entifier Prece&ence in a +ursor Be careful a%out naming i&entifiers !hen you mix PL/SQL aria%les in !ith &ata%ase columns# 't is, for instance, common practice to gi e a aria%le the same name as the column !hose &ata it is suppose& to represent# $his ma"es perfect sense until you !ant to reference those local aria%les in a SQL statement along !ith the column# 'n the follo!ing example, ' !ant to fetch each employee !ho !as hire& more than three years ago an&, using a local aria%le, a&& P=<<< to their salary# $he employee ta%le has a column name& @salary#@ ,nfortunately, this proce&ure relies on a local
L

aria%le of the same name to achie e its en&s# 1lthough this co&e !ill compile !ithout error, it !ill not pro&uce the &esire& result* PRO+-D,R- impro e)QOL 'S /. Local aria%le !ith same name as column* ./ salary 0,MB-R *6 =<<</ +,RSOR &ou%le)sal)cur 'S S-L-+$ salary Q salary (ROM employee W2-R- hire&ate R 1DD)MO0$2S 8S3SD1$-, 9?>:/ B-7'0 'nstea& of a&&ing P=<<< to each personEs salary, this co&e !ill instea& &ou%le his or her salary# 'nsi&e the SQL statement, any unAualifie& reference to @salary@ is resol e& %y using the column name& @salary#@ ' coul& achie e the &esire& effect %y Aualifying the PL/SQL aria%le !ith the name of the proce&ure, as follo!s* +,RSOR &ou%le)sal)cur 'S S-L-+$ salary Q impro e!Q"L.salary (ROM employee W2-R- hire&ate R 1DD)MO0$2S 8S3SD1$-, 9?>:/ 'n this situation, you are informing the compiler that the secon& reference to salary is that aria%le @o!ne&@ %y the impro e)QOL proce&ure# 't !ill then a&& the current alue of that aria%le to the salary column alue# ' &o not, ho!e er, recommen& that you ma"e use of Aualifie& local aria%le names in this !ay# 'f your local aria%le names conflict !ith &ata%ase column or ta%le names, change the name of your aria%le# Best of all, a oi& this "in& of &uplication %y using a stan&ar& naming con ention for local aria%les !hich represent &ata%ase information# >#G#G $he +ursor R-$,R0 +lause One of the most significant ne! features in PL/SQL Bersion ; is the full support for pac"ages an& the resulting mo&ulariCation of co&e that is no! possi%le !ith that construct# Pac"ages intro&uce an enhancement to the !ay you can &eclare a cursor* the R-$,R0 clause# When you group programs together into a pac"age, you can ma"e only the specification, or hea&er information, of those programs a aila%le to &e elopers# 1lthough a &e eloper can tell from the specification !hat the mo&ule is calle& an& ho! to call it, he or she &oes not nee& to see any of the un&erlying co&e# 1s a result, you can create true %lac" %oxes %ehin& !hich you can hi&e complex implementational
=<

&etails# With Bersion ; of PL/SQL you can accomplish the same o%4ecti e !ith cursors %y using the cursor R-$,R0 clause# $he R-$,R0 clause allo!s you to create a specification for a cursor !hich is separate from its %o&y 8the S-L-+$ statement:# 3ou may then place cursors in pac"ages an& hi&e the implementation &etails from &e elopers# +onsi&er the follo!ing cursor &eclaration !ith R-$,R0 clause* +,RSOR caller)cur 8i&)in '0 0,MB-R: R-$,R0 callerF 'S S-L-+$ . (ROM caller W2-R- caller)i& 6 i&)in/ $he specification of the caller)cur cursor is* +,RSOR caller)cur 8i&)in '0 0,MB-R: R-$,R0 callerF !hile the %o&y of the caller)cur cursor is* S-L-+$ . (ROM caller W2-R- caller)i& 6 i&)in/ - erything up to %ut not inclu&ing the 'S "ey!or& is the specification, !hile e erything follo!ing the 'S "ey!or& is the %o&y# 3ou can inclu&e a R-$,R0 clause for any cursor you !rite in PL/SQL Bersion ;, %ut it is reAuire& only for cursors !hich are containe& in a pac"age specification# $he R-$,R0 clause may %e ma&e up of any of the follo!ing &atatype structures* 1 recor& &efine& from a &ata%ase ta%le, using the F attri%ute 1 recor& &efine& from a programmer9&efine& recor& 2ere is an example of a cursor &efine& in a pac"age# (irst, the pac"age specification pro i&es the name of the cursor an& the R-$,R0 &atatype 8an entire ro! from the company ta%le:* P1+S17- company 'S +,RSOR company)cur 8i&)in 0,MB-R: R-$,R0 companyF/ -0D company/ $hen the follo!ing pac"age %o&y repeats the cursor specification an& a&&s the SQL statement* P1+S17- BOD3 company 'S +,RSOR company)cur 8i&)in 0,MB-R: R-$,R0 companyF 'S S-L-+$ . (ROM company W2-R- company)i& 6 i&)in/ -0D company/
==

$he num%er of expressions in the cursorEs select list must match the num%er of columns in the recor& i&entifie& %y ta%le)nameF or PLSQL)recor&F# $he &atatypes of the elements must also %e compati%le# 'f the secon& element in the select list is type 0,MB-R, then the secon& column in the R-$,R0 recor& cannot %e type B1R+21R; or BOOL-10# Why place cursors in a pac"ageT (or the same reasons you !oul& place a proce&ure or a function in a pac"age* a pac"age is a collection of logically relate& o%4ects# By grouping the co&e into a pac"age you ma"e it easier for a &e eloper to i&entify an& use the co&e 8see+hapter =>, Pac"ages, for more &etails:# Pac"age& cursors are essentially %lac" %oxes# $his is a& antageous to &e elopers %ecause they ne er ha e to co&e or e en see the S-L-+$ statement# $hey only nee& to "no! !hat recor&s the cursor returns, in !hat or&er it returns them, an& !hich columns are in the column list# When cursor information is limite& on this "in& of @nee& to "no!@ %asis, it protects &e elopers an& the o erall application from change# Suppose that a year from no! the W2-R- clause of a Auery has to change# 'f a pac"age& cursor is not use&, then each program that has a har&co&e& or local cursor !ill ha e to %e mo&ifie& to meet the ne! specification# 'f, on the other han&, all &e elopers simply access the same cursor, then changes !ill only nee& to %e ma&e to that pac"age& &eclaration of the cursor# $he programs can then %e recompile& to automatically support this change#

6.# "pening Cursors $he first step in using a cursor is to &efine it in the &eclaration section# $he next step you must perform %efore you try to extract or fetch recor&s from a cursor is to open that cursor# $he syntax for the OP-0 statement is simplicity itself* OP-0 Rcursor)nameU M 8 argument M, argument ###N : N/ !here Rcursor)nameU is the name of the cursor you &eclare& an& the arguments are the alues to %e passe& if the cursor !as &eclare& !ith a parameter list# When you open a cursor, PL/SQL executes the Auery for that cursor# 't also i&entifies the acti e set of &ata 99 that is, the ro!s from all in ol e& ta%les that meet the criteria in the W2-R- clause an& 4oin con&itions# $he OP-0 &oes not itself actually retrie e any of these ro!s 99 that action is performe& %y the (-$+2 statement# Regar&less of !hen you perform the first fetch, ho!e er, the rea& consistency mo&el in the Oracle RDBMS guarantees that all fetches !ill reflect the &ata as it existe& !hen the cursor !as opene&# 'n other !or&s, from the moment you open your cursor until the moment that cursor is close&, all &ata fetche& through the cursor !ill ignore any inserts, up&ates, an& &eletes performe& after the cursor !as opene&# (urthermore, if the S-L-+$ statement in your cursor uses a (OR ,PD1$- clause, then, !hen the cursor is opene&, all the ro!s i&entifie& %y the Auery are loc"e&# 8$his
=;

topic is co ere& in Section >#==, @S-L-+$ (OR ,PD1$- in +ursors@ later in this chapter#: 3ou shoul& open a cursor only if it has %een close& or !as ne er opene&# 'f you try to open a cursor that is alrea&y open you !ill get the follo!ing error* OR19<>H==* PL/SQL* cursor alrea&y open 3ou can %e sure of a cursorEs status %y chec"ing the F'SOP-0 cursor attri%ute %efore you try to open the cursor* '( 0O$ company)curF'SOP-0 $2-0 OP-0 company)cur/ -0D '(/
Section >#L, @+ursor 1ttri%utes@ later in the chapter, explains the &ifferent cursor attri%utes

an& ho! to ma"e %est use of them in your programs#

6.6 $etc%ing &'()*'+,-./ 0rom Cursors 1 S-L-+$ statement creates a irtual ta%le in SQL* its return set is a series of ro!s &etermine& %y the W2-R- clause 8or lac" thereof: an& !ith columns &etermine& %y the column list of the S-L-+$# So a cursor represents that irtual ta%le !ithin your PL/SQL program# 'n almost e ery situation, the point of &eclaring an& opening a cursor is to return, or fetch, the ro!s of &ata from the cursor an& then manipulate the information retrie e&# PL/SQL pro i&es a (-$+2 statement for this action# $he general syntax for a (-$+2 is sho!n %elo!* (-$+2 Rcursor)nameU '0$O Rrecor&)or) aria%le)listU/ !here Rcursor)nameU is the name of the cursor from !hich the recor& is fetche& an& Rrecor&)or) aria%le)listU is the PL/SQL &ata structure into !hich the next ro! of the acti e set of recor&s is copie&# 3ou can fetch into a recor& structure 8&eclare& !ith the F attri%ute or $3P- &eclaration statement: or you can fetch into a list of one or more aria%les 8PL/SQL aria%les or application9specific %in& aria%les such as Oracle (orms items:# $he follo!ing examples illustrate the ariety of possi%le fetches* (etch into a PL/SQL recor&* (-$+2 company)cursor '0$O company)rec/ (etch into a aria%le* (-$+2 ne!)%alance)cursor '0$O ne!)%alance)&ollars/
=?

(etch into the ro! of a PL/SQL ta%le ro!, a aria%le, an& an Oracle (orms %in& aria%le* (-$+2 emp)name)cur '0$O emp)name 8=:, hire&ate, *&ept#min)salary/ >#>#= Matching +olumn List !ith '0$O +lause When you fetch into a list of aria%les, the num%er of aria%les must match the num%er of expressions in the S-L-+$ list of the cursor# When you fetch into a recor&, the num%er of columns in the recor& must match the num%er of expressions in the S-L-+$ list of the cursor# 'f you &o not match up the column list in the cursorEs Auery !ith the '0$O elements in the (-$+2, you !ill recei e the follo!ing compile error* PLS9<<?LG* !rong num%er of alues in the '0$O list of a (-$+2 statement LetEs loo" at ariations of (-$+2 to see !hat !ill an& !ill not !or"# Suppose ' ha e &eclare& the follo!ing cursor, recor&s, an& aria%les* +ursor that selects 4ust three columns from &r)seuss ta%le an& a recor& %ase& on that cursor* +,RSOR green)eggs)cur 8character)in '0 B1R+21R;: 'S S-L-+$ ham)Auantity, times)refuse&, excuse (ROM &r)seuss W2-R- %oo")title 6 E7R--0 -77S 10D 21ME 10D character)name 6 character)in/ green)eggs)rec green)eggs)curF/ +ursor for all columns in &r)seuss ta%le an& a recor& %ase& on the &r)seuss ta%le* +,RSOR &r)seuss)cur 'S S-L-+$ . (ROM &r)seuss/ &r)seuss)rec &r)seuss)curF/ Programmer9&efine& recor& type !hich contains all three of the green)eggs)cur columns, follo!e& %y &eclaration of recor&* $3P- green)eggs)rectype 'S R-+ORD 8hamV &r)seuss#ham)AuantityF$3P-/ sai&noV &r)seuss#times)refuse&F$3P-, reason &r)seuss#excuseF$3P-:/ full)green)eggs)rec green)eggs)rectype/
=G

Programmer9&efine& recor& type !hich contains only t!o of the three cursor columns, follo!ing %y &eclaration of recor&* $3P- partial)green)eggs)rectype 'S R-+ORD 8hamV &r)seuss#ham)AuantityF$3P-/ sai&noV &r)seuss#times)refuse&F$3P-:/ partial)rec partial)green)eggs)rectype/ 1 set of local PL/SQL aria%les* ham)amount 0,MB-R/ refuse&)count '0$-7-R/ lousy)excuse B1R+21R;8><:/ 0o! that e erything is &eclare&, ' then OP-0 the cursor for the @Sam ' 1m@ character 8passe& as an argument: as follo!s* OP-0 green)eggs)cur 8ESam ' 1mE:/ 1ll of the follo!ing fetches !ill compile !ithout error %ecause the num%er an& type of items in the '0$O clause match those of the cursor* (-$+2 green)eggs)cur '0$O green)eggs)rec/ (-$+2 green)eggs)cur '0$O ham)amount, refuse&)count, lousy)excuse/ (-$+2 green)eggs)cur '0$O full)green)eggs)rec/ (-$+2 &r)seuss)cur '0$O &r)seuss)rec/ 0otice that you can (-$+2 a cursorEs ro! into either a ta%le9%ase& or a programmer9 &efine& recor&# 3ou &o not ha e to !orry a%out recor& type compati%ility in this situation# PL/SQL 4ust nee&s to %e a%le to match up a cursor column/expression !ith a aria%le/fiel& in the '0$O clause# 1s you can see from the a%o e (-$+2es, you are not restricte& to using any single recor& or aria%le list for a particular (-$+2 99 e en in the same program# 3ou can &eclare multiple recor&s for the same cursor an& use all those &ifferent recor&s for &ifferent fetches# 3ou can also fetch once '0$O a recor& an& then later '0$O a aria%le list, as sho!n %elo!* OP-0 green)eggs)cur 8ESam ' 1mE:/ (-$+2 green)eggs)cur '0$O green)eggs)rec/ (-$+2 green)eggs)cur '0$O amount)of)ham, num)re4ections, reason/ +LOS- green)eggs)cur/ PL/SQL is ery flexi%le in ho! it matches up the cursor columns !ith the '0$O clause# >#>#; (etching Past the Last Ro! Once you open a cursor, you can (-$+2 from it until there are no more recor&s left in
=H

the acti e set# 1t this point the F0O$(O,0D cursor attri%ute for the cursor is set to $R,-# 1ctually, you can e en (-$+2 past the last recor&I 'n this case, PL/SQL !ill not raise any exceptions# 't 4ust !onEt &o anything for you# Because there is nothing left to fetch, though, it also !ill not mo&ify the alues in the '0$O list of the (-$+2# $he fetch !ill not set those alues to 0,LL# 3ou shoul&, therefore, ne er test the alues of '0$O aria%les to &etermine if the (-$+2 against the cursor succee&e&# 'nstea&, you shoul& chec" the alue of the F0O$(O,0D attri%ute, as explaine& in Section >#L#

>#J +olumn 1liases in +ursors $he S-L-+$ statement of the cursor inclu&es the list of columns that are returne& %y that cursor# 5ust as !ith any S-L-+$ statement, this column list may contain either actual column names or column expressions, !hich are also referre& to as calculate& or irtual columns# 1 column alias is an alternati e name you pro i&e to a column or column expression in a Auery# 3ou may ha e use& column aliases in SQL.Plus in or&er to impro e the rea&a%ility of a& hoc report output# 'n that situation, such aliases are completely optional# 'n an explicit cursor, on the other han&, column aliases are reAuire& for calculate& columns !hen* 3ou (-$+2 into a recor& &eclare& !ith a F &eclaration against that cursor# 3ou !ant to reference the calculate& column in your program# +onsi&er the follo!ing Auery# (or all companies !ith sales acti ity &uring =LLG, the S-L-+$ statement retrie es the company name an& the total amount in oice& to that company 8assume that the &efault &ate format mas" for this instance is WDD9MO09 3333E:* S-L-+$ company)name, S,M 8in )amt: (ROM company +, in oice ' W2-R- +#company)i& 6 '#company)i& 10D '#in oice)&ate B-$W--0 E<=95109=LLGE 10D E?=9D-+9=LLGE/ 'f you run this SQL statement in SQL.Plus, the output !ill loo" something li"e this* +OMP103)01M999999999999999999999999 1+M- $,RBO '0+# W1S2'07$O0 21'R +O# S,M 8'0B)1M$: 99999999999999999999999999 =<<< ;H#;<

S,M 8'0B)1M$: &oes not ma"e a particularly attracti e column hea&er for a report,
=>

%ut it !or"s !ell enough for a Auic" &ip into the &ata as an a& hoc Auery# LetEs no! use this same Auery in an explicit cursor an& a&& a column alias* D-+L1R+,RSOR comp)cur 'S S-L-+$ company)name, S,M 8in )amt: total)sales (ROM company +, in oice ' W2-R- +#company)i& 6 '#company)i& 10D '#in oice)&ate B-$W--0 E<=95109=LLGE 10D E?=9D-+9 =LLGE/ comp)rec comp)curF/ B-7'0 OP-0 comp)cur/ (-$+2 comp)cur '0$O comp)rec/ ### -0D/ With the alias in place, ' can get at that information 4ust as ' !oul& any other column in the Auery* '( comp)rec#total)sales U H<<< $2-0 DBMS)O,$P,$#P,$)L'08E 3ou ha e excee&e& your cre&it limit of PH<<< %y E OO $O)+21R 8H<<<9company)rec#total)sales, EPLLLLE::/ -0D '(/ 'f you fetch a ro! into a recor& &eclare& !ith F, the only !ay to access the column or column expression alue is to &o so %y the column name 99 after all, the recor& o%tains its structure from the cursor itself# >#K +losing +ursors -arly on ' !as taught that ' shoul& al!ays clean up after myself# $his rule is particularly important as it applies to cursors*
When you are done with a cursor, close it#2ere is the syntax for a +LOS- cursor statement*

+LOS- Rcursor)nameU !here Rcursor)nameU is the name of the cursor you are closing# 1n open cursor uses a certain amount of memory/ the exact amount &epen&s on the acti e set for the cursor# 't can, therefore, use up Auite a lot of the Share& 7lo%al 1rea of the RDBMS# $he cursor can also cause the &ata%ase to issue ro!9le el loc"s !hen the (OR ,PD1$- clause is use& in the S-L-+$ statement# >#K#= Maximum 0um%er of +ursors When your &ata%ase instance is starte&, an initialiCation parameter calle&
=J

OP-0)+,RSORS specifies the maximum num%er of open cursors that a single9user process can ha e at once# $his parameter &oes not control a system9!i&e feature, %ut rather the maximum a&&ress/memory space use& %y each process# 'f you are sloppy an& &o not close your cursors, you an& all other users might encounter the &rea&e& error message* OR19<=<<<* maximum open cursors excee&e& 3ou !oul& rather not &eal !ith this situation# (or one thing, you !ill nee& to com% through your co&e an& chec" for opene& cursors !hich ha e not %een close&# - en more frightening, your &ata%ase a&ministrator might insist that you tune your application so as to re&uce the num%er of cursors you are using 99 real co&e changesI ' say this in 4est, %ut in fact L<F of all the tuning that can %e &one for an application has nothing to &o !ith the &ata%ase, an& e erything to &o !ith the application# 1re the SQL statements tune&T 1re you closing all opene& cursorsT 1n& so on# When you close a cursor, you &isa%le it# Because the cursor no longer has an acti e set associate& !ith it, you cannot fetch recor&s from the cursor# $he memory for that cursor is release& an& the num%er of cursors mar"e& as currently open in your session is &ecrease& %y one, pulling you a!ay from the %rin" of error OR19<=<<<# 3ou shoul& close a cursor only if it is currently open# 3ou can %e sure of a cursorEs status %y chec"ing the F'SOP-0 cursor attri%ute %efore you try to close the cursor* '( company)curF'SOP-0 $2-0 +LOS- company)cur/ -0D '(/ >#K#; +losing Local +ursors 'f you &eclare a cursor in a PL/SQL %loc" 8an anonymous %loc", proce&ure, or function:, the cursor is only &efine& !ithin 8is @local to@: that %loc"# When execution of the %loc" terminates, PL/SQL !ill automatically close any local cursors !hich !ere left open !ithout raising an exception# ' recommen&, ho!e er, that you still inclu&e +LOS- statements for any cursor you opene& in your programs# DonEt &epen& on the runtime engine to &o your cleaning up for you# 'n a&&ition, if your cursor is &efine& in a pac"age, then its scope is not limite& to any particular PL/SQL %loc"# 'f you open such a cursor, it !ill stay open until you +LOSit explicitly or you &isconnect your Oracle session# >#L +ursor 1ttri%utes 3ou can manipulate cursors using the OP-0, (-$+2, an& +LOS- statements# When you nee& to get information a%out the current status of your cursor, or the result of the last fetch from a cursor, you !ill access cursor attri%utes#
=K

Both explicit an& implicit cursors ha e four attri%utes, as sho!n in $a%le >#=#
$a%le >#=* +ursor 1ttri%utes 0ameDescriptionF(O,0DReturns $R,- if recor& !as fetche& successfully, (1LS- other!ise# F0O$(O,0DReturns $R,- if recor& !as not fetche& successfully, (1LS- other!ise# FROW+O,0$Returns num%er of recor&s fetche& from cursor at that point in time# F'SOP-0Returns $R,- if cursor is open, (1LS- other!ise# $o o%tain information a%out the

execution of the cursor, you appen& the cursor attri%ute name to the name of your cursor# (or example, if you &eclare a cursor as follo!s* +,RSOR caller)cur 'S S-L-+$ caller)i&, company)i& (ROM caller/ then the four attri%utes associate& !ith the cursor are* caller)curF(O,0D caller)curF0O$(O,0D caller)curFROW+O,0$ caller)curF'SOP-0 Some of the !ays you can access the attri%utes of an explicit cursor are sho!n %elo! in %ol&* D-+L1R+,RSOR caller)cur 'S S-L-+$ caller)i&, company)i& (ROM caller/ caller)rec caller)curF/ B-7'0 /. Only open the cursor if it is not yet open ./ '( 0O$ caller!cur1IS"PE2 $2-0 OP-0 caller)cur -0D '(/ (-$+2 caller)cur '0$O caller)rec/ /. Seep fetching until no more recor&s are (O,0D ./ W2'L- caller!cur1$"32D LOOP DBMS)O,$P,$#P,$)L'08E5ust fetche& recor& num%er E OO $O)+21R &caller!cur14"5C"326//7 (-$+2 caller)cur '0$O caller)rec/ -0D LOOP/ CL"SE caller!cur7 -0D/ PL/SQL &oes pro i&e these same attri%utes for an implicit cursor# Because an implicit cursor has no name, PL/SQL assigns the generic name SQL to it# ,sing this name, you
=L

can access the attri%utes of an implicit cursor# (or more information on this topic, see Section >#L#H, @'mplicit SQL +ursor 1ttri%utes@ later in the chapter# 3ou can reference cursor attri%utes in your PL/SQL co&e, as sho!n in the prece&ing example, %ut you cannot use those attri%utes insi&e a SQL statement# 'f you try to use the FROW+O,0$ attri%ute in the W2-R- clause of a S-L-+$, for example* S-L-+$ caller)i&, company)i& (ROM caller W2-R- company)i& 6 company)curFROW+O,0$/ then you !ill get a compile error* PLS9<<;;L* 1ttri%ute expression !ithin SQL expression $he four explicit cursor attri%utes are examine& in &etail in the follo!ing sections# >#L#= $he F(O,0D 1ttri%ute $he F(O,0D attri%ute reports on the status of your most recent (-$+2 against the cursor# $he attri%ute e aluates to $R,- if the most recent (-$+2 against the explicit cursor returne& a ro!, or (1LS- if no ro! !as returne&# 'f the cursor has not yet %een opene&, a reference to the F(O,0D attri%ute raises the '0B1L'D)+,RSOR exception# 3ou can e aluate the F(O,0D attri%ute of any open cursor, %ecause you reference the cursor %y name# 'n the follo!ing example, ' loop through all the callers in the caller)cur cursor, assign all calls entere& %efore to&ay to that particular caller, an& then fetch the next recor&# 'f ' ha e reache& the last recor&, then the F0O$(O,0D attri%ute is set to $R,- an& ' exit the simple loop# OP-0 caller)cur/ LOOP (-$+2 caller)cur '0$O caller)rec/ -D'$ W2-0 0O$ caller)curF(O,0D/ ,PD1$- call S-$ caller)i& 6 caller)rec#caller)i& W2-R- call)timestamp R S3SD1$-/ -0D LOOP/ +LOS- call)cur/ 'n this next example, ' "eep a count of the total num%er of or&ers entere& for a particular company# 'f ' ha e fetche& my last or&er 8F(O,0D is (1LS-:, then ' &isplay a message in Oracle (orms informing the user of the total num%er of or&ers* OP-0 or&er)cur/ LOOP (-$+2 or&er)cur '0$O or&er)num%er, company)i&/ -D'$ W2-0 or&er)curF0O$(O,0D/
;<

&o)other)stuff)then)"eep)count/ *or&er#count)or&ers *6 *or&er#count)or&ers Q =/ -0D LOOP/ +LOS- or&er)cur/ '( *or&er#count)or&ers U = $2-0 DBMS)O,$P,$#P,$)L'08E1 total of E OO $O)+21R 8*or&er#count)or&ers: OO E or&ers ha e %een foun&#E:/ -LS/. OO ' hate to co&e messages li"e E1 total of = or&ers !as foun&#E OO 't ma"es me soun& illiterate# So ' !ill inclu&e a special9case OO message !hen 4ust one or&er is foun&# ./ DBMS)O,$P,$#P,$)L'0-8E5ust one or&er !as foun&#E:/ -0D '(/ >#L#; $he F0O$(O,0D 1ttri%ute $he F0O$(O,0D attri%ute is the opposite of F(O,0D# 't returns $R,- if the explicit cursor is una%le to fetch another ro! %ecause the last ro! !as fetche&# 'f the cursor is una%le to return a ro! %ecause of an error, the appropriate exception is raise&# 'f the cursor has not yet %een opene&, a reference to the F0O$(O,0D attri%ute raises the '0B1L'D)+,RSOR exception# 3ou can e aluate the F0O$(O,0D attri%ute of any open cursor, %ecause you reference the cursor %y name# When shoul& you use F(O,0D an& !hen shoul& you use F0O$(O,0DT $he t!o attri%utes are &irectly, logically oppose&, so !hate er you can &o !ith one you can also &o !ith a 0O$ of the other# 'n other !or&s, once a fetch has %een performe& against the open cursor Rcursor)nameU, the follo!ing expressions are eAui alent* Rcursor)nameUF(O,0D Rcursor)nameUF0O$(O,0D Rcursor)nameUF0O$(O,0D Rcursor)nameUF(O,0D 6 0O$ 6 0O$

,se !hiche er formulation fits most naturally in your co&e# 'n a pre ious example, ' issue& the follo!ing statement* -D'$ W2-0 0O$ caller)curF(O,0D/ to terminate the loop# 1 simpler an& more &irect statement !oul& use the F0O$(O,0D instea& of F(O,0D, as follo!s* -D'$ W2-0 caller)recF0O$(O,0D/
;=

>#L#? $he FROW+O,0$ 1ttri%ute $he FROW+O,0$ attri%ute returns the num%er of recor&s fetche& from a cursor at the time that the attri%ute is Auerie&# When you first open a cursor, its FROW+O,0$ is set to Cero# 'f you reference the FROW+O,0$ attri%ute of a cursor that is not open, you !ill raise the '0B1L'D)+,RSOR exception# 1fter each recor& is fetche&, FROW+O,0$ is increase& %y one# $his attri%ute can %e reference& in a PL/SQL statement, %ut not in a SQL statement# 3ou can use FROW+O,0$ to limit the num%er of recor&s fetche& from a cursor# $he follo!ing example retrie es only the first ten recor&s from the cursor, pro i&ing the top ten companies placing or&ers in =LL?* D-+L1R+,RSOR company)cur 'S S-L-+$ company)name, company)i&, total)or&er (ROM company)re enue) ie! W2-R- $O)0,MB-R 8$O)+21R 8or&er)&ate:: 6 =LL? ORD-R B3 total)or&er D-S+/ company)rec company)curF/ B-7'0 OP-0 company)cur/ LOOP (-$+2 company)cur '0$O company)rec/ -D'$ W2-0 company)curFROW+O,0$ U =< OR company)curF0O$(O,0D/ DBMS)O,$P,$#P,$)L'08E+ompany E OO company)rec#company)name OO E ran"e& num%er E OO $O)+21R 8company)curFROW+O,0$: OO E#E:/ -0D LOOP/ +LOS- company)cur/ >#L#G $he F'SOP-0 1ttri%ute $he F'SOP-0 attri%ute returns $R,- if the cursor is open/ other!ise, it returns (1LS-# 'n most cases !hen you use a cursor, you open it, fetch from it, an& close it, all !ithin one routine# Most of the time it is easy to "no! !hether your cursor is open or close&# 'n some cases, ho!e er, you !ill sprea& your cursor actions out o er a !i&er area of co&e, perhaps across &ifferent routines 8possi%le if the cursor is &eclare& in a pac"age:# 'f so, it !ill ma"e sense to use the F'SOP-0 attri%ute to ma"e sure that a cursor is open %efore you perform a fetch* '( 0O$ caller)curF'SOP-0 $2-0 OP-0 caller)cur/ -0D '(/ (-$+2 caller)cur '0$O caller)rec/ ###
;;

0O$-* Remem%er that if you try to open a cursor that has alrea&y %een opene&, you !ill recei e a runtime error* OR19<>H==* PL/SQL* cursor alrea&y open >#L#H 'mplicit SQL +ursor 1ttri%utes When the RDBMS opens an implicit cursor to process your reAuest 8!hether it is a Auery or an '0S-R$ or an ,PD1$-:, it ma"es cursor attri%utes a aila%le to you !ith the SQL cursor# $his is not a cursor in the !ay of an explicit cursor# 3ou cannot open, fetch from, or close the SQL cursor, %ut you can access information a%out the most recently execute& SQL statement through SQL cursor attri%utes# $he SQL cursor has the same four attri%utes as an explicit cursor* SQLF(O,0D SQLF0O$(O,0D SQLFROW+O,0$ SQLF'SOP-0 >#L#> Differences Bet!een 'mplicit an& -xplicit +ursor 1ttri%utes $he alues returne& %y implicit cursor attri%utes &iffer from those of explicit cursor attri%utes in the follo!ing !ays* 'f the RDBMS has not opene& an implicit SQL cursor in the session, then the SQLFROW+O,0$ attri%ute returns 0,LL instea& of raising the '0B1L'D)+,RSOR error# References to the other attri%utes 8'SOP-0, (O,0D, 0O$(O,0D: all return (1LS-# 3ou !ill ne er raise the '0B1L'D)+,RSOR error !ith an implicit cursor attri%ute reference# $he F'SOP-0 attri%ute !ill al!ays return (1LS- 99 %efore an& after the SQL statement# 1fter the statement is execute& 8!hether it is S-L-+$, ,PD1$-, D-L-$-, or '0S-R$:, the implicit cursor !ill alrea&y ha e %een opene& an& close& implicitly# 1n implicit cursor can ne er %e open outsi&e of the statement itself# SQL cursor attri%utes are al!ays set accor&ing to the results of the most recently execute& SQL statement# $hat SQL statement might ha e %een execute& in a store& proce&ure !hich your program calle&/ you might not e en %e a!are of that SQL statement execution# 'f you plan to ma"e use of a SQL cursor attri%ute, ma"e sure that you reference that attri%ute imme&iately after you execute the SQL statement# $he F(O,0D attri%ute returns $R,- if an ,PD1$-, D-L-$-, or '0S-R$ affecte& at least one recor&# 't !ill return (1LS- if those statements faile& to affect any recor&s# 't returns $R,- if an implicit S-L-+$ returns one ro!# $he %eha ior of the F0O$(O,0D attri%ute for ,PD1$-, D-L-$-, or
;?

'0S-R$ statements is the opposite of F(O,0D# $he situation for an implicit S-L-+$ is a %it &ifferent* !hen you use an implicit S-L-+$ statement, ne er rely on the F0O$(O,0D an& F(O,0D attri%utes# When an implicit S-L-+$ statement &oes not return any ro!s, PL/SQL imme&iately raises the 0O)D1$1)(O,0D exception# When an implicit S-L-+$ statement returns more than one ro!, PL/SQL imme&iately raises the $OO)M103)ROWS exception# 'n either case, once the exception is raise&, control shifts to the exception section of the PL/SQL %loc"#

6.89 Cursor Parameters 3ou are pro%a%ly familiar !ith parameters for proce&ures an& functions# Parameters pro i&e a !ay to pass information into an& out of a mo&ule# ,se& properly, parameters impro e the usefulness an& flexi%ility of mo&ules# PL/SQL also allo!s you to pass parameters into cursors# $he same rationale for using parameters in mo&ules applies to parameters for cursors*
1 parameter ma"es the cursor more reusa%le# 'nstea& of har&co&ing a alue

into the W2-R- clause of a Auery to select particular information, you can use a parameter an& then pass &ifferent alues to the W2-R- clause each time a cursor is opene&#
1 parameter a oi&s scoping pro%lems# When you pass parameters instea& of

har&co&ing alues, the result set for that cursor is not tie& to a specific aria%le in a program or %loc"# 'f your program has neste& %loc"s, you can &efine the cursor at a higher9le el 8enclosing: %loc" an& use it in any of the su%%loc"s !ith aria%les &efine& in those local %loc"s# 3ou can specify as many cursor parameters as you !ant an& nee&# When you OP-0 the parameter, you nee& to inclu&e an argument in the parameter list for each parameter, except for trailing parameters that ha e &efault alues# When shoul& you parameteriCe your cursorT ' apply the same rule of thum% to cursors as to mo&ules* if ' am going to use the cursor in more than one place, !ith &ifferent alues for the same W2-R- clause, then ' shoul& create a parameter for the cursor# LetEs ta"e a loo" at the &ifference %et!een parameteriCe& an& unparameteriCe& cursors# (irst, a cursor !ithout any parameters* +,RSOR 4o"e)cur 'S S-L-+$ name, category, last)use&)&ate (ROM 4o"e/ $he result set of this cursor is all the ro!s in the 4o"e ta%le# 'f ' 4ust !ante& to retrie e all 4o"es in the 2,SB10D category, ' !oul& nee& to a&& a W2-R- clause as follo!s*
;G

+,RSOR 4o"e)cur 'S S-L-+$ name, category, last)use&)&ate (ROM 4o"e W2-R- category 6 E2,SB10DE/ ' &i&nEt use a cursor parameter to accomplish this tas", nor &i& ' nee& to# $he 4o"e)cur cursor no! retrie es only those 4o"es a%out hus%an&s# $hatEs all !ell an& goo&, %ut !hat if ' also !oul& li"e to see light%ul% 4o"es an& then chic"en9an&9egg 4o"es an& finally, as my ten9year9ol& !oul& certainly &eman&, all my "noc"9"noc" 4o"esT >#=<#= 7eneraliCing +ursors !ith Parameters ' really &onEt !ant to !rite a separate cursor for each &ifferent category 99 that is &efinitely not a &ata9&ri en approach to programming# 'nstea&, ' !oul& much rather %e a%le to change the 4o"e cursor so that it can accept &ifferent categories an& return the appropriate ro!s# $he %est 8though not the only: !ay to &o this is !ith a cursor parameter* D-+L1R/. OO +ursor !ith parameter list consisting of a single OO string parameter# ./ +,RSOR 4o"e)cur 8category)in B1R+21R;: 'S S-L-+$ name, category, last)use&)&ate (ROM 4o"e W2-R- category 6 ,PP-R 8category)in:/ 4o"e)rec4o"e)curF/ B-7'0 /. 0o! !hen ' open the cursor, ' also pass the argument ./ OP-0 4o"e)cur8*4o"e#category:/ (-$+2 4o"e)cur '0$O 4o"e)rec/ ' a&&e& a parameter list after the cursor name an& %efore the 'S "ey!or&# ' too" out the har&co&e& @2,SB10D@ an& replace& it !ith @,PP-R 8category)in:@ so that ' coul& enter @2,SB10D@, @hus%an&@, or @2uS%1nD@ an& the cursor !oul& still !or"# 0o! !hen ' open the cursor, ' specify the alue ' !ish to pass as the category %y inclu&ing that alue 8!hich can %e a literal, constant, or expression: insi&e parentheses# 1t the moment the cursor is opene&, the S-L-+$ statement is parse& an& %oun& using the specifie& alue for category)in# $he result set is i&entifie& an& the cursor is rea&ie& for fetching# 6.89.2 "pening Cursors :it% Parameters ' can OP-0 that same cursor !ith any category ' li"e# 0o! ' &onEt ha e to !rite a
;H

separate cursor to accommo&ate this reAuirement* OP-0 4o"e)cur 8*4o"e#category:/ OP-0 4o"e)cur 8Ehus%an&E:/ OP-0 4o"e)cur 8EpoliticianE:/ OP-0 4o"e)cur 8*4o"e#relation OO E '09L1WE:/ $he most common place to use a parameter in a cursor is in the W2-R- clause, %ut you can ma"e reference to it any!here in the S-L-+$ statement, as sho!n here* D-+L1R+,RSOR 4o"e)cur 8category)in B1R+21R;: 'S S-L-+$ name, category)in, last)use&)&ate (ROM 4o"e W2-R- category 6 ,PP-R 8category)in:/ 'nstea& of returning the category from the ta%le, ' simply pass %ac" the category)in parameter in the select list# $he result !ill %e the same either !ay, %ecause my W2-R- clause restricts categories to the parameter alue# >#=<#? Scope of +ursor Parameters $he scope of the cursor parameter is confine& to that cursor# 3ou cannot refer to the cursor parameter outsi&e of the S-L-+$ statement associate& !ith the cursor# $he follo!ing PL/SQL fragment !ill not compile %ecause the program)name i&entifier is not a local aria%le in the %loc"# 'nstea&, it is a formal parameter for the cursor an& is &efine& only insi&e the cursor* D-+L1R+,RSOR scariness)cur 8program)name B1R+21R;: 'S S-L-+$ S,M 8scary)le el: total)scary)le el (ROM tales)from)the)crypt W2-R- prog)name 6 program)name/ B-7'0 program)name *6 E$2- BR-1$2'07 M,MM3E/ /. 'llegal reference ./ OP-0 scariness)cur 8program)name:/ -0D/ >#=<#G +ursor Parameter Mo&es $he syntax for cursor parameters is ery similar to that of proce&ures an& functions, !ith the restriction that a cursor parameter can %e an '0 parameter only# 3ou cannot specify O,$ or '0 O,$ mo&es for cursor parameters 8see +hapter =H, Proce&ures an& (unctions, for more information on parameter mo&es:# $he '0 an& '0 O,$ mo&es are use& to pass alues out of a proce&ure through that parameter# $his &oesnEt ma"e sense for a cursor# Balues cannot %e passe& %ac" out of a
;>

cursor through the parameter list# 'nformation is retrie e& from a cursor only %y fetching a recor& an& copying alues from the column list !ith an '0$O clause# >#=<#H Default Balues for Parameters +ursor parameters can %e assigne& &efault alues# 2ere is an example of a parameteriCe& cursor !ith a &efault alue* +,RSOR emp)cur 8emp)i&)in 0,MB-R *6 <: 'S S-L-+$ employee)i&, emp)name (ROM employee W2-R- employee)i& 6 emp)i&)in/ So if 5oe SmithEs employee 'D is =<<=, the follo!ing statements !oul& set my)emp)i& to =<<= an& my)emp)name to 5O- SM'$2* OP-0 emp)cur 8=<<=:/ (-$+2 emp)cur '0$O my)emp)i&, my)emp)name/ Because the emp)i&)in parameter has a &efault alue, ' can also open an& fetch from the cursor !ithout specifying a parameter# 'f ' &o not specify a alue for the parameter, the cursor uses the &efault alue#

>#== S-L-+$ (OR ,PD1$- in +ursors When you issue a S-L-+$ statement against the &ata%ase to Auery some recor&s, no loc"s are place& on the selecte& ro!s# 'n general, this is a !on&erful feature %ecause the num%er of recor&s loc"e& at any gi en time is 8%y &efault: "ept to the a%solute minimum* only those recor&s !hich ha e %een change& %ut not yet committe& are loc"e&# - en then, others !ill %e a%le to rea& those recor&s as they appeare& %efore the change 8the @%efore image@ of the &ata:# $here are times, ho!e er, !hen you !ill !ant to loc" a set of recor&s e en %efore you change them in your program# Oracle offers the (OR ,PD1$- clause of the S-L-+$ statement to perform this loc"ing# When you issue a S-L-+$###(OR ,PD1$- statement, the RDBMS automatically o%tains exclusi e ro!9le el loc"s on all the ro!s i&entifie& %y the S-L-+$ statement, hol&ing the recor&s @for your changes only@ as you mo e through the ro!s retrie e& %y the cursor# 0o one else !ill %e a%le to change any of these recor&s until you perform a ROLLB1+S or a +OMM'$# 2ere are t!o examples of the (OR ,PD1$- clause use& in a cursor* +,RSOR toys)cur 'S S-L-+$ name, manufacturer, preference)le el, sell)at)yar&sale)flag (ROM my)sons)collection
;J

W2-R- hours)use& 6 < (OR ,PD1$-/ +,RSOR fall)4o%s)cur 'S S-L-+$ tas", expecte&)hours, tools)reAuire&, &o)it)yourself)flag (ROM !interiCe W2-R- year 6 $O)+21R 8S3SD1$-, E3333E: (OR ,PD1$- O( tas"/ $he first cursor uses the unAualifie& (OR ,PD1$- clause, !hile the secon& cursor Aualifies the (OR ,PD1$- !ith a column name from the Auery# 3ou can use the (OR ,PD1$- clause in a S-L-+$ against multiple ta%les# 'n this case, ro!s in a ta%le are loc"e& only if the (OR ,PD1$- clause references a column in that ta%le# 'n the follo!ing example the (OR ,PD1$- clause &oes not result in any loc"e& ro!s in the !interiCe ta%le* +,RSOR fall)4o%s)cur 'S S-L-+$ !#tas", !#expecte&)hours, !#tools)reAuire&, !#&o)it)yourself)flag (ROM !interiCe !, hus%an&)config hc W2-R- year 6 $O)+21R 8S3SD1$-, E3333E: (OR ,PD1$- O( hus%an&)config#max)procrastination)allo!e&/ $he (OR ,PD1$- O( clause only mentions the max)procrastination)allo!e& column/ no columns in the !interiCe ta%le are liste&# $he O( list of the (OR ,PD1$- clause &oes not restrict you to changing only those columns liste&# Loc"s are still place& on all ro!s/ the O( list 4ust gi es you a !ay to &ocument more clearly !hat you inten& to change# 'f you simply state (OR ,PD1$in the Auery an& &o not inclu&e one or more columns after the O( "ey!or&, then the &ata%ase !ill then loc" all i&entifie& ro!s across all ta%les liste& in the (ROM clause# (urthermore, you &o not ha e to actually ,PD1$- or D-L-$- any recor&s 4ust %ecause you issue& a S-L-+$###(OR ,PD1$- 99 that act simply states your intention to %e a%le to &o so# (inally, you can appen& the optional "ey!or& 0OW1'$ to the (OR ,PD1$- clause to tell Oracle not to !ait if the ta%le has %een loc"e& %y another user# 'n this case, control !ill %e returne& imme&iately to your program so that you can perform other !or" or simply !ait for a perio& of time %efore trying again# Without the 0OW1'$ clause, your process !ill %loc" until the ta%le is a aila%le# $here is no limit to the !ait time unless the ta%le is remote# (or remote o%4ects, the Oracle initialiCation parameter, D'S$R'B,$-D)LO+S)$'M-O,$, is use& to set the limit# >#==#= Releasing Loc"s !ith +OMM'$ 1s soon as a cursor !ith a (OR ,PD1$- clause is OP-0e&, all ro!s i&entifie& in the result set of the cursor are loc"e& an& remain loc"e& until your session issues either a +OMM'$ statement to sa e any changes or a ROLLB1+S statement to cancel those
;K

changes# When either of these actions occurs, the loc"s on the ro!s are release&# 1s a result, you cannot execute another (-$+2 against a (OR ,PD1$- cursor after you +OMM'$ or ROLLB1+S# 3ou !ill ha e lost your position in the cursor# +onsi&er the follo!ing program, !hich assigns !interiCation chores*M=N M=N +a eat* ' &onEt !ant to set false expectations !ith anyone, especially my !ife# $he co&e in this %loc" is purely an example# 'n reality, ' set the max)procrastination)allo!e& to fi e years an& let my house &ecay until ' can affor& to pay someone to &o something, or my !ife &oes it, or she gi es me an ultimatum# 0o! you "no! !hy ' &eci&e& to !rite a %oo"### D-+L1R/. 1ll the 4o%s in the (all to prepare for the Winter ./ +,RSOR fall)4o%s)cur 'S S-L-+$ tas", expecte&)hours, tools)reAuire&, &o)it)yourself)flag (ROM !interiCe W2-R- year 6 $O)+21R 8S3SD1$-, E3333E: 10D complete&)flag 6 E0O$3-$E (OR ,PD1$- O( tas"/ B-7'0 /. (or each 4o% fetche& %y the cursor### ./ (OR 4o%)rec '0 fall)4o%s)cur LOOP '( 4o%)rec#&o)it)yourself)flag 6 E3O,+10DO'$E $2-0 /. OO ' ha e foun& my next 4o%# 1ssign it to myself 8li"e someone OO is going to &o itI: an& then commit the changes# ./ ,PD1$- !interiCe S-$ responsi%le 6 ES$-B-0E W2-R- tas" 6 4o%)rec#tas" 10D year 6 $O)+21R 8S3SD1$-, E3333E:/ +OMM'$/ -0D '(/ -0D LOOP/ -0D/ Suppose this loop fin&s its first 3O,+10DO'$ 4o%# 't then commits an assignment of a 4o% to S$-B-0# When it tries to (-$+2 the next recor&, the program raises the follo!ing exception* OR19<=<<;* fetch out of seAuence 'f you e er nee& to execute a +OMM'$ or ROLLB1+S as you (-$+2 recor&s from a S-L-+$ (OR ,PD1$- cursor, you shoul& inclu&e co&e 8such as a loop -D'$ or other con&itional logic: to halt any further fetches from the cursor#
;L

>#==#; $he W2-R- +,RR-0$ O( +lause PL/SQL pro i&es the W2-R- +,RR-0$ O( clause for %oth ,PD1$- an& D-L-$statements insi&e a cursor in or&er to allo! you to easily ma"e changes to the most recently fetche& ro! of &ata# $he general format for the W2-R- +,RR-0$ O( clause is as follo!s* ,PD1$- ta%le)name S-$ set)clause W2-R- +,RR-0$ O( cursor)name/ D-L-$(ROM ta%le)name W2-R- +,RR-0$ O( cursor)name/ 0otice that the W2-R- +,RR-0$ O( clause references the cursor an& not the recor& into !hich the next fetche& ro! is &eposite&# $he most important a& antage to using W2-R- +,RR-0$ O( !here you nee& to change the ro! fetche& last is that you &o not ha e to co&e in t!o 8or more: places the criteria use& to uniAuely i&entify a ro! in a ta%le# Without W2-R- +,RR-0$ O(, you !oul& nee& to repeat the W2-R- clause of your cursor in the W2-R- clause of the associate& ,PD1$-s an& D-L-$-s# 1s a result, if the ta%le structure changes in a !ay that affects the construction of the primary "ey, you ha e to ma"e sure that each SQL statement is upgra&e& to support this change# 'f you use W2-R- +,RR-0$ O(, on the other han&, you only ha e to mo&ify the W2-R- clause of the S-L-+$ statement# $his might seem li"e a relati ely minor issue, %ut it is one of many areas in your co&e !here you can le erage su%tle features in PL/SQL to minimiCe co&e re&un&ancies# ,tiliCation of W2-R- +,RR-0$ O(, F$3P-, an& F &eclaration attri%utes, cursor (OR loops, local mo&ulariCation, an& other PL/SQL language constructs can ha e a %ig impact on re&ucing the pain you may experience !hen you maintain your Oracle9 %ase& applications# LetEs see ho! this clause !oul& impro e the pre ious example# 'n the 4o%s cursor (OR loop a%o e, ' !ant to ,PD1$- the recor& that !as currently (-$+2e& %y the cursor# ' &o this in the ,PD1$- statement %y repeating the same W2-R- use& in the cursor %ecause 8tas", year: ma"es up the primary "ey of this ta%le* W2-R- tas" 6 4o%)rec#tas" 10D year 6 $O)+21R 8S3SD1$-, E3333E:/ $his is a less than i&eal situation, as explaine& a%o e* ' ha e co&e& the same logic in t!o places, an& this co&e must %e "ept synchroniCe&# 't !oul& %e so much more con enient an& natural to %e a%le to co&e the eAui alent of the follo!ing statements*
Delete the record I just fetched.or* Update these columns in that row I just fetched.1 perfect fit for W2-R- +,RR-0$ O(I ?<

$he next ersion of my !interiCation program %elo! uses this clause# ' ha e also s!itche& to a simple loop from (OR loop %ecause ' !ant to exit con&itionally from the loop* D-+L1R+,RSOR fall)4o%s)cur 'S S-L-+$ ### same as %efore ### / 4o%)rec fall)4o%s)curF/ B-7'0 OP-0 fall)4o%s)cur/ LOOP (-$+2 fall)4o%s)cur '0$O 4o%)rec/ '( fall)4o%s)curF0O$(O,0D $2-0 -D'$/ -LS'( 4o%)rec#&o)it)yourself)flag 6 E3O,+10DO'$E $2-0 ,PD1$- !interiCe S-$ responsi%le 6 ES$-B-0E W2-R- +,RR-0$ O( fall)4o%s)cur/ +OMM'$/ -D'$/ -0D '(/ -0D LOOP/ +LOS- fall)4o%s)cur/ -0D/ 6.82 Cursor ;aria<les 'n PL/SQL Release ;#?, a aila%le !ith the release of Oracle Ser er Release J#?, you can create an& use cursor aria%les# ,nli"e an explicit cursor, !hich names the PL/SQL !or" area for the result set, a cursor aria%le is instea& a 8Release ;#?: reference to that !or" area# -xplicit an& implicit cursors are %oth static in that they are tie& to specific Aueries# $he cursor aria%le can %e opene& for any Auery, e en &ifferent Aueries !ithin a single program execution# $he most important %enefit of the cursor aria%le is that it pro i&es a mechanism for passing results of Aueries 8the ro!s returne& %y fetches against a cursor: %et!een &ifferent PL/SQL programs 99 e en %et!een client an& ser er PL/SQL programs# Prior to PL/SQL Release ;#?, you !oul& ha e ha& to fetch all &ata from the cursor, store it in PL/SQL aria%les 8perhaps a PL/SQL ta%le:, an& then pass those aria%les as arguments# With cursor aria%les, you simply pass the reference to that cursor# $his impro es performance an& streamlines your co&e# 't also means that the cursor is, in effect, share& among the programs !hich ha e access to the cursor aria%le# 'n a client9ser er en ironment, for example, a program on the client si&e coul& open an& start fetching from the cursor aria%le, then pass that aria%le as an argument to a store& proce&ure on the ser er# $his store& program coul&
?=

then continue fetching an& pass control %ac" to the client program to close the cursor# 3ou can also perform the same steps %et!een &ifferent store& programs, on the same or &ifferent &ata%ase instances# +ursor Baria%les in PL/SQL Release ;#; +ursor aria%les first %ecame a aila%le in PL/SQL Release ;#;# $his first ersion of cursor aria%les allo!e& you to open an& close the cursor o%4ects !ithin PL/SQL, %ut you coul& fetch through these cursor aria%les only !ithin a host language 8using the Oracle +all 'nterface 99 O+' 99 or a precompiler li"e Pro.+:# 't !as not until Release ;#? that the PL/SQL language ma&e cursor aria%les a aila%le for @self9containe&@ execution, in&epen&ent of any host language# Because the focus of this %oo" is on stan&alone PL/SQL &e elopment, ' present cursor aria%les as a PL/SQL Release ;#? enhancement# 'f you &o ha e PL/SQL Release ;#; an& !or" !ith PL/SQL in a host language en ironment, you can still use cursor aria%les# 5ust &onEt try to (-$+2 !ithin PL/SQL an& &onEt expect any of the cursor attri%utes to %e a aila%le for your cursor aria%les# 0O$-* $he client9ser er aspect of this sharing !ill only really come into play !hen the Oracle De eloper/;<<< tools are con erte& to use PL/SQL Release ;#? or a%o e# $his process, sho!n in (igure >#;, offers &ramatic ne! possi%ilities for &ata sharing an& cursor management in PL/SQL programs# $igure 6.2= 4e0erencing a cursor aria<le across t:o programs $he co&e you !rite to ta"e a& antage of cursor aria%les is ery similar to that for explicit cursors# $he follo!ing example &eclares a cursor type 8calle& a R-( +,RSOR type: for the company ta%le, then opens, fetches from, an& closes the cursor* D-+L1R/. +reate the cursor type# ./ $3P- company)curtype 'S R-( +,RSOR R-$,R0 companyF/ /. Declare a cursor aria%le of that type# ./ company)cur ar company)curtype/ /. Declare a recor& !ith same structure as cursor aria%le# ./ company)rec companyF/ B-7'0 /. Open the cursor aria%le, associating !ith it a SQL statement# ./ OP-0 company)cur ar (OR S-L-+$ . (ROM company/ /. (etch from the cursor aria%le# ./ (-$+2 company)cur ar '0$O company)rec/ /. +lose the cursor o%4ect associate& !ith aria%le# ./
?;

+LOS- company)cur ar/ -0D/ $hat loo"s an a!ful lot li"e explicit cursor operations, except for the follo!ing* $he R-( +,RSOR type &eclaration $he OP-0 (OR syntax !hich specifie& the Auery at the time of the open While the syntax is ery similar, the fact that the cursor aria%le is a aria%le opens up many ne! opportunities in your programs# $hese are explore& in the remain&er of this section# >#=;#= (eatures of +ursor Baria%les +ursor aria%les let you* 1ssociate a cursor aria%le !ith &ifferent Aueries at &ifferent times in your program execution# 'n other !or&s, a single cursor aria%le can %e use& to fetch from &ifferent result sets# Pass a cursor aria%le as an argument to a proce&ure or function# 3ou can, in essence, share the results of a cursor %y passing the reference to that result set# -mploy the full functionality of static PL/SQL cursors for cursor aria%les# 3ou can OP-0, +LOS-, an& (-$+2 !ith cursor aria%les !ithin your PL/SQL programs# 3ou can reference the stan&ar& cursor attri%utes 99 F'SOP-0, F(O,0D, F0O$(O,0D, an& FROW+O,0$ 99 for cursor aria%les# 1ssign the contents of one cursor 8an& its result set: to another cursor aria%le# Because the cursor aria%le is a aria%le, it can %e use& in assignment operations# $here are, ho!e er, restrictions on referencing this "in& of aria%le, a&&resse& later in this chapter# >#=;#; Similarities to Static +ursors One of the "ey &esign reAuirements for cursor aria%les !as that as much as possi%le the semantics use& to manage cursor o%4ects !oul& %e the same as that of static cursors# While the &eclaration of a cursor aria%le an& the syntax for opening it are enhance&, the follo!ing cursor operations are unchange& for cursor aria%les* $he +LOS- statement# 'n the follo!ing example ' &eclare a R-( +,RSOR type an& a cursor aria%le %ase& on that type# $hen ' close the cursor aria%le using the same syntax as for that of a static cursor* D-+L1R $3P- ar)cur)type 'S R-( +,RSOR/ ar)cur ar)cur)type/ B-7'0
??

+LOS- ar)cur/ -0D/

+ursor attri%utes# 3ou can use any of the four cursor attri%utes !ith exactly

the same syntax as for that of a static cursor# $he rules go erning the use an& alues returne& %y those attri%utes match that of explicit cursors# 'f ' ha e &eclare& a aria%le cursor as in the pre ious example, ' coul& use all the cursor attri%utes as follo!s* ar)curF'SOOP-0 ar)curF(O,0D ar)curF0O$(O,0D ar)curFROW+O,0$

(etching from the cursor aria%le# 3ou use the same (-$+2 syntax !hen

fetching from a cursor aria%le into local PL/SQL &ata structures# $here are, ho!e er, a&&itional rules applie& %y PL/SQL to ma"e sure that the &ata structures of the cursor aria%leEs ro! 8the set of alues returne& %y the cursor o%4ect: match that of the &ata structures to the right of the '0$O "ey!or&# $hese rules are &iscusse& in Section >#=;#>, @Rules for +ursor Baria%les@# Because the syntax for these aspects of cursor aria%les remain unchange&, ' !onEt co er them again in the remain&er of this section# 'nstea& ' !ill focus on the ne! capa%ilities a aila%le an& the change& syntax reAuire& for cursor aria%les# >#=;#? Declaring R-( +,RSOR $ypes an& +ursor Baria%les 5ust as !ith a PL/SQL ta%le or a programmer9&efine& recor&, you must perform t!o &istinct &eclaration steps in or&er to create a cursor aria%le* = +reate a reference& cursor $3P-# ; Declare the actual cursor aria%le %ase& on that type# $he syntax for creating a reference& cursor type is as follo!s* $3P- cursor)type)name 'S R-( +,RSOR M R-$,R0 return)type N/ !here cursor)type)name is the name of the type of cursor an& return)type is the R-$,R0 &ata specification for the cursor type# $he return)type can %e any of the &ata structures ali& for a normal cursor R-$,R0 clause, &efine& using the F attri%ute or %y referencing a pre iously9&efine& recor& $3P-# 0otice that the R-$,R0 clause is optional !ith the R-( +,RSOR type statement# Both of the follo!ing &eclarations are ali&* $3P- company)curtype 'S R-( +,RSOR R-$,R0 companyF/ $3P- generic)curtype 'S R-( +,RSOR/ $he first form of the R-( +,RSOR statement is calle& a strong type %ecause it attaches a recor& type 8or ro! type: to the cursor aria%le type at the moment of
?G

&eclaration# 1ny cursor aria%le &eclare& using that type can only %e use& !ith SQL statement an& (-$+2 '0$O &ata structures !hich match the specifie& recor& type# $he a& antage of a strong R-( $3P- is that the compiler can &etermine !hether or not the &e eloper has properly matche& up the cursor aria%leEs (-$+2 statements !ith its cursor o%4ectEs Auery list# $he secon& form of the R-( +,RSOR statement, in !hich the R-$,R0 clause is missing, is calle& a !ea" type# $his cursor aria%le type is not associate& !ith any recor& &ata structure# +ursor aria%les &eclare& !ithout the R-$,R0 clause can %e use& in much more flexi%le !ays than the strong type# $hey can %e use& !ith any Auery, !ith any ro!type structure 99 arying e en !ithin the course of a single program# >#=;#?#= Declaring cursor aria%les $he syntax for &eclaring a cursor aria%le is* cursor)name cursor)type)name/ !here cursor)name is the name of the cursor an& cursor)type)name is the name of the type of cursor pre iously &efine& !ith a $3P- statement# 2ere is an example of the creation of a cursor aria%le* D-+L1R/. +reate a cursor type for sports cars# ./ $3P- sports)car)cur)type 'S R-( +,RSOR R-$,R0 carF/ /. +reate a cursor aria%le for sports cars# ./ sports)car)cur sports)car)cur)type/ B-7'0 ### -0D/ 't is ery important to &istinguish %et!een &eclaring a cursor aria%le an& creating an actual cursor o%4ect 99 the result set i&entifie& %y the cursor SQL statement# $he cursor aria%le is nothing more than a reference or pointer# 1 constant is nothing more than a alue, !hereas a aria%le points to its alue# Similarly, a static cursor acts as a constant, !hereas a cursor aria%le points to a cursor o%4ect# $hese &istinctions are sho!n in (igure >#?# 0otice that t!o &ifferent cursor aria%les in &ifferent programs %oth refer to the same cursor o%4ect# (igure >#?* $he referencing character of cursor aria%les Declaration of a cursor aria%le &oes not create a cursor o%4ect# $o &o that, you must instea& use the OP-0 (OR syntax to create a ne! cursor o%4ect an& assign it to the aria%le#
?H

>#=;#G Opening +ursor Baria%les 3ou assign a alue 8the cursor o%4ect: to a cursor !hen you OP-0 the cursor# So the syntax for the OP-0 statement is no! mo&ifie& in PL/SQL Release ;#? to accept a S-L-+$ statement after the (OR clause, as sho!n %elo!* OP-0 cursor)name (OR select)statement/ !here cursor)name is the name of a cursor or cursor aria%le an& select)statement is a SQL S-L-+$ statement# (or strong R-( +,RSOR type cursor aria%les, the structure of the S-L-+$ statement 8the num%er an& &atatypes of the columns: must match or %e compati%le !ith the structure specifie& in the R-$,R0 clause of the type statement# (igure >#G offers an example of the "in& of compati%ility reAuire&# (igure >#G@ contains the full set of compati%ility rules# (igure >#G* +ompati%le R-( +,RSOR ro!type an& S-L-+$ list 'f cursor)name is a cursor aria%le &efine& !ith a !ea" R-( +,RSOR type, you can OP-0 it for any Auery, !ith any structure# 'n the follo!ing example, ' open 8assign a alue to: the cursor aria%le t!ice, !ith t!o &ifferent Aueries* D-+L1R$3P- emp)curtype 'S R-( +,RSOR/ emp)cur ar emp)curtype/ B-7'0 OP-0 emp)cur ar (OR S-L-+$ . (ROM emp/ OP-0 emp)cur ar (OR S-L-+$ employee)i& (ROM emp/ OP-0 emp)cur ar (OR S-L-+$ company)i&, name (ROM company/ -0D/ $hat last open &i&nEt e en ha e anything to &o !ith the employee ta%leI 'f the cursor aria%le has not yet %een assigne& to any cursor o%4ect, the OP-0 (OR statement implicitly creates an o%4ect for the aria%le# 'f at the time of the OP-0 the cursor aria%le alrea&y is pointing to a cursor o%4ect, then OP-0 (OR &oes not create a ne! o%4ect# 'nstea&, it reuses the existing o%4ect an& attaches a ne! Auery to that o%4ect# $he cursor o%4ect is maintaine& separately from the cursor or Auery itself# >#=;#H (etching from +ursor Baria%les 1s mentione& earlier, the syntax for a (-$+2 statement using a cursor aria%le is the same as that for static cursors* (-$+2 Rcursor aria%le nameU '0$O Rrecor& nameU/ (-$+2 Rcursor aria%le nameU '0$O R aria%le nameU, R aria%le nameU ###/
?>

When the cursor aria%le !as &eclare& !ith a strong R-( +,RSOR type, the PL/SQL compiler ma"es sure that the &ata structure8s: liste& after the '0$O "ey!or& are compati%le !ith the structure of the Auery associate& !ith cursor aria%le# >#=;#H#= Strong an& !ea" R-( +,RSOR types 'f the cursor aria%le is of the !ea" R-( +,RSOR type, the PL/SQL compiler cannot perform the same "in& of chec"# Such a cursor aria%le can (-$+2 into any &ata structures, %ecause the R-( +,RSOR type it is not i&entifie& !ith a ro!type at the time of &eclaration# 1t compile time, there is no !ay to "no! !hich cursor o%4ect 8an& associate& SQL statement: !ill %e assigne& to that aria%le# +onseAuently, the chec" for compati%ility must happen at run time, !hen the (-$+2 is a%out to %e execute&# 1t this point, if the Auery an& the '0$O clause &o not structurally match 8an& PL/SQL !ill use implicit con ersions if necessary an& possi%le:, then the PL/SQL runtime engine !ill raise the pre&efine& )M'SM1$+2 exception# >#=;#H#; 2an&ling the )M'SM1$+2 exception Before PL/SQL actually performs its (-$+2, it chec"s for compati%ility# 1s a result, you can trap the )M'SM1$+2 exception an& attempt to (-$+2 from the cursor aria%le using a &ifferent '0$O clause 99 an& you !ill not ha e s"ippe& any ro!s in the result set# - en though you are executing a secon& (-$+2 statement in your program, you !ill still retrie e the first ro! in the result set of the cursor o%4ectEs Auery# $his functionality comes in especially han&y for !ea" R-( +,RSOR types# 'n the follo!ing example, a centraliCe& real estate &ata%ase stores information a%out properties in a ariety of ta%les, one for homes, another for commercial properties, etc# $here is also a single, central ta%le !hich stores an a&&ress an& a %uil&ing type 8home, commercial, etc#:# ' use a single proce&ure to open a !ea" R-( +,RSOR aria%le for the appropriate ta%le, %ase& on the street a&&ress# -ach in&i i&ual real estate office can then call that proce&ure to scan through the matching properties* = Define my !ea" R-( +,RSOR type* ; $3P- %uil&ing)curtype 'S R-( +,RSOR/ ? +reate the proce&ure# 0otice that the mo&e of the cursor aria%le parameter is '0 O,$* G PRO+-D,R- open)site)list 5 8a&&ress)in '0 B1R+21R;, 6 site)cur)inout '0 O,$ %uil&ing)curtype: J 'S 8 home)type +O0S$10$ '0$-7-R *6 =/ 9 commercial)type +O0S$10$ '0$-7-R *6 ;/
10 11

/. 1 static cursor to get %uil&ing type# ./


?J

12 13 14 15 16

+,RSOR site)type)cur 'S S-L-+$ site)type (ROM property)master W2-R- a&&ress 6 a&&ress)in/ site)type)rec site)type)curF/

=JB-7'0 18 /. 7et the %uil&ing type for this a&&ress# ./ 19 OP-0 site)type)cur/ 20 (-$+2 site)type)cur '0$O site)type)rec/ 21 +LOS- site)type)cur/
22 23 24 25 26 27 28 29 30 31

/. 0o! use the site type to select from the right ta%le#./ '( site)type)rec#site)type 6 home)type $2-0 /. ,se the home properties ta%le# ./ OP-0 site)cur)inout (OR S-L-+$ . (ROM home)properties W2-R- a&&ress L'S- EFE OO a&&ress)in OO EFE/

-LS'( site)type)rec#site)type 6 commercial)type 32 $2-0 33 /. ,se the commercial properties ta%le# ./ 34 OP-0 site)cur)inout (OR 35 S-L-+$ . (ROM commercial)properties 36 W2-R- a&&ress L'S- EFE OO a&&ress)in OO EFE/ 37 -0D '(/ ?K-0D open)site)list/ ?L0o! that ' ha e my open proce&ure, ' can use it to scan properties# 'n the follo!ing example, ' pass in the a&&ress an& then try to fetch from the cursor, assuming a home property# 'f the a&&ress actually i&entifies a commercial property, PL/SQL !ill raise the )M'SM1$+2 exception 8incompati%le recor& structures:# $he exception section then fetches again, this time into a commercial %uil&ing recor&, an& the scan is complete#M;N M;N $he @prompt@ an& @sho!@ programs reference& in the example interact !ith users an& are not &ocumente& here# D-+L1R/. Declare a cursor aria%le# ./ %uil&ing)cur ar %uil&ing)curtype/ /. Define recor& structures for t!o &ifferent ta%les# ./ home)rec home)propertiesF/ commercial)rec commercial)propertiesF/ B-7'0 /. 7et the a&&ress from the user# ./ prompt)for)a&&ress 8a&&ress)string:/
?K

/. 1ssign a Auery to the cursor aria%le %ase& on the a&&ress# ./ open)site)list 8a&&ress)string, %uil&ing)cur ar:/ /. 7i e it a tryI (etch a ro! into the home recor&# ./ (-$+2 %uil&ing)cur ar '0$O home)rec/ /. 'f ' got here, the site !as a home, so &isplay it# ./ sho!)home)site 8home)rec:/ -D+-P$'O0 /. 'f the first recor& !as not a home### ./ W2-0 )M'SM1$+2 $2-0 /. (etch that same =st ro! into the commercial recor&# ./ (-$+2 %uil&ing)cur ar '0$O commercial)rec/ /. Sho! the commercial site info# ./ sho!)commercial)site 8commercial)rec:/ -0D/

>#=;#> Rules for +ursor Baria%les $his section examines in more &etail the rules an& issues regar&ing the use of cursor aria%les in your programs# $his inclu&es ro!type matching rules, cursor aria%le aliases, an& scoping issues# Remem%er that the cursor aria%le is a reference to a cursor o%4ect or Auery in the &ata%ase# 't is not the o%4ect itself# 1 cursor aria%le is sai& to @refer to a gi en Auery@ if either of the follo!ing is true* 1n OP-0 statement (OR that Auery !as execute& !ith the cursor aria%le# 1 cursor aria%le !as assigne& a alue from another cursor aria%le that refers to that Auery# 3ou can perform assignment operations !ith cursor aria%les an& also pass these aria%les as arguments to proce&ures an& functions# 'n or&er to perform such actions %et!een cursor aria%les 8an& to %in& a cursor aria%le to a parameter:, the &ifferent cursor aria%les must follo! a set of compile9time an& runtime ro!type matching rules# >#=;#>#= +ompile9time ro!type matching rules $hese are the rules that PL/SQL follo!s at compile9time* $!o cursor aria%les 8inclu&ing proce&ure parameters: are compati%le for assignments an& argument passing if any of the follo!ing are true* Both aria%les 8or parameters: are of a strong R-( +,RSOR type !ith
?L

the same Rro!type)nameU# Both aria%les 8or parameters: are of some !ea" R-( +,RSOR type, regar&less of the Rro!type)nameU# One aria%le 8parameter: is of any strong R-( +,RSOR type, an& the other is of any !ea" R-( +,RSOR type# 1 cursor aria%le 8parameter: of a strong R-( +,RSOR type may %e OP-0 (OR a Auery that returns a ro!type !hich is structurally eAual to the Rro!type)nameU in the original type &eclaration# 1 cursor aria%le 8parameter: of a !ea" R-( +,RSOR type may %e OP-0 (OR any Auery# $he (-$+2 from such a aria%le is allo!e& '0$O any list of aria%les or recor& structure# 'n other !or&s, if either of the cursor aria%les are of the !ea" R-( +,RSOR type, then the PL/SQL compiler cannot really ali&ate !hether the t!o &ifferent cursor aria%les !ill %e compati%le# $hat !ill happen at runtime/ the rules are co ere& in the next section# >#=;#>#; Run9time ro!type matching rules $hese are the rules that PL/SQL follo!s at run time* 1 cursor aria%le 8parameter: of a !ea" R-( +,RSOR type may %e ma&e to refer to a Auery of any ro!type regar&less of the Auery or cursor o%4ect to !hich it may ha e referre& earlier# 1 cursor aria%le 8parameter: of a strong R-( +,RSOR type may %e ma&e to refer only to a Auery !hich matches structurally the Rro!type)nameU of the R-$,R0 clause of the R-( +,RSOR type &eclaration# $!o recor&s 8or lists of aria%les: are consi&ere& structurally matching !ith implicit con ersions if %oth of the follo!ing are true* $he num%er of fiel&s is the same in %oth recor&s 8lists:# (or each fiel& in one recor& 8or aria%le on one list:, a correspon&ing fiel& in the secon& list 8or aria%le in secon& list: has the same PL/SQL &atatype, or one !hich can %e con erte& implicitly %y PL/SQL to match the first# (or a cursor aria%le 8parameter: use& in a (-$+2 statement, the Auery associate& !ith the cursor aria%le must structurally match !ith implicit con ersions the recor& or list of aria%les of the '0$O clause of the (-$+2 statement# $his is, %y the !ay, the same rule use& for static cursors# >#=;#>#? +ursor aria%le aliases 'f you assign one cursor aria%le to another cursor aria%le, those t!o cursor aria%les %ecome aliases for the same cursor o%4ect# $hey share the reference to the cursor
G<

o%4ect 8result set of the cursorEs Auery:# 1n action ta"en against the cursor o%4ect through one aria%le is also a aila%le to an& reflecte& in the other aria%le# $he follo!ing anonymous %loc" illustrates the !ay cursor aliases !or"* = D-+L1R; $3P- cur ar)type 'S R-( +,RSOR/ ? cur ar= cur ar)type/ G cur ar; cur ar)type/ H story fairy)talesF/ > B-7'0 J /. 1ssign cursor o%4ect to cur ar=# ./ K OP-0 cur ar= (OR S-L-+$ . (ROM fairy)tales/ L =< /. 1ssign same cursor o%4ect to cur ar;# ./ == cur ar; *6 cur ar=/ =; =? /. (etch first recor& from cur ar=# ./ =G (-$+2 cur ar= '0$O story/ =H => /. (etch secon& recor& from cur ar;# ./ =J (-$+2 cur ar; '0$O story/ =K =L /. +lose the cursor o%4ect %y referencing cur ar;# ./ ;< +LOS- cur ar;/ ;= ;; /. $his statement raises '0B1L'D)+,RSOR exceptionI ./ ;? (-$+2 cur ar= '0$O story/ ;G -0D/ $he follo!ing ta%le is an explanation of cursor aria%le actions#
Lines1ction=9HDeclare my !ea" R-( +,RSOR type an& cursor aria%le through line H#K+reates a cursor o%4ect an& assigns it to cur ar=#==1ssigns that same cursor o%4ect to the secon& cursor aria%le, cur ar;#=G(etches the first recor& using the cur ar= aria%le#=J(etches the secon& recor& using the cur ar; aria%le# 80otice that it &oesnEt matter !hich of the t!o aria%les you use# $he pointer to the current recor& resi&es !ith the cursor o%4ect, not any particular aria%le#:;<+loses the cursor o%4ect referencing cur ar;#;?Raises the '0B1L'D)+,RSOR exception !hen ' try to fetch again from the cursor o%4ect# 8When ' close& the cursor through cur ar;, it also close& it as far as cur ar= !as concerne&#:1ny change of state in a cursor o%4ect !ill %e seen through any

cursor aria%le !hich is an alias to that cursor o%4ect# >#=;#>#G Scope of cursor o%4ect $he scope of a cursor aria%le is the same as that of a static cursor* the PL/SQL %loc" in !hich the aria%le is &eclare& 8unless &eclare& in a pac"age, !hich ma"es the aria%le glo%ally accessi%le:# $he scope of the cursor o%4ect to !hich a cursor aria%le is assigne&, ho!e er, is a &ifferent matter#
G=

Once an OP-0 (OR creates a cursor o%4ect, that cursor o%4ect remains accessi%le as long as at least one acti e cursor aria%le refers to that cursor o%4ect# $his means that you can create a cursor o%4ect in one scope 8PL/SQL %loc": an& assign it to a cursor aria%le# $hen, %y assigning that cursor aria%le to another cursor aria%le !ith a &ifferent scope, the cursor o%4ect remains accessi%le e en if the original cursor aria%le has gone out of scope# 'n the follo!ing example ' use neste& %loc"s to &emonstrate ho! the cursor o%4ect can persist outsi&e of the scope in !hich it !as originally create&* D-+L1R/. Define !ea" R-( +,RSOR type, cursor aria%le an& local aria%le ./ $3P- cur ar)type 'S R-( +,RSOR/ cur ar= cur ar)type/ &o)you)get)it B1R+21R;8=<<:/ B-7'0 /. OO 0este& %loc" !hich creates the cursor o%4ect an& OO assigns it to the cur ar= cursor aria%le# ./ D-+L1Rcur ar; cur ar)type/ B-7'0 OP-0 cur ar; (OR S-L-+$ punch)line (ROM 4o"es/ cur ar= *6 cur ar;/ -0D/ /. OO $he cur ar; cursor aria%le is no longer acti e, OO %ut @the %aton@ has %een passe& to cur ar=, !hich OO &oes exist in the enclosing %loc"# ' can therefore OO fetch from the cursor o%4ect, through this other OO cursor aria%le# ./ (-$+2 cur ar= '0$O &o)you)get)it/ -0D/ 6.82.> Passing Cursor ;aria<les as ?rguments 3ou can pass a cursor aria%le as an argument in a call to a proce&ure or function# When you use a cursor aria%le in the parameter list of a program, you nee& to specify the mo&e of the parameter an& the &atatype 8the R-( +,RSOR type:# >#=;#J#= '&entifying the R-( +,RSOR type 'n your program hea&er, you must i&entify the R-( +,RSOR type of your cursor aria%le parameter# $o &o this, that cursor type must alrea&y %e &efine&#
G;

'f you are creating a local mo&ule !ithin another program 8see +hapter =H for more information a%out local mo&ules:, then you can also &efine the cursor type in the same program# 't !ill then %e a aila%le for the parameter# $his approach is sho!n %elo!* D-+L1R/. Define the R-( +,RSOR type# ./ $3P- cur ar)type 'S R-( +,RSOR R-$,R0 companyF/ /. Reference it in the parameter list# ./ PRO+-D,R- open)Auery 8cur ar)out O,$ cur ar)type: 'S local)cur cur ar)type/ B-7'0 OP-0 local)cur (OR S-L-+$ . (ROM company/ cur ar)out *6 local)cur/ -0D/ B-7'0 ### -0D/ 'f you are creating a stan&alone proce&ure or function, then the only !ay you can reference a pre9existing R-( +,RSOR type is %y placing that type statement in a pac"age# 1ll aria%les &eclare& in the specification of a pac"age act as glo%als !ithin your session, so you can then reference this cursor type using the &ot notation as sho!n %elo!* = +reate the pac"age !ith a R-( +,RSOR type &eclaration* ; P1+S17- company ? 'S 4 /. Define the R-( +,RSOR type# ./ 5 $3P- cur ar)type 'S R-( +,RSOR R-$,R0 companyF/ > -0D pac"age/ J 'n a stan&alone proce&ure, reference the R-( +,RSOR type %y prefacing the name of the cursor type !ith the name of the pac"age* K PRO+-D,R- open)company 8cur ar)out O,$ company#cur ar)type: 'S L B-7'0 10 ### ==-0D/ See +hapter => for more information on this feature# >#=;#J#; Setting the parameter mo&e 5ust li"e other parameters, a cursor aria%le argument can ha e one of the follo!ing three mo&es*
G?

'0 +an only %e rea& %y program O,$ +an only %e !ritten to %y program '0 O,$ Rea&/!rite in program Remem%er that the alue of a cursor aria%le is the reference to the cursor o%4ect an& not the state of the cursor o%4ect# 'n other !or&s, the alue of a cursor aria%le &oes not change after you fetch from or close a cursor# Only t!o operations, in fact, may change the alue of a cursor aria%le change, that is, the cursor o%4ect to !hich the aria%le points* 1n assignment to the cursor aria%le 1n OP-0 (OR statement 'f the cursor aria%le alrea&y pointe& to a cursor o%4ect, then the OP-0 (OR !oul&nEt actually change the reference# 't !oul& simply change the Auery associate& !ith the o%4ect# $he (-$+2 an& +LOS- operations affect the state of the cursor o%4ect, %ut not the reference to the cursor o%4ect itself, !hich is the alue of the cursor aria%le# 2ere is an example of a program !hich has cursor aria%les as parameters* PRO+-D,R- assign)cur ar 8ol&)cur ar)in '0 company#cur ar)type, ne!)cur ar)out O,$ company#cur ar)type: 'S B-7'0 ne!)cur ar)out *6 ol&)cur ar)in/ -0D/ $his proce&ure copies the ol& company cursor aria%le to the ne! aria%le# $he first parameter is an '0 parameter %ecause it appears only on the right9han& si&e of the assignment# $he secon& parameter must %e an O,$ 8or '0 O,$: parameter, %ecause its alue is change& insi&e the proce&ure# 0otice that the cur ar)type is &efine& !ithin the company pac"age# >#=;#K +ursor Baria%le Restrictions +ursor aria%les are su%4ect to the follo!ing restrictions/ Oracle may remo e some of these in future releases# +ursor aria%les cannot %e &eclare& in a pac"age since they &o not ha e a persistent state#
GG

3ou cannot use RP+s 8Remote Proce&ure +alls: to pass cursor aria%les from one ser er to another# 'f you pass a cursor aria%le as a %in& or host aria%le to PL/SQL, you !ill not %e a%le to fetch from it from !ithin the ser er unless you also open it in that same ser er call# $he Auery you associate !ith a cursor aria%le in an OP-09(OR statement cannot use the (OR ,PD1$- clause# 3ou cannot test for cursor aria%le eAuality, ineAuality, or nullity using comparison operators# 3ou cannot assign 0,LLs to a cursor aria%le# Data%ase columns cannot store cursor aria%le alues# 3ou !ill not %e a%le to use R-( +,RSOR types to specify column types in statements to +R-1$$1BL-s or +R-1$- B'-Ws# $he elements in a neste& ta%le, in&ex9%y ta%le, or aria%le array 8B1RR13: cannot store the alues of cursor aria%les# 3ou !ill not %e a%le to use R-( +,RSOR types to specify the element type of a collection# +ursor aria%les cannot %e use& !ith &ynamic SQL 8through use of the DBMS)SQL pac"age:# >#=? Wor"ing !ith +ursors $he follo!ing sections offer some practical applications of cursors# $hey are also &esigne& to %e programs you can put to use in your o!n en ironments !ith a fe! changes# $he follo!ing files on the companion &is" offer a&&itional examples* arcurs#&oc -xplanation of ho! to emulate a cursor aria%le !ith local mo&ules unAuein#&oc -xplanation of ho! to guarantee uniAue entry !ith cursors unAuein#ff +o&e reAuire& to guarantee uniAue entry !ith cursors >#=?#= Bali&ating (oreign Sey -ntry !ith +ursors 1 hefty percentage of our co&e can %e ta"en up !ith ali&ating the entry or selection of foreign "eys# +onsi&er the example of an application !hich maintains companies an& employees of that company# On the employee maintenance screen, ' !ant to let my user enter the name or partial name of the company that employs a person# 'f the user has i&entifie& a uniAue company, the form &isplays that name an& stores the company 'D on the null can as# 'f the userEs entry fin&s more than one match, a message is
GH

&isplaye&# 'f no matches are foun&, the entry is re4ecte&# 2o! shoul& ' implement this reAuirementT Well, the first thing that comes to the min&s of many programmers is the follo!ing* ,se a cursor !hich, !ith a single fetch, employs the +O,0$ %uilt9in to compute the total num%er of companies that match the enemy# $his is, perhaps, the most o% ious an& &irect solution to the reAuirement 99 !hen it is phrase& as follo!s* $o fin& out if the userEs entry has more than one match, count up 4ust ho! many matches there are# >#=?#=#= 'nefficiency of group functions in cursors $here are, ho!e er, t!o serious pro%lems !ith using the +O,0$ group function in my cursor*
$he cursor &oes far too much !or" on my %ehalf# By using +O,0$, the

cursor must scan through all recor&s 8or, ' hope, those i&entifie& %y the in&ex: an& count up the total num%er of matching recor&s# 3et, all ' really nee& to "no! is !hether more than one company matche& the entry# $he performance penalty on this +O,0$ coul& %e se ere if the Auery goes out o er a net!or" or if the userEs entry matches many of the recor&s# What if a user entere& a percent sign 8F:T 1ll recor&s !oul& then match# 1n application shoul& ne er punish a user for poorly thought9out &ata entry#
$he cursor &oes not &o enough for me# 'f the +O,0$9%ase& Auery &i& return a

alue of =, ' !oul& still ha e to go %ac" to the company ta%le an& S-L-+$ the 'D for that company !ith another cursor# 1s a result, ' !oul& ha e co&e& the same Auery t!ice# $his re&un&ancy intro&uces maintenance an& performance issues# 3ou shoul& use +O,0$ only !hen you nee& to "no! or &isplay the total num%er of matches for the userEs entry# 'n this scenario, ' &onEt really nee& that total/ ' nee& only to "no! if the total is greater than one 8i#e#, if there is more than one match:# ' can o%tain this "no!le&ge in a much more efficient an& straightfor!ar& manner# >#=?#=#; ,sing multiple fetches more efficiently ,se a cursor that, !ith multiple fetches, &etermines if there are at least t!o companies that match the entry# $his approach ta"es a %it more sophistication an& thought, %ut is al!ays a %etter performer an& offers more flexi%ility to programmers# $o employ the multiple9fetch techniAue, ta"e the follo!ing steps* = Declare a cursor !hich returns the company)i& of all companies that match the alue in the item* ; +,RSOR company)cur ? 'S 4 S-L-+$ company)i&
G>

5 6

(ROM company W2-R- company)name L'S- *company#company)name OO EFE/

J (etch t!ice against this cursor# 'f ' can fetch t!ice successfully 8company)cur F0O$(O,0D is (1LS- %oth times:, that means that there is more than one match for the company name# 'f ' can fetch only once %efore the F0O$(O,0D cursor attri%ute returns (1LS-, then ' ha e foun& a uniAue match# 'f the ery first fetch fails, then there is no match for the name# K Because my cursor returns the company)i&, ' &o not ha e to perform another select once ' ha e &etermine& that ' ha e a uniAue match# ' simply use the 'D that !as pro i&e& in the first fetch# $he proce&ure in the follo!ing example supports the foreign "ey ali&ation reAuirements !ith a &ou%le fetch against the cursor 8it is co&e& for Oracle (orms, %ut can %e a&apte& easily to other tool en ironments:* /. (ilename on companion &is"* f" al#fp ./ PRO+-D,R- ali&ate)company 8comp)name)inout '0 O,$ company#company)nameF$3P-, comp)i&)out O,$ company#company)i&F$3P-: 'S /. +ursor as explaine& a%o e ./ +,RSOR company)cur 'S S-L-+$ company)i&, company)name (ROM company W2-R- company)name L'S- comp)name)inout OO EFE/ /. Declare t!o recor&s against the same cursor# ./ company)rec company)curF/ &uplicate)rec company)curF/ B-7'0 /. Open an& perform the first fetch against cursor# ./ OP-0 company)cur/ (-$+2 company)cur '0$O company)rec/ '( company)curF0O$(O,0D $2-0 /. 0ot e en one match for this name# Display message an& re4ect# ./ M-SS178E 0o company foun& !ith name li"e @E OO comp)name)inout OO E@#E:/ +LOS- company)cur/ R1'S- (ORM)$R'77-R)(1'L,R-/ -LS/. OO (oun& one match# 0o! (-$+2 again, %ut this time (-$+2 into the OO &uplicate)rec recor&# $his is 4ust a @place hol&er@# ' &onEt OO nee& to see the contents of the recor&# ' 4ust nee& to "no! if
GJ

OO ' can successfully retrie e another recor& from the cursor# ./ (-$+2 company)cur '0$O &uplicate)rec/ '( company)curF0O$(O,0D $2-0 /. OO (oun& = match, %ut not secon&# ,niAueI 1ssign alues to OO the O,$ parameters an& close the cursor# ./ comp)i&)out *6 company)rec#company)i&/ comp)name)inout *6 company)rec#company)name/ +LOS- company)cur/ -LS/. OO 1t least t!o matches foun& for this name# ' &onEt "no! ho! OO many more an& ' &o not care# Re4ect !ith message# ./ M-SS17- 8E More than one company matches name li"e @E OO comp)name)inout OO E@#E:/ +LOS- company)cur/ R1'S- (ORM)$R'77-R)(1'L,R-/ -0D '(/ -0D '(/ -0D/ +all this proce&ure in the When9Bali&ate9'tem trigger so that any changes to the company name can %e ali&ate&# 2ere is an example of an actual call to ali&ate)company* ali&ate)company 8*employee#company)name, *employee#company)i&:/ 0otice that the first parameter 8the company name: is an '0 O,$ parameter# ' !ant to let the user enter 4ust a part of the name an& let the application figure out if that entry is enough to uniAuely i&entify a company# 'f a single match is foun&, the form replaces the partial entry !ith the full name# ' %elie e strongly that !e shoul& &esign our applications to allo! the user to enter the minimal amount of information necessary to get the 4o% &one# Our applications shoul& %e smart enough to ta"e a& antage of the &um%, %rute strength of our +P,s in or&er to lift some of the %ur&en off the user# >#=?#; Managing a Wor" Queue !ith S-L-+$ (OR ,PD1$1s &iscusse& earlier, a cursor !ith a S-L-+$###(OR ,PD1$- syntax issues a ro!9 le el loc" on each ro! i&entifie& %y the Auery# ' encountere& a ery interesting application of this feature !hile helping a client resol e a pro%lem# $he client offers a &istri%ution pac"age !hich trac"s !arehouse in entory# $he !or"
GK

Aueue screen assigns !arehouse floor pac"ers their next tas"s# $he pac"er opens the screen an& reAuests a tas"# $he screen fin&s the next unassigne& tas" an& assigns it to the pac"er# 1 tas" might in ol e collecting arious pro&ucts together for shipment or returning pro&ucts to the shelf# +ompletion of this tas" can ta"e any!here %et!een one an& ten minutes# When the tas" is complete&, the pac"er !ill commit the changes or close the screen, performing an implicit commit# (or the amount of time it ta"es a pac"er to finish the tas", that recor& must %e tagge& as @assigne&@ so that no other pac"er is gi en the same 4o% to &o# $he first attempt at implementing this feature in ol e& the use of a status flag# Whene er a pac"er !as assigne& a tas", the flag on that tas" !as set to 1SS'70-D an& the tas" recor& committe&# $he screen then exclu&es that tas" from the !or" Aueue# $he pro%lem !ith this approach is that the status ha& to %e committe& to the &ata%ase so that other users coul& see the ne! status# $his commit not only interrupte& the actual transaction in the screen, %ut also create& a num%er of hea&aches* What if the user ne er completes the tas" an& exits the screenT $he form !oul& ha e to &etect this scenario 8an& there are generally many !ays to cancel/exit: an& up&ate the status flag to 1B1'L1BL-, !hich in ol es yet another commit# Worse yet, !hat if the &ata%ase goes &o!n !hile the user is performing the tas"T $hat tas" !ill &isappear from the !or" Aueue until manual inter ention resets the status# My client nee&e& a mechanism %y !hich the tas" coul& %e flagge& as ,01B1'L1BL!ithout ha ing to perform commits, %uil& complex chec"s into the form, an& &e elop crash9reco ery gui&elines# $hey nee&e& a program that !oul& step through each of the open tas"s in priority until it foun& a tas" that !as unassigne&# $he S-L-+$###(OR ,PD1$- construct pro e& to %e the perfect ans!er, in com%ination !ith t!o Aueries against the tas" ta%le 99 an explicit cursor an& an implicit cursor using a (OR ,PD1$clause# $he function in the follo!ing example returns the primary "ey of the next unassigne& tas" using a cursor against the tas" ta%le to loo" through all open tas"s in priority or&er# $he tas"s returne& %y this first cursor inclu&e those !hich are assigne& %ut @in process@ 8an& shoul& therefore not %e assigne& again:# (or each tas" retrie e& from this cursor, the function then tries to o%tain a loc" on that recor& using the (OR ,PD1$-###0OW1'$ clause# 'f the S-L-+$ statement cannot o%tain a loc", it means that tas" is %eing han&le& %y another pac"er# So the function fetches the next tas" an& tries, once again, to o%tain a loc", continuing on in this fashion until a free tas" is foun& or the last tas" is fetche&# 0otice that the next)tas" function &oes not perform any commits, so it &oesnEt ha e to &o any "in& of complicate& clean9up# 't simply reAuests the loc" an& returns the primary "ey for that tas"# $he calling program can then offer this tas" to the pac"er !ho !ill issue the commit, freeing the loc", !hen she or he is &one !ith the tas"* /. (ilename on companion &is"* selup&t#sf ./ (,0+$'O0 next)tas" R-$,R0 tas"#tas")i&F$3PGL

'S /. +ursor of all open tas"s, assigne& an& unassigne& ./ +,RSOR tas")cur 'S S-L-+$ tas")i& (ROM tas" W2-R- tas")status 6 EOP-0E ORD-R B3 tas")priority, &ate)entere& D-S+/ /. $he recor& for the a%o e cursor ./ tas")rec tas")curF/ /. OO 1n exception for error OR19<<<HG* OO @resource %usy an& acAuire !ith 0OW1'$ specifie&@ ./ recor&)loc"e& -D+-P$'O0 PR17M1 -D+-P$'O0)'0'$ 8recor&)loc"e&, 9HG:/ /. OO Baria%les !hich &etermine !hether function shoul& continue OO to loop through the cursorEs recor&s# ./ foun&)unassigne&)tas" BOOL-10 *6 (1LS-/ more)tas"s BOOL-10 *6 $R,-/ /. $he primary "ey of the unassigne& tas" to %e returne& ./ return) alue tas"#tas")i&F$3P- *6 0,LL/ B-7'0 /. Open the cursor an& start up the loop through its recor&s ./ OP-0 tas")cur/ W2'L- 0O$ foun&)unassigne&)tas" 10D more)tas"s LOOP /. (etch the next recor&# 'f nothing foun&, !e are &one ./ (-$+2 tas")cur '0$O tas")rec/ more)tas"s *6 tas")curF(O,0D/ '( more)tas"s $2-0 /. OO 1 recor& !as fetche&# +reate an anonymous %loc" !ithin OO the function so that ' can trap the recor&)loc"e& OO exception an& still stay insi&e the cursor loop# ./ B-7'0 /. $ry to get a loc" on the current tas" ./ S-L-+$ tas")i& '0$O return) alue (ROM tas" W2-R- tas")i& 6 tas")rec#tas")i& (OR ,PD1$- O( tas")i& 0OW1'$/
H<

/. OO 'f ' get to this line then ' !as a%le to get a loc" OO on this particular tas"# 0otice that the S-L-+$ '0$O OO has therefore alrea&y set the functionEs return alue# OO 0o! set the Boolean to stop the loop# ./ foun&)unassigne&)tas" *6 $R,-/ -D+-P$'O0 W2-0 recor&)loc"e& $2-0 /. Recor& !as alrea&y loc"e&, so 4ust "eep on going ./ 0,LL/ -0D/ -0D '(/ -0D LOOP/ /. OO Return the tas" i&# 0otice that if an unassigne& tas" !as 0O$ OO foun&, ' !ill simply return 0,LL per &eclaration &efault# ./ +LOS- tas")cur/ R-$,R0 return) alue/ -D+-P$'O0 /. OO 7eneral exception han&ler for the function* if an error occurre&, OO then close the cursor an& return 0,LL for the tas" 'D# ./ W2-0 O$2-RS $2-0 +LOS- tas")cur/ R-$,R0 0,LL/ -0D/

H=

..........># Pac"ages

Contents=
$he Benefits of Pac"ages O er ie! of Pac"age Structure $he Pac"age Specification $he Pac"age Bo&y Pac"age Data Pac"age 'nitialiCation

1 pac"age is a collection of PL/SQL o%4ects that are pac"age& or groupe& together !ithin a special B-7'09-0D syntax, a "in& of @meta9%loc"#@ 2ere is a partial list of the "in&s of o%4ects you can place in a pac"age* +ursors Baria%les 8scalars, recor&s, ta%les, etc#: +onstants -xception names PL/SQL ta%le an& recor& $3P- statements Proce&ures (unctions Pac"ages are among the least un&erstoo& an& most un&erutiliCe& features of PL/SQL# $hat is a shame, %ecause the pac"age structure is also one of the most useful constructs for %uil&ing !ell9&esigne& PL/SQL9%ase& applications# Pac"ages pro i&e a structure in !hich you can organiCe your mo&ules an& other PL/SQL elements# $hey encourage proper structure& programming techniAues in an en ironment that often %efu&&les the implementation of structure& programming# Oracle +orporation itself uses the pac"age construct to exten& the PL/SQL language# 1ppen&ix +, Built9'n Pac"ages, contains &escriptions of many of these pre&efine& pac"ages# 'n fact, the most %asic operators of the PL/SQL language, such as the Q an& L'S- operators an& the '0S$R function, are all &efine& in a special
H;

pac"age calle& S$10D1RD# 'f Oracle %elie es that pac"ages are the !ay to go !hen it comes to %uil&ing %oth fun&amental an& complex programs, &onEt you thin" that you coul& %enefit from the sameT Pac"ages are, %y nature, highly mo&ular# When you place a program unit into a pac"age you automatically create a context for that program# By collecting relate& PL/SQL elements in a pac"age, you express that relationship in the ery structure of the co&e itself# Pac"ages are often calle& @the poor manEs o%4ects@ %ecause they support some, %ut not all, o%4ect9oriente& rules# (or example, pac"ages allo! you to encapsulate an& a%stract your &ata an& functions# $he PL/SQL pac"age is a &ecepti ely simple, po!erful construct# 3ou can in 4ust a fe! hours learn the %asic elements of pac"age syntax an& rules/ thereEs not all that much to it# 3ou can spen& &ays an& !ee"s, ho!e er, unco ering all the nuances an& implications of the pac"age structure# $his chapter 99 an& the next one fille& !ith examples of pac"ages 99 !ill help you a%sor% the features an& %enefits of the PL/SQL pac"age more rapi&ly# =>#= $he Benefits of Pac"ages Before !e explore all the aspects of !or"ing !ith pac"ages, letEs re ie! some of the most important %enefits of the pac"age* =>#=#= -nforce& 'nformation 2i&ing When you %uil& a pac"age, you &eci&e !hich of the pac"age elements are pu%lic 8can %e reference& outsi&e of the pac"age: an& !hich are pri ate 8a aila%le only !ithin the pac"age itself:# 3ou also can restrict access to the pac"age to only the specification# 'n this !ay, you use the pac"age to hi&e the implementational &etails of your programs# $his is most important !hen you !ant to isolate the most olatile aspects of your application, such as platform &epen&encies, freAuently changing &ata structures, an& temporary !or"aroun&s# =>#=#; O%4ect9Oriente& Design While PL/SQL &oes not yet offer full o%4ect9oriente& capa%ilities, pac"ages &o offer the a%ility to follo! many o%4ect9oriente& &esign principles# $he pac"age gi es &e elopers ery tight control o er ho! the mo&ules an& &ata structures insi&e the pac"age can %e accesse&# 3ou can, therefore, em%e& all the rules a%out an& access to your entities 8!hether they are &ata%ase ta%les or memory9%ase& structures: in the pac"age# Because this is the only !ay to !or" !ith that entity, you ha e in essence create& an a%stracte& an& encapsulate& o%4ect# =>#=#? $op9Do!n Design 1 pac"ageEs specification can %e !ritten %efore its %o&y# 3ou can, in other !or&s, &esign the interface to the co&e hi&&en in the pac"age 8the mo&ules, their names, an& their parameters: %efore you ha e actually implemente& the mo&ules themsel es# $his
H?

feature &o etails nicely !ith top9&o!n &esign, in !hich you mo e from high9le el reAuirements to functional &ecompositions to mo&ule calls# Of course, you can &esign the names of stan&alone mo&ules 4ust as you can the names of pac"ages an& their mo&ules# $he %ig &ifference !ith the pac"age specification is that you can compile it e en !ithout its %o&y# (urthermore, an& most remar"a%ly, programs that call pac"age& mo&ules !ill compile successfully 99 as long as the specification compiles# =>#=#G O%4ect Persistence PL/SQL pac"ages offer the a%ility to implement glo%al &ata in your application en ironment# 7lo%al &ata is information that persists across application components/ it isnEt 4ust local to the current mo&ule# 'f you &esigne& screens !ith SQL.(orms or Oracle (orms, you are pro%a%ly familiar !ith its 7LOB1L aria%les, !hich allo! you to pass information %et!een screens# $hose glo%als ha e their limitations 87LOB1L aria%les are al!ays represente& as fixe&9length +21R aria%les !ith a length of ;HG:, %ut they sure can %e useful# O%4ects &eclare& in a pac"age specification 8that is, isi%le to anyone !ith -D-+,$authority on that pac"age: act as glo%al &ata for all PL/SQL o%4ects in the application# 'f you ha e access to the pac"age, you can mo&ify pac"age aria%les in one mo&ule an& then reference those change& aria%les in another mo&ule# $his &ata persists for the &uration of a user session 8connection to the &ata%ase:# 'f a pac"age& proce&ure opens a cursor, that cursor remains open an& is a aila%le to other pac"age& routines throughout the session# 3ou &o not ha e to explicitly &efine the cursor in each program# 3ou can open it in one mo&ule an& fetch it in another mo&ule# 'n a&&ition, pac"age aria%les can carry &ata across the %oun&aries of transactions, %ecause they are tie& to the session itself an& not to a transaction# =>#=#H Performance 'mpro ement When an o%4ect in a pac"age is reference& for the first time, the entire pac"age 8alrea&y compile& an& ali&ate&: is loa&e& into memory 8the Share& 7lo%al 1rea MS71N of the RDBMS:# 1ll other pac"age elements are there%y ma&e imme&iately a aila%le for future calls to the pac"age# PL/SQL &oes not ha e to "eep retrie ing program elements or &ata from &is" each time a ne! o%4ect is reference&# $his feature is especially important in a &istri%ute& execution en ironment# 3ou may reference pac"ages from &ifferent &ata%ases across a local area or e en a !i&e area net!or"# 3ou !ant to minimiCe the net!or" traffic in ol e& in executing your co&e# Pac"ages also offer performance a& antages on the &e elopment si&e 8!ith potential impact on o erall &ata%ase performance:# $he Oracle RDBMS automatically trac"s the ali&ity of all program o%4ects 8proce&ures, functions, pac"ages: store& in the &ata%ase# 't &etermines !hat other o%4ects that program is &epen&ent on, such as ta%les# 'f a &epen&ent o%4ect such as a ta%leEs structure changes, for example, then all programs that rely on that o%4ect are flagge& as in ali&# $he &ata%ase then automatically recompiles these in ali& programs %efore they are use&#
HG

3ou can limit automatic recompiles %y placing functions an& proce&ures insi&e pac"ages# 'f program 1 calls pac"age& mo&ule B, it &oes so through the pac"ageEs specification# 1s long as the specification of a pac"age& mo&ule &oes not change, any program that calls the mo&ule is not flagge& as in ali& an& !ill not ha e to %e recompile&# $his chapter shoul& pro i&e you !ith all the information an& examples you nee& to put pac"ages to !or" imme&iately in your applications# 'f you are still unsure a%out pac"ages after rea&ing it, try out a couple of small pac"ages# $est those har&9to9 %elie e features li"e glo%al pac"age &ata to pro e to yourself that they really !or" as a& ertise&# -xamine carefully the examples in +hapter =K, O%4ect $ypes# Do !hate er you nee& to &o to incorporate pac"ages into e ery le el of your application, from &ata%ase ser er to client applications#

By Ste en (euerstein X Bill Pri%yl/ 'SB0 =9H>HL;9??H9L-

http*//%oo"s#%ianor#net/Oracle/

%ttp=//:::.comp.dit.ie/<t ierne@/"racle/PLSQL 129Introduction.pd0 %ttp=//:::.comp.dit.ie/<t ierne@/"racle/PLSQL 129Introduction.pd0


HH

Input arguments to cursor 'nput arguments are the alues that are passe& to cursor at the time of opening the cursor# $hese alues are passe& to parameters that are &e IIII http*//sri"anthtechnologies#com/ %oo"s/ora%oo"/ch=K#p&f http*//%a"s#gaC#ru/ora&oc/plsAl/to plsAl#htm http*//pu%li%#%oul&er#i%m#com/in focenter/i&shelp/ ===/in&ex#4spT
H>

topic6/com#i%m#esAlc#&oc/sii9=G9 GG==H#htm http*//pu%li%#%oul&er#i%m#com/in focenter/i&shelp/ ===/in&ex#4spT topic6/com#i%m#esAlc#&oc/sii9=G9 GG==H#htm

HJ

You might also like