Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
2,58 MB
Nội dung
Collections 277 10 The key here is that the arguments to the constructor are dept1 and dept2 . Both of those are records of type department%ROWTYPE , and so match the element type of the table. Obviously it’s a bit cumbersome to set things up this way. To add more entries to a table than those you created with the constructor, you need to extend the table, as discussed in the following section. Extending a Nested Table To extend a nested table so that you can add more entries to it, use the extend method. The extend method allows you to add one entry, or several entries. It also allows you to clone an existing entry one or more times. The syntax for the extend method is as fol- lows. collection.extend[(entries_to_add[, entry_to_clone])]; In this syntax the parameters are as follows: • collection is the name of the nested table. • entries_to_add is a variable or constant indicating the number of new entries you want to add. • entry_to_clone is a variable or constant indicating which entry you want to clone. Listing 10.4 shows the extend method being used and illustrates how constructors work. L ISTING 10.4 The extend Method, Adding Entries to a Nested Table 1: DECLARE 2: --Declare a cursor that returns all department records. 3: CURSOR all_depts IS 4: SELECT * 5: FROM department 6: ORDER BY dept_name; 7: 8: --Define a nested table type. 9: TYPE dept_table IS TABLE OF department%ROWTYPE; 10: 11: --Declare a nested table variable to 12: --hold the employee records that we read in. 13: depts dept_table; 14: depts_max PLS_INTEGER; 15: inx1 PLS_INTEGER; 16: BEGIN 17: --Initialize the index into the table. 18: depts_max := 0; 19: A NALYSIS , S YNTAX , I NPUT continues 14 7982 ch10 11/30/99 1:13 PM Page 277 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 20: --Initialize the table by creating one empty entry. 21: depts := dept_table (); 22: 23: FOR dept IN all_depts LOOP 24: depts_max := depts_max + 1; 25: depts.extend; 26: depts(depts_max).dept_id := dept.dept_id; 27: depts(depts_max).dept_name := dept.dept_name; 28: depts(depts_max).no_of_emps := dept.no_of_emps; 29: END LOOP; 30: 31: --Clone the first entry five times. 32: depts.extend(5,1); 33: 34: --Display the results. 35: FOR inx1 IN 1 depts_max+5 LOOP 36: DBMS_OUTPUT.PUT_LINE ( 37: depts(inx1).dept_id || 38: ‘ ‘ || depts(inx1).dept_name); 39: END LOOP; 40: END; 41: / 403 BOTTLING 402 DISTILLATION 501 Employee Newsletters 401 FERMENTATION 405 GUTTING 404 SCALE PROCESSING 502 Technical Writing 406 UNLOADING 403 BOTTLING 403 BOTTLING 403 BOTTLING 403 BOTTLING 403 BOTTLING Line 8 declares the type for the nested table, and defines it to match the depart- ment table. In line 13, the variable depts is declared, and becomes the nested table. However, the table can’t be used until it is initialized, which happens in line 21, with a call to the constructor. Because no values are passed to the constructor, the nested table is created with zero entries. The FOR loop in lines 23–29 reads the department records, and inserts them into the table. Before each record is inserted, a call to extend is made in order to add space for the new entry. After all the data has been read, another call to extend is made in line 32 278 Day 10 L ISTING 10.4 continued O UTPUT A NALYSIS 14 7982 ch10 11/30/99 1:13 PM Page 278 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Collections 279 10 to clone the first entry five times. The last FOR loop, in lines 35–39, dumps the contents of the table to the screen (if you are using SQL*Plus) by using the DBMS_OUTPUT pack- age. Sure enough, you can see that the first entry has been replicated five more times at the end of the table. Removing Entries from a Nested Table You can remove entries from a nested table by using the delete method, just as you do with index-by tables. The following example deletes entry 10 from the depts table: depts.delete(10); You can reuse entries after you delete them. The other entries in the table are not renum- bered. Another method of removing rows from a nested table is to invoke the trim method on the table. The trim method removes a specified number of entries from the end of the table. nested_table.trim[(entries_to_trim)]; In this syntax the parameters are as follows: • nested_table is the name of the nested table. • entries_to_trim is the number of entries to remove from the end. The default is 1 . The trim method applies only to nested tables and variable-sized arrays. It cannot be applied to index-by tables. Listing 10.5 is an extension of Listing 10.4. This time, after the new entries are added to the table and displayed, the trim method is used to remove them. L ISTING 10.5 The trim Method 1: DECLARE 2: --Declare a cursor that returns all department records. 3: CURSOR all_depts IS 4: SELECT * 5: FROM department 6: ORDER BY dept_name; 7: 8: --Define a nested table type. 9: TYPE dept_table IS TABLE OF department%ROWTYPE; 10: 11: --Declare a nested table variable to , S YNTAX , I NPUT continues 14 7982 ch10 11/30/99 1:13 PM Page 279 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 12: --hold the employee records that we read in. 13: depts dept_table; 14: depts_max PLS_INTEGER; 15: inx1 PLS_INTEGER; 16: BEGIN 17: --Initialize the index into the table. 18: depts_max := 0; 19: 20: --Initialize the table by creating one empty entry. 21: depts := dept_table (); 22: 23: FOR dept IN all_depts LOOP 24: depts_max := depts_max + 1; 25: depts.extend; 26: depts(depts_max).dept_id := dept.dept_id; 27: depts(depts_max).dept_name := dept.dept_name; 28: depts(depts_max).no_of_emps := dept.no_of_emps; 29: END LOOP; 30: 31: --Clone the first entry five times. 32: depts.extend(5,1); 33: 34: --Display the results. 35: FOR inx1 IN 1 depts_max+5 LOOP 36: DBMS_OUTPUT.PUT_LINE ( 37: depts(inx1).dept_id || 38: ‘ ‘ || depts(inx1).dept_name); 39: END LOOP; 40: --Trim off the five clones of entry #1 41: depts.trim(5); 42: 43: --Delete the first entry. 44: depts.delete(1); 45: 46: --Display the new count. 47: DBMS_OUTPUT.PUT_LINE(depts.count); 48: 49: --Display the results. 50: FOR inx1 IN 1 depts_max+5 LOOP 51: IF depts.exists(inx1) THEN 52: DBMS_OUTPUT.PUT_LINE ( 53: depts(inx1).dept_id || 54: ‘ ‘ || depts(inx1).dept_name); 55: END IF; 56: END LOOP; 57: 58: END; 59: / 280 Day 10 L ISTING 10.5 continued 14 7982 ch10 11/30/99 1:13 PM Page 280 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Collections 281 10 403 BOTTLING 402 DISTILLATION 501 Employee Newsletters 401 FERMENTATION 405 GUTTING 404 SCALE PROCESSING 502 Technical Writing 406 UNLOADING 403 BOTTLING 403 BOTTLING 403 BOTTLING 403 BOTTLING 403 BOTTLING 7 402 DISTILLATION 501 Employee Newsletters 401 FERMENTATION 405 GUTTING 404 SCALE PROCESSING 502 Technical Writing 406 UNLOADING Up through line 39, this listing is the same as Listing 10.4. Departments are read from the database, the first one is cloned, and the results are displayed. After that, in line 41, the trim method is used to remove the five clones of entry 1. Next, the delete method is called in line 44 to delete the first entry as well. Line 47 displays the new count, telling how many entries are now in the table. It also serves a more interest- ing purpose than that: PL/SQL doesn’t seem to recognize that you trimmed and deleted entries until after you reference the table’s count, so line 47 is really a bug workaround. Finally, lines 50–56 display the table entries that remain after the deleting and trimming. O UTPUT A NALYSIS If you remove line 47 (which invokes the count method) from Listing 10.5, and run it again, the second list of departments will match the first. In other words, the exists method won’t recognize that you deleted some entries. This is true with Oracle release 8.1.5, and is almost certainly a bug. The workaround is to invoke the count method at least once. Note Using Variable-Sized Arrays Like nested tables, variable-sized arrays or varrays also came into existence with the release of Oracle8. Var rays are similar to nested tables, but they have a fixed maximum size. They differ from nested tables in that when you store a varray into a data- base column, the order of elements is preserved. N EW T ERM 14 7982 ch10 11/30/99 1:13 PM Page 281 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Declaring and Initializing a Varray To create a varray, you use the VARRAY keyword in a type declaration to create an array type. Then you can use that type to declare one or more variables. The syntax for declar- ing a varray type is as follows. TYPE type_name IS {VARRAY|VARYING ARRAY} (size) OF entry_type [NOT NULL]; In this syntax the parameters are as follows: • type_name is the name of the array type. • size is the number of elements you want the array to hold. • entry_type is the data type for elements of the array. • NOT NULL prohibits array entries from being null. Varrays need to be initialized just as nested tables do. Before you can use a varray, you need to call its constructor. You can pass values to the constructor, and those values are used to create array elements, or you can invoke the constructor with no parameters in order to create an empty array. The code in Listing 10.6 shows a varray being declared, and the constructor being called to create the array with some initial data. L ISTING 10.6 Declaring and Creating a Varray 1: DECLARE 2: --Define an array type 3: TYPE dept_array IS VARRAY(100) OF VARCHAR2(30); 4: 5: --Define the array variable and other variables. 6: depts dept_array; 7: inx1 PLS_INTEGER; 8: 9: BEGIN 10: --Initialize the array and create two entries 11: --using the constructor. 12: depts := dept_array (‘Dept One’,’Dept Two’); 13: 14: --Display the contents of the two entries. 15: FOR inx1 IN 1 2 LOOP 16: DBMS_OUTPUT.PUT_LINE(depts(inx1)); 17: END LOOP; 18: END; 19: / 282 Day 10 You need the Enterprise Edition of Oracle8i in order to use varrays. Note , S YNTAX , I NPUT 14 7982 ch10 11/30/99 1:13 PM Page 282 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Collections 283 10 Dept One Dept Two Line 3 declares a type that results in a 100-element array of VARCHAR2(30) val- ues. The depts variable is declared in line 6 to be of this type. In line 12 the array is initialized by calling the constructor. In this example, two values are supplied to the constructor, so the array is created with those two elements. The size of the array is still 100 because that’s what is specified in the type declaration. The elements created by the constructor are numbers 1 and 2, and elements 3 through 100 are empty. Adding and Removing Data from a Varray After you’ve initialized a varray, you can add data to and remove it from the varray just as you do with a nested table. If you want to add more elements to the array than you created when you initialized it, you can call the extend method. However, you can only extend an array up to the maximum size specified in the array type definition. Listing 10.7 shows the contents of the department table being read into a varray. L ISTING 10.7 Reading Data into a Varray 1: DECLARE 2: --Declare a cursor that returns all department records. 3: CURSOR all_depts IS 4: SELECT * 5: FROM department 6: ORDER BY dept_name; 7: 8: --Define a varray type. 9: TYPE dept_array IS VARRAY(100) OF department%ROWTYPE; 10: 11: --Declare a varray variable to 12: --hold the employee records that we read in. 13: depts dept_array; 14: inx1 PLS_INTEGER; 15: inx2 PLS_INTEGER; 16: BEGIN 17: --Initialize the index into the array. 18: inx1 := 0; 19: 20: --Initialize the array. 21: depts := dept_array (); 22: 23: FOR dept IN all_depts LOOP 24: inx1 := inx1 + 1; 25: depts.extend(); O UTPUT A NALYSIS I NPUT continues 14 7982 ch10 11/30/99 1:13 PM Page 283 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 26: depts(inx1).dept_id := dept.dept_id; 27: depts(inx1).dept_name := dept.dept_name; 28: depts(inx1).no_of_emps := dept.no_of_emps; 29: END LOOP; 30: 31: --Display the results. 32: FOR inx2 IN 1 depts.count LOOP 33: DBMS_OUTPUT.PUT_LINE ( 34: depts(inx2).dept_id || 35: ‘ ‘ || depts(inx2).dept_name); 36: END LOOP; 37: END; 38: / The array type is declared in line 9, with a maximum size of 100 entries. The actual array variable is declared in line 13. The call to the constructor in line 21 initializes the array. It now exists, but with zero entries. As each entry is added, the array must be extended in order to hold that entry. Line 25, inside the FOR loop, does this. Note that extend cannot be used to grow the array beyond the maximum specified size of 100 entries. Taking Advantage of Bulk Binding PL/SQL bulk binding is a new feature with Oracle8i. Bulk binding lets you code SQL statements that operate on all entries in a collection, without having to loop through that collection by using PL/SQL code. Several of the examples so far in this les- son have used a cursor FOR loop to load data from a database table into a PL/SQL table or array. The switch from SQL (for the fetch) to PL/SQL (to add the data to the array) is called a context switch, and consumes quite a bit of overhead. You can use the bulk bind- ing feature to avoid much of that overhead. 284 Day 10 L ISTING 10.7 continued A NALYSIS N EW T ERM If you are not using Oracle8i, you won’t be able to execute any of the bulk binding examples shown in this chapter. Note Two new keywords support binding. BULK COLLECT is used with SELECT statements to place all the data into a collection. FORALL is used with INSERT , UPDATE , and DELETE statements to execute those statements once for each element in a collection. 14 7982 ch10 11/30/99 1:13 PM Page 284 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Collections 285 10 Using BULK COLLECT You can use the BULK COLLECT keywords to have the results of a SELECT statement placed directly into a collection. You can use BULK COLLECT with SELECT INTO state- ments, and also with FETCH statements. For example, if dept_ids and dept_names were both nested tables, you could issue the following SELECT statement: SELECT dept_id, dept_name BULK COLLECT INTO dept_ids, dept_names FROM department; If you had a cursor named all_depts that returned the same data, you could write BULK COLLECT into the FETCH statement, like this: OPEN all_depts; FETCH all_depts BULK COLLECT INTO dept_ids, dept_names; CLOSE all_depts; For some reason, Oracle does not allow you to use BULK COLLECT in a collection of records. Thus, if you are selecting 10 columns, you need to declare 10 collections, one for each column. Listing 10.8 shows an example of BULK COLLECT being used to load all department names and IDs into a nested table. L ISTING 10.8 An Example Showing the Use of BULK COLLECT 1: DECLARE 2: --Declare a cursor that returns all department records. 3: CURSOR all_depts IS 4: SELECT dept_id, dept_name 5: FROM department 6: ORDER BY dept_name; 7: 8: --Define a nested table type for each column. 9: TYPE dept_id IS TABLE OF department.dept_id%TYPE; 10: TYPE dept_name IS TABLE OF department.dept_name%TYPE; 11: 12: --Declare a nested table variable for each column. 13: dept_ids dept_id; 14: dept_names dept_name; 15: inx1 PLS_INTEGER; 16: BEGIN 17: OPEN all_depts; 18: FETCH all_depts BULK COLLECT INTO dept_ids, dept_names; 19: CLOSE all_depts; 20: 21: --Display the results. 22: FOR inx1 IN 1 dept_ids.count LOOP I NPUT continues 14 7982 ch10 11/30/99 1:13 PM Page 285 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 23: DBMS_OUTPUT.PUT_LINE ( 24: dept_ids(inx1) || 25: ‘ ‘ || dept_names(inx1)); 26: END LOOP; 27: END; 28: / 403 BOTTLING 402 DISTILLATION 501 Employee Newsletters 401 FERMENTATION 405 GUTTING 404 SCALE PROCESSING 502 Technical Writing 406 UNLOADING The all_depts cursor declared in lines 3–6 returns two values: the department ID and name. Lines 9 and 10 declare nested table types for each of these columns. Corresponding nested table variables are declared in lines 13–14. The FETCH statement in line 18 then uses the BULK COLLECT keyword to read all the data selected directly into the arrays. This is much faster than fetching one row at a time using a PL/SQL loop. Note that Listing 10.8 contain no call to the nested tables’ constructor methods. The FETCH statement takes care of that for you. The ability to do bulk binds is a great feature. The single annoying thing about it is that you cannot declare a nested table of department%rowtype , and use that as the target. BULK COLLECT won’t handle tables of records. 286 Day 10 O UTPUT A NALYSIS I imagine that some future release of Oracle will remove the restriction against BULK COLLECT loading tables of records. At least I hope that hap- pens. Note Using FORALL The FORALL keyword allows you to base a Data Manipulation Language (DML) state- ment (that is, INSERT , UPDATE ,or DELETE ) on the contents of a collection. When FORALL is used, the statement is executed once for each entry in the collection, but only one con- text switch is made from PL/SQL to SQL. The resulting performance is much faster than what you get when you code a loop in PL/SQL. L ISTING 10.8 continued 14 7982 ch10 11/30/99 1:13 PM Page 286 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. [...]... central point of control for maintaining the number of employees in a department, and they relieve you from having to program and test this logic several places in your application Maintaining History This last example concerning triggers will involve using them to maintain a historical record of changes made to the data in a table The approach will be to create an audit trail table containing the data... contain the data to be inserted In a delete trigger, the situation is reversed The field values in :OLD contain the data about to be deleted and the :NEW values will be null Uses for Triggers The possible uses for database triggers are varied and are limited only by your imagination Some common uses are • Enforcing business rules • Maintaining referential integrity • Enforcing security • Maintaining... • Generating column values, including primary key values • Replication of data The next few sections show some examples of these uses Maintaining Data Integrity A common use for triggers is to assist in maintaining the integrity of the data stored in the database Suppose that you wanted to store a count of the number of employees in each department and that you wanted to store this count in the department... see a trigger definition Instead, build a file containing these commands Be sure to save it with the SQL extension You can then use the SQL*Plus @ command to execute the file whenever you like Listing 11.10 shows this command file being used to extract the definition of the maintain_pay_history trigger INPUT/ OUTPUT 1: 2: 3: 4: LISTING 11.10 Extracting the Definition for the emp_dept_ins Trigger SQL>... 18-Jun-1997 06:06 pm Jerome Finkbeiner 8 301 18-Jun-1997 06:06 pm Jerome Finkbeiner 9 301 18-Jun-1997 06:06 pm Jerome Finkbeiner 307 P PAY_RATE - -H 2000 S 4000000 11 You can see from lines 1 and 2 that the history table is initially empty A new employee is then inserted (lines 3–5), his pay rate is changed from hourly to salaried (lines 7–10), and finally the employee is deleted (lines 12–13) The COLUMN... places, resulting in several possible points of failure Furthermore, if you weren’t developing a new system from scratch, you might already have several applications written and in production Changing these would be costly The solution? You guessed it write some triggers Listing 11.6 shows a trigger that will maintain a chronological salary history for employees in the sample database INPUT LISTING 11.6... counts for each department INPUT LISTING 11.3 Triggers to Maintain Departmental Employee Counts 1: CREATE OR REPLACE TRIGGER emp_dept_ins 2: AFTER INSERT ON emp_dept 3: FOR EACH ROW continues 11 302 Day 11 LISTING 11.3 continued 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: BEGIN Increment the employee count... commands in lines 11–13 prompt you for the trigger to extract, and for the name of the file in which to place it The SPOOL command in line 17 tells SQL*Plus to begin writing output to that file The subsequent queries extract the trigger, and another SPOOL command is used in line 32 to close the file ANALYSIS Of course, you don’t want to actually type all the commands shown in Listing 11.9 into SQL*Plus... 22: FOR inx1 IN 1 dept_ids.count LOOP 23: dept_names(inx1) := UPPER(dept_names(inx1)); 24: 25: DBMS_OUTPUT.PUT_LINE ( 26: dept_ids(inx1) || 27: ‘ ‘ || dept_names(inx1)); 28: END LOOP; continues 10 288 Day 10 LISTING 10.9 continued 29: 30: FORALL x IN dept_ids.first dept_ids.last 31: UPDATE department 32: SET dept_name = dept_names(x) 33: WHERE dept_id = dept_ids(x); 34: END; 35: / Aside from lines 30–33,... value needed to be maintained Unfortunately, as you add programmers to a project and as the number of programs that need to maintain this value increases, the likelihood of a mistake also increases Triggers provide you with a mechanism to centralize the code to maintain a counter like this Because you have to deal with inserts, updates, and deletes, three triggers are needed to maintain the departmental . read in. 13: depts dept_array; 14: inx1 PLS_INTEGER; 15: inx2 PLS_INTEGER; 16: BEGIN 17: --Initialize the index into the array. 18: inx1 := 0; 19: 20: --Initialize. BOTTLING 403 BOTTLING 403 BOTTLING 403 BOTTLING Line 8 declares the type for the nested table, and defines it to match the depart- ment table. In line 13,