Q: | |
29-28. | The matches are: 1 and d 2 and b 3 and c 4 and a
|
Q: | |
29-29. | There are several possible ways to improve the program. The following optimization eliminates the nested loops by accumulating the schedule in reverse, resulting in a program that is both faster and easier to understand (note that there are several versions on the book’s web page):
/* Filename on web page: presvalue.sql */
PROCEDURE build_lease_schedule
IS
pv_total_lease NUMBER (9) := 0;
one_year_pv NUMBER (9) := 0;
BEGIN
fixed_count := 0;
var_count := 0;
FOR year_count IN REVERSE 1 .. 20
LOOP
one_year_pv :=
pv_of_fixed (year_count) +
pv_of_variable (year_count);
pv_total_lease :=
pv_total_lease + one_year_pv;
pv_table (year_count) :=
pv_total_lease;
END LOOP;
END; |
Q: | |
29-30. | You can create an anonymous PL/SQL block within an IF statement to declare and use memory only when necessary: PROCEDURE only_as_needed (...) IS
BEGIN
IF <condition>
THEN
DECLARE
big_string VARCHAR2(32767) := ten_minute_lookup (...);
big_list list_types.big_strings_tt;
BEGIN
use_big_string (big_string);
Process_big_list (big_list);
END;
ELSE
/* Nothing big
going on here */
...
END IF;
END; |
Q: | |
29-31. | You can use native dynamic SQL to easily change the status of a trigger:
/* Filename on web page: settrig_status.sp */
CREATE OR REPLACE PROCEDURE settrig (tab IN VARCHAR2, action IN VARCHAR2)
IS
v_action VARCHAR2 (10) := UPPER (action);
v_other_action VARCHAR2 (10) := 'DISABLE';
BEGIN
IF v_action = 'DISABLE'
THEN
v_other_action := 'ENABLE';
END IF;
FOR rec IN (SELECT trigger_name
FROM user_triggers
WHERE table_owner = USER
AND table_name = UPPER (tab)
AND status = v_other_action)
LOOP
EXECUTE IMMEDIATE 'ALTER TRIGGER ' ||
rec.trigger_name ||
' ' ||
v_action;
DBMS_OUTPUT.put_line (
'Set status of ' || rec.trigger_name || ' to ' || v_action
);
END LOOP;
END; |
Q: | |
29-32. | You can use DBMS_JOB to run the calc_comp procedure in parallel: DECLARE
job# PLS_INTEGER;
PROCEDURE doit (job IN VARCHAR2) IS BEGIN
DBMS_JOB.SUBMIT (
job#, 'calc_comp (''' || job || ''')', SYSDATE, NULL);
COMMIT;
END;
BEGIN
doit ('CLERK'),
doit ('VP'),
doit ('PROGRAMMER'),
END; |
Q: | |
29-33. | Yes, the fact that the compensation at one level is the sum of the sublevels affects your ability to run calc_comp in parallel; the processes started by DBMS_JOB must be independent (i.e., not rely on the results of a previous calculation). |
Q: | |
29-34. | Although statements (a) and (b) do the same thing (i.e., they are logically equivalent), they are structurally different (version (a) uses “2” as a multiplier, version (b) uses “2.0”). The effect of this small difference is that it prevents the PL/SQL engine from using the preparsed cursor for (a) in computing the result set of (b). In order to reuse two logically equivalent statements, they must be physically identical. |
Q: | |
29-35. | Your script must query the all_dependencies data dictionary table: SELECT owner || '.' || name refs_table,
referenced_owner || '.' || referenced_name table_referenced
FROM all_dependencies
WHERE TYPE IN ('PACKAGE', 'PACKAGE BODY', 'PROCEDURE', 'FUNCTION')
AND referenced_type IN ('TABLE', 'VIEW'), |
Q: | |
29-36. | You can use the bulk collection operations to perform this relatively complex task with a minimal amount of code: FUNCTION whack_emps_by_dept (deptlist dlist_t)
RETURN enolist_t
IS
enolist enolist_t;
BEGIN
FORALL aDept IN deptlist.FIRST..deptlist.LAST
DELETE emp WHERE deptno IN deptlist(aDept)
RETURNING empno BULK COLLECT INTO enolist;
RETURN enolist;
END; |
Q: | |
29-37. | You can use packaged variables (and an anonymous PL/SQL block) to eliminate the calls to the DBMS_SQL.BIND_VARIABLES procedure: CREATE OR REPLACE PACKAGE myvars
IS
empno emp.empno%TYPE;
deptno emp.deptno%TYPE;
ename emp.ename%TYPE;
END;
/
CREATE OR REPLACE PROCEDURE bindnone
IS
cur INTEGER := DBMS_SQL.OPEN_CURSOR;
rows_inserted INTEGER;
BEGIN
DBMS_SQL.PARSE (
cur,
'BEGIN
INSERT INTO emp (empno, deptno, ename)
VALUES (myvars.empno, myvars.deptno, myvars.ename);
END;',
DBMS_SQL.NATIVE
);
FOR rowind IN 1 .. 1000
LOOP
myvars.empno := rowind;
myvars.deptno := 40;
myvars.ename := 'Steven' || rowind;
rows_inserted := DBMS_SQL.EXECUTE (cur);
END LOOP;
DBMS_SQL.CLOSE_CURSOR (cur);
END; |
Q: | |
29-38. | Before Oracle8, VARCHAR2 variables were treated as variable length but stored as fixed length. Hence, in an Oracle 7.3 database, this script consumes 32,767 * 32,767 bytes (approximately 1 GB!) of memory, even though each element of the array is NULL. |
Q: | |
29-39. | Pass-by-value is a problem for large collections. For example, suppose that you have a large index-by table with 50,000 elements. Passing the table as an IN OUT parameter creates another copy of the structure in memory. Additionally, any updates made to this copy must be propagated back to the original collection variables. There are two ways to mitigate the problem. The first is to change the parameters into packaged variables. The second method, available only in Oracle8i, is to use the NOCOPY keyword in the parameter declaration. NOCOPY switches the parameter “more” to pass by reference. There are several potential pitfalls with these approaches: Since changes take effect immediately on the variables themselves, rather than on a copy, it’s impossible to “roll back” if an exception occurs. Global variables, unless carefully encapsulated behind a well-defined API, can create a host of maintenance problems. The NOCOPY keyword, like a pragma, is just a request to the compiler, not a command. For example, it’s ignored when the program is called via RPC; when you pass just one element of a collection; when collection elements are constrained (i.e., NOT NULL); when parameters are records with anchored declarations; or when implicit datatypes are required.
|