На рис. 1 изображено представление архитектуры, стоящей за Oracle TimesTen. В контексте Oracle TimesTen базу данных принято называть хранилищем данных. Представление хранилища данных на диске называют файлом контрольной точки. Когда хранилище данных открывается в первый раз, все его содержимое считывается в память из файла контрольной точки. Последующие операции INSERT, DELETE, SELECT, UPDATE и другие операции базы данных выполняются в памяти. Изменения хранилища данных, обусловленные выполнением этих операций, периодически и асинхронно записываются в дисковый файл контрольной точки. Когда хранилище данных останавливается, любые остающиеся незаписанными изменения записываются в файл контрольной точки прежде, чем хранилище данных будет закрыто.
Рисунок 1.Архитектура Oracle TimesTen
Кроме того, Oracle TimesTen использует память на диске как механизм восстановления для защиты транзакций. Транзакции регистрируются в журналах. Если система “упала” и должна быть перезапущена, файл контрольной точки будет прочитан в память, затем будут применены журналы транзакций и хранилище данных будет снова открыто для бизнес-деятельности. Периодическая запись изменений базы данных в файл контрольной точки во время обычной деятельности сводит к минимуму время, необходимое для любой операции восстановления, которая могла бы произойти.
Как было только что описано, Oracle TimesTen может выполняться как автономная база данных, но при желании ее могло также выполнять как ускоритель производительности для данных, перемещаемых в/из Oracle Database. На рис. 1 показан агент кэша и сервис тиражирования, который может подключить Oracle TimesTen к Oracle Database. В сценарии этой статьи некоторые данные хранятся в Oracle TimesTen, как бы для иллюстрации автономного подхода, а другие данные хранятся в Oracle Database, чтобы проиллюстрировать повышение производительности. В случае необходимости вы можете объединить оба подхода.
Поскольку Oracle TimesTen получает свое преимущество в производительности благодаря хранению всего хранилища данных в памяти, почему бы не попробовать просто запустить Oracle Database и сконфигурировать буферный кэш настолько большим, чтобы он мог удержать в памяти всю базу данных? Разве в таком случае Oracle Database не будет работать точно так же, как Oracle TimesTen? Это хороший и справедливый вопрос, достойный того, чтобы уделить ему внимание.
Продукт Oracle TimesTen с самого начала был спроектирован как размещающаяся в оперативной памяти база данных. В нем полностью отсутствует логический ввод/вывод в том смысле, который наличествует в Oracle Database. Нет никаких блоков базы данных. Нет никакого буферного кэша. Статьи индекса вместо того, чтобы содержать идентификаторы логических строк, указывают непосредственно на адреса памяти, где могут быть найдены эти целевые строки. Для перехода от статьи индекса к строке в Oracle TimesTen требуется простое разыменование указателя. Благодаря использованию драйверов TimesTen Data Manager 6.0, ваше приложение имеет прямой доступ к памяти, в которой хранятся данные; отпадает необходимость в каких-либо дорогостоящих контекстных переключателях.
Однако, память не является ключом к решению всех проблем. За последние несколько лет было написано очень много о ненулевой стоимости логического ввода/вывода в Oracle Database, и хотя Oracle TimesTen может сделать стоимость обращения к находящимся в оперативной памяти данным намного более близкой к нулю, какая-то стоимость все таки остается. По-прежнему очень важен хороший дизайн. По-прежнему остается применимой теория очередей. Все еще важна оптимизация производительности, чтобы сократить ненужную обработку.
Следующие шаги:
ПРОЧТИТЕ дополнительно: Oracle TimesTen Краткий архитектурный обзор Oracle TimesTen In-Memory Database Руководство по инсталляции Oracle TimesTen In-Memory Database ЗАГРУЗИТЕ Oracle TimesTen демонстрационные скрипты этой статьи |
Вероятно, данные о телефонных номерах и адресах в системах реагирования на чрезвычайные ситуации будут использоваться только для чтения. Вы хотите, чтобы для данного телефонного номера диспетчеры видели связанный с ним адрес, но вы не хотите, чтобы те же самые диспетчеры отвлекались от своей работы при необходимости отредактировать информацию адреса. В таком случае разумно сделать для диспетчеров таблицу TELEPHONES доступной только для чтения.
Прежде, чем будет создан кэш данных телефонов/адресов только для чтения, следует создать пользователя – администратора кэша в Oracle Database. Этот пользователь владеет триггерами и таблицами, которые Oracle TimesTen создает в Oracle Database для отслеживания изменений в данных таким образом, чтобы они могли быть использованы для эффективного обновления кэшированных данных в Oracle TimesTen. Например, войдя в систему как пользователь system, выполните следующий оператор, чтобы создать администратора кэша с именем ttdispatch:
CREATE USER ttdispatch identified by ttdispatch default tablespace users quota unlimited on users;
Затем выполните следующие операции предоставления привилегий, чтобы позволить администратору кэша создавать триггеры для таблиц, принадлежащих другим пользователям (например, владельцу схемы DISPATCH):
grant create any trigger to ttdispatch;
Затем войдите в систему, как владелец схемы отправки, и предоставьте администратору кэша доступ SELECT к таблице TELEPHONES:
GRANT SELECT ON phones TO ttdispatch;
Теперь переключитесь к Oracle TimesTen и создайте группу кэша. Чтобы сделать это:
Находясь в ttIsql, соединитесь с вашим хранилищем данных.
Сделайте вызов встроенной процедуры ttCacheUidPwdSet() и специфицируйте для Oracle TimesTen имя пользователя/пароль, чтобы использовать их при соединении с Oracle Database как администратор кэша.
Вызовите ttCacheStart(), чтобы запустить агент кэширования TimesTen, который является ответственным за выполнение фактической работы по поиску данных из Oracle Database и кэширование этих данных в Oracle TimesTen.
Джонатан Генник, перевод (Май-Июнь 2007)
Источник: , журнал Oracle Magazine, №6 (ноябрь/декабрь) 2006.
Постоянно находящаяся в оперативной памяти база данных Oracle TimesTen всегда наготове.
За пару прошлых лет корпорация Oracle сделала несколько стратегических приобретений, и одним из наиболее интересных из них является постоянно находящаяся в оперативной памяти база данных, которая называется TimesTen. База данных Oracle TimesTen позволяет разрабатывать в реальном времени стратегически важные приложения, в которых время реакции должно измеряться даже не миллисекундами, а микросекундами. Она может использоваться автономно или как быстрый двунаправленный кэш для данных, к которым часто обращаются из Oracle Database.
Хорошим примером приложения, которое может многое выиграть от применения Oracle TimesTen, является диспетчерская система реагирования на чрезвычайные ситуации, типа системы, к которой в США можно подключиться, набирая номер 911. (Фактически, БД Oracle TimesTen уже развернута, по крайней мере, в одном из приложений, связанных со службой 911.) Время ответа в таких системах является более чем критическим; это – вопрос жизни и смерти.
Когда в большинстве регионов США вы набираете номер 911, диспетчер сразу же видит на экране ваш номер телефона и адрес, а часто система предлагает карту, показывающую ваше местоположение и, возможно, даже информацию об известных проблемах со здоровьем в вашем местоположении. Например, пациент с серьезной аллергией, которому прописано иметь при себе автоинжектор эпинэфрина, может захотеть зарегистрировать эту информацию у диспетчеров группы реагирования на чрезвычайные ситуации. Немедленный доступ к адресу и другой критической информации является ключевым для быстрого получения помощи.
В предлагаемой статье описывается реализация элементарной системы баз данных, которая могла бы использоваться для отображения критической информации диспетчерам группы реагирования на чрезвычайные ситуации. Тем самым будет показано, как Oracle TimesTen может работать с Oracle Database, чтобы обеспечить надежное, порядка микросекунд, время отклика, гарантируя при этом, что критические данные всегда будут немедленно доступными.
Если вы не проявите осторожность при проектировании своего приложения, то подвергнетесь риску, что попытка зафиксировать в Oracle Database вставки в таблицу CALL_LOG, которые были успешно зафиксированы в Oracle TimesTen, потерпит неудачу. Любые такие ошибки тиражирования регистрируются в имеющем расширение .awterr асинхронном файле ошибок со сквозной записью, в том же самом каталоге, в котором содержатся файлы контрольной точки.
Один из способов избежать проблем при тиражировании состоит в том, чтобы тщательно продумать, какая база данных будет "владельцем", или управляющей, для заданной таблицы или для набора из связанных таблиц. В сценарии диспетчирования все вставки в таблицу CALL_LOG делаются в Oracle TimesTen. Не позволяется вносить никаких изменений данных со стороны Oracle Database. Таким образом, все вставки должны распространяться из Oracle TimesTen в Oracle Database без ошибок.
Гибкость Oracle TimesTen намного превышает то, что было здесь показано. Кроме того, с Oracle TimesTen
Вы можете создавать кэши, в которые данные, при необходимости, вводятся автоматически, а затем удаляются после истечения заданного промежутка времени.
Вы можете определить асинхронную регистрацию, или даже полное отсутствие регистрации вообще, обменивая долговечность на производительность.
Вы можете использовать тиражирование из Oracle TimesTen в Oracle TimesTen, чтобы создавать резервные базы данных для обеспечения высокой доступности или для зеркалирования баз данных для выравнивания загрузки.
Вы можете автоматически пропускать запросы к Oracle Database, когда они обращаются к таблицам, найденным в Oracle Database, а не в Oracle TimesTen.
Если объединить ее с нормальной базой данных и хорошим дизайном приложения в оперативной памяти, производительность Oracle TimesTen делает возможной работу как стратегически важных, так и критичных ко времени поддерживаемых базой данных приложений. Когда минимизация микросекунд может сэкономить деньги или жизни, может пригодиться Oracle TimesTen In-Memory Database.
Благодарю Сэма Дрейка (Sam Drake) и Саймона Лоу (Simon Law) из Oracle за их долготерпение при ответах на многочисленные вопросы относительно этой статьи. Отдельные благодарности отделу шерифа округа Олжер, шт. Мичиган, за предоставление возможности детального знакомства с диспетчерским центром округа; а также службе скорой медицинской помощи округа Олжер, предложившей использованный в этой статье сценарий примера.
Джонатан Генник () – опытный профессионал Oracle и зарегистрированный член сети Oak Table Network. Он написал пользующиеся спросом книги – “Карманное руководство по SQL” и “Карманный справочник по Oracle SQL*Plus”, опубликованные издательством O'Reilly Media.
Возможно выполнение Oracle TimesTen на нескольких платформах, в том числе, на различных дистрибутивах Linux, Windows XP и Windows Server 2003, Solaris и HP-UX. Руководство по инсталляции постоянно находящейся в памяти базы данных Oracle TimesTen обеспечивает команды инсталляции для всех поддерживаемых платформ. Инсталляция Windows является очень простой, и именно ее использовали при выполнении примера для этой статьи.
Доступ к Oracle TimesTen, в конечном счете, осуществляется через ODBC. (Приложения Java используют JDBC, а драйвер JDBC TimesTen, в свою очередь, использует ODBC.) Создание хранилища данных Oracle TimesTen столь же просто, как определение источника данных ODBC и последующее соединение с ним. В целях этой статьи примем, что у вас имеется схема Oracle Database, которая называется DISPATCH, с таблицами, указанными в листинге 1. Далее предположим, что вы установили Oracle TimesTen и ваше приложение для реагирования на чрезвычайные ситуации – то самое, которое сообщает критическую информацию на сервер Windows, когда поступает запрос. Чтобы создать хранилище данных Oracle TimesTen для поддержки приложения для отправки и кэширования критических данных из Oracle Database используйте следующие шаги:
Листинг 1. Схема DISPATCH в Oracle Database 10g
CREATE TABLE phones ( phone_num VARCHAR2(8), street_addr VARCHAR2(20), city VARCHAR2(15), PRIMARY KEY (phone_num));
CREATE TABLE call_log ( call_num NUMBER(9), event_time TIMESTAMP, event VARCHAR2(80), PRIMARY KEY (call_num, event_time));
GRANT SELECT, UPDATE, INSERT ON call_log TO ttdispatch;
INSERT INTO phones VALUES ('555-1234','100 W. Munising Ave', 'Munising'); INSERT INTO phones VALUES ('555-2345','101 E. Varnum', 'Munising'); INSERT INTO phones VALUES ('555-3456','E2904 S. First', 'Trenary'); INSERT INTO phones VALUES ('555-4567','N3284 M-67', 'Limestone'); INSERT INTO phones VALUES ('555-5678','N7569 Spruce St.', 'AuTrain'); INSERT INTO phones VALUES ('555-6789','112 Colwell', 'Grand Marais');
COMMIT;
Принимая во внимание, что источником данных о телефонах/адресах является Oracle Database, а адресатом – Oracle TimesTen, вы хотите, чтобы зарегистрированные данные текли в другом направлении. Диспетчеры должны регистрировать события, имеющие отношение к звонкам в 911, в Oracle TimesTen. Однако, вы хотите, чтобы данные журнала были переданы для долгосрочного хранения в Oracle Database. С этой целью можно создать асинхронную группу кэша со сквозной записью. В листинге 4 показан предназначенный для этой цели оператор Oracle TimesTen.
Листинг 4. Создание кэша со сквозной записью
CREATE ASYNCHRONOUS WRITETHROUGH CACHE GROUP call_log_data FROM dispatch.call_log ( call_num BIGINT, event_time TIMESTAMP, event VARCHAR(80), PRIMARY KEY (call_num, event_time) ) UNIQUE HASH ON (call_num, event_time) PAGES=1000;
После создания группы, запустите сервис тиражирования Oracle TimesTen, вызывая для этого из ttIsql встроенную процедуру ttRepStart:
CALL ttRepStart();
Начиная с этого момента, все строки, которые ваше диспетчерское приложение вставляет в таблицу CALL_LOG Oracle TimesTen, будут вставлены в таблицу CALL_LOG в Oracle Database. Поскольку группа кэша является асинхронной, Oracle TimesTen зафиксирует транзакции, использующие вставки в CALL_LOG, не дожидаясь, пока соответствующие вставки будут зафиксированы в Oracle Database. Если подключение к Oracle Database будет прервано, то Oracle TimesTen будет отслеживать недавно вставленные записи журнала и отправлять их в Oracle Database, когда подключение будет установлено вновь.
Асинхронный кэш со сквозной записью дает вам лучшую производительность. Однако, вы можете создать синхронную группу кэша, если вы хотите гарантировать, что когда транзакция, использующая таблицу CALL_LOG, фиксируется в Oracle TimesTen, любые изменения в таблице CALL_LOG будут также зафиксированы и в Oracle Database. Выбор полностью остается за вами и определяется только вашими деловыми потребностями.
Таблица в Oracle TimesTen создается так же, как и в любой другой базе данных. Например, можно создать следующую таблицу, чтобы позволить постоянным жителям сделать записи данных, доступ к которым может стать потенциально спасительным для диспетчеров в чрезвычайных ситуациях:
CREATE TABLE emergency_info ( phone_num VARCHAR(8), info VARCHAR(160), PRIMARY KEY (phone_num));
Теперь вы можете записать комментарии, которые могут оказаться полезными для диспетчеров и респондентов, оказавшихся в критическом положении:
INSERT INTO emergency_info VALUES ('555-1234', 'Child Jeff allergic to egg white. Epinephrine autoinjector in orange box near refrigerator.');
INSERT INTO emergency_info VALUES ('555-2345', 'Bedridden resident needs help exiting home in case of fire.');
Типы данных Oracle TimesTen не совпадают в точности с типами данных из Oracle Database. Например, в Oracle TimesTen используется тип данных VARCHAR, а не VARCHAR2. См. документацию по Oracle TimesTen In-Memory Database API и Справочное руководство по SQL, где можно найти подробную информацию о типах данных, включая то, как типы данных Oracle Database могут быть отображены на типы данных Oracle TimesTen.
Таблица типа CALL_LOG будет постоянно расти. Вы не сможете сохранить всю таблицу подобного типа в размещенной в оперативной памяти базе данных. Вам придется предусмотреть условия, в соответствии с которыми должна производиться очистка устаревших журнальных записей. Один из подходов, которым можно было бы воспользоваться, состоит в том, чтобы сохранять в Oracle TimesTen только записи журнала за текущий день, оставляя Oracle Database всю работу по долговременному, архивному хранению. С этой целью можно ежедневно выполнять приведенный в листинге 5 оператор UNLOAD CACHE GROUP. Оператор удалит из Oracle TimesTen все записи, кроме записей текущего дня, оставив при этом нетронутой таблицу CALL_LOG в Oracle Database. В таком случае диспетчеры смогут получить доступ к журнальным записям текущего дня непосредственно из Oracle TimesTen. Любой из тех, кому потребуется просмотреть журнальные записи прошлых дней, может сделать запрос к Oracle Database.
Листинг 5.Удаление старых записей журналов
UNLOAD CACHE GROUP call_log_data WHERE NOT( EXTRACT(YEAR FROM EVENT_TIME) = EXTRACT(YEAR FROM SYSDATE) AND EXTRACT(MONTH FROM EVENT_TIME) = EXTRACT(MONTH FROM SYSDATE) AND EXTRACT(DAY FROM EVENT_TIME) = EXTRACT(DAY FROM SYSDATE) );
Давнее средство SQL Trace позволяет следить за выполнением запросов SQL серверными процессами, обслуживающими сеансы связи с СУБД. В версии Oracle 10 появился системный пакет DBMS_MONITOR, разрешающий отслеживать выдачу запросов более разнообразно, в том числе, на уровнях службы БД, приложения или его фрагмента, или же узла кластера. В статье показано, как это делается.
В то время как слежение за действиями сеанса (своего или чужого) и всех сеансов экземпляра СУБД не вызывает вопросов, остальные единицы слежения могут требовать пояснения.
Понятие служба БД (SERVICE) формировалось в Oracle, начиная с версии 8, и к настоящему времени используется для логического группирования сеансов с целью как раз таки иметь обобщенную единицу слежения и управления при использовании общей БД разными приложениями. Например, сеансы одной и той же службы БД могут присутствовать одновременно на разных узлах кластера в конфигурации RAC, а в рамках одной и той же СУБД могут одновременно исполняться сеансы разных служб. Службу предлагается связывать с набором приложений, объединенных общими свойствами, пороговыми характеристиками или правилами потребления ресурсов СУБД.
Понятие модуль (MODULE) используется для обозначения периода работы конкретного приложения в течение сеанса, а понятие действия (ACTION) – конкретного фрагмента приложения, то есть эпизода выдачи приложением определенной последовательности команд SQL.
Назначение опознавателя клиента (CLIENT IDENTIFIER) – метить сеансы, например, установленные по схеме использования общих серверных процессов (shared server), или сеансы конечных пользователей при работе через сервер приложений (например, приложений для web).
Давнее средство Oracle SQL Trace позволяет фиксировать «профиль запросов SQL», выдаваемых серверными процессами, обслуживающими сеансы связи с СУБД, и представляет собой полезный инструмент для выявления проблемных запросов и анализа загрузки СУБД приложениями. До версии 10 оно могло включаться и выключаться только для конкретных сеансов связи с СУБД. Это не всегда удобно, поскольку в жизни более насущны профилирование и отладка приложения, или даже его фрагментов, а между приложением и сеансом чаще всего нет взаимнооднозначной связи.
Новый для версии 10 пакет DBMS_MONITOR расширил ранее имевшуюся возможность профилирования SQL-действий сеанса (своего либо чужого) слежением за действиями отдельных приложений и их частей. Для этого используется модель «служба БД» — «модуль» — «действие». Между этими понятиями нет формальной зависимости, но методически предлагается сопоставлять «службе БД» группу логически связанных сеансов, «модулю» — приложение, работающее с данными, а «действию» — эпизод работы приложения. Точнее, средствами пакета можно отслеживать выдачу запросов SQL с точностью до следующих единиц:
«служб», отождествляемых значением SERVICE_NAME, «модулей», отождествляемых значением MODULE, «действий», отождествляемых значением ACTION, сеансов, отождествляемых значением CLIENT_IDENTIFIER, сеансов, отождествляемых значениями sid, serial#>, экземпляров СУБД, отождествляемых значением INSTANCE_NAME.
Просмотр и установка единиц слежения рассматриваются ниже.
Вдобавок пакет DBMS_MONITOR позволяет собирать обобщенную статистику выполняемых сеансами или приложениями запросов.
Из указанных выше единиц слежения наиболее разработанной является служба. Она не только позволяет следить за действиями приложений, но и является единицей управления. Вот пример возможностей ее употребления в этом качестве на настоящий момент:
В планировщике заданий со службой можно связать конкретный класс заданий (пакет DBMS_SCHEDULER, процедура CREATE_JOBCLASS).
Сеансы, осуществляемые в рамках службы, можно указать в определении группы потребителей ресурсов СУБД при работе с распределителем (диспетчером) ресурсов (пакет DBMS_RESOURCE_MANAGER, процедура SET_CONSUMER_GROUP_MAPPING).
В системе предупредительной сигнализации именно для конкретной службы можно устанавливать пороговые значения для общего времени (ELAPSED_TIME_PER_CALL) и процессорного времени (CPU_TIME_PER_CALL) обработки запросов SQL, о превышении которых СУБД будет докладывать автоматически (пакет DBMS_SERVER_ALERT, процедура SET_THRESHOLD).
Процедура DISCONNECT_SESSION из пакета DBMS_SERVICE останавливает все сеансы требуемой службы.
Упомянутые пакеты DBMS_SCHEDULER, DBMS_SERVER_ALERT и DBMS_SERVICE, а также процедура SET_CONSUMER_GROUP_MAPPING старого пакета DBMS_RESOURCE_MANAGER появились в версии 10.
Кроме того, все реализованные программно возможности слежения и управления широко представлены соответствующими web-страницами OEM.
Процедура SERV_MOD_ACT_TRACE_ENABLE пакета DBMS_MONITOR позволяет следить за выдачей запросов SQL отдельными «приложениями», «модулями» в составе приложений и «действиями» в рамках модулей. Пример:
DBMS_MONITOR.SERV_MOD_ACT_TRACE_ENABLE ( service_name => 'prima.class' , module_name => 'SQL*Plus' , action_name => DBMS_MONITOR.ALL_ACTIONS
, waits => TRUE , binds => TRUE , instance_name => NULL ); /* * ... выжидаем, пока длится работа
*/ DBMS_MONITOR.SERV_MOD_ACT_TRACE_DISABLE ( service_name => 'prima.class' , module_name => 'SQL*Plus' , action_name => DBMS_MONITOR.ALL_ACTIONS , instance name => NULL );
Для параметра MODULE_NAME по аналогии можно указать ALL_MODULES.
Таким же образом включается трассирование запросов, поступающих из текущего или чужого сеансов (SESSION_TRACE_ENABLE/DISABLE), сеансов, помеченных CLIENT_IDENTIFIER (CLIENT_ID_TRACE_ENABLE/DISABLE) и из всех сеансов подряд, или же порождаемых определенным экземпляром СУБД, например, из конфигурации RAC (DATABASE_TRACE_ENABLE/DISABLE).
Просмотр профиля выдачи запросов SQL выполняется с помощью программ tkprof и trcsess. Последняя программа стала поставляться в ПО версии Oracle 10 и как раз таки и выбирает из трассировочных файлов разных процессов данные, относящиеся к указанным единицам слежения. Вот пример, как может выглядеть совместное использование этих программ:
>trcsess output=out.txt module=SQL*Plus c:\oracle\admin\prima\udump\* >tkprof out.txt final.txt
Упражнение. Включить трассирование всех запросов SQL, поступающих от сеансов SQL*Plus, и наблюдать накопление профиля выдачи запросов. Отключить трассирование.
Для сеансов, помеченных CLIENT_IDENTIFIER, и для приложений, помеченных комбинациями SERVICE_NAME – MODULE – ACTION, пакет DBMS_MONITOR позволяет собирать не только профиль выдачи запросов SQL, но и статистику затрат СУБД на обработку запросов.
Пример со сбором статистики о работе определенных клиентов. Выдадим:
CONNECT / as sysdba EXECUTE DBMS_MONITOR.CLIENT_ID_STAT_ENABLE ( 'Web client' ) HOST sqlplus scott/tiger EXECUTE DBMS_SESSION.SET_IDENTIFIER ( 'Web client' ) SELECT COUNT ( * ) FROM dual; EXIT
Здесь с помощью команды HOST программы SQL*Plus был осуществлен короткий запуск сеанса, помеченного значением CLIENT_IDENTIFIER = 'Web client'.
Продолжим в изначальном сеансе от имени SYS:
SYS> SELECT aggregation_type, primary_id 2> FROM dba_enabled_aggregations;
AGGREGATION_TYPE PRIMARY_ID ------------------------------------------------------------------------ CLIENT_ID Web client
SYS> COLUMN client_identifier FORMAT A20 SYS> COLUMN stat_name FORMAT A35 SYS> COLUMN value FORMAT 99999999 SYS> SELECT client_identifier, stat_name, value 2> FROM v$client_stats
3> ;
CLIENT_IDENTIFIER STAT_NAME VALUE ----------------------------------------------------------------------- Web client user calls 4 Web client DB time 3807 Web client DB CPU 3807 Web client parse count (total) 2 Web client parse time elapsed 421 Web client execute count 4 Web client sql execute elapsed time 1940 Web client opened cursors cumulative 2 ...
SYS> EXECUTE DBMS_MONITOR.CLIENT_ID_STAT_DISABLE ( 'Web client' )
PL/SQL procedure successfully completed.
Вот перечень системных таблиц, позволяющих контролировать сбор статистики и наблюдать результаты:
DBA_ENABLED_AGGREGATIONS
V$CLIENT_STATS
V$SERVICE_STATS
V$SERV_MOD_ACT_STATS
V$SERVICEMETRIC
V$SERVICEMETRIC_HISTORY
Последние две дают подробную информацию о расходовании процессорного времени.
Это был пример с использованием процедур CLIENT_ID_STAT_ENABLE/DISABLE. Процедуры SERV_MOD_ACT_STAT_ENABLE/DISABLE используются аналогично.
Упражнение. Включить сбор обобщенной статистики выполнения запросов SQL всех соединений к СУБД по SQL*Plus и наблюдать ее накопление. Отключить сбор статистики.
Значение CLIENT_IDENTIFIER выставляется программно с помощью пакета DBMS_SESSION или же вызовом OCI setClientIdentifier (только JDBC в версии 9). Пример:
SQL> @userenv CLIENT_IDENTIFIER
CLIENT IDENTIFIER ------------------------------------------------------------------------
SQL> EXECUTE DBMS_SESSION.SET_IDENTIFIER ( 'Web client' )
PL/SQL procedure successfully completed.
/ CLIENT_IDENTIFIER ------------------------------------------------------------------------ Web client
Значения MODULE и ACTION устанавливаются в программе с помощью пакета DBMS_APPLICATION_INFO. Пример:
SQL> EXECUTE DBMS_APPLICATION_INFO.SET_MODULE – 2( 'some module', 'some action' )
PL/SQL procedure successfully completed.
SQL> @userenv MODULE
MODULE ------------------------------------------------------------------------ some module
SQL> @userenv ACTION
ACTION ------------------------------------------------------------------------ some action
SQL> EXECUTE DBMS_APPLICATION_INFO.SET_ACTION ( '' )
PL/SQL procedure successfully completed.
SCOTT> /
ACTION ------------------------------------------------------------------------
SQL>
Значение SERVICE_NAME выставляется автоматически при установлении соединения, а управление службами происходит посредством пакета DBMS_SERVICE.CREATE_SERVICE.
Простые примеры создания, включения, подключения, отключения и удаления:
EXECUTE DBMS_SERVICE.CREATE_SERVICE ( 'secunda', 'secunda.class' ) EXECUTE DBMS_SERVICE.START_SERVICE ( 'secunda' ) CONNECT /@localhost:1521/ secunda.class AS SYSDBA EXECUTE DBMS_SERVICE.STOP_SERVICE ( 'secunda' ) EXECUTE DBMS_SERVICE.DELETE_SERVICE ( 'secunda' )
Еще один способ создания служб – через командный интерфейс с помощью программы srvctl.
Возможные значения SERVICE_NAME указываются в сетевых установках Oracle и регистрируются в качестве службы БД процессом listener. Значения зарегистрированных в рамках БД служб можно наблюдать в таблице V$SERVICES. Помимо сконфигурированных, всегда имеются две внутренние службы: SYS$BACKGROUND используется внутренними процессами СУБД, а к SYS$USERS причисляются соединения пользователей, не указавших желаемую им службу.
Пример просмотра:
SQL> CONNECT scott/tiger@prima.class
Connected. SQL> @userenv SERVICE_NAME
SERVICE_NAME ------------------------------------------------------------------------ prima.class
Упражнение. Просмотреть список служб, зарегистрированных для рабочей БД. Объяснить наличие служб SYS$USERS и SYS$BACKGROUND.
Упомянутые значения единиц слежения за работой приложения наблюдаемы в программе, либо из полей таблиц словаря-справочника, либо из встроенного контекста сеанса с именем USERENV:
Значение | Поле таблиц | Параметр контекста USERENV сеанса |
SERVICE_NAME | V$SESSION, V$SERVICES | SERVICE_NAME |
MODULE | V$SESSION | MODULE |
ACTION | V$SESSION | ACTION |
CLIENT_IDENTIFIER | V$SESSION | CLIENT_IDENTIFIER |
SID | V$SESSION | SID |
SERIAL# | V$SESSION | |
INSTANCE_NAME | V$INSTANCE | INSTANCE_NAME |
Например, все значения, кроме SERIAL#, наблюдаемы в соответствующих параметрах контекста USERENV сеанса связи клиентской программы с СУБД.
Для дальнейшего просмотра этих параметров в SQL*Plus удобно подготовить файл с параметризованным обращением к контексту USERENV посредством системной функции SYS_CONTEXT:
SELECT SYS_CONTEXT ( 'userenv', '&1' ) AS &1 FROM dual SAVE userenv REPLACE SET VERIFY OFF
,
преподаватель технологий Oracle
... Паллада Афина,
За руку взявши, воскликнула к бурному богу
Ярею: "... не бросим ли мы и Троян и ахеян
Спорить одних, да Кронид промыслитель их славу присудит ?... "
Гомер, Илиада
Одним из бедствий времени был застарелый
произвол доносчиков и их подстрекателей...
Светоний, Жизнь Двенадцати Цезарей:
Божественный Тит
Речь не идет о разработке генератора отчетов Говоря об экспорте данных, имеется в виду именно экспорт данных, а не создание готового отчета с заголовками, формулами, красивым форматированием. Не нужно думать о пользователях плохо. Получив "сырые" данные в Excel они смогут в течении пары минут "раскрасить" их до готового отчета. Передача данных обратно из Excel в Forms - не требуется В противном случае непонятно зачем же нам нужен Forms, если данные могут корректироваться в Excel. Forms становится просто не нужной прослойкой между БД и Excel.
Но, как говорится, лучше один раз увидеть. Так вот, нам нужна утилита которая позволяет, имея вот это:
В результате получить вот это:
Павел Лузанов Источник: сайт П.Лузанова, 01 Ноября 2003г.,
http://www.geocities.com/luzanovp/frm2xls.html,
,Январь/Февраль 2004
Как и следует из названия, речь пойдёт о создании утилиты для экспорта данных из приложений Oracle Forms в MS Excel.
Введение или Почему пользователя так любят Excel
Постановка задачи. Требования и допущения Технические аспекты решения Решение Приложение
После того как мы определились с целями и прежде чем бросаться в бой (пардон, в программирование) необходимо четко сформулировать требования к предстоящей реализации. Таковыми стали.
Итак, теперь нужно выбрать конкретный вариант реализации.
Существует два базовых подхода к созданию файлов Excel из внешних приложений.
Первый подразумевает использование "родных" технологий Microsoft (DDE, OLE, VBX, ActiveX, ...). И Forms6i позволяет их использовать, но этот вариант не годится из-за требования совместимости с последующими версиями Forms. А, как известно, в версии 9i пакетов VBX, OLE2, DDE больше нет.
Второй подход заключается в том, чтобы создать файл в формате, который MS Excel сможет прочитать. Такими форматами являются: html, csv|tsv (comma|tab separated values).
Вот этим то мы и воспользуемся! Из предложенных форматов выбираем tsv (html требует больше места, а "," может встречаться в данных).
Экспортировать будем данные текущего блока формы (в котором находится курсор). Именно это позволит сделать утилиту универсальной. Ведь всегда можно программно определить первый, последний, следующий, предыдущий элементы текущего блока и перемещаться по записям вне зависимости от того, в каком блоке, какой формы, какого приложения Oracle Forms мы находимся!
Другое дело, что не всегда нужно экспортировать значения всех элементов блока (например, не нужно экспортировать невидимые пользователю элементы), поэтому, было принято решение о том, что экспортироваться будут только те элементы, для которых справедливо:
тип элемента один из TEXT ITEM, DISPLAY ITEM, LIST, CHECKBOX, RADIO GROUP элемент отображается на какой-либо канве и имеет свойства VISIBLE, DISPLAYED установленными в TRUE
В качестве первой строки экспортного файла было бы не плохо выгружать заголовки (PROMPT) элементов, тогда пользователь сможет визуально оценить какому элементу в блоке формы, соответствует какой столбец в таблице Excel. При таком подходе, это не всегда очевидное соответствие, ведь элементы могут располагаться даже в разных окнах одной формы.
Если вы используете общее меню для всего приложения, то для вызова утилиты удобнее всего создать специальный пункт меню "Экспорт в Excel". При желании его можно вывести как иконку в панель инструментов (Visible In Horizontal Menu Toolbar = Yes).
Для формирования файлов будем пользоваться пакетом TEXT_IO. Сами файлы должны иметь расширение xls для того, чтобы их можно было сразу открыть: host('cmd /C start excel filename.xls');
Совместимость с последующими версиями Forms Разработка велась для версии 6i, но с "прицелом" на возможный переход на 9i(10G) Независимость от платформы и способа эксплуатации Утилита должна работать как для режима клиент-сервер, так и для эксплуатации через Forms Server, причем не важно на какой платформе: будь то Windows или *nux Единственное условие, это наличие на клиентском компьютере MS Excel (а иначе куда экспортировать? :-) Универсальность Утилита должна работать для всех существующих форм и для всех форм, которые будут написаны в будущем
Ну почему пользователи так любят Excel!
Почему, после того как им установили новую систему (разработанную собственными силами или купленную) они непременно хотят получить данные в Excel.
Казалось бы, в системе предусмотрено множество отчетных форм (причем по количеству и внешнему виду согласованные с этими же пользователями!), но они всё равно "хотят Excel". Им, видите ли, так привычнее. Издеваются, не иначе :-)
Однако попробуем разобраться, для чего же вот этот Excel может быть действительно полезен.
В любой системе есть потребность в так называемых нерегламентированных отчетах. Т.е. в таких отчетах, которые заранее не предусмотрены в системе в связи с тем, что требования к ним заранее невозможно формализовать (какие данные требуются, в каком виде, в какое время...)
Каким образом, пользователь может сформулировать, какие данные ему нужны? Да во многих случаях, он просто будет исходить из того, как он видит эти данные в экранных формах. Учитывая, что экранные формы Oracle Forms, как правило, позволяют выполнять нерегламентированные запросы (режим ввода запроса), а экранных форм в серьёзной системе не мало, то получается, что данные, которые пользователь может получить в экранных формах, представляют собой солидный источник данных для нерегламентированных отчетов.
Исходя из этого, экспорт данных из экранной формы в Excel выглядит весьма перспективно. Ведь чтобы не говорили противники Microsoft, Excel, действительно, весьма удобный продукт для обработки табличных данных.
Это особенно актуально для такой системы как Oracle Applications (Oracle E-Business Suite), где подавляющее большинство экранных форм используют однозаписные блоки данных, где в каждый момент времени на экране находится только одна запись, а работа с формой начинается с ввода и выполнения запроса. Желание посмотреть на все отобранные записи в виде списка - весьма законное!
Еще одной распространенной задачей, которую мог бы решить экспорт данных в Excel, является печать справочных данных (справочников). В любой системе существует большое количество различного рода справочников. Экранные формы для ввода в них данных - всегда присутствуют (иначе, как данные будут попадать в систему), а вот про печать справочных данных частенько забывают.
Итак, возможность экспорта данных из экранных форм в Excel, не такая уж и плохая идея. Она позволяет решить проблемы с выводом на печать справочников, с просмотром в виде списка данных из однозаписных блоков Oracle Forms и в какой-то степени помогла бы решить проблему с нерегламентированными отчетами.
Осталось лишь определиться, как же эту возможность реализовать.
Полтавский П. В., разработчик ПО, компания
Постановка задачи
В процессе эксплуатации автоматизированной системы учета операций с ценными бумагами возникла необходимость создания таблицы для хранения детальной информации (истории сделок). Для её заполнения был разработан механизм, реализуемый набором триггеров БД и серверных пакетных процедур, вызываемых из триггеров. Но при тестировании данного механизма возникла следующая ситуация: при срабатывании триггера вызываемая им серверная процедура запрашивает данные из таблицы, на которой создан триггер. В результате возникает ошибка мутации:
ORA-04091: table <имя_таблицы> is mutating, trigger/function may not see it
(описание этого явления выходит за рамки данной статьи). Для решения проблемы были использованы следующие методы:
использование триггеров различных типов (FOR EACH ROW и STATEMENT) и глобальных переменных пакета; применение заданий серверных процессов (JOBs).
Использование триггеров различных типов и глобальных переменных пакета
При вставке или изменении записи из таблицы документов выполняется процедура инициализации глобальной переменной пакета значением столбца внутреннего идентификатора записи сделки. Это происходит при срабатывании следующего триггера (FOR EACH ROW trigger):
CREATE OR REPLACE TRIGGER DB_DOC_T1 after insert or update on DB_DOC FOR EACH ROW
begin if (inserting or updating) then DP_STNDS.Set_Id_Deal(:new.id_deal); else DP_STNDS.Set_Id_Deal(:old.id_deal); end if; end;
Пакетная процедура DP_STNDS.Set_Id_Deal и функция DP_STNDS.Get_Id_Deal чтения инициализированного значения пакетной переменной определяются следующим образом: … PROCEDURE Set_Id_deal(p_id_deal in db_doc.id_deal%type) is begin dp_id_deal:= p_id_deal; end;
FUNCTION Get_Id_Deal_No RETURN dp_id_deal db_doc.id_deal%type is begin return dp_id_deal ; end; …
Переменная dp_id_deal является глобальной переменной пакета DP_STNDS и объявляется в теле пакета:
CREATE OR REPLACE PACKAGE BODY DP_STNDS AS -- Переменные пакета dp_id_deal db_doc.id_deal%type; … END PACKAGE BODY DP_STNDS;
И посетители, и сотрудники проходят через проходную, где отмечаются в таблице CHECKPOINT:
CREATE TYPE checkpoint_typ AS OBJECT ( entrytime DATE , person person_typ ) /
CREATE TABLE checkpoint OF checkpoint_typ;
Вот как они могут "проходить":
INSERT INTO checkpoint VALUES ( SYSDATE , employee_typ ( 'Scott', 1111 ) );
INSERT INTO checkpoint VALUES ( SYSDATE , visitor_typ ( 'Adams', 333 ) );
INSERT INTO checkpoint VALUES ( SYSDATE , doctor_typ ( 'Smith', 2222, 'Therapeutist', '7778899' ) );
INSERT INTO checkpoint VALUES ( SYSDATE , stuff_typ ( 'Alice', 4444, 'Office-cleaner' ) );
Можно проверить, кто прошел:
SELECT * FROM checkpoint;
Обратите внимание на использованное упрощение: никто не запретил пройти через проходную просто сотруднику (Scott), то есть не врачу и не обслуживающему персоналу. Желание запретить такого рода вставки в таблицу во многих случаях возникает вполне законно. Для запрета следовало было описать тип EMPLOYEE_TYP (а заодно и PERSON_TYP) как "абстрактный" (это термин объектно-ориентированного подхода):
CREATE TYPE employee_typ UNDER person_typ ( empid NUMBER ) NOT FINAL NOT INSTANTIABLE /
,
Если бы мне принадлежал весь этот край, я бы
отдал его за то, чтобы сама королева Англии лежала в моих объятиях.
Carmina Burana, если верить Карлу Орфу
К сожалению объектные свойства дают разработчику не только приятные моменты. Одной из оборотных сторон является сложность, более высокая, чем при работе с таблицами. В жизни далеко не всегда получается придумать схему данных правильно и сразу, и оставить будущему только работу с самими данными. Часто приходится вносить изменения в схему при наличии уже имеющихся данных. Тут-то объектный подход и обнаружит больше затруднений, чем поначалу могло бы хотеться.
Выше упоминалась содержательно некорректная возможность добавить в таблицу CHECKPOINT сотрудника Scott, для которого не указано, то ли он доктор, то ли принадлежит обслуживающему персоналу. Говорилось, как можно исправить ситуацию, придав типу EMPLOYEE_TYP свойство NOT INSTANTIABLE. Если мы захотим сделать это сейчас, то у нас ничего не выйдет:
SQL> ALTER TYPE employee_typ NOT INSTANTIABLE; ALTER TYPE employee_typ NOT INSTANTIABLE * ERROR at line 1: ORA-22327: cannot change a type to NOT INSTANTIABLE if
it has dependent tables
К сожалению дело не в том, что в таблице CHECKPOINT отмечен сотрудник, противоречащий изменению свойства типа EMPLOYEE_TYP. Сообщение об ошибке говорит, что изменение свойства типа INSTANTIABLE на NOT INSTANTIABLE возможно лишь при отсутствии таблиц с полями этого типа. Увы, но нам придется пожертвовать таблицей:
DROP TABLE CHECKPOINT;
ALTER TYPE employee_typ NOT INSTANTIABLE CASCADE;
Кроме того, поскольку у EMPLOYEE_TYP есть подтипы, потребовалось указать слово CASCADE, чтобы изменения распространились на них (можете проверить, что указание CASCADE не спасет ситуацию с ошибкой ORA-22327 выше).
К счастью обратное изменение свойства типа, с NOT INSTANTIABLE на INSTANTIABLE, не потребует никаких жертв и срабатывает всегда.
Пример просмотра (SELECT * …) уже приводился. Однако для получения данных в табличном виде запрос придется усложнить. Проблема, которую придется учитывать в таком запросе - наличие в поле PERSON разных строк одной и той же таблицы значений разных типов. Бороться с проблемой позволяет специальное расширение SQL:
SELECT TREAT (person AS person_typ).name FROM checkpoint;
(В скобках отмечу, что к сожалению для выдачи данных базового типа ANYTYPE в Oracle придумано отдельное решение).
Обратите внимание, что столбец для выдачи можно формировать, трактуя поле PERSON отправной таблицы и как, например, EMPLOYEE_TYP, несмотря на то, что в CHECKPOINT имеются и строки другого типа:
SELECT TREAT (person AS employee_typ).empid FROM checkpoint;
(Действительно, строки выводятся даже для персон, не являющихся сотрудниками, вследствие чего не имеющих табельного номера EMPID).
Отбор входивших персон конкретного типа можно сформулировать во фразе WHERE, для которой придуманы другие специальные конструкции:
SELECT * FROM checkpoint WHERE person IS OF (employee_typ);
Обратите внимание, что выдались строки с данными типа EMPLOYEE_TYP и всех его подтипов. Если же строки с данными подтипов выдавать не нужно, можно указать следующее:
SELECT * FROM checkpoint WHERE person IS OF (ONLY employee_typ);
Вот как можно объединить два приведенные выше варианта отбора - строк по типам во фразе WHERE и столбцов с атрибутами, возможно других, типов:
SELECT SYSDATE, c.person.name, TREAT (c.person AS doctor_typ).phone FROM checkpoint c WHERE person IS OF (employee_typ);
Обратите внимание, что по синтаксису, принятом в Oracle написать во фразе SELECT выдачу C.PERSON.PHONE, наподобие C.PERSON.NAME, не получится. Не получится даже написать SELECT c.person.empid …, несмотря на то, что фразой WHERE отбираются сотрудники, которые имеют табельный номер EMPID. Для обращения к свойствам подтипов вам все равно придется написать SELECT TREAT (c.person AS doctor_typ).empid …, указав явно подтип, где возникло это свойство. Для свойства NAME приведения типа не требуется, так как оно задано для ("основного") типа столбца PERSONS, то есть PERSONS_TYP, а не для подтипов PERSONS_TYP.
Предположим, имеется поликлиника, в здании которой могут находиться сотрудники и посетители. У тех и других есть для учета имена, но у сотрудников к тому же табельные номера, а у посетителей - номер регистрационной карты. Классическую ситуацию "типы-подтипы" можно в Oracle разрешить объектными средствами, например так:
CREATE TYPE person_typ AS OBJECT ( name VARCHAR2(30) ) NOT FINAL /
CREATE TYPE employee_typ UNDER person_typ ( empid NUMBER ) NOT FINAL /
CREATE TYPE visitor_typ UNDER person_typ ( regid NUMBER ) NOT FINAL /
Если бы фраза NOT FINAL в определении PERSON_TYP отсутствовала, не удалось бы создать подтипы EMPLOYEE_TYP и VISITOR_TYP. У подтипов же эта фраза оставлена на всякий случай, который сейчас представится. Например, пусть нужно среди сотрудников отличать врачей от обслуживающего персонала:
CREATE TYPE doctor_typ UNDER employee_typ ( speciality VARCHAR2(30) , phone VARCHAR2(7) ) /
CREATE TYPE stuff_typ UNDER employee_typ ( job VARCHAR2(30) ) /
Данные о взаимозависимостях типов можно посмотреть в USER_TYPES:
SELECT supertype_name, type_name FROM user_types ORDER BY 1, 2;
В завершение нужно заметить, что пример в этой статье был сознательно упрощен. Конечно, в жизни вряд ли было бы целесообразно в таблицу CHECKPOINT включать объектное поле, хранящее все моделируемые свойства людей. (Это соответствует и "физике процесса": едва ли требуется сообщать вахтеру специализацию входящего врача и рабочий телефон, достаточно табельного номера). Персоны скорее всего должны бы рассматриваться как самостоятельные объекты, а в таблицу CHECKPOINT следовало бы включить ссылки на них. Но для понимания темы, затронутой в статье, это не существенно.
Очередная трудность связана с тем, что в PL/SQL не поддерживает прямой вызов унаследованного конструктора. (Проще говоря, конструкторы базового типа не наследуются!). Например: пусть у нас есть класс t_ParentType в котором определен пользовательский (user-defined) конструктор:
---------------------------------------------------------------- --спецификация объектного типа t_ParentType: - ---------------------------------------------------------------- create or replace type t_ParentType as object ( v_Field1 varchar2(32), constructor function t_ParentType(v_pName varchar2) return self as result ) not final; ---------------------------------------------------------------- --тело объектного типа t_ParentType: - ---------------------------------------------------------------- create or replace type body t_ParentType as constructor function t_ParentType(v_pName varchar2) return self as result is begin self.v_Field1 := v_pName; return; end; end;
Теперь мы определяем объектный тип t_ChildType, который является наследником t_ParentType. В типе t_ChildType также определен пользовательский конструктор: ---------------------------------------------------------------- --Спецификация объектного типа t_ChildType, - --который является наследником : t_ParentType - ---------------------------------------------------------------- create or replace type t_ChildType under t_ParentType ( v_Field2 varchar2(64), constructor function t_ChildType(v_pName varchar2, v_pDescription varchar2) return self as result );
В реализации конструктора типа t_ChildType попытаемся вызвать унаследованный конструктор:
---------------------------------------------------------------- --Тело объектного типа t_ChildType - --в конструкторе необходимо вызвать унаследованный конструктор - ---------------------------------------------------------------- create or replace type body t_ChildType is constructor function t_ChildType(v_pName varchar2, v_pDescription varchar2) return self as result is begin t_ParentType(v_pName => v_pName); self.v_Field2 := v_pDescription; return; end; end;
Игорь Мельников, Oracle 8i/9i Certified Professional DBA, группа компаний TopS Business Integrator
Статья была опубликована в журнале ORACLE MAGAZINE Русское издание
Объектно-ориентированное расширение языка PL/SQL поддерживает статические методы типа, однако во многих случаях бывает необходимо использовать статические атрибуты класса, к сожалению PL/SQL не поддерживает такие поля. Нам бы хотелось иметь подобный код:
create or replace type t_ParentType as object ( v_Name varchar2(50), static v_Const varchar2(32) := 'Scott Tiger' );
Увы, мы получаем ошибку:
ORA-06545: PL/SQL: compilation error - compilation aborted ORA-06550: line 5, column 12: PLS-00103: Encountered the symbol "V_CONST" when expecting one of the following: function procedure
Для реализации таких атрибутов можно использовать статический метод, который бы возвращал требуемое значение. Если значение атрибута также имеет объектный тип, то в качестве места хранения значения такого атрибута можно использовать вспомогательный пакет. Для защиты переменной от модификации необходимо поместить её объявление в тело пакета.
Следующий листинг реализует данный подход:
-------------------------------------------------------------- --Значение данного типа должен иметь атрибут объектного типа - -------------------------------------------------------------- create or replace type t_DictConst as object ( v_Id number(9), v_Name varchar2(50), v_Code varchar2(15), v_Description varchar2(250) ); -------------------------------------------------------------- --Спецификация вспомогательного пакета для типа t_ParentType:- --функция getConst возвращает объект типа t_DictConst - -------------------------------------------------------------- create or replace package serv$ParentType is function getConst return t_DictConst; end; -------------------------------------------------------------- --Тело пакета: объект-константа формируется в процедуре init - -------------------------------------------------------------- create or replace package body serv$ParentType is
v_gDictConst t_DictConst; function getConst return t_DictConst is begin return v_gDictConst; end; procedure init is begin v_gDictConst := new t_DictConst(1,'Scott Tiger', '01','Scott Tiger - Oracle demo-user'); end; begin init; end;
Следующий объектный тип реализует статический метод, который возвращает объект-константу:
-------------------------------------------------------------- --Спецификация объектного типа t_ParentType - --Статический метод возвращает константу - -------------------------------------------------------------- create or replace type t_ParentType as object ( v_Name varchar2(50),
static function getConst return t_DictConst ); create or replace type body t_ParentType is
static function getConst return t_DictConst is begin return serv$ParentType.getConst; end; end;
Вышеописанная методика демонстрируется в данном примере .
Oracle10g Rel.1 (10.1) PL/SQL User's Guide and Reference
Oracle10g Rel.1 (10.1) Application Developer's Guide - Object-Relational Features
С этой проблемой PL/SQL-программисты сталкиваются наиболее часто. Проблема связана с тем, что в PL/SQL отсутствует синтаксическая конструкция для вызова метода типа-предка. В случае порождения нового объектного типа от родительского, виртуальный метод переопределеяется, далее в этом порожденном типе нам необходимо вызвать данный метод типа-предка.
Например: пусть у нас есть класс t_ParentType в котором определен метод getName:
------------------------------------------------------------ --спецификация объектного типа t_ParentType: - ------------------------------------------------------------ create or replace type t_ParentType as object ( v_Field1 varchar2(32), member function getName return varchar2 ) not final; ------------------------------------------------------------ --тело объектного типа t_ParentType: - ------------------------------------------------------------ create or replace type body t_ParentType as member function getName return varchar2 is begin return self.v_Field1; end; end;
Теперь мы определяем объектный тип t_ChildType, который является наследником t_ParentType. В типе t_ChildType метод getName является виртуальным и переопределен. Для этого использовано ключевое слово OVERRIDING: ------------------------------------------------------------ --Спецификация объектного типа t_ChildType, - --который является наследником : t_ParentType - --Внимание: метод getName переопределен - ------------------------------------------------------------ create or replace type t_ChildType under t_ParentType ( v_Field2 varchar2(64),
overriding member function getName return varchar2 ) not final;
В реализации метода getName попытаемся вызвать унаследованный метод getName (объектного типа t_ParentType)
------------------------------------------------------------ --Тело объектного типа t_ChildType, - --в методе getName необходимо вызвать унаследованный метод - ------------------------------------------------------------ create or replace type body t_ChildType is overriding member function getName return varchar2 is begin return (???) getName ' ' v_Field2; -- как вызвать -- метод предка ??? end; end;
Приведенный пример обращения к Oracle относится к архитектуре клиент-сервер. Ею возможности Perl не исчерпываются. Когда вы познакомитесь с технологиями Perl подробнее, вы обнаружите целый ряд дополнительных способов использования языка:
Технологии Oracle позволяют организовать обращение из PL/SQL к внешним процедурам на Perl, наподобие как для C. Чтобы это сделать, нужно иметь определенную квалификацию в Oracle, и это экзотика, но это возможно, а вследствие своеобразных языковых возможностей Perl не бесполезно ! Дополнительный модуль Apache под названием mod-perl позволяет запускать из браузера программы на Perl гораздо эффективнее, чем посредством CGI. Одна из привлекательных возможностей при такой работе – обращение к БД посредством модуля Perl Apache::DBI. В этом случае соединения с Oracle будут браться из «пула соединений» совсем как при работе с сервлетами на Java и с теми же выгодами. Два конкурирующих решения позволяют использовать Perl в качестве языка, встроенного в HTML, наподобие страницам JavaServer или ASP, выполняющего вычисления на сервере web. Это так называемые системы Embperl и Mason, реализованные в Perl посредством модулей соответственно HTML::Embperl и HTML::Mason. Оба варианта обладают развитой функциональностью и подобно всем прочим разработкам для Perl общедоступны и открыты.
Доступ к БД под управлением Oracle может через sqlplus указанным выше способом. Способностей Perl вполне хватит отбросить из выходного файла лишние строки и символы и извлечь в свои внутренние структуры содержательную часть. Иногда этого достаточно, но чаще вам потребуется обращение в БД их программы на Perl напрямую, например потому, что это эффективнее, или потому что sqlplus в наличии нет.
Perl позволяет общаться напрямую со многими типами БД, и общение с СУБД Oracle реализовано в этом языке как частный случай общего решения. Правда для «настоящих» СУБД (не «персональных») история этого общего решения началась в Perl с 90-ми годами именно с частного случая Oracle. С другой стороны для Oracle в Perl есть и сугубо собственное решение, общение через OCI, которое здесь на рассматривается (для справки: это реализуется с помощью модуля Oracle::OCI в Perl).
Общение с БД «вообще» реализуется в Perl посредством специального интерфейса под названием DBI (буква i – independent – означает независимость от конкретного типа БД). Для работы с конкретной БД вдобавок требуется установить драйвер DBD, расчитанный на этот тип базы (буква d – dependent – означает зависимость от конкретного типа БД). Интерфейс DBI и драйвер Oracle DBD реализованы в виде модулей Perl, и их можно установить со страниц архивов Perl, называемых CPAN (Comprehensive Perl Archive Network): . В ActivePerl это можно сделать проще с помощью программы ppm. Достаточно войти в нее и выполнить команды:
ppm PPM> install DBI PPM> install DBD-Oracle PPM> query PPM> exit
Команда query позволит просмотреть все установленные модули и удостовериться в наличии DBI и DBD-Oracle.
Теперь все готово к проверке работы с Oracle. Составим файл HelloMiller.pl:
use strict; use DBI;
my $dbh = DBI ->connect ( 'dbi:Oracle:orcl', 'scott', 'tiger', {RaiseError => 1, AutoCommit => 0} );;
my $sql = qq{ SELECT ename, sal, COMM FROM emp };
my $sth = $dbh->prepare( $sql );
$sth->execute();
while ( my($ename, $sal, $comm) = $sth->fetchrow_array) { printf ("%-10s %7.2d %7.2d", $ename, $sal, $comm); if ($ename =~ /MILLER/) { print ", Hello Miller !"; } print "\n"; }
$dbh->disconnect();
Запустим программу:
HelloMiller.pl или perl HelloMiller.pl
Идею поприветствовать Миллера я заимствовал из книги .
, Преподаватель технологий Oracle
... «Нужен мне работник: Повар, конюх и плотник. А где найти мне такого Служителя не слишком дорогого ?» А. С. Пушкин, Сказка о попе и его работнике Балде
Наберите в консольном окошке perl –v и убедитесь, что сиcтема Perl установлена и нужной версии.
Многие языки программирования используются не так, как мыслились их создателям – отчасти или даже полностью. Perl задумывался как язык сценариев для командной оболочки OC, более развитый и переносимый. Сейчас он развился до языка программирования, но полностью цели своего далекого прошлого не изменил. Составим файл scriptexample.pl: print "this is a perl scripting example:\n";
$resultfile = "scott.tables"; $tempfile = "temp.file";
open(TMP, ">$tempfile");
print TMP <<EOF; SET PAGESIZE 0 SET FEEDBACK OFF SET LINESIZE 30 SPOOL $resultfile SELECT table_name FROM user_tables; SPOOL OFF EXIT EOF
print `sqlplus -s scott/tiger <$tempfile`;
print "\nreturn code = ", system "sqlplus -s scott/tiger <$tempfile";
open (TABLES, $resultfile); print "\n\n", <TABLES>;
print "\nend of example\n";
Обратите внимание на следующие особенности:
При формировании текста задания вместо простого spool scott.tables указан для примера более общий вариант, указывающий на возможность динамического формирования текста. sqlplus в программе вызывается двумя способами. Первый использует специальные кавычки `...`, а второй – оператор языка system. Второй чуть более длинен, но зато позволяет обрабатывать код завершения программы. Текст, полученный в sqlplus, тут же в программе пожет прочитываться и обрабатываться (print "\n\n", <TABLES>;). В случае Unix в качестве первой строки потребуется добавить что-то вроде #!/usr/bin/perl. Это упростит запуск программы, но в Windows этого можно и не делать.
Наберем в командной строке консольного окошка ОС:
scriptexample.pl
или же
perl scriptexample.pl
Именно благодаря возможностям компоновки сценариев (только чуть затронутым приведенным примером) Perl иногда называют «клейкой лентой информационных систем».
Другую известную особенность языка составляют возможности разбора регулярных выражений. Возьмем пример со страницы и чуть упростим его. Подготовим файл href.pl:
print "$2\n" while m{ < \s* A \s+ HREF \s* = \s* (["']) (.*?) \1 \s* >
}gsix;
Наберем (в синтаксисе Windows):
href.pl C:\Perl\html\release.html
(Файл RELEASE.html входит в стандартную поставку ActivePerl). Обратите внимание, что на экране появились как глобальные адреса ссылок (http, ftp, mailto), так и локальные.
Вообще-то, если вы установили Oracle вместе с сервером HTTP (то есть Apache), то Perl в вашем распоряжении уже есть. Например, для версий 8.1.6 – 9.2 он находится в каталоге [ORACLE_HOME]/Apache/perl/5.00503/bin (замените косые черточки на обратные для Windows). Если спуститься на каталог ниже и выдать perl –v, станет окончательно ясно, то в штатной поставке Oracle использована версия Perl 5.0. Она не самая последняя и к тому же представленна тут в ограниченном варианте. Он достаточен для решения внутренних задач, поставленных здесь перед Perl фирмой Oracle, а вам для реальной работы лучше установить у себя более свежую версию.
Отправной страницей web для установки Perl служит . Тут можно найти исходные тексты для построения исполняемых модулей Perl на разных платформах. Но часто проще воспользоваться уже готовыми исполняемыми модулями, имеющимися для многих платформ, например:
– для AIX
– для Solaris
– для HP MPE/iX
– для Windows, Linux и Solaris.
Perl по духу близок к Unix, но по иронии судьбы проще всего его установить именно для Windows с указанной выше страницы web фирмы ActiveState. (Правда, в последнее время ActiveState распространила эту простоту и на Linux с Solaris). Если вы воспользуетесь услугами этой страницы, уступите соблазну выкачать из интернета более позднюю версию 5.8.4, а остановите лучше свой выбор на 5.6.1. Это сделает удобнее работу с Oracle, так как по состоянию на лето 2004 года для более поздней версии готовый драйвер для DBI для Oracle имелся, а для более новой отсутствовал.
Полученный из интернета файл Windows, например ActivePerl-5.6.1.638-MSWin32-x86.msi, нужно запустить (или распаковать, если выбрали файл формата zip), и через полминуты Perl установлен. В Unix потребуется выполнить традиционную, но несложную процедуру построения (make).
Дальнейшее изложение для определенности будет предполагать установку ActivePerl на Windows; подгонка сказанного под другие платформы потребует некоторых навыков, но не глубоких познаний.