|
|
|||||||||||||||||||||||||||||
|
(Dynamic SQL Comes to PL/SQL. Using the DBMS_SQL Package, by Steve Bobrowski. ORACLE MAGAZINE, vol.IX/num.2)Источник: gaz Стив Бобровски
Я хотел бы посвятить эту статью памяти Роберта Куи (Robert Kooi), моего друга и блестящего разработчика, внесшего значительный вклад в развитие сервера Oracle7, особенно в части PL/SQL. Администраторы и разработчики часто пренебрегают теми преимуществами, которые можно получить от применения некоторых выдающихся, но скрытых драгоценных возможностей, которыми обладает сервер Oracle7. Я прежде всего имею в виду пакеты утилит DBMS, которые позволяют разработчикам приложений применять расширенные средства сервера базы данных, такие как сигналы (alerts), коммуникационные каналы (communication pipes) и управляемые сервером блокировки ресурсов (server-managed resource locking). В Oracle7 версии 7.1 имеется поистине фантастическое добавление к семейству пакетов DBMS: новый пакет DBMS_SQL обеспечивает разработчиков инструментарием для создания динамически формируемых предложений SQL в программах на PL/SQL. Статические и динамические предложения SQL Многие из современных приложений Oracle на компьютерах-клиентах содержат только статические SQL-предложения. По этому сценарию разработчик конструирует некое приложение посредством планирования возможных транзакций и последующего включения в приложение соответствующих SQL-предложений (*). Следовательно, приложение ограничено некоторым количеством четко определенных обращений к базе данных, которые фактически компилируются как часть приложения. (*) [Примечание автора: Когда разрабатывается сложное производственное приложение для работы в режиме клиент/сервер, обычно для этого приложения кодируется много SQL-предложений в хранимых процедурах, которые затем реагируют на вызовы приложения, чтобы обеспечить работу процедур с базой данных. Однако, поскольку скомпилированные приложения обладают лишь статическими вызовами процедур, а SQL-предложения в хранимых в базе данных процедурах также уже скомпилированы, то общее положение о том, что все такие приложения являются статическими, остается в силе.] Этот метод использования статических SQL-предложений может быть хорош или плох в зависимости от того, что Вы хотите от приложения. Для примера, механизм статических SQL-предложений безупречен, когда Вы хотите сконструировать приложение, которое жестко контролирует, что пользователи могут или не могут делать. Поскольку Вы встраиваете все возможные SQL-предложения в это приложение, пользователи не могут сбиться с предназначенного им пути. Но когда Вы хотите разработать приложение, которое обладало бы гибкостью, обеспечивающей управление непредусмотренными транзакциями, SQL-предложения или даже наборы SQL-предложений (имея в виду статические SQL-предложения и их характерологические особенности), не могут помочь Вам. Таким образом, возникает потребность в динамически формируемых SQL-предложениях. Используя метод динамически формируемых SQL-предложений, приложения строят их во время исполнения (runtine), и в этом смысле Вы можете создавать приложения, которые изменяются по выбору, определяемому пользователем. В качестве общедоступного интерфейса к динамически формируемым SQL-предложениям можно представить себе, например, инструментарий типа SQL*Plus, при помощи которого пользователи могут работать с SQL-предложениями любого типа. Введение в динамически формируемые SQL-предложения Основным положением в понимании использования динамических SQL-предложений является то, что если некоторое SQL-предложение является динамически формируемым, то программа должна построить правильное SQL-предложение, выполняя последовательность определенных шагов, формирующих это предложение; выполнить его, а в случае, если это SQL-предложение представляет собой запрос к базе данных [запросное и незапросные предложения - см. таблицу 1. Прим. переводчика], а также определить его колонки и выборку строк возвращаемого набора. Тип выборки и число шагов, требуемое для выполнения динамических SQL-предложений, изменяется в зависимости от типа SQL-предложения, что и вынуждает программу определенным образом определять и выполнять эти предложения. Различные возможности выделили четыре формальных метода формирования динамических SQL-предложений. Таблица 1 кратко излагает некоторые особенности каждого метода. В последней колонке таблицы 1 приводятся последовательности списков вызовов подпрограмм пакета DBMS_SQL, которые программа на PL/SQL должна выполнить, чтобы реализовать динамически формируемые SQL-предложения каждого типа. Если Вы еще не очень свободно владеете механизмом формирования динамических SQL-предложений и хотели бы получить больше информации, рекомендую обратиться к главе о динамических SQL-предложениях в Руководстве "Programmer`s Guide to the Oracle Precompilers" (Руководство программиста по прекомпиляторам Oracle). Не дублируя сведений, которые можно получить из документации, эта статья фокусирует внимание читателей на использовании пакета DBMS_SQL, чтобы показать возможность применения динамически формируемых SQL-предложений в программах на PL/SQL. Таблица 1.
Роль пакета DBMS_SQL Пакет DBMS_SQL включает много процедур и функций, которые обеспечивают процедурный интерфейс на PL/SQL для реализации различных этапов определения и выполнения динамически формируемых SQL и PL/SQL предложений в хранимых процедурах, функциях и пакетах. Вставка "Краткая сводка DBMS_SQL API" являет собой в сжатом концентрированном виде справочник (quick-reference) по наиболее общим и часто используемым процедурам и функциям программного интерфейса приложений (API - application programming interface), которым является пакет DBMS_SQL. Для более полной характеристики конструкций, перечисленных во вставке "Краткая сводка DBMS_SQL API", рекомендую обратиться к Приложению к документации по Серверу Oracle7 (Oracle7 Server Documentation Addendum) или внимательно изучить скрипт, который Oracle7 выполняет, чтобы создать пакет DBMS_SQL (например, во всех UNIX-системах - это скрипт $ORACLE_HOME/rdbms/admin/dbmssql.sql). Примеры простых и сложных процедур> Давайте рассмотрим несколько примеров, иллюстрирующих использование пакета DBMS_SQL, чтобы выявить, как формируются динамические SQL-предложения в программах на PL/SQL. На листинге 1 приведены две простые процедуры, которые некоторое приложение может использовать, чтобы создавать и удалять временные таблицы, которые можно индивидуально применять в сессии конкретного пользователя. Отметим, на что следует особо обратить внимание в этом листинге: # Процедуры показывают, как используя пакет DBMS_SQL, динамически сформировать в хранимой процедуре SQL-предложения, относящиеся к первому типу (таблица 1); # Процедуры показывают, как пакет DBMS_SQL позволяет программам на PL/SQL выполнить SQL-предложения языка определения данных (DDL - data_difinition_language) такие, как CREATE TABLE и DROP TABLE (см. вставку "Несколько слов о PL/SQL и DDL SQL"); # Процедуры применяют функцию UNIQUE_SESSION_ID другого пакета утилит DBMS_SESSION, чтобы идентифицировать и использовать уникальный идентификатор сессии конкретного пользователя, который вызвал эти процедуры. ЛИСТИНГ 1. Использование пакета DBMS_SQL для формирования динамических DDL SQL-предложений внутри хранимой процедуры (по методу 1) CREATE PROCEDURE create_temp_dept
(tname IN OUT VARCHAR2)
AS
cur INTEGER; - хранит идентификатор (ID) курсора
ret INTEGER; - хранит возвращаемое по вызову значение
str VARCHAR2(250); - хранит команды
BEGIN
-
- генерация временной таблицы по имени DEPT, используя заранее
- заданное (hard-coded) имя
- и возврат значения функции
DBMS_SESSION.UNIQUE_SESSION_ID
-
tname := dept_t // dbms_session.unique_session_id
-
- генерация команды CREATE TABLE по заранее заданному тексту
- и переменной tname
-
str := 'CREATE TABLE '//tname
// ' (deptno INTEGER,'
// ' dname VARCHAR2(14),'
// ' loc VARCHAR2(13), '
// 'TABLESPACE temp '
// 'STORAGE ('
// 'INITIAL 10K NEXT 10K MAXEXTENTS 2 )';
-
- Динамически формируемое DDL SQL-предложение по методу 1
-
cur := dbms_sql.open_cursor;
dbms_sql.parse(cur, str, dbms_sql.v7);
ret := dbms_sql.execute(cur);
dbms_sql.close_cursor(cur);
END;
CREATE PROCEDURE drop_temp_dept
(tname IN OUT VARCHAR2)
AS
cur INTEGER; - хранит идентификатор (ID) курсора
ret INTEGER; - хранит возвращаемое по вызову значение
str VARCHAR2(250); - хранит команды
BEGIN
-
- генерация временной таблицы по имени DEPT, используя заранее
- заданное (hard-coded) имя
- и возврат значения функции
DBMS_SESSION.UNIQUE_SESSION_ID
-
tname := dept_t // dbms_session.unique_session_id;
-
- генерация команды DROP TABLE по заранее заданному тексту
- и переменной tname
-
str := 'DROP TABLE '//tname;
-
- Динамически формируемое DDL SQL-предложение по методу 1
-
cur := dbms_sql.open_currsor;
dbms_sql.parse(cur. str, dbms_sql.v7);
ret := dbms_sql.execute(cur);
dbms_sql.close_currsor(cur);
END;
Как можно увидеть из примера, приведенного на листинге 1, динамическое формирование SQL-предложения по методу 1 при помощи пакета DBMS_SQL требует всего несколько действий и очень просто в реализации. Приведенный ниже безымянный блок на PL/SQL служит простой иллюстрацией, как инструментальные средства SQL*Plus или SQL*DBA вызывают хранимые процедуры CREATE_TEMP_DEPT и DROP_TEMP_DEPT, чтобы создать или уничтожить временную таблицу DEPT в период сессии с базой данных. Перед выполнением этого блока не забудьте установить переменную среды SQL*Plus serveroutput в положение ON. [Прим. переводчика: отображение вывода хранимых процедур (функция DBMS_OUTPUT.PUT_LINE) в SQL*Plus опредяется переменной set serverout[put] {ON/OFF} [SIZE n]
где SIZE - количество байтов вывода, сколько буферируется сервером Oracle7. По умолчанию это значение равно 2000, но не может превышать 1,000,000. Вывод отображается после выполнения сервером Oracle7 блока на PL/SQL.] DECLARE
x VARCHAR2(50);
BEGIN
create_temp_dept(x);
dbms_output.put_line(x //' table created') ;
drop_temp_dept(x);
dbms_output.put_line(x //' table dropped') ;
END;
/
Теперь давайте рассмотрим несколько более сложный пример, который показывает, как представить динамически формируемое SQL-предложение для запроса в хранимой функции. Функция DEPT_LIST_BUILDER, приведенная на Листинге 2, показывает, как создать функцию, которая строит выходной буфер, содержащий список ограничений номеров отделов и имен клиентов приложения. ЛИСТИНГ 2. Использование пакета DBMS_SQL для формирования динамического SQL-предложения внутри хранимой функции DEPT_LIST_BUILDER (по методу 3) CREATE FUNCTION dept_list_builder
(loc_col IN CHAR DEFAULT 'Y' )
RETURN LONG
AS
stmt VARCHAR2(250); - хранит команду
select_list VARCHAR2(50); - хранит список выборки
deptid INTEGER; - хранит список данных deptno
deptnm VARCHAR2(14); - хранит список данных dname
deptlc VARCHAR2(13); - хранит список данных loc
cur INTEGER ; - хранит идентификатор курсора
ret INTEGER; - хранит возвращаемое по вызову значение
output LONG(32760); - хранит список ограничений
BEGIN
-
- Построение списка SELECT с использованием значения флажка loc_col .
- Список запросов SELECT всегда содержит колонки DETNO и DNAME .
-
select_lst := ' deptno, dname';
IF UPPER(loc_col) = 'Y' THEN
select_list := select_list //' ,loc';
END IF;
-
- Построение команды SELECT. Владелец процедуры должен иметь
- привилегию SELECT для таблицы SCOTT.DEPT
-
stmt := 'SELECT ' // select_list
//' FROM scott.dept ORDER BY deptno';
-
- Динамическое формирование SQL-предложения по методу 3
-
- Открытие курсора и разборка запроса
-
cur := dbms_sql.open_cursor;
dbms_sql.parse(cur, str, dbms_sql.v7);
-
- Определение колонок в запросе
-
dbms_sql.define_column(cur,1,deptid);
dbms_sql.define_column(cur,2,deptnm,14);
IF UPPER(loc_col) = 'Y' THEN
dbms_sql.define_column(cur,3,deptlc,13);
END IF;
-
- Выполнение запроса
-
ret := dbms_sql.execute(cur);
-
- Извлечение записей, разграничение и помещение списка
- в выходной буфер
- ',' - ограничитель указывает на конец поля .
- ';' - ограничитель указывает на конец записи .
-
LOOP
IF dbms_sql.fetch_row(cur) > 0 THEN
dbms_sql.column_value(cur,1,deptid);
dbms_sql.column_value(cur,2,deptnm);
output := output // deptid // ',' // deptnm;
IF UPPER(loc_col) = 'Y' THEN
dbms_sql.column_value(cur,3,deptlc);
output := output // ',' // deptlc;
END IF;
output := output //':';
ELSE
EXIT;
END IF;
END LOOP;
dbms_sql.close_cursor(cur);
RETURN output;
END dept_list_builder;
Отметим, что функция DEPT_LIST_BUILDER демонстрирует дополнительные действия, требуемые для исполнения запроса с динамически формируемым SQL-предложением:
Следующий ниже неимеющий названия блок на PL/SQL показывает, как вызвать функцию и получить возвращенное значение функции DEPT_LIST_BUILDER, применяя SQL*Plus или SQL*DBA. Перед выполнением этого блока не забудьте установить переменную serveroutput в положение ON. DECLARE
outputbuffer VARCHAR2(2000);
BEGIN
outputbuffer := dept_list_builder('Y');
-
- Попробуйте выполнить вышеприведенный вызов с значением 'N',
- чтобы самим увидеть динамическое SQL-предложение в действии
-
dbms_output.put_line(outputbuffer);
END;
Заключение Реализация динамически формируемых SQL-предложений в программах на PL/SQL является достаточно простым действием, если только Вы поняли, как определять и выполнять различные типы динамически формируемых SQL-предложений, используя процедуры и функции пакета DBMS_SQL. Хотя эта статья не является руководством по динамическим SQL-предложениям и пакету DBMS_SQL, можно надеяться, что представленная здесь информация послужит расширению Ваших знаний в обоих направлениях, а результатом будет Ваша возможность лучше конструировать приложения, работающие с базами данных Oracle7. Вставка 1.
"Краткая сводка DBMS_SQL API"
(A Quick Reference to the DBMS_SQL API)
"Несколько слов о PL/SQL и DDL SQL" (A Word About PL/SQL and DDL SQL) Примеры, приведенные на листинге 1, показывают, как обойти стороной ограниченность PL/SQL в Oracle7 версии 7.1, а именно, отсутствие поддержки DDL (data definition language - язык определения данных) SQL-предложений. Не удивительно ли Вам, почему PL/SQL непосредственно не поддерживает предложения DDL SQL? Для того, чтобы ответить на этот вопрос, рассмотрим, как реагирует Oracle7, когда Вы создаете программу на PL/SQL. Когда компилируется программа PL/SQL. Oracle7 производит больше, чем просто проверку синтаксиса предложений - он также проверяет зависимости объектов (object dependencies) базы данных и проверку полномочий на право доступа (security auhorizations - авторизационную защиту), чтобы удостовериться, что программа на PL/SQL сделана правильно. Кроме того, для хранимых в базе данных PL/SQL-программ, таких как процедуры и триггеры, Oracle7 автоматически сохраняет путь по цепочкам объектных зависимостей (track of object-dependency chains), так что сервер может при необходимости автоматически сделать недействительными (invalidate) или переправить (revalidate) объекты, которые зависят друг от друга. Это встроенная в сервер возможность снимает неудобство от необходимости ручного сохранения пути объектных зависимостей и ручной проверки или рекомпиляции объектов, когда имеет место что-либо простое, как например, модификация таблицы. Когда же схемы сложных приложений имеют много зависимых между собой объектов, ручное управление объектной зависимостью может стать задачей исключительной значимости. Теперь, помня об имеющемся в Oracle7 автоматическом механизме поддержания объектных зависимостей, рассмотрим, что случилось бы, если PL/SQL непосредственно поддерживал бы DDL SQL-предложения. При наличии такой возможности программа на PL/SQL могла бы, среди прочего, создавать объекто-подобные (database-objectlike) таблицы базы данных. Но этот сценарий содержит парадокс - Oracle7 не разрешает построения правильной программа на PL/SQL, которая зависела бы от еще не существующих объектов базы данных. Этот пример демонстрирует простую мысль, что чтобы что-либо получить, чаще всего приходится от чего-то отказываться. В случае с PL/SQL программисты Oracle7 обычно выбирают отказ от возможности применения DDL SQL-предложений и взамен получают автоматический механизм проверки объектных зависимостей и правильности программ. В Oracle7 версии 7.1 пакет DBMS_SQL обеспечивает удобное средство, чтобы обойти это ограничение DDL в PL/SQL без подрыва Oracle7-механизма поддержки обеспечения объектных зависимостей. Поскольку предложения DDL SQL являются внутренними по отношению к программе PL/SQL, поскольку они, динамически формируемые, строятся во время выполнения, поэтому Oracle7 может допустить правильность построения программы. Следует, однако, понимать, что когда программа на PL/SQL использует пакет DBMS_SQL, чтобы построить предложения DDL SQL, программа должна брать на себя ответственность за возможные ошибки, которые могут быть результатом нарушения объектных зависимостей и прав доступа, которые Oracle7 не проверяет во время компиляции. [Об авторе: Стив Бобровски - президент софтверной компании Animated Learning, которая специализируется в области разработки мультимедийных обучающих программ для освоения технологии клиент/сервер. Он - автор книги "Mastering Oracle7 & Client/Server Computing" (Sybex, 1994). (Эту книгу вы можете купить в "Книжной лавке" ЕАГПО. Редакторы журнала SELECT признали эту книгу лучшей из всех книг, не входящих в документацию по Oracle7. Кстати, Стив Бобровски был основным автором этой документации. Наша просьба ("Мир Oracle") разрешить перепечатку одной из глав книги "Mastering Oracle7 & Client/Server Computing" в нашем бюллетене утонула в недрах издательства Sybex, которому принадлежат права на эту книгу. Вслед за редакторами Select мы также рекомендуем вам эту книгу. - Ред. "Мир Oracle"). Вы можете связаться с ним по телефону 408.688.7735 или по Internet-адресу stevebob@netcom.com. Автор благодарит консультанта корпорации Oracle Gail Turk, которая подсказала идею этой статьи.] Ссылки по теме
|
|
||||||||||||||||||||||||||||||