Август 2005


Профессионалу разработчику


Владимир Пржиялковский,
преподаватель технологий Oracle
prz@yandex.ru,
www.ccas.ru/prz/

Два критерия профессионализма программирования Oracle:
(2) употребление ссылки на курсор в запросе SQL

... Родила царица в ночь
Не то сына, не то дочь;
Не мышонка, не лягушку,
А неведому зверюшку.

А. С. Пушкин, “Сказка о царе Салтане”

Аннотация

Рассматриваются возможности использования ссылок на курсор в предложения SQL в Oracle.

Ссылки на курсор в предложении SQL

Ссылки на курсор могут использоваться не только в программе, но и в предложении SQL. Это обеспечивается указанием во фразе SELECT предложения SELECT выражения CURSOR для формирования столбца результата. Если мы попытаемся сделать это в SQL*Plus, результат окажется довольно неожиданный:

SQL> SELECT
  2    dname
  3  , CURSOR ( SELECT ename FROM emp WHERE emp.deptno = dept.deptno )
  4  FROM dept;

DNAME          CURSOR(SELECTENAMEFR
-------------- --------------------
ACCOUNTING     CURSOR STATEMENT : 2

CURSOR STATEMENT : 2

ENAME
----------
CLARK
KING
MILLER

RESEARCH       CURSOR STATEMENT : 2

CURSOR STATEMENT : 2

ENAME
----------
SMITH
JONES
SCOTT
ADAMS
FORD

SALES          CURSOR STATEMENT : 2

... ... ... ...

На деле произошло вот что: предложение SELECT … FROM dept вернуло во втором столбце результата каждой своей строки ссылку на курсор, сформированный динамически предложением внутри CURSOR ( … ). Программа SQL*Plus проинтерпретировала эту ссылку, перебрав строки, словно как мы это делали в примерах предыдущей статьи, и выдала строки на экран. В результате мы увидели на экране перечень четырех строк-отделов, и для каждого отдела – перечень сотрудников.

Форма выдачи занятная, но, кажется, практически бесполезная. Так зачем же нужно выражение CURSOR в предложении SELECT ? Чтобы ответить на этот вопрос, снова вернемся к программированию.

Программная обработка

Рассмотрим следующий пример блока PL/SQL, где используется (а) обычный курсор для SELECT и (б) курсорное выражение внутри:

SET SERVEROUTPUT ON

DECLARE 
CURSOR main_cur IS 
SELECT
  dname
, CURSOR ( SELECT ename FROM emp WHERE emp.deptno = dept.deptno )
FROM dept
;
deptname dept.dname%TYPE;
empname emp.ename%TYPE;
inner_cur SYS_REFCURSOR;

BEGIN
OPEN main_cur;
LOOP 
   FETCH main_cur INTO deptname, inner_cur;
   EXIT WHEN main_cur%NOTFOUND;
   dbms_output.put_line ( '-----------------' || deptname );
   LOOP 
      FETCH inner_cur INTO empname;
      EXIT WHEN inner_cur%NOTFOUND;
      dbms_output.put_line( empname );
   END LOOP;
   CLOSE inner_cur;
END LOOP;
CLOSE main_cur;
END;
/

В результате такой организации запроса мы не просто выдали список отделов и их сотрудников, но смогли обработать каждый раз возникающий перечень сотрудников программно. Если бы не было выражения CURSOR, такого рода программную обработку можно было бы организовать только заведя в программе два курсора, а не один (а при использовании в одном предложении SELECT сразу нескольких курсорных выражений, возможно вложенных, – более чем два).

Более того, этот один программный курсор с выражениями CURSOR просто и точно указывает на логику обработки предложения SELECT (то есть играет документирующую роль), в то время как определение в программе нескольких курсоров без курсорных выражений ничего не говорит о логике их совместной обработки, так что понять последнюю мы сможем только по процедурным конструкциям, а давно известно, что понять, что делает программа, по процедурным конструкциям гораздо труднее, чем по описаням структур.

Более того, при использовании курсорного выражения мы избавлены от необходимости его открывать, сразу приступая к операциям FETCH, что быстрее в исполнении и экономнее в тексте.

Резюме: использование ссылки на курсор в предложении SELECT упрощает код программной обработки, улучшает читаемость кода и повышает эффективность его исполнения.

Применение в XSQL

В некоторых случаях использование курсорного выражения в SELECT дает выгоду и вне программы.

Средства XSQL, поставляемые в рамках штатной поставки ПО СУБД Oracle, позволяют хранить на сервере web сервлет, порождающий по запросу SELECT к базе данных документ XML. Этот сервлет способен обрабатывать ссылки на курсор, порождаемые курсорным выражением.

Подготовим файл departments.xsql:

<?xml version="1.0"?>
<xsql:query connection="demo" xmlns:xsql="urn:oracle-xsql">
SELECT
  dname
, CURSOR ( SELECT ename FROM emp WHERE emp.deptno = dept.deptno )
  AS employees
FROM dept
</xsql:query>

Создадим каталог company в месте файловой системы, предназначенном для расположения файлов формата XSQL. Например, при установленном расширении XDK ПО Oracle версии 9.2 этим местом может быть $ORACLE_HOME/xdk/demo/java. Обратимся браузером по адресу http://localhost:7778/xsql/company/departments.xsql (сервер HTTP при этом должен быть запущен). Получим примерно такой результат:

В этом изображении для краткости узлы ROW с атрибутами num = "2" и num = "3" свернуты средствами браузера.

Полученный документ XML может играть для сервера приложений в web как самостоятельную роль (выдавая результат обращений по определенному адресу в web в формате XML), так и роль исходного материала для дальнейшего преобразования XSL (выдавая результат в конечном итоге в формате HTML, в том числе с графическим оформлением). Эти преобразования здесь не рассматриваются.

Замечательно, что обрабатывающий запросы XSQL сервлет, разработанный фирмой Oracle, способен обрабатывать не только одиночные, но и вложенные выражения типа CURSOR. Подготовим еще один файл multicursor.xsql и положим его в тот же каталог company:

<?xml version="1.0"?>
<xsql:query connection="demo" xmlns:xsql="urn:oracle-xsql">
SELECT
  TO_CHAR ( SYSTIMESTAMP,'SS:FF3' )
  AS seconds
, CURSOR ( SELECT TO_CHAR ( SYSTIMESTAMP,'SS:FF3' ) secondsone 
                , CURSOR ( SELECT TO_CHAR( SYSTIMESTAMP,'SS:FF3' )
                           AS secondstwo
                           FROM DUAL )
                  AS leveltwo
           FROM DUAL ) 
  AS levelone
FROM dept
</xsql:query>

Таблица DEPT здесь взята только ради примера из нескольких строк.

Обратимся браузером по адресу http://localhost:7778/xsql/company/multicursor.xsql и увидим примерно такой результат:

Как и в первом случае, некоторые узлы документа свернуты средствами браузера ради экономии места в статье, занимаемого картинкой.

Обращение к SYSTIMESTAMP позволяет наблюдать процесс вычисления ответа сервлетом Oracle.

E-mail this page