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

15.2.3 Generic group by Procedure

How many times have you written a query along these lines:

SELECT some-columns, COUNT(*)

FROM your-table

GROUP BY some-columns;

And then there is the variation involving the HAVING clause (you don't want to see all the counts; you just want to see those groupings where there is more than one identical value, and so on). These are very common requirements, but with NDS, you can easily build a program that does all the work for you, for any table, and for any single column (and this is extensible to multiple columns as well).

Here is the header of such a procedure:

/* File on web: countby.sp */

PROCEDURE countBy (

tab IN VARCHAR2,

col IN VARCHAR2,

atleast IN INTEGER := NULL,

sch IN VARCHAR2 := NULL,

maxlen IN INTEGER := 30)

where:

tab

Is the name of the table.

col

Is the name of the column.

sch

Is the name of the schema (default of NULL = USER).

atleast

If you supply a non-NULL value for atleast, then the SELECT statement includes a HAVING COUNT(*) greater than that value.

maxlen

Is used for formatting of the output.

You can look at the countby.sp file available on the O'Reilly site to see the full implementation; here is all the code except that for the formatting (header string and so on):

IS

TYPE cv_type IS REF CURSOR;

cv cv_type;

SQL_string VARCHAR2(2000) :=

'SELECT ' || col || ', COUNT(*)

FROM ' || NVL (sch, USER) || '.' || tab ||

' GROUP BY ' || col;

v_val VARCHAR2(32767);

v_count INTEGER;

BEGIN

IF atleast IS NOT NULL

THEN

SQL_string := SQL_string || ' HAVING COUNT(*) >= ' || atleast;

END IF;

OPEN cv FOR SQL_String;

LOOP

FETCH cv INTO v_val, v_count;

EXIT WHEN cv%NOTFOUND;

DBMS_OUTPUT.PUT_LINE (RPAD (v_val, maxlen) || ' ' || v_count);

END LOOP;

CLOSE cv;

END;

As you start to build more and more of these generic utilities, you will find that it doesn't take very much code or effort—you just have to think through the steps of the SQL string construction carefully.

15.2.4 Generic group by Package

Simply displaying information is useful for test purposes, but in many cases you want to work with the queried information further. Let's build on the countby procedure shown in the previous section to provide an implementation in which the results of the dynamic query are stored in an associative array for subsequent analysis.

Here is the specification of the package:

/* File on web: countby.pkg */

CREATE OR REPLACE PACKAGE grp

IS

TYPE results_rt IS RECORD (

val VARCHAR2(4000),

countby INTEGER);

TYPE results_tt IS TABLE OF results_rt

INDEX BY BINARY_INTEGER;

FUNCTION countBy (

tab IN VARCHAR2,

col IN VARCHAR2,

atleast IN INTEGER := NULL,

sch IN VARCHAR2 := NULL,

maxlen IN INTEGER := 30)

RETURN results_tt;

END grp;

The implementation of the countby function is virtually the same as the procedure. The main difference is that I now have a record structure to fetch into and an associative array to fill. You can see both of these changes in the loop that fetches the rows:

LOOP

FETCH cv INTO rec;

EXIT WHEN cv%NOTFOUND;

retval(cv%ROWCOUNT) := rec;

END LOOP;

With this package in place, I can very easily build programs that access this analytical information. Here is one example:

/* File on web: countby.tst */

DECLARE

results grp.results_tt;

indx PLS_INTEGER;

minrow PLS_INTEGER;

maxrow PLS_INTEGER;

BEGIN

results := grp.countby ('employee', 'department_id');

/* Find min and max counts. */

indx := results.FIRST;

LOOP

EXIT WHEN indx IS NULL;

IF minrow IS NULL OR

minrow > results(indx).countby

THEN

minrow := indx;

END IF;

IF maxrow IS NULL OR

maxrow < results(indx).countby

THEN

maxrow := indx;

END IF;

/* Perform other processing as well... */

/* Move to next group count. */

indx := results.NEXT(indx);

END LOOP;

END;

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