|
|
|||||||||||||||||||||||||||||
|
(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, которая подсказала идею этой статьи.] Ссылки по теме
|
|