Cursors in Dbms
Cursors in Dbms
Cursors Page5 A cursor is a name for a specific private SQL area in which information for processing the specific statement is kept. PL/SQL uses both implicit and explicit cursors. PL/SQL implicitly declares a cursor for all SQL data manipulation statements on a set of rows, including queries that return only one row. For queries that return more than one row, you can explicitly declare a cursor to process the rows individually. For example, Example 16 on page 1-10 declares an explicit cursor. For information about cursors, see Managing Cursors in PL/SQL on page 6-7. %TYPE Attribute The %TYPE attribute provides the data type of a variable or database column. This is particularly useful when declaring variables that will hold database values. For example, assume there is a column named last_name in a table named employees. To declare a variable named v_last_name that has the same data type as column last_name, use dot notation and the %TYPE attribute, as follows: v_last_name employees.last_name%TYPE; Declaring v_last_name with %TYPE has two advantages. First, you need not know the exact data type of last_name. Second, if you change the database definition of last_name, perhaps to make it a longer character string, the data type of v_last_ name changes accordingly at run time. For more information about %TYPE, see Using the %TYPE Attribute on page 2-12 and %TYPE Attribute on page 13-119. %ROWTYPE Attribute In PL/SQL, records are used to group data. A record consists of a number of related fields in which data values can be stored. The %ROWTYPE attribute provides a record type that represents a row in a table. The record can store an entire row of data selected from the table or fetched from a cursor or cursor variable. See Cursors on page 1-10. Columns in a row and corresponding fields in a record have the same names and data types. In the following example, you declare a record named dept_rec, whose fields have the same names and data types as the columns in the departments table: dept_rec departments%ROWTYPE; -- declare record variable You use dot notation to reference fields, as follows: v_deptid := dept_rec.department_id; If you declare a cursor that retrieves the last name, salary, hire date, and job class of an employee, you can use %ROWTYPE to declare a record that stores the same information. The FETCH statement in Example 16 assigns the value in the last_name column of the employees table to the last_name field of employee_rec, the value in the salary column is to the salary field, and so on. Example 16 Using %ROWTYPE with an Explicit Cursor SQL> DECLARE 2 CURSOR c1 IS 3 SELECT last_name, salary, hire_date, job_id 4 FROM employees 5 WHERE employee_id = 120; 7 employee_rec c1%ROWTYPE; 8 9 BEGIN 10 OPEN c1; 11 FETCH c1 INTO employee_rec; 12 DBMS_OUTPUT.PUT_LINE('Employee name: ' || employee_rec.last_name); 13 END; 14 / Employee name: Weiss PL/SQL procedure successfully completed. SQL> For more information about %ROWTYPE, see Using the %ROWTYPE Attribute on page 2-15 and %ROWTYPE Attribute on page 13-105. Collections PL/SQL collection types let you declare high-level data types similar to arrays, sets, and hash tables found in other languages. In PL/SQL, array types are known as varrays (short for variable-size arrays), set types are known as nested tables, and hash table types are known as associative arrays. Each kind of collection is an ordered group of elements, all of the same type. Each element has a unique subscript that determines its position in the collection. When declaring collections, you use a TYPE definition. See Defining Collection Types on page 5-6. To reference an element, use subscript notation with parentheses, as shown in Example 17. Example 17 Using a PL/SQL Collection Type SQL> DECLARE 2 TYPE staff_list IS TABLE OF employees.employee_id%TYPE; 3 staff staff_list; 4 lname employees.last_name%TYPE; 5 fname employees.first_name%TYPE; 6 BEGIN 7 staff := staff_list(100, 114, 115, 120, 122); 8 9 FOR i IN staff.FIRST..staff.LAST LOOP 10 SELECT last_name, first_name INTO lname, fname 11 FROM employees 12 WHERE employees.employee_id = staff(i); 13 14 DBMS_OUTPUT.PUT_LINE (TO_CHAR(staff(i)) 15 || ': ' 16 || lname 17 || ', ' 18 || fname 19 ); 20 END LOOP; 21 END; 22 / 100: King, Steven 114: Raphaely, Den 115: Khoo, Alexander 120: Weiss, Matthew 122: Kaufling, Payam PL/SQL procedure successfully completed. SQL> SQL Cursors (Implicit) SQL cursors are managed automatically by PL/SQL. You need not write code to handle these cursors. However, you can track information about the execution of an SQL cursor through its attributes. Topics: Attributes of SQL Cursors Guidelines for Using Attributes of SQL Cursors Attributes of SQL Cursors SQL cursor attributes return information about the execution of DML and DDL statements, such INSERT, UPDATE, DELETE, SELECT INTO, COMMIT, or ROLLBACK statements. The cursor attributes are %FOUND, %ISOPEN %NOTFOUND, and %ROWCOUNT. The values of the cursor attributes always refer to the most recently executed SQL %FOUND Attribute: Has a DML Statement Changed Rows? Page6 %ISOPEN Attribute: Always FALSE for SQL Cursors %NOTFOUND Attribute: Has a DML Statement Failed to Change Rows? %ROWCOUNT Attribute: How Many Rows Affected So Far? %FOUND Attribute: Has a DML Statement Changed Rows? Until a SQL data manipulation statement is executed, %FOUND yields NULL. Thereafter, %FOUND yields TRUE if an INSERT, UPDATE, or DELETE statement affected one or more rows, or a SELECT INTO statement returned one or more rows. Otherwise, %FOUND yields FALSE. In Example 67, you use %FOUND to insert a row if a delete succeeds. Example 67 Using SQL%FOUND CREATE TABLE dept_temp AS SELECT * FROM departments; DECLARE dept_no NUMBER(4) := 270; BEGIN DELETE FROM dept_temp WHERE department_id = dept_no; IF SQL%FOUND THEN -- delete succeeded INSERT INTO dept_temp VALUES (270, 'Personnel', 200, 1700); END IF; END; / %ISOPEN Attribute: Always FALSE for SQL Cursors The database closes the SQL cursor automatically after executing its associated SQL statement. As a result, %ISOPEN always yields FALSE. %NOTFOUND Attribute: Has a DML Statement Failed to Change Rows? %NOTFOUND is the logical opposite of %FOUND. %NOTFOUND yields TRUE if an INSERT, UPDATE, or DELETE statement affected no rows, or a SELECT INTO statement returned no rows. Otherwise, %NOTFOUND yields FALSE. %ROWCOUNT Attribute: How Many Rows Affected So Far? %ROWCOUNT yields the number of rows affected by an INSERT, UPDATE, or DELETE statement, or returned by a SELECT INTO statement. %ROWCOUNT yields 0 if an INSERT, UPDATE, or DELETE statement affected no rows, or a SELECT INTO statement returned no rows. In Example 68, %ROWCOUNT returns the number of rows that were deleted. Example 68 Using SQL%ROWCOUNT CREATE TABLE employees_temp AS SELECT * FROM employees; DECLARE mgr_no NUMBER(6) := 122; BEGIN DELETE FROM employees_temp WHERE manager_id = mgr_no; DBMS_OUTPUT.PUT_LINE ('Number of employees deleted: ' || TO_CHAR(SQL%ROWCOUNT)); END; / If a SELECT INTO statement returns more than one row, PL/SQL raises the predefined exception TOO_MANY_ROWS and %ROWCOUNT yields 1, not the actual number of rows that satisfy the query. The value of the SQL%ROWCOUNT attribute refers to the most recently executed SQL statement from PL/SQL. To save an attribute value for later use, assign it to a local variable immediately. The SQL%ROWCOUNT attribute is not related to the state of a transaction. When a rollback to a savepoint is performed, the value of SQL%ROWCOUNT is not restored to the old value before the savepoint was taken. Also, when an autonomous transaction is exited, SQL%ROWCOUNT is not restored to the original value in the parent transaction. Guidelines for Using Attributes of SQL Cursors When using attributes of SQL cursors, consider the following: The values of the cursor attributes always refer to the most recently executed SQL statement, wherever that statement is. It might be in a different scope (for example, in a sub-block). To save an attribute value for later use, assign it to a local variable immediately. Doing other operations, such as subprogram calls, might change the value of the variable before you can test it. The %NOTFOUND attribute is not useful in combination with the SELECT INTO statement: If a SELECT INTO statement fails to return a row, PL/SQL raises the predefined exception NO_DATA_FOUND immediately, interrupting the flow of control before you can check %NOTFOUND. A SELECT INTO statement that invokes a SQL aggregate function always returns a value or a null. After such a statement, the %NOTFOUND attribute is always FALSE, so checking it is unnecessary. Explicit Cursors When you need precise control over query processing, you can explicitly declare a cursor in the declarative part of any PL/SQL block, subprogram, or package. You use three statements to control a cursor: OPEN, FETCH, and CLOSE. First, you initialize the cursor with the OPEN statement, which identifies the result set. Then, you can execute FETCH repeatedly until all rows have been retrieved, or you can use the BULK COLLECT clause to fetch all rows at once. When the last row has been processed, you release the cursor with the CLOSE statement. This technique requires more code than other techniques such as the SQL cursor FOR loop. Its advantage is flexibility. You can: Process several queries in parallel by declaring and opening multiple cursors. Process multiple rows in a single loop iteration, skip rows, or split the processing into more than one loop. Topics: Declaring a Cursor Opening a Cursor Fetching with a Cursor Fetching Bulk Data with a Cursor Closing a Cursor Attributes of Explicit Cursors Declaring a Cursor You must declare a cursor before referencing it in other statements. You give the cursor a name and associate it with a specific query. You can optionally declare a return type for the cursor, such as table_name%ROWTYPE. You can optionally specify parameters that you use in the WHERE clause instead of referring to local variables. These parameters can have default values. Example 69 shows how you can declare cursors. Example 69 Declaring a Cursor DECLARE my_emp_id NUMBER(6); -- variable for employee_id my_job_id VARCHAR2(10); -- variable for job_id my_sal NUMBER(8,2); -- variable for salary CURSOR c1 IS SELECT employee_id, job_id, salary FROM employees WHERE salary > 2000; my_dept departments%ROWTYPE; -- variable for departments row CURSOR c2 RETURN departments%ROWTYPE IS SELECT * FROM departments WHERE department_id = 110; The cursor is not a PL/SQL variable: you cannot assign a value to a cursor or use it in an expression. Cursors and variables follow the same scoping rules. Naming cursors after database tables is possible but not recommended. A cursor can take parameters, which can appear in the associated query wherever constants can appear. The formal parameters of a cursor must be IN parameters; they supply values in the query, but do not return any values from the query. You cannot impose the constraint NOT NULL on a cursor parameter. As the following example shows, you can initialize cursor parameters to default values. You can pass different numbers of actual parameters to a cursor, accepting or
Cursor
statement. Before the database opens the SQL cursor, its attributes yield NULL. The SQL cursor has another attribute, %BULK_ROWCOUNT, designed for use with the FORALL statement. For more information, see Counting Rows Affected by FORALL (%BULK_ROWCOUNT Attribute) on page 12-14. Topics: overriding the default values as you please. Also, you can add new formal parameters Page7 without having to change existing references to the cursor. DECLARE CURSOR c1 (low NUMBER DEFAULT 0, high NUMBER DEFAULT 99) IS SELECT * FROM departments WHERE department_id > low AND department_id < high; Cursor parameters can be referenced only within the query specified in the cursor declaration. The parameter values are used by the associated query when the cursor is opened. Opening a Cursor Opening the cursor executes the query and identifies the result set, which consists of all rows that meet the query search criteria. For cursors declared using the FOR UPDATE clause, the OPEN statement also locks those rows. An example of the OPEN statement follows: DECLARE CURSOR c1 IS SELECT employee_id, last_name, job_id, salary FROM employees WHERE salary > 2000; BEGIN OPEN c1; Rows in the result set are retrieved by the FETCH statement, not when the OPEN statement is executed. Fetching with a Cursor Unless you use the BULK COLLECT clause, explained in Fetching with a Cursor on page 6-11, the FETCH statement retrieves the rows in the result set one at a time. Each fetch retrieves the current row and advances the cursor to the next row in the result set. You can store each column in a separate variable, or store the entire row in a record that has the appropriate fields, usually declared using %ROWTYPE. For each column value returned by the query associated with the cursor, there must be a corresponding, type-compatible variable in the INTO list. Typically, you use the FETCH statement with a LOOP and EXIT WHEN NOTFOUND statements, as shown in Example 610. Note the use of built-in regular expression functions in the queries. Example 610 Fetching with a Cursor DECLARE v_jobid employees.job_id%TYPE; -- variable for job_id v_lastname employees.last_name%TYPE; -- variable for last_name CURSOR c1 IS SELECT last_name, job_id FROM employees WHERE REGEXP_LIKE (job_id, 'S[HT]_CLERK'); v_employees employees%ROWTYPE; -- record variable for row CURSOR c2 is SELECT * FROM employees WHERE REGEXP_LIKE (job_id, '[ACADFIMKSA]_M[ANGR]'); BEGIN OPEN c1; -- open the cursor before fetching LOOP -- Fetches 2 columns into variables FETCH c1 INTO v_lastname, v_jobid; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE( RPAD(v_lastname, 25, ' ') || v_jobid ); END LOOP; CLOSE c1; DBMS_OUTPUT.PUT_LINE( '-------------------------------------' ); OPEN c2; LOOP -- Fetches entire row into the v_employees record FETCH c2 INTO v_employees; EXIT WHEN c2%NOTFOUND; DBMS_OUTPUT.PUT_LINE( RPAD(v_employees.last_name, 25, ' ') || v_employees.job_id ); END LOOP; CLOSE c2; END; / The query can reference PL/SQL variables within its scope. Any variables in the query are evaluated only when the cursor is opened. In Example 611, each retrieved salary is multiplied by 2, even though factor is incremented after every fetch. Example 611 Referencing PL/SQL Variables Within Its Scope DECLARE my_sal employees.salary%TYPE; my_job employees.job_id%TYPE; factor INTEGER := 2; CURSOR c1 IS SELECT factor*salary FROM employees WHERE job_id = my_job; BEGIN OPEN c1; -- factor initially equals 2 LOOP FETCH c1 INTO my_sal; EXIT WHEN c1%NOTFOUND; factor := factor + 1; -- does not affect FETCH END LOOP; CLOSE c1; END; / To change the result set or the values of variables in the query, you must close and reopen the cursor with the input variables set to their new values. However, you can use a different INTO list on separate fetches with the same cursor. Each fetch retrieves another row and assigns values to the target variables, as shown inExample 612. Example 612 Fetching the Same Cursor Into Different Variables DECLARE CURSOR c1 IS SELECT last_name FROM employees ORDER BY last_name; name1 employees.last_name%TYPE; name2 employees.last_name%TYPE; name3 employees.last_name%TYPE; BEGIN OPEN c1; FETCH c1 INTO name1; -- this fetches first row FETCH c1 INTO name2; -- this fetches second row FETCH c1 INTO name3; -- this fetches third row CLOSE c1; END; / If you fetch past the last row in the result set, the values of the target variables are TYPE NameTab IS TABLE OF employees.last_name%TYPE; Page8 ids IdsTab; names NameTab; CURSOR c1 IS SELECT employee_id, last_name; FROM employees WHERE job_id = 'ST_CLERK'; BEGIN OPEN c1; FETCH c1 BULK COLLECT INTO ids, names; CLOsE c1; -- Here is where you process the elements in the collections FOR i IN ids.FIRST .. ids.LAST LOOP IF ids(i) > 140 THEN DBMS_OUTPUT.PUT_LINE( ids(i) ); END IF; END LOOP; FOR i IN names.FIRST .. names.LAST LOOP IF names(i) LIKE '%Ma%' THEN DBMS_OUTPUT.PUT_LINE( names(i) ); END IF; END LOOP; END; / Closing a Cursor The CLOSE statement disables the cursor, and the result set becomes undefined. Once a cursor is closed, you can reopen it, which runs the query again with the latest values of any cursor parameters and variables referenced in the WHERE clause. Any other operation on a closed cursor raises the predefined exception INVALID_CURSOR. Attributes of Explicit Cursors Every explicit cursor and cursor variable has four attributes: %FOUND, %ISOPEN %NOTFOUND, and %ROWCOUNT. When appended to the cursor or cursor variable name, these attributes return useful information about the execution of a SQL statement. You can use cursor attributes in procedural statements but not in SQL statements. Explicit cursor attributes return information about the execution of a multiple-row query. When an explicit cursor or a cursor variable is opened, the rows that satisfy the associated query are identified and form the result set. Rows are fetched from the result set. Topics: %FOUND Attribute: Has a Row Been Fetched? %ISOPEN Attribute: Is the Cursor Open? %NOTFOUND Attribute: Has a Fetch Failed? %ROWCOUNT Attribute: How Many Rows Fetched So Far? %FOUND Attribute: Has a Row Been Fetched? After a cursor or cursor variable is opened but before the first fetch, %FOUND returns NULL. After any fetches, it returns TRUE if the last fetch returned a row, or FALSE if the last fetch did not return a row. Example 614 uses %FOUND to select an action. Example 614 Using %FOUND DECLARE CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11; my_ename employees.last_name%TYPE; my_salary employees.salary%TYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO my_ename, my_salary; IF c1%FOUND THEN -- fetch succeeded DBMS_OUTPUT.PUT_LINE('Name = ' || my_ename || ', salary = ' || my_salary); ELSE -- fetch failed, so exit loop EXIT; END IF; END LOOP; END; / If a cursor or cursor variable is not open, referencing it with %FOUND raises the predefined exception INVALID_CURSOR. %ISOPEN Attribute: Is the Cursor Open? %ISOPEN returns TRUE if its cursor or cursor variable is open; otherwise, %ISOPEN returns FALSE. Example 615 uses %ISOPEN to select an action. Example 615 Using %ISOPEN DECLARE CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11; the_name employees.last_name%TYPE; the_salary employees.salary%TYPE; BEGIN IF c1%ISOPEN = FALSE THEN -- cursor was not already open OPEN c1; END IF; FETCH c1 INTO the_name, the_salary; CLOSE c1; END; / %NOTFOUND Attribute: Has a Fetch Failed? %NOTFOUND is the logical opposite of %FOUND. %NOTFOUND yields FALSE if the last fetch returned a row, or TRUE if the last fetch failed to return a row. In Example 616, you use %NOTFOUND to exit a loop when FETCH fails to return a row. Example 616 Using %NOTFOUND DECLARE CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11; my_ename employees.last_name%TYPE; my_salary employees.salary%TYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO my_ename, my_salary; IF c1%NOTFOUND THEN -- fetch failed, so exit loop -- Another form of this test is -- "EXIT WHEN c1%NOTFOUND OR c1%NOTFOUND IS NULL;" EXIT; ELSE -- fetch succeeded DBMS_OUTPUT.PUT_LINE ('Name = ' || my_ename || ', salary = ' || my_salary); END IF; END LOOP; END; / Before the first fetch, %NOTFOUND returns NULL. If FETCH never executes successfully,
Cursor
undefined. Eventually, the FETCH statement fails to return a row. When that happens, no exception is raised. To detect the failure, use the cursor attribute %FOUND or %NOTFOUND. For more information, see Using Cursor Expressions on page 6-31. Fetching Bulk Data with a Cursor The BULK COLLECT clause lets you fetch all rows from the result set at once. See Retrieving Query Results into Collections (BULK COLLECT Clause) on page 12-17. In Example 613, you bulk-fetch from a cursor into two collections. Example 613 Fetching Bulk Data with a Cursor DECLARE the loop is never exited, because the EXIT WHEN statement executes only if its WHEN condition is true. To be safe, you might want to use the following EXIT statement Page9 instead: EXIT WHEN c1%NOTFOUND OR c1%NOTFOUND IS NULL; If a cursor or cursor variable is not open, referencing it with %NOTFOUND raises an INVALID_CURSOR exception. %ROWCOUNT Attribute: How Many Rows Fetched So Far? When its cursor or cursor variable is opened, %ROWCOUNT is zeroed. Before the first fetch, %ROWCOUNT yields zero. Thereafter, it yields the number of rows fetched so far. The number is incremented if the last fetch returned a row. Example 617 uses %ROWCOUNT to test if more than ten rows were fetched. Example 617 Using %ROWCOUNT DECLARE CURSOR c1 IS SELECT last_name FROM employees WHERE ROWNUM < 11; name employees.last_name%TYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO name; EXIT WHEN c1%NOTFOUND OR c1%NOTFOUND IS NULL; DBMS_OUTPUT.PUT_LINE(c1%ROWCOUNT || '. ' || name); IF c1%ROWCOUNT = 5 THEN DBMS_OUTPUT.PUT_LINE('--- Fetched 5th record ---'); END IF; END LOOP; CLOSE c1; END; / If a cursor or cursor variable is not open, referencing it with %ROWCOUNT raises INVALID_CURSOR. Table 61 shows the value of each cursor attribute before and after OPEN, FETCH, and CLOSE statements execute. DBMS_OUTPUT.PUT_LINE ('Name = ' || item.last_name || ', Job = ' || item.job_id); END LOOP; END; / Parameterized Cursor Instead of referring to local variables, you can declare a cursor that accepts parameters, and pass values for those parameters when you open the cursor. If the query is usually issued with certain values, you can make those values the defaults. You can use either positional notation or named notation to pass the parameter values. Example 622 displays the wages paid to employees earning over a specified wage in a specified department. Example 622 Passing Parameters to a Cursor FOR Loop DECLARE CURSOR c1 (job VARCHAR2, max_wage NUMBER) IS SELECT * FROM employees WHERE job_id = job AND salary > max_wage; BEGIN FOR person IN c1('CLERK', 3000) LOOP -- process data record DBMS_OUTPUT.PUT_LINE ('Name = ' || person.last_name || ', salary = ' || person.salary || ', Job Id = ' || person.job_id ); END LOOP; END; / In Example 623, several ways are shown to open a cursor. Example 623 Passing Parameters to Explicit Cursors DECLARE emp_job employees.job_id%TYPE := 'ST_CLERK'; emp_salary employees.salary%TYPE := 3000; my_record employees%ROWTYPE; CURSOR c1 (job VARCHAR2, max_wage NUMBER) IS SELECT * FROM employees WHERE job_id = job AND salary > max_wage; BEGIN -- Any of the following statements opens the cursor: -- OPEN c1('ST_CLERK', 3000); OPEN c1('ST_CLERK', emp_salary); -- OPEN c1(emp_job, 3000); OPEN c1(emp_job, emp_salary); OPEN c1(emp_job, emp_salary); LOOP FETCH c1 INTO my_record; EXIT WHEN c1%NOTFOUND; -- process data record DBMS_OUTPUT.PUT_LINE ('Name = ' || my_record.last_name || ', salary = ' || my_record.salary || ', Job Id = ' || my_record.job_id ); END LOOP; END; / To avoid confusion, use different names for cursor parameters and the PL/SQL variables that you pass into those parameters. A formal parameter declared with a default value does not need a corresponding actual parameter. If you omit the actual parameter, the formal parameter assumes its default value when the OPEN statement executes. If the default value of a formal parameter is an expression, and you provide a corresponding actual parameter in the OPEN statement, the expression is not evaluated. Page 9
In Table 61: Referencing %FOUND, %NOTFOUND, or %ROWCOUNT before a cursor is opened or after it is closed raises INVALID_CURSOR. After the first FETCH, if the result set was empty, %FOUND yields FALSE, %NOTFOUND yields TRUE, and %ROWCOUNT yields 0. Cursor FOR LOOP Topics: SQL Cursor FOR LOOP Explicit Cursor FOR LOOP SQL Cursor FOR LOOP With PL/SQL, it is very simple to issue a query, retrieve each row of the result into a %ROWTYPE record, and process each row in a loop: You include the text of the query directly in the FOR loop. PL/SQL creates a record variable with fields corresponding to the columns of the result set. You refer to the fields of this record variable inside the loop. You can perform tests and calculations, display output, or store the results somewhere else. Here is an example that you can run in SQL*Plus. It does a query to get the name and job Id of employees with manager Ids greater than 120. BEGIN FOR item IN ( SELECT last_name, job_id FROM employees WHERE job_id LIKE '%CLERK%' AND manager_id > 120 ) LOOP DBMS_OUTPUT.PUT_LINE ('Name = ' || item.last_name || ', Job = ' || item.job_id); END LOOP; END; / Before each iteration of the FOR loop, PL/SQL fetches into the implicitly declared record. The sequence of statements inside the loop is executed once for each row that satisfies the query. When you leave the loop, the cursor is closed automatically. The cursor is closed even if you use an EXIT or GOTO statement to leave the loop before all rows are fetched, or an exception is raised inside the loop. See LOOP Statements on page 13-79. Explicit Cursor FOR LOOP If you must reference the same query from different parts of the same subprogram, you can declare a cursor that specifies the query, and process the results using a FOR loop. DECLARE CURSOR c1 IS SELECT last_name, job_id FROM employees WHERE job_id LIKE '%CLERK%' AND manager_id > 120; BEGIN FOR item IN c1 LOOP
QUERY OPTiMIZATION
n Alternative ways of evaluating a given query Equivalent expressions Different algorithms for each operation (Chapter 13) Cost difference between a good and a bad way of evaluating a query can be enormous Example: performing a r X s followed by a selection r.A = s.B is much slower than performing a join on the same condition n Need to estimate the cost of operations Depends critically on statistical information about relations which the database must maintain E.g. number of tuples, number of distinct values for join attributes, etc. Need to estimate statistics for intermediate results to compute cost of complex expressions n
Relations generated by two equivalent expressions have the same set of attributes and contain the same set of tuples, although their attributes may be ordered differently
Generation of query-evaluation plans for an expression involves several steps: 1. Generating logically equivalent expressions Use equivalence rules to transform an expression into an equivalent one. 2. Annotating resultant expressions to get alternative query plans 3. Choosing the cheapest plan based on estimated cost n The overall process is called cost based optimization.
Cursor
DBMS_OUTPUT.PUT_LINE ('Name = ' || item.last_name || ', Job = ' || item.job_id); END LOOP; END; /