Конвертация Unixtime в Oracle Date

Известно, что unixtime считается в секундах и начальная точка отсчета берется 01.01.1970 00:00:00. Тогда зная это можно преобразовать unixtime в oracle date по следующей формуле:

select to_char(to_date('01.01.1970 00:00:00','dd.mm.yyyy hh24:mi:ss') + 1/24/60/60 * :unixtime, 'dd.mm.yyyy hh24:mi:ss') as from_unix from dual;

Например, переменная :unixtime = 1391010620, тогда запрос вернет нам следующее:

select to_char(to_date('01.01.1970 00:00:00','dd.mm.yyyy hh24:mi:ss') + 1/24/60/60 * 1391010620, 'dd.mm.yyyy hh24:mi:ss') as from_unix from dual;

FROM_UNIX
-------------------
29.01.2014 15:50:20
Реклама

Переполнение NONPAGED/PAGED POOL

  • Исследования проводились для Windows XP, 2003

Если один из пулов (paged pool или nonpaged pool) бесконечно растет и не высвобождается память для работы иных процессов, то это может привести к зависанию или нестабильной работе ОС или приложений.

1. Для исследования того, какой процесс (драйвер) вызывает переполнение paged pool (nonpaged pool) пула памяти, понадобится набор утилит из Windows Rootkit:

  • POOLMON.exe — в реальном времени отображает заполнение и освобождение пулов памяти процессами(драйверами), но вместо их названия показывает их теги(tags).
  • STRINGS.exe — по тегу найденному при помощи POOLMON, определяет название процессов
  • SIGCHECK.exe — по названию процесса находит полное его описание (к какому приложению он относится, версия, размер и т.п.)

2. Где можно посмотреть загрузки пулов. Это можно сделать, открыв Менеджер Задач (в cmd> taskmgr). Перейти в закладку Производительность(Perfomance). В отдельном блоке Kernel Memory (K) увидим Total, Paged, Nonpaged.

3. Анализ пулов
3.1 Запускаем POOLMON.exe. В нем выполняем сортировку по типу пула (NONPAGED или PAGED), нажимая букву <P>. Затем сортируем по величине занимаемой области пула в байтах, нажимая букву <B>.
3.2 Запоминаем название тега драйвера (например, SaEe), который занимает больше всего пула памяти или странно себя ведет, т.е. периодически увеличивается, при этом почти не высвобождая память для других процессов.

4. Поиск драйвера процесса по тегу его образа
4.1 Поскольку большинство образов драйверов находятся в директории %Systemroot%\System32\Drivers, то утилиты STRINGS и SIGCHECK нужно поместить в каталог c:\WINDOWS\system32\drivers\.
4.2 Открыть CMD и выполнить команду:

c:\WINDOWS\system32\drivers\strings.exe * | findstr tag 

,где <tag> — имя тага процесса (вводить не менее 3-х символа тега).

Пример,

c:\WINDOWS\system32\drivers\strings.exe * | findstr SaEe

Данная команда выведет все процессы содержащие 4 заданных символа в названии образа драйвера.
Пример,

C:\WINDOWS\system32\drivers\srtsp.sys: hSaEeWj

Из результат поиска видим, что по настоящему тег образа драйвера может состоять из большего количества символов, чем отобразил POOLMON. В нашем случае поиск привели к образу драйвера srtsp.sys
4.3 Получив имя образа драйвера, при помощи SIGCHECK можно получить информации о том, от какого приложения этот драйвер. Для этого выполняем в CMD:

c:\WINDOWS\system32\drivers\Sigcheck srtsp.sys

В результате получили следующую информацию:

C:\WINDOWS\system32\drivers\srtsp.sys: 
Verified: Signed 
Signing date: 23:08 04.03.2011 
Publisher: Symantec Corporation 
Description: Symantec AutoProtect 
Product: AutoProtect 
Version: 10.3 
File version: 10.3.6.4

Наш драйвер относится к Антивирусу Symantec (продукт AutoProtect автозащита самого антивируса), его версия 10.3.6.4 5. Теперь, когда у нас есть «имя винновника» загрузки пула, можно удалить или переустановить приложение, либо попытаться обновить драйвер, или же бороздить Google в поисках готового решения подобной проблемы для данного приложения.

Создание нового отчета в MS Report Builder

MS Report Builder поддерживает синтаксис MS SQL Server. Поэтому получить отчет можно, как с помощью простых (Select-query) или сложных запросов (Dynamic-query), а также используя T-SQL (begin … declare … end) или хранимые процедуры/функции.

1. Создание нового отчета
Заходим в основное меню (ALT+F) и выбираем New
В открывшемся окне New Report or Dataset выбираем Blank report

2. Добавление источника данных
Вызываем контекстное меню в Data Sources — Add Data Source…
♦ В открывшемся окне выбираем источник (к примеру, SCOM)
♦ В поле Name можем указать свое имя источника
♦ Ставим галочку напротив Use a shared connection or report model
♦ Жмем Test connection. Если вернуло «Connection created successfully», то все работает. Иначе причина в соединении с источником.

3. Создание кода отчета
Вызываем контекстное меню в Datasets — Add Dataset…
♦ В области Query
♦ В поле Name можем указать свое имя Dataset (например, MYSET)
♦ Ставим галочку напротив Use dataset embedded in my report
♦ Выбираем из спиcка созданный нами Data source -> SCOM
♦ В поле Query пишем скрипт запроса или декларированного pl/sql блока, если установлена галочка Query text: Text
♦♦ И вводим в поле код запроса или T-SQL
♦ Если хотите использовать хранимую процедуру/функцию, то галочку ставим — Query text: Stored procedure
♦♦ И выбираем из списка хранимую процедуру/функцию
♦♦ Жмем Refresh fields
♦ В области Fields должны появится поля, которые можно будет выводит в отчете
♦ В остальных областях настройки можно делать походу редактирования отчета
♦ Жмем Ok

4. Создание формы в виде таблицы
На панеле меню переходим в раздел Insert
♦ Выбираем Table
♦♦ Теперь в окне редакторе появился шаблон таблицы
♦♦ Чтобы добавить поле в таблицу, его следует перетянуть из Datasets в шаблон таблицы.
♦♦ Структуру таблицы и содержимое можно форматировать
♦♦ Также можно добавлять в форму дополнительные объекты, как картинки, графики, индикаторы и т.д.

Запуск приложени по расписанию от SYSTEM в Windows Server 2008 R2

Для того, чтобы приложение запустилось по расписанию (Task Scheduler) от SYSTEM в Windows Server 2008 R2, необходимо, чтобы существовал каталог desktop по одному из путей (в зависимости от разрядности приложения):

c:\Windows\SysWOW64\config\systemprofile\desktop    (x86)
c:\Windows\System32\config\systemprofile\desktop    (x64)

Если каталога нет, то создаем его вручную.

Импорт из ActiveDirectory в Oracle

Подключиться из Oracle к AD возможно с помощью пакета DBMS_LDAP (доступ по Lightweight Directory Access Protocol).

Создадим таблицу для регистрации данных из AD.

CREATE TABLE USERS_AD
( CN          VARCHAR2(256 BYTE),
JOB_TITLE   VARCHAR2(256 BYTE),
DEPARTMENT  VARCHAR2(256 BYTE),
LOGIN       VARCHAR2(256 BYTE),
STATUS      VARCHAR2(256 BYTE),
DN_SHORT    VARCHAR2(128 BYTE),
DN          VARCHAR2(512 BYTE)
);

Создадим процедуру извлечения данных из AD и регистрации их в таблице USERS_AD.

CREATE OR REPLACE procedure AD_TO_DB
AS

ldap_server Varchar2 (256);
ldap_port Varchar2 (256);
ldap_dn Varchar2 (256);
ldap_user Varchar2 (256);
ldap_password Varchar2 (256);
ldap_base Varchar2 (256);
ldap_session dbms_ldap.session;
retval PLS_INTEGER;
ldap_message dbms_ldap.message;
ldap_attrs dbms_ldap.string_collection;
ldap_entry dbms_ldap.message;
ldap_attr_name VARCHAR2(256);
ldap_ber_elmt dbms_ldap.ber_element;
ldap_attr_index PLS_INTEGER;
entry_index PLS_INTEGER;
attr_index PLS_INTEGER;
i PLS_INTEGER;
ldap_vals dbms_ldap.string_collection;
ldap_log Varchar2 (256);
ldap_cn Varchar2 (256);
ldap_dep Varchar2 (256);
ldap_title Varchar2 (256);
ldap_stat Varchar2 (128);
ldap_dn_short Varchar2 (128);

begin

execute immediate 'truncate table USER_FROM_AD';
commit;

retval := -1;

--Подключение к серверу AD
ldap_server := 'ldap.terra.int'; --указать ваш dns или ip сервера AD
ldap_port := '389'; --by default TCP port 389
ldap_session := dbms_ldap.init(hostname => ldap_server,
portnum => ldap_port);
dbms_output.put_line(RPAD('LDAP session ',25,' ') || ': ' || RAWTOHEX(SUBSTR(ldap_session,1,8)) || '(returned from init)');

--Авторизация в AD
ldap_user := 'cn=droid,ou=specials,dc=terra,dc=int'; --указать пользователя домена, который имеет доступ к AD
ldap_password := 'xxxxxxx';
--запускаем простую аутентификацию к AD по имени пользователя и ввода пароля
retval := dbms_ldap.simple_bind_s(ld => ldap_session,
dn => ldap_user,
passwd => ldap_password);
dbms_output.put_line(RPAD('LDAP User ',25,' ') || ': ' || ldap_user);
dbms_output.put_line(RPAD('LDAP Pass ',25,' ') || ': ' || ldap_password);

dbms_ldap.use_exception := TRUE;

--Просмотр аттрибутов по ветке AD
ldap_base := 'ou=regions,ou=company users,dc=terra,dc=int'; --интересующая ветка AD
ldap_attrs(1) := 'cn';  --ФИО
ldap_attrs(2) := 'title'; --должность
ldap_attrs(3) := 'department';  --подразделение
ldap_attrs(4) := 'sAMAccountName';  --логин
ldap_attrs(5) := 'userAccountControl'; --статус
--filter => 'objectclass = *'
--запускаем функцию синхронного поиска в каталоге с фильтром по объектному классу - пользователи
retval := DBMS_LDAP.search_s(ld       => ldap_session,
base     => ldap_base,
scope    => DBMS_LDAP.SCOPE_SUBTREE,
filter   => 'objectclass=User',
attrs    => ldap_attrs,
attronly => 0,
res      => ldap_message);
dbms_output.put_line(RPAD('search_s Returns ',25,' ') || ': '|| TO_CHAR(retval));
dbms_output.put_line(RPAD('LDAP message ',25,' ') || ': ' || RAWTOHEX(SUBSTR(ldap_message,1,8)) || '(returned from search_s)');

--Подсчет вернувшихся значений
retval := dbms_ldap.count_entries(ld => ldap_session,
msg => ldap_message);
dbms_output.put_line(RPAD('Number of Entries ',25,' ') || ': '|| TO_CHAR(retval));

--Получение первой записи
ldap_entry := dbms_ldap.first_entry(ld => ldap_session,
msg => ldap_message);
entry_index := 0;

<< entry_loop >>
WHILE ldap_entry IS NOT NULL
LOOP

--Вывести текущую запись
ldap_dn := DBMS_LDAP.get_dn(ld => ldap_session,
ldapentry => ldap_entry);

ldap_dn_short := trim(replace(substr(replace(ldap_dn,substr(ldap_dn,-35,100),''),instr(replace(ldap_dn,substr(ldap_dn,-35,100),''),'OU=',-1)),'OU=',''));
--Получить первый атрибут
ldap_attr_name := dbms_ldap.first_attribute(ld => ldap_session,
ldapentry => ldap_entry,
ber_elem => ldap_ber_elmt);
ldap_cn := NULL;
ldap_title := NULL;
ldap_dep := NULL;
ldap_log := NULL;
ldap_stat := NULL;

attr_index := 1;
<< attributes_loop >>
WHILE ldap_attr_name IS NOT NULL
LOOP

--Получить значение, связанное с атрибутом
ldap_vals := DBMS_LDAP.get_values (ld => ldap_session,
ldapentry => ldap_entry,
attr => ldap_attr_name);

<< values_loop >>
FOR i in ldap_vals.FIRST..ldap_vals.LAST
LOOP

CASE ldap_attr_name
WHEN 'cn' THEN ldap_cn := ldap_vals(i);
WHEN 'title' THEN ldap_title := ldap_vals(i);
WHEN 'department' THEN ldap_dep := ldap_vals(i);
WHEN 'sAMAccountName' THEN ldap_log := ldap_vals(i);
WHEN 'userAccountControl' THEN ldap_stat := ldap_vals(i);
END CASE;

END LOOP values_loop;
--Следующий аттрибут
ldap_attr_name := dbms_ldap.next_attribute(ld => ldap_session,
ldapentry => ldap_entry,
ber_elem => ldap_ber_elmt);

attr_index := attr_index + 1;

END LOOP attributes_loop;

--Освобождаем ber element
IF (ldap_ber_elmt IS NOT NULL) THEN
dbms_ldap.ber_free(ber => ldap_ber_elmt, freebuf => 0);
END IF;

--Следующее значение для аттрибута
ldap_entry := dbms_ldap.next_entry(ld => ldap_session,
msg => ldap_entry);

entry_index := entry_index + 1;

--Если получены данные, то пишем их в таблицу
IF entry_index > 0 THEN
EXECUTE IMMEDIATE 'INSERT INTO USER_AD values(:1, :2, :3, :4, :5, 6:, :7)'
USING ldap_cn, ldap_title, ldap_dep, ldap_log, ldap_stat, ldap_dn_short, ldap_dn;
END IF;

END LOOP entry_loop;

COMMIT;

--Сообщение LDAP
retval := dbms_ldap.msgfree(lm => ldap_message);
DBMS_OUTPUT.PUT_LINE('AD operation successful!');

--Завершение сессии в AD
retval := DBMS_LDAP.unbind_s(ld => ldap_session);
dbms_output.put_line(RPAD('unbind_res Returns ',25,' ') || ': ' ||TO_CHAR(retval));

--Исключение ошибок
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(' Error code : ' || TO_CHAR(SQLCODE));
dbms_output.put_line(' Error Message : ' || SQLERRM);
dbms_output.put_line(' Exception encountered .. exiting');

END AD_TO_DB;
/

Теперь можно запустить процедуру и получить пользовательские данные из AD.

Получить курсы валют с сайта www.nbrb.by

  • Oracle 11g

На сайте http://www.nbrb.by каждый день обновляются курсы валют для РБ. Курсы валют можно получить в виде XML-файла, если пройти по этой ссылке: http://www.nbrb.by/Services/XmlExRates.aspx?ondate=01/28/2014. Здесь подробное описание структуры XML-файла: http://www.nbrb.by/statistics/Rates/XML/.

Немного о параметрах XML-файла… Параметр ondate – дата в формате месяц/день/год, на которую запрашивается официальный курс. Если параметр ondate не задан, то Вы получите официальный курс на последнюю дату установления.

Каждый элемент Currency содержит атрибут ID (внутренний код валюты – целое число) и вложенные элементы:

  • NumCode – цифровой код
  • CharCode – буквенный код
  • Scale – номинал
  • Name – наименование валюты
  • Rate – курс.

Конечно имеется много способов получения данных из XML-файлов, но рассмотрим случай, когда нужно получить данные из XML-файла с сайта, не сохраняя файл за ранее, чтобы импортировать их в базу.

В Oracle имеются различные методы обработки xml-файлов, мы будем использовать XMLTYPE (функция конструктор позволяющая извлекать и работать с xml-данными, -узлами и -фрагментами), HTTPURITYPE (подтип UriType, который обеспечивает поддержку протокола HTTP, при этом используется пакет UTL_HTTP, чтобы получить доступ к HTTP URL. Прокси и безопасные кошельки не поддерживаются в Oracle 11g) и XMLTable (функция позволяющая запросить результат XQuery, возвращаемый функцией, в виде виртуальной реляционной таблицы, используя SQL).

Так как cтруктура XML-файла и методы работы с XML-данными (XMLTYPE, HTTPURITYPE, XMLTable) известны, можно получить курсы валют:

-- Получаем список курсов валют, которые обновляются ежедневно (update Daily)
SELECT x.VAL_NAME, x.VAL_RATE
FROM (select substr(HTTPURITYPE('http://www.nbrb.by/Services/XmlExRates.aspx?ondate='||to_char(sysdate+1,'MM/DD/YYYY')).getclob(),
regexp_instr(HTTPURITYPE('http://www.nbrb.by/Services/XmlExRates.aspx?ondate='||to_char(sysdate+1,'MM/DD/YYYY')).getclob(),
'<+', 1)) XML_CODE FROM DUAL) t,
XMLTable(
'/DailyExRates/Currency'
passing xmltype(T.XML_CODE)
columns VAL_KOD number path 'NumCode'
, VAL_NAME varchar2(30) path 'CharCode'
, VAL_RATE number path 'Rate'
) x;

-- Получаем список курсов валют, которые обновляются ежемесячно (update Monthly)
SELECT x.VAL_NAME, x.VAL_RATE
FROM (select substr(HTTPURITYPE('http://www.nbrb.by/Services/XmlExRates.aspx?ondate='||to_char(sysdate,'MM/DD/YYYY')||to_char('&')||'period=1').getclob(),
regexp_instr(HTTPURITYPE('http://www.nbrb.by/Services/XmlExRates.aspx?ondate='||to_char(sysdate,'MM/DD/YYYY')||to_char('&')||'period=1').getclob(),
'<+', 1)) XML_CODE FROM DUAL) t,
XMLTable(
'/MonthlyExRates/Currency'
passing xmltype(T.XML_CODE)
columns VAL_KOD number path 'NumCode'
, VAL_NAME varchar2(30) path 'CharCode'
, VAL_RATE number path 'Rate'
) x;

В запросе используются функции SUBSTR, REGEXP_INSTR для удаления скрытых служебных символов, которые влияют на работу функции XMLTYPE.

Чтобы данный запрос работал должен быть разрешен доступ к сайту http://www.nbrb.by с помощью Access Control List (пакет dbms_network_acl_admin) и выданы права на пакет UTL_HTTP.

Сброс последовательности в 0 (sequence to zero)

  • Oracle 9i, 10g, 11g

В Oracle нет команды сбросить счетчик последовательности в нулевое значение, за то есть другой вариант, и довольно простой. Например, у нас имеется последовательность ddl_log_seq.nextval, которую уже ни раз вызывали:

select ddl_log_seq.currval from dual;

   NEXTVAL
----------
      1132

В моем случае последовательность ddl_log_seq имеет текущее значение 1132. Зная это значение, изменяем инкремент последовательности:

alter SEQUENCE ddl_log_seq increment by -1132 nocache;

Затем, сбрасываем последовательность ddl_log_seq в 0:

select ddl_log_seq.nextval from dual;

   NEXTVAL
----------
         0

Возвращаем наш изначальный инкремент:

alter SEQUENCE ddl_log_seq increment by 1;

Создание DDL-trigger для событий CREATE, ALTER, DROP

  • Oracle 10g, 11g

Для начала создадим таблицу, куда триггер будет регистрировать события CREATE, ALTER, DROP.

CREATE TABLE TESTER.DDL_LOG
(DDL_ID       INTEGER,
VER_ID       INTEGER,
ATTEMPT_DT   DATE,
OPERATION    VARCHAR2(30 BYTE),
COMP         VARCHAR2(128 BYTE),
MODULE       VARCHAR2(255 BYTE),
OS_USER      VARCHAR2(255 BYTE),
DB_USER      VARCHAR2(30 BYTE),
OBJ_OWNER    VARCHAR2(30 BYTE),
OBJECT_NAME  VARCHAR2(30 BYTE),
OBJECT_TYPE  VARCHAR2(30 BYTE),
SQL_TEXT     CLOB,
SQL_LENGTH   NUMBER);

, где ddl_id — порядковый номер строк,
ver_id — порядковый номер версии по группе [obj_owner, object_name, object_type],
attempt_dt — дата изменения объекта,
operation — операция (CREATE|ALTER|DROP),
comp — хост, с которого выполнялось событие,
module — программный модуль, в котором выполнялось событие,
os_user — ОС пользователь,
db_user — БД пользователь,
obj_owner  — схема, к которой относится объект,
object_name — имя объекта,
object_type — тип объекта,
sql_text — тело объекта (программный код),
sql_length — длина кода в символах

Создадим еще последовательность для генерации порядкового номера строк таблицы.

CREATE SEQUENCE TESTER.ddl_log_seq
START WITH 0
increment by 1
MAXVALUE 999999999
MINVALUE 0
NOCYCLE
NOCACHE
ORDER;

Теперь создадим DDL-триггер, который будет срабатывать после событий CREATE, ALTER, DROP, и повесим его на базу (создавать под учеткой as sysdba).

CREATE OR REPLACE TRIGGER DDL_TRIGGER
AFTER CREATE OR ALTER OR DROP ON DATABASE
DECLARE
vddl_id NUMBER;
vver_id NUMBER;
temp_clob CLOB;
sql_clob CLOB;
vsql_text ora_name_list_t;
n NUMBER;
text RAW(1000);
text_blob BLOB;
convert_warn INTEGER;
dest_offset  INTEGER := 1;
src_offset   INTEGER := 1;
lang_context INTEGER := DBMS_LOB.default_lang_ctx;
csid         INTEGER := DBMS_LOB.default_csid;

BEGIN

-- Если нужно, то указываем, для каких схем и типов объектов будет срабатывать триггер после событий CREATE, ALTER, DROP
IF ora_dict_obj_owner IN ('BIB', 'BIPS', 'BANKS', 'MTB', 'RAILWAY', 'STUSER') AND ora_dict_obj_type IN ('TABLE', 'PROCEDURE', 'FUNCTION', 'PACKAGE') THEN

-- Формируем номер для новой строки
SELECT TESTER.ddl_log_seq.nextval INTO vddl_id FROM dual;

-- Формируем последовательный номер версии для группы [obj_owner,object_name,object_type]
SELECT CASE
WHEN EXISTs(SELECT count(1) vver_id FROM TESTER.ddl_log
WHERE obj_owner = ora_dict_obj_owner
AND object_name = ora_dict_obj_name AND object_type = ora_dict_obj_type
GROUP BY obj_owner,object_name,object_type)
THEN (SELECT max(ver_id)+1 vver_id FROM TESTER.ddl_log
WHERE obj_owner = ora_dict_obj_owner
AND object_name = ora_dict_obj_name AND object_type = ora_dict_obj_type
GROUP BY obj_owner,object_name,object_type)
ELSE 1
END INTO vver_id
FROM dual;

-- Получаем программный код объекта в CLOB после DDL-события (построчное склеивание для кодировки AL32UTF8 и не только)
n := ora_sql_txt(vsql_text);
DBMS_LOB.CREATETEMPORARY(text_blob,true);
DBMS_LOB.open(text_blob,dbms_lob.lob_readwrite);
FOR i IN 1 .. n LOOP
text:=UTL_RAW.CAST_TO_RAW(vsql_text(i));
DBMS_LOB.writeappend(text_blob,utl_raw.length(text),text);
END LOOP;

DBMS_LOB.createtemporary(sql_clob, true);
DBMS_LOB.converttoclob (sql_clob,
text_blob,
DBMS_LOB.getlength (text_blob),
dest_offset,
src_offset,
csid,
lang_context,
convert_warn);

DBMS_LOB.close(text_blob);
DBMS_LOB.FreeTEMPORARY(text_blob);

-- Событие СREATE
IF ora_sysevent = 'CREATE' THEN

INSERT INTO TESTER.ddl_log
SELECT vddl_id, vver_id, SYSDATE, ora_sysevent, sys_context('USERENV','HOST'), sys_context('USERENV','MODULE'),
sys_context('USERENV','OS_USER'), USER, ora_dict_obj_owner, ora_dict_obj_name, ora_dict_obj_type,
EMPTY_CLOB(), 0 FROM DUAL;

-- Блокируем строку для апдейта поля типа CLOB
SELECT sql_text into temp_clob
FROM TESTER.ddl_log
WHERE ddl_id = vddl_id AND ver_id = vver_id
FOR UPDATE OF sql_text, sql_length;

UPDATE TESTER.ddl_log set sql_text = sql_clob, sql_length = dbms_lob.getlength(sql_clob)
WHERE ddl_id = vddl_id AND ver_id = vver_id;

-- Событие DROP
ELSIF ora_sysevent = 'DROP' THEN

INSERT INTO TESTER.ddl_log
SELECT vddl_id, vver_id, SYSDATE, ora_sysevent, sys_context('USERENV','HOST'), sys_context('USERENV','MODULE'),
sys_context('USERENV','OS_USER'), USER, ora_dict_obj_owner, ora_dict_obj_name, ora_dict_obj_type,
EMPTY_CLOB(), 0 FROM DUAL;

SELECT sql_text into temp_clob
FROM TESTER.ddl_log
WHERE ddl_id = vddl_id AND ver_id = vver_id
FOR UPDATE OF sql_text, sql_length;

UPDATE TESTER.ddl_log set sql_text = sql_clob, sql_length = 0
WHERE ddl_id = vddl_id AND ver_id = vver_id;

-- Событие ALTER
ELSE
-- Для некторых объектов получить программный код после изменения можно только, используя пакет DBMS_METADATA
IF ora_dict_obj_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE') THEN

sql_clob:=trim(DBMS_METADATA.GET_DDL(ora_dict_obj_type,ora_dict_obj_name,ora_dict_obj_owner,'COMPATIBLE','ORACLE','DDL'));

END IF;

INSERT INTO TESTER.ddl_log
SELECT vddl_id, vver_id, SYSDATE, ora_sysevent, sys_context('USERENV','HOST'), sys_context('USERENV','MODULE'),
sys_context('USERENV','OS_USER'), USER, ora_dict_obj_owner, ora_dict_obj_name, ora_dict_obj_type,
EMPTY_CLOB(), NULL FROM DUAL;

SELECT sql_text into temp_clob
FROM TESTER.ddl_log
WHERE ddl_id = vddl_id AND ver_id = vver_id
FOR UPDATE OF sql_text;

UPDATE TESTER.ddl_log SET sql_text = sql_clob, sql_length = dbms_lob.getlength(sql_clob)
WHERE ddl_id = vddl_id AND ver_id = vver_id;
END IF;
END IF;
END;

Для включения/октлючения триггера (выполнять под учеткой as sysdba).

ALTER TRIGGER DDL_TRIGGER ENABLE;
ALTER TRIGGER DDL_TRIGGER DISABLE;

Разбить одну строку на несколько строк

  • Oracle 10g, 11g

Используя иерархический запрос (connect by level) и, зная, что, например, запятая (в кач-ве delimeter) будет делить строку на части, а также задействовав регулярное выражение regexp_substr, можем разбить строку на несколько строк.

with t as
(select 'O,r,a,c,l,e' str from dual)
select regexp_substr(str, '[^,]+', 1, level) res from t
connect by regexp_substr(str, '[^,]+', 1, level) is not null;

res
---
O
r
a
c
l
e