Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming PL SQL.doc
Скачиваний:
2
Добавлен:
01.07.2025
Размер:
5.06 Mб
Скачать

14.4.1 Limiting Rows Retrieved with bulk collect

Oracle provides a LIMIT clause for BULK COLLECT that allows you to limit the number of rows fetched from the database. The syntax is:

FETCH ... BULK COLLECT INTO ... [LIMIT rows];

where rows can be any literal, variable, or expression that evaluates to a NUMBER (otherwise, Oracle will raise a VALUE_ERROR exception).

Let's take a look at an example. Suppose that my transportation table has a very large number of rows of vehicles that qualify as major polluters. I don't want to retrieve more than 5,000 rows. I can then add the LIMIT clause:

DECLARE

CURSOR major_polluters IS

SELECT name

FROM transportation

WHERE TYPE = 'AUTOMOBILE' AND mileage < 20;

names name_varray;

BEGIN

OPEN major_polluters;

FETCH major_polluters

BULK COLLECT INTO names

LIMIT 5000;

...

END;

14.4.2 Bulk Fetching of Multiple Columns

As you have seen in previous examples, you certainly can bulk fetch the contents of more than one column. It would be most elegant if we could fetch those multiple columns into a single collection of records. This feature became available in Oracle9i Release 2.

Suppose that I would like to retrieve all the information in my transportation table for each vehicle whose mileage is less than 20 miles per gallon. In Oracle9i Release 2 and above, I can do so with a minimum of coding fuss:

DECLARE

-- Declare the type of collection

TYPE VehTab IS TABLE OF transportation%ROWTYPE;

-- Instantiate a particular collection from the TYPE.

gas_guzzlers VehTab;

BEGIN

SELECT *

BULK COLLECT INTO gas_guzzlers

FROM transportation

WHERE mileage < 20;

...

Prior to Oracle9i Release 2, the above code would raise this exception:

PLS-00597: expression 'GAS_GUZZLERS' in the INTO list is of wrong type

If you are running Oracle8i or Oracle9i Release 1, you will need to declare multiple collections and then fetch individually into those collections:

DECLARE

guzzler_type name_varray;

guzzler_name name_varray;

guzzler_mileage number_varray;

CURSOR low_mileage_cur IS

SELECT vehicle_type, name, mileage

FROM transportation WHERE mileage < 10;

BEGIN

OPEN low_mileage_cur;

FETCH low_mileage_cur BULK COLLECT

INTO guzzler_type, guzzler_name, guzzler_mileage;

END;

Note that you can use the LIMIT clause with a BULK COLLECT into a collection of records, just as you would with any other BULK COLLECT statement.

14.4.3 Using the returning Clause with Bulk Operations

You've now seen BULK COLLECT put to use for both implicit and explicit query cursors. You can also use BULK COLLECT inside a FORALL statement, in order to take advantage of the RETURNING clause.

The RETURNING clause, introduced in Oracle8, allows you to obtain information (such as a newly updated value for a salary) from a DML statement. RETURNING can help you avoid additional queries to the database to determine the results of DML operations that just completed.

Suppose that Congress has passed a law requiring that a company pay its highest-compensated employee no more than 50 times the salary of its lowest-paid employee. I work in the IT department of the newly merged company Northrop-Ford-Mattel-Yahoo-ATT, which employs a total of 250,000 workers. The word has come down from on high: the CEO is not taking a pay cut, so we need to increase the salaries of everyone who makes less than 50 times his 2004 total compensation package of $145 million—and decrease the salaries of all upper management except for the CEO. After all, somebody's got to make up for this loss in profit.

Wow! I have lots of updating to do, and I want to use FORALL to get the job done as quickly as possible. However, I also need to perform various kinds of processing on the employee data and then print a report showing the change in salary for each affected employee. That RETURNING clause would come in awfully handy here, so let's give it a try.

See the onlyfair.sql file on the O'Reilly site for all of the steps shown here, plus table creation and INSERT statements.

First, I'll create a reusable function to return the compensation for an executive:

/* File on web: onlyfair.sql */

FUNCTION salforexec (title_in IN VARCHAR2) RETURN NUMBER

IS

CURSOR ceo_compensation IS

SELECT salary + bonus + stock_options +

mercedes_benz_allowance + yacht_allowance

FROM compensation

WHERE title = title_in;

big_bucks NUMBER;

BEGIN

OPEN ceo_compensation;

FETCH ceo_compensation INTO big_bucks;

RETURN big_bucks;

END;

In the main block of the update program, I declare a number of local variables and the following query to identify underpaid employees and overpaid employees who are not lucky enough to be the CEO:

DECLARE

big_bucks NUMBER := salforexec ('CEO');

min_sal NUMBER := big_bucks / 50;

names name_varray;

old_salaries number_varray;

new_salaries number_varray;

CURSOR affected_employees (ceosal IN NUMBER)

IS

SELECT name, salary + bonus old_salary

FROM compensation

WHERE title != 'CEO'

AND ((salary + bonus < ceosal / 50)

OR (salary + bonus > ceosal / 10)) ;

At the start of my executable section, I load all of this data into my collections with a BULK COLLECT query:

OPEN affected_employees (big_bucks);

FETCH affected_employees

BULK COLLECT INTO names, old_salaries;

Then I can use the names collection in my FORALL update:

FORALL indx IN names.FIRST .. names.LAST

UPDATE compensation

SET salary =

GREATEST(

DECODE (

GREATEST (min_sal, salary),

min_sal, min_sal,

salary / 5),

min_sal )

WHERE name = names (indx)

RETURNING salary BULK COLLECT INTO new_salaries;

I use DECODE to give an employee either a major boost in yearly income or an 80% cut in pay to keep the CEO comfy. I end it with a RETURNING clause that relies on BULK COLLECT to populate a third collection: the new salaries.

Finally, because I used RETURNING and don't have to write another query against the compensation table to obtain the new salaries, I can immediately move to report generation:

FOR indx IN names.FIRST .. names.LAST

LOOP

DBMS_OUTPUT.PUT_LINE (

RPAD (names(indx), 20) ||

RPAD (' Old: ' || old_salaries(indx), 15) ||

' New: ' || new_salaries(indx)

);

END LOOP;

Here, then, is the report generated from the onlyfair.sql script:

John DayAndNight Old: 10500 New: 2900000

Holly Cubicle Old: 52000 New: 2900000

Sandra Watchthebucks Old: 22000000 New: 4000000

Now everyone can afford quality housing and health care. And tax revenue at all levels will increase, so public schools can get the funding they need.

The RETURNING column values or expressions returned by each execution in FORALL are added to the collection after the values returned previously. If you use RETURNING inside a non-bulk FOR loop, previous values are overwritten by the latest DML execution.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]