# Oracle PLSQL Language- P26

Chia sẻ: Thanh Cong | Ngày: | Loại File: PDF | Số trang:50

0
40
lượt xem
4

## Oracle PLSQL Language- P26

Mô tả tài liệu

Tham khảo tài liệu 'oracle plsql language- p26', công nghệ thông tin, cơ sở dữ liệu phục vụ nhu cầu học tập, nghiên cứu và làm việc hiệu quả

Chủ đề:

Bình luận(0)

Lưu

## Nội dung Text: Oracle PLSQL Language- P26

1. value as shown below: new_rate := mortgage_rate (down_payment, prime_rate, mortgage_type); The mortgage_rate function was, unfortunately, setting new_rate to NULL. After examining the function, it was clear to me that the only way it could return a NULL was if one of the inputs was NULL. I then used this information as follows: 1. I had just examined the global variable holding the mortgage type. That global value was transferred to the mortgage_type variable in an earlier program and passed to my current module, so I knew that it was OK. 2. I performed a walk-through of the code and could not identify how any of the other two variables could be NULL. 3. So I inserted a trace statement before and after the call to mortgage_rate. My code now looked like this: DBMS_OUTPUT.PUT_LINE ('Inputs: ' || TO_CHAR (down_payment) || '-' || TO_CHAR (prime_rate)); new_rate := mortgage_rate ( down_payment, prime_rate, bank, mortgage_type); DBMS_OUTPUT.PUT_LINE ('Rate: ' || NVL (TO_CHAR (new_rate), 'Missing')); 4. I ran the program and no matter what data I entered on the screen, my trace statements remained the same: Inputs: 55000-9.5 Rate: Missing I wracked my obviously overwrought brain: what could cause a stored function to return a NULL value? I looked at the source code for the function again. There wasn't much to it. Just division and multiplication. How could it return a NULL without a NULL input? After two hours of this nonsense, I finally said to myself, "Well, you know that you really haven't verified the value of the mortgage_type variable." I knew that it was OK, but, hey, it wouldn't hurt to check -- and if I didn't solve this one soon I would have to actually ask for help. So I modified my trace statement and, sure enough, the mortgage type was NULL. Turns out that while the global variable held the proper value, the previous program did not pass it to the local variable properly. My assumption did me in. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
2. One of the first assumptions you will make in your testing is that you have valid inputs to your program. This can also be one of the most dangerous assumptions to make. Data errors are perhaps the most frustrating of all the kinds of bugs you will encounter. You follow all the right steps for debugging and analyzing your code, only to discover that there is nothing actually wrong with your program. Instead, the data that drives your program is wrong. I encounter this problem most often in the following situations: q I am testing my code in multiple database instances. Never assume that the data structures and actual rows of data are identical in all instances. Never assume that all indexes have been defined the same way. q The reference data is still unstable. What is the valid code for a "closed" call? What are the ten valid company types? If this data is still changing, your program is likely to break. If you do not understand why your program is doing what it is doing, make a list of all your assumptions and then test those -- including the data you rely on to run your program. There is a good chance that your error was introduced very early into the process. 24.2.8 Leverage Existing Utilities -- Or Build Your Own As you build more and more complex programs, you will find it increasingly difficult and incredibly frustrating to manage and debug these programs without a utility of some kind. Take the time to investigate what is available and what it will do for you. Historically, Oracle Corporation has been very slow to offer debugging and other programmer- oriented utilities. Third-party vendors seem to have taken a clue from Oracle and also have not hurried to provide a strong set of tools for PL/SQL developers. As of mid-year 1997, that situation is finally changing. You can now purchase debuggers from the following vendors: q Oracle Corporation: Procedure Builder q Platinum Technology: SQL Station Debugger q Technosolutions: SQL Navigator q Compuware: XPediter/SQL All of these products greatly improve the ability to debug client-side PL/SQL; you will need to carefully examine the specific benefits and features before deciding which of these (and, I hope, by the time this book is published, others as well) fit your needs most closely. If, on the other hand, you cannot find anything that will help (or you can't get the approval to buy the utility of your dreams), you might consider building your own. I have found in the past that it is relatively straightforward to implement utilities that have a significant impact on my debugging capabilities. I built XRay Vision, a debugger for SQL*Forms, implemented entirely in SQL*Forms itself, which gave me the ability to view and modify all variables in my programs. You'll find this Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
3. debugger, xrayvizn.zip, on the RevealNet Web site. While you are unlikely to be using SQL*Forms at this point, you may find the source code of interest (stored in the good, old INP files). When Oracle Forms 4.x (even the version in Oracle Developer/2000 that has its own source code debugger) came along, I realized that it was impossible to view and change data structures created at runtime (record groups, parameter lists, etc.). So I built a utility named Arjy (pronounced "RG" for Record Group), which gave me that access. The shareware version of Arjy, arjy.zip is also available at the RevealNet Web site. The basic PL/SQL product from Oracle Corporation will never have everything you need. If you can't find what you need to get the job done, then get creative and take a crack at meeting your own needs. Dive in and build your own utility. Not only will you improve your productivity (and that of others), but you will gain a feeling of intense satisfaction from solving your own problems all by yourself. 24.2.9 Build Debugging Messages into Your Packages If you do not use a GUI-based, source code debugger, you probably spend a fair amount of time throwing debug or trace messages into your code and then removing them when things are fixed. A much better approach is to leave these messages intact, but give yourself lots of flexibility in deciding when you want to see them. The simplest model for this technique is actually built right into the DBMS_OUTPUT package. The DBMS_OUTPUT.PUT_LINE procedure displays output from your PL/SQL program when that program completes execution. But it will not show anything unless the package itself is enabled with a call to DBMS_OUTPUT.ENABLE and/or unless from within SQL*Plus you issue the SET SERVEROUTPUT ON command. Furthermore, this is an all-or-nothing proposition: you either see no output, or you see output from every call to this procedure from every corner of your application. That can be overwhelming if you have inserted lots of trace messages. You can easily come up with a more discerning technique when working with packages. Suppose I have developed a package to perform calculations for profit-and-loss statements. My p_and_l package maintains a last statement date as a global variable for use within the package. I build a "get and set" layer of code around the variable so that applications can retrieve and manipulate the variable -- but only through my code layer. Here is the package: CREATE OR REPLACE PACKAGE p_and_l IS PROCEDURE set_lastdate (date_in IN DATE); FUNCTION lastdate RETURN DATE; /* Lots of other stuff, too! */ . . . END p_and_l; / Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
4. CREATE OR REPLACE PACKAGE BODY p_and_l IS g_lastdate DATE; PROCEDURE set_lastdate (date_in IN DATE) IS BEGIN /* Date cannot be in future. */ g_lastdate := LEAST (SYSDATE, date_in); END; FUNCTION lastdate RETURN DATE IS BEGIN RETURN g_lastdate; END; END p_and_l; / As I test this package as part of a large, complex application, suppose that I find that the last date variable is being set improperly, but I can't figure out what is doing it and why. I can go into the p_and_l.set_lastdate procedure and insert a call to DBMS_OUTPUT.PUT_LINE as follows: PROCEDURE set_lastdate (date_in IN DATE) IS BEGIN DBMS_OUTPUT.PUT_LINE (setting date to  || TO_CHAR (date_in)); /* Date cannot be in future. */ g_lastdate := LEAST (SYSDATE, date_in); END; but then I have to see all the output in my application and try to find this one statement among all the others. The approach I take instead is to provide a debug "toggle" in my package which allows me to focus output to just the statements I need to see. With the toggle technique, I add three programs to my package specification: CREATE OR REPLACE PACKAGE p_and_l IS PROCEDURE set_lastdate (date_in IN DATE); FUNCTION lastdate RETURN DATE; PROCEDURE dbg; PROCEURE nodbg; FUNCTION debugging RETURN BOOLEAN; Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
5. /* Lots of other stuff, too! */ . . . END p_and_l; / I also modify the package body to both implement this toggle and use it inside the set_lastdate procedure: CREATE OR REPLACE PACKAGE BODY p_and_l IS g_lastdate DATE; g_dbg BOOLEAN := FALSE; PROCEDURE dbg IS BEGIN g_dbg := TRUE; END; PROCEDURE nodbg IS BEGIN g_dbg := FALSE; END; FUNCTION debugging RETURN BOOLEAN RETURN g_dbg; END; PROCEDURE set_lastdate (date_in IN DATE) IS BEGIN IF debugging THEN DBMS_OUTPUT.PUT_LINE (before set  || TO_CHAR (g_lastdate)); END IF; /* Date cannot be in future. */ g_lastdate := LEAST (SYSDATE, date_in); IF debugging THEN DBMS_OUTPUT.PUT_LINE (after set  || TO_CHAR (g_lastdate)); END IF; END; FUNCTION lastdate RETURN DATE IS BEGIN RETURN g_lastdate; END; END p_and_l; / Then if I want to see what is happening to the g_lastdate variable, I can issue the debug command in SQL*Plus for this package and see just the output I need: Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
6. SQL> exec p_and_l.dbg SQL> exec testing_program before set 12-JAN-97 after set 15-JAN-97 Of course, you'd probably want to see more information, such as the execution call stack to see what program called the p_and_l.set_lastdate procedure. You can add anything you want -- all within the confines of the IF debugging clause -- and that information will be available only on a need-to-know basis. You might even decide to free yourself from the confines of DBMS_OUTPUT by writing information out to a database pipe. Furthermore, if you set as a standard in your group that every package is to have a debug toggle, then it will be much easier for users of those packages to debug their own use (or misuse) of that reusable code. They know that there will be a program named PKG.dbg which can be used to extract additional information about package processing. This technique is explored in more detail and with a slightly different focus (production support) in Chapter 26, Tracing PL/SQL Execution. Previous: 24.1 The Wrong Oracle PL/SQL Next: 25. Tuning PL/SQL Way to Debug Programming, 2nd Edition Applications 24.1 The Wrong Way to Book Index 25. Tuning PL/SQL Debug Applications The Oracle Library Navigation Copyright (c) 2000 O'Reilly & Associates. All rights reserved. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
7. Previous: 24.2 Debugging Chapter 25 Next: 25.2 Tuning Access Tips and Strategies to Compiled Code 25. Tuning PL/SQL Applications Contents: Analyzing Program Performance Tuning Access to Compiled Code Tuning Access to Your Data Tuning Your Algorithms Overview of PL/SQL8 Enhancements Tuning an application is a very complicated process. Really, it deserves a book of its own. Fortunately, there is such a book: Oracle Performance Tuning by Mark Gurry and Peter Corrigan.[1] Many of the ideas in this section are drawn from -- and explored more thoroughly in -- Gurry and Corrigan's book. [1] O'Reilly & Associates, Second Edition, 1996. There are other books on Oracle tuning as well. Before diving into the particulars, I want to be sure that you recognize the different aspects of tuning PL/SQL that you might want to perform: q Analyzing program performance. Before you can tune your application, you need to figure out what is running slowly and where you should focus your efforts. q Tuning access to compiled code. Before your code can be executed (and perhaps run too slowly), it must be loaded into the System Global Area (SGA) of the Oracle instance. This process can benefit from a focused tuning effort. q Tuning access to your data. Much of the tuning you do will attempt to optimize the way PL/ SQL programs manipulate data in the Oracle database, both queries and DML (updates, inserts, deletes). Lots of the issues here involve tuning SQL statements (out of the scope of this book), but there are certainly steps you can take in your PL/SQL code and environment to improve the performance of even an optimally constructed chunk of SQL. q Tuning your algorithms. As a procedural language, PL/SQL is often used to implement complex formulas and algorithms. You make use of conditional statements, loops, perhaps even GOTOs and reusable modules (I hope) to get the job done. These algorithms can be Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
8. written in many different ways, some of which perform very badly. How do you tune poorly written algorithms? A tough question with no simple answers; I offer some case studies at the end of this chapter which will perhaps give you some fresh approaches to bring to bear on your own code. The following sections address each of these areas of PL/SQL tuning. 25.1 Analyzing Program Performance Before you can tune your application, you need to know what is causing it to slow down. To do this, you usually need to be able to analyze the performance of your application. Oracle offers a number of database monitoring and diagnostic tools, as do third-party vendors like Platinum Technology and Quest. Check Oracle documentation and Chapter 10 of Oracle Performance Tuning for more details, and be aware of the following major tools: MONITOR A SQL*DBA facility that lets you look at various system activity and performance tables. SQL_TRACE A utility that writes a trace file containing performance statistics. TKPROF A utility that translates the SQL_TRACE file into readable output and can also show the execution plan for a SQL statement. EXPLAIN PLAN A statement that analyzes and displays the execution plan for a SQL statement. ORADBX An undocumented tool that allows you to track a running process and create a trace file in the same format as the SQL_TRACE trace file. You can then run TKPROF against the trace file to obtain the execution plan details, as well as disk I/O, parsing, and CPU usage. ANALYZE A statement that compiles statistics for use by the cost-based optimizer to construct its execution plan. The statement also produces other useful information that can be used to detect chained rows and help with capacity planning. UTLBSTAT (begin) and UTLESTAT (end) Scripts that produce a snapshot of how the database is performing from the time you start UTLBSTAT until you run UTLESTAT. Enterprise Manager/Performance Pack Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
9. An Oracle product introduced with Oracle7.3 that provides some excellent tuning tools, including Oracle Performance Manager, Oracle Trace, and Oracle Expert. 25.1.1 Use the DBMS_UTILITY.GET_TIME Function The tools listed in the previous section provide varying levels of detail and granularity; however, they all do require some effort -- often on the part of a person other than the PL/SQL developer in need -- to get them enabled. And then they require even more effort to interpret results. Don't get me wrong; I am not really complaining. It's just that, quite frankly, PL/SQL developers often want to examine the performance of a particular program and do not want to have to deal with all that other stuff. No problem! PL/SQL provides a mechanism to obtain timings of code execution that are accurate to 100th of a second: the DBMS_UTILTY.GET_TIME function. Yes, that's right. I said 100th of a second. For those of you who have programmed in Oracle over the past few years, this should be a welcome surprise. Before the advent of the DBMS_UTILITY package, the only way to measure elapsed time was to use SYSDATE and examine the difference in the time component. Sadly, this component only records times down to the nearest second. This doesn't help much when you need to measure subsecond response time. DBMS_UTILTY.GET_TIME returns the number of hundredths of seconds which have elapsed since some arbitrary point in time. I don't remember what that point is and, well, that's the whole point. A single value returned by a call to dbms_utility.get_time is, by itself, meaningless. If, on the other hand, you call this built-in function twice and then take the difference between the two returned values, you will have determined the number of hundredths of seconds which elapsed between the two calls. So if you sandwich the execution of your own program between calls to DBMS_UTILTY. GET_TIME, you will have discovered how long it takes to run that program. The anonymous block below shows how to use GET_TIME to determine the time it takes to perform the calc_totals procedure: DECLARE time_before BINARY_INTEGER; time_after BINARY_INTEGER; BEGIN time_before := DBMS_UTILITY.GET_TIME; calc_totals; time_after := DBMS_UTILITY.GET_TIME; DBMS_OUTPUT.PUT_LINE (time_after - time_before); END; I found myself relying on GET_TIME frequently as I developed the code in this book, because I wanted to analyze the performance impact of a particular approach or technique. Is it faster to raise an exception or execute an IF statement? Is it faster to load 100 rows in a table or concatenate 100 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
10. substrings into a long string? There are two basic approaches you can take to using this handy function: q Write again and again the kind of script you see above, changing the program or lines of code executed. q Encapsulate the way dbms_utility.get_time operates inside a package, which will hide the details and make it easier to use. You will find on the companion disk an explanation and code for such a package, sp_timer, in the files sptimer.sps and sptimer.spb. In addition, you will find in Advanced Oracle PL/SQL Programming with Packages a more complete performance timing utility based on DBMS_UTILITY. GET_TIME in the PLVtmr package. Once you have encapsulated your usage of DBMS_UTILITY.GET_TIME, it is very easy to put together scripts which not only analyze performance, but also compare different implementations. The following script, for example, executes two different versions of the is_number function (see "Section 25.4, "Tuning Your Algorithms"" for more information on this function) and displays the resulting elapsed times (using the PLVtmr and p packages from the PL/Vision library; again, see Advanced Oracle PL/SQL Programming with Packages: SET VERIFY OFF DECLARE b BOOLEAN; BEGIN PLVtmr.set_factor (&1) PLVtmr.capture; FOR repind IN 1 .. &1 -- Number of iterations LOOP b := isnum ('&2'); -- The string to test IF repind = 1 THEN p.l (b); END IF; END LOOP; PLVtmr.show_elapsed (TO_NUMBER Version'); PLVtmr.set_factor (&1) PLVtmr.capture; FOR repind IN 1 .. &1 LOOP b := isnum_translate ('&2'); PLVtmr.last_timing := 15; IF repind = 1 THEN Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
11. p.l (b); END IF; END LOOP; PLVtmr.show_elapsed (TRANSLATE Version'); END; / Previous: 24.2 Debugging Oracle PL/SQL Next: 25.2 Tuning Access Tips and Strategies Programming, 2nd Edition to Compiled Code 24.2 Debugging Tips and Book Index 25.2 Tuning Access to Strategies Compiled Code The Oracle Library Navigation Copyright (c) 2000 O'Reilly & Associates. All rights reserved. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
12. Previous: 25.1 Analyzing Chapter 25 Next: 25.3 Tuning Access Program Performance Tuning PL/SQL Applications to Your Data 25.2 Tuning Access to Compiled Code Before your code can be executed (and perhaps run too slowly), it must be loaded into the System Global Area (SGA) of the Oracle instance (described in more detail in Chapter 23, Managing Code in the Database). There are two elements to PL/SQL code in shared memory: the code segment and the data segment. This loading process can benefit from its own special tuning effort. 25.2.1 Tune the Size of the Shared Pool of the SGA Before you can execute a stored package module or reference a stored package object, the compiled code for that package must be loaded into the SGA. Clearly, if the package is already present in shared memory, your code executes more quickly. An important element of tuning an application which is heavily dependent on stored packages (especially large ones) is to optimize package access so that the most often-used packages are always present when needed. The default method for maintaining packages in the SGA (or "shared pool") is to let the RDBMS manage the code using its least-recently-used algorithm. The first time you reference a package, the compiled code is loaded into the shared pool. It is then available to anyone with EXECUTE authority on that package. It remains in the shared pool until the memory is needed by other memory-based resources and that package has not been used most recently. At that point, the package is flushed from the shared pool. The next time an object in the package is needed, the whole package has to be loaded once again into memory. The larger your shared pool, the more likely it is that your programs will be resident in memory the next time they are needed. Yet if you make your shared pool too large, you will be wasting memory. You should monitor your shared buffer pool to make sure it is retaining all the parsed SQL cursors and PL/SQL code segments which are commonly referenced in your application. If you find that too much swapping is occurring, increase the size of the shared buffer pool (as physical memory permits) by adjusting the SHARED_POOL_SIZE parameter in your INIT.ORA file. You can display all the objects currently in the shared pool that are larger than a specified size (in the example, 25KB) with the following statement (make sure you have SET SERVEROUTPUT ON in SQL*Plus before you make this call): Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
13. SQL> exec dbms_shared_pool.sizes (25); 25.2.2 Pin Critical Code into the SGA To increase your ability to tune application performance, Oracle supplies the DBMS_SHARED_POOL package to pin a package in the shared pool. When a package is pinned, the RDBMS does not apply its least-recently-used algorithm to that package. The package remains in memory for as long as the database instance is available (or until you explicitly "unpin" the package, as described below). At this time, only packages can be pinned in the shared pool; this fact provides added incentive to define your procedures and functions inside packages, rather than as standalone modules. You will only want to pin code when absolutely necessary, since you can end up setting aside too much memory for code, resulting in a degradation in performance of other aspects of the application. In fact, Oracle Corporation warns that the KEEP and UNKEEP procedures may not be supported in future releases, since it might provide an "automatic mechanism" which replaces these procedures. The usual candidates for pinning are particularly large programs. Prior to Oracle 7.3, Oracle requires contiguous memory in the SGA to store the code, so if sufficient space is not available at a given point of execution, Oracle will either raise an error or start swapping out other programs to make room. Neither scenario is optimal. Usually you will pin programs right after the database is started up, so that all the critical elements of your application are in place for all users. Here is an example of the pinning of the order entry package (owned by the appowner schema): DBMS_SHARED_POOL.KEEP ('appowner.ordentry'); Here is the code you would execute to unpin the same package from shared memory: DBMS_SHARED_POOL.UNKEEP ('appowner.ordentry'); Keep the following factors in mind when working with the DBMS_SHARED_POOL package: q When you call the KEEP procedure, the package is "queued" for pinning in the SGA. However, the KEEP procedure does not immediately load the package into the shared pool -- that happens when the package is first referenced, either to execute a module or to use one of the declared objects, such as a global variable or a cursor. q Oracle recommends that you pin all your packages in the shared pool soon after instance startup, when the SGA is still relatively unfragmented. That way it can set aside contiguous blocks of memory for large packages. q If you pin a package which is not owned by SYS (DBMS_OUTPUT, for example, is owned by SYS), you must grant EXECUTE on the package to PUBLIC before you execute the KEEP Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
14. procedure. Once you have pinned the package, you can revoke the PUBLIC access. q The SQL DDL command, ALTER SYSTEM FLUSH SHARED_POOL, does not affect pinned packages. You must either explicitly UNKEEP a package or restart the database to release the package from the shared pool. 25.2.2.1 Candidates for pinning in the shared pool You might consider pinning the following packages in the shared pool to improve performance: STANDARD Package which implements the core elements of the PL/SQL language. DBMS_STANDARD Package of standard database-level modules, such as RAISE_APPLICATION_ERROR. DBMS_UTILITY Package of low-level database utilities which are used to analyze schemas and objects. DBMS_DESCRIBE Package containing a utility to describe the structure of a stored module. DBMS_OUTPUT Package which allows programmers to display output to the screen. In addition, if you make frequent use of any other Oracle-provided "SYS" packages such as DBMS_LOCK or DBMS_PIPE, pinning those objects could improve performance as well. You are probably getting the idea that the more you pin into the shared pool, the better off you are. Certainly that is true, at least as true as the statement: "If all your data is stashed in memory, your applications will run much faster." Memory is always quicker than disk access. The problem is making sure you have enough memory. The more you pin into the shared pool, the less space is left in the SGA for other memory-based resources, such as data dictionary latches, application data, and shared SQL. Since a pinned object is never aged out of the SGA using a least-recently-used algorithm, other elements in the SGA are instead pushed out of the way. You can use SQL to generate a script to KEEP packages in the SGA. You can use the following SQL statement to access the v$db_object_cache to generate a KEEP for each package currently in the shared pool: SELECT 'EXECUTE DBMS_SHARED_POOL.KEEP('''||name||''');' FROM v$db_object_cache Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
15. WHERE type='PACKAGE'; You can also generate a KEEP statement for every package currently stored in the database with these other SQL statements: SELECT DISTINCT 'EXECUTE DBMS_SHARED_POOL.KEEP('''|| name||''');' FROM user_source WHERE type='PACKAGE'; SELECT DISTINCT 'EXECUTE DBMS_SHARED_POOL.KEEP('''|| object_name||''');' FROM user_objects WHERE object_type='PACKAGE'; 25.2.3 Tune ACCESS$Table to Reduce First Execution Time of Code When a database object is first referenced in a PL/SQL program, the PL/SQL engine checks the ACCESS$ table (owned by SYS) to see if the executor of the program has authority on that database object. The structure of this table is shown here: SQL> desc access$Name Null? Type ------------------------------- -------- ---- D_OBJ# NOT NULL NUMBER ORDER# NOT NULL NUMBER COLUMNS RAW(32) TYPES NOT NULL NUMBER The PL/SQL engine searches through this table on the D_OBJ# column, so if you create a nonunique index on the D_OBJ# column, you may in some cases reduce significantly the amount of time needed to perform this security check. 25.2.4 Creating Packages with Minimal Interdependencies Design your code (preferably, most of it inside packages) so that you only load into memory the code you need at any given moment in time. To accomplish this objective, you should create more smaller packages, each of which is tightly focused on implementing functionality in a given area. The Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 16. alternative, which too many of us employ without giving it much thought, is to create a few large packages that group together lots of different elements of functionality. The problem with this approach is that if I need to execute one function in that 32K package, the entire package still must be loaded up into memory. Suppose that my application then touches another element of the package, such as a constant or perhaps a different function in a different functional area. The least-recently-used algorithm will then ensure that all the memory for that package continues to be set aside, perhaps crowding out other smaller programs. The result can be excessive swapping of code. As you build your programs and design your package interfaces, be on the lookout for an opportunity to break up a single package into two or even more distinct packages with minimal interdependencies. 25.2.5 Reducing Memory Usage of Package Variables Prior to PL/SQL8, any data declared in a package simply stayed around until the end of the session, whether or not it was needed any more by the application. This is an important feature of PL/SQL packages (persistent, global data), but it limits scalability since such memory grows linearly with the number of users. To help applications better manage memory usage, PL/SQL8 provides the pragma SERIALLY_REUSABLE, which lets you mark some packages as "serially reusable." You can so mark a package if its state is needed only for the duration of a call to the server (for example, an OCI call to the server, a PL/SQL client-to-server, or server-to-server RPC). The global memory for such packages is not kept in the memory area per user, but instead in a small SGA pool. At the end of the call to the server, this memory is returned to the pool for reuse. Before reuse, the package global variables are initialized to NULL or to the default values provided. The pool is kept in SGA memory so that the work area of a package can be reused across users who have requests for the same package. In this scheme, the maximum number of work areas needed for a package is only as many as there are concurrent users of the package, which is typically much fewer than the total number of logged on users. The use of "serially reusable" packages does increase the shared-pool requirements slightly, but this is more than offset by the decrease in the per-user memory requirements. Further, Oracle ages out work areas not in use when it needs to reclaim shared pool memory. The following example shows how global variables in a "serially reusable" package behave across call boundaries: CREATE OR REPLACE PACKAGE sr_pkg IS PRAGMA SERIALLY_REUSABLE; num NUMBER := 0; Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 17. PROCEDURE print_pkg; PROCEDURE init_pkg (n NUMBER); END sr_pkg; / CREATE OR REPLACE PACKAGE BODY sr_pkg IS -- the body is required to have the pragma since the -- specification of this package has the pragma PRAGMA SERIALLY_REUSABLE; -- Print package state PROCEDURE print_pkg is BEGIN DBMS_OUTPUT.PUT_LINE ('num: ' || sr_pkg.num); END; -- Initialize package state PROCEDURE init_pkg(n NUMBER) IS BEGIN sr_pkg.num := n; END; END sr_pkg; / Now I will exercise this package. First, I enable output from SQL*Plus: SQLPLUS> set serveroutput on; Next, I initialize the package with a value of 4 and then display package contents -- all within a single PL/SQL block: SQLPLUS> begin -- initialize and print the package SR_PKG.init_pkg(4); -- Print it in the same call to the server. -- We should see the new values. SR_PKG.print_pkg; end; / Statement processed. num: 4 And we see that initial value of 4. If I had not placed the call to sr_pkg.print_pkg inside the same PL/ SQL block, however, that package variable would lose its setting, as you can see in the following steps: Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 18. SQLPLUS> begin -- We should see that the package state is reset to the -- initial (default) values. SR_PKG.print_pkg; end; / Statement processed. num: 0 Use this feature with care! Many of the packages I have constructed over the years absolutely rely on the persistent data feature. Previous: 25.1 Analyzing Oracle PL/SQL Next: 25.3 Tuning Access Program Performance Programming, 2nd Edition to Your Data 25.1 Analyzing Program Book Index 25.3 Tuning Access to Your Performance Data The Oracle Library Navigation Copyright (c) 2000 O'Reilly & Associates. All rights reserved. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 19. Previous: 25.2 Tuning Chapter 25 Next: 25.4 Tuning Your Access to Compiled Code Tuning PL/SQL Applications Algorithms 25.3 Tuning Access to Your Data Much of the tuning you do will attempt to optimize the way PL/SQL programs manipulate data in the Oracle database, both queries and DML (updates, inserts, deletes). Lots of the issues here involve tuning SQL statements, and I am not even going to attempt to show you how to tune SQL (definitely not my strong suit). There are certainly steps you can take, though, in your PL/SQL code and environment to improve the performance of even an optimally constructed chunk of SQL. 25.3.1 Use Package Data to Minimize SQL Access When you declare a variable in a package body or specification, its scope is not restricted to any particular procedure or function. As a result, the scope of package-level data is the entire Oracle session, and the value of that data persists for the entire session. Take advantage of this fact to minimize the times you have to access data from the SQL layer. Performing lookups against structures located in your own Program Global Area (PGA) is much, much faster than going through the SGA -- even if the data you want is resident in shared memory. This tip will come in handy most when you find that your application needs to perform multiple lookups which do not change during your session. Suppose, for example, that one of your programs needs to obtain a unique session identifier to avoid overlaps with other sessions. One of the ways to do this is to call DBMS_LOCK.ALLOCATE_UNIQUE to retrieve a unique lockname. Here is the inefficient way to do this: I need the ID or lockname in the calc_totals procedure. So I make the call to the built-in package right inside the procedure: PROCEDURE calc_totals IS v_lockname VARCHAR2(100); BEGIN v_lockname := DBMS_LOCK.ALLOCATE_UNIQUE; /* Use the lock name to issue a request for data. */ send_request (v_lockname); . . . Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 20. END; The problem with this approach is that every time calc_totals is called, the built-in function is also called to get the unique value -- a totally unnecessary action. After you get it the first time, you needn't get it again. Packages provide a natural repository for these "session-level" pieces of data. The following sesspkg (session package) defines a variable to hold the lock name and then assigns it a value on initialization of the package: PACKAGE sesspkg IS lockname CONSTANT VARCHAR2(100) := DBMS_LOCK. ALLOCATE_UNIQUE; END; Now the calc_totals procedure can directly reference the package global in its call to send_request. The first time calc_totals is called, the sesspkg package is instantiated and the built-in function called. All subsequent calls to calc_totals within that session will not, however, result in any additional processing inside sesspkg. Instead, it simply returns the value resident in the constant. PROCEDURE calc_totals IS BEGIN /* Use the lock name to issue a request for data. */ send_request (sesspkg.lockname); END; You can apply a similar technique for lookups against database tables -- both persistent and session- based. In the following example, the SELECT from v$parameter will be executed only once per session, regardless of how many times the db_name function is executed. Since the database name will never change during the session, this is reasonable. Note also the judicious use of a function with side effects, where one side effect (modification of a persistent package variable) is used to gain the efficiency: /* Filename on companion disk: dbdata.spp */ CREATE OR REPLACE PACKAGE db_data IS /* function to return the name of the database */ FUNCTION db_name RETURN VARCHAR2; END db_data; / CREATE OR REPLACE PACKAGE BODY db_data IS Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.