Сегодня наконец появилось графоманское настроение и я хочу описать решение по использованию среды вычислений R в Oracle BI.
Думаю, не нужно говорить, что из себя представляет R.
Очевидно, что это крутая штука – раз компания Oracle включила ее в платную опцию Advanced Analytics (Oracle R Enterprise).
Крутая она хотя бы потому, что мировым научным сообществом под нее пишется огромное количество бесплатных библиотек, позволяющих анализировать ваши данные. Так в данном сообщении я продемонстрирую использование библиотеки forecast, позволяющей строить прогнозные модели данных временных рядов.
Библиотеку разработал и поддерживает профессор Rob Hyndman
Хочу заметить, что опция Oracle Advanced Analytics платная.
Да, она обеспечивает тесную интеграцию ваших данных (внутри БД Oracle) и вычислительного движка R, что существенно ускоряет обработку действительно больших объемов данных.
Но что если данных, которые нужно обрабатывать, у вас не очень немного. Или денег на оплату опции жалко…
В этом случае можно вполне обойтись интеграцией R-среды и вашей БД аналогично приводимому далее примеру.
А тут будет картинка для привлечения внимания.
А именно – результат работы в виде отчета Oracle BI Publisher, отображающего прогноз среднемесячной температуры на основании накопленной статистики температур.
Основная идея данного примера в том, что внутри БД Oracle с помощью PL/SQL пакета формируется файл с инструкциями R в файловой системе сервера БД;
затем с помощью Java Stored Procedure вызывается на выполнение утилита RScript с параметром-созданным файлом;
среди инструкций R внутри файла содержатся как обращения к БД с помощью специальной библиотеки ROracle,
так и логика по генерации прогнозных значений с помощью библиотеки forecast;
сгенерированные и записанные обратно в БД прогнозные данные визуализируются в Oracle BI Publisher.
Разобью пример на следующие шаги:
1) Загрузка в БД Oracle данных о среднемесячных температурах по регионам России.
2) Установка инструментальной среды R на сервер с БД Oracle. Инсталляция необходимых библиотек.
3) Создание R-скрипта, считывающего данные из БД, генерирующего прогноз и отображающего сформированные данные в виде графика.
4) Создание PL/SQL пакета, формирующего R-скрипт как файл в файловой системе сервера БД, запускающего R-скрипт.
5) Создание отчета в Oracle BI Publisher, визуализирующего сформированные R-скриптом прогнозы.
Документирую все шаги. Сообщение получается длинным, поэтому некоторые части текста скрыты спойлерами.
Загрузка в БД Oracle данных о среднемесячных температурах по регионам России
Открыть спойлер
Установка инструментальной среды R на сервер с БД Oracle. Инсталляция необходимых библиотек
1) Скачиваем с http://cran.rstudio.com/ дистрибутив с инструментальной средой R и базовым набором библиотек.
У меня используется сервер БД Oracle с операционной системой Windows.
Поэтому я скачиваю дистрибутив под Windows.
2) Установим R учитывая битность платформы
3) Откроем установленную инструментальную среду R.
Как я уже говорил, ценность этого инструмента во многом определяется наличием огромного количества библиотек.
Загрузим две, которые потребуются нам в работе: forecast и ROracle
Сделать это можно как напрямую в среде R с помощью команды
install.packages("forecast")Тогда потребуется указать зеркало, с которого будет производиться закачка библиотеки.
Очевидно, что данный метод предполагает наличие интернет соединения на сервере.
Если же доступа к интернету напрямую с сервера нет, то можно выкачать нужные пакеты и инсталлировать их вручную:
скачать zip-архив с пакетом-библиотекой (например, Windows binary пакета forecast с сайта http://robjhyndman.com/software/forecast/
и загрузить с помощью среды R через пункт меню "Пакеты" – "Установить пакеты из локальных zip-файлов")
Сложнее будет процесс установки пакета ROracle – данная библиотека нужна нам для получения и записи данных БД напрямую из R.
a) Скачаем дистрибутив пакета с http://cran.r-project.org/web/packages/ROracle/index.html
b) Изучаем инструкцию по инсталляции
c) Устанавливаем библиотеку DBI
Согласно пункту инструкции по установке "Windows - Oracle Client (Oracle home based client)" создаем
переменные окружения OCI_INC и OCI_LIB64 (либо OCI_LIB32 на 32-битной платформе)
В моем случае это:
OCI_INC = d:\app\product\11.2.0\dbhome_1\OCI\include
OCI_LIB64 = d:\app\product\11.2.0\dbhome_1\BIN
Не забываем проверить, чтобы в переменной окружения PATH был указан путь bin-каталога БД Oracle:
PATH = d:\app\product\11.2.0\dbhome_1\BIN;…
А также чтобы была корректно установлена переменная окружения ORACLE_HOME:
ORACLE_HOME = d:\app\product\11.2.0\dbhome_1
d) Качаем утилиту RTools (версия 3.1) по адресу http://cran.r-project.org/bin/windows/Rtools/ (она позволяет компилировать R-бинарники их исходных кодов, но нам она потребуется для компиляции и корректной установки библиотеки ROracle)
Устанавливаем RTools с параметрами по умолчанию.
По завершению установки добавляем в начало переменной окружения PATH пути до bin-директорий RTools и его компилятора, а также путь до bin-директории самой среды R:
PATH = c:\Program Files\R\R-3.0.3\bin\x64;c:\Rtools\bin;c:\Rtools\gcc-4.6.3\bin;…
e) Открываем командную строку, переходим в каталог с загруженным архивом исходником библиотеки ROracle (в моем случае это C:\TEMP)
И запускаем команду
R CMD INSTALL ROracle_1.1-11.tar.gz
f) Если все сделано правильно, то при выполнении команды в среде R
library(ROracle)Не будет выдано никаких сообщений об ошибках.
Создание R-скрипта, считывающего данные из БД, генерирующего прогноз и отображающего сформированные данные в виде графика
Теперь мы готовы обратиться из среды R к БД Oracle за данными с помощью библиотеки ROracle,
с помощью библиотеки forecast сгенерировать прогнозные значения и визуализировать их в виде графика в той же среде R.
Для этого скопируем и вставим в окно среды R следующий набор команд:
library(ROracle) library(forecast) drv <- dbDriver("Oracle") con <- dbConnect(drv,"DWH","DWH","ORCL") rs <- dbSendQuery(con, " with t as (select t.* from TEMPERATURE_DATA t where t.observer_num = 26063 and t.year_num >= 2000) SELECT measure FROM (select t.year_num, 1 as month_num, t.ind_1 as measure from t union all select t.year_num, 2 as month_num, t.ind_2 from t union all select t.year_num, 3 as month_num, t.ind_3 from t union all select t.year_num, 4 as month_num, t.ind_4 from t union all select t.year_num, 5 as month_num, t.ind_5 from t union all select t.year_num, 6 as month_num, t.ind_6 from t union all select t.year_num, 7 as month_num, t.ind_7 from t union all select t.year_num, 8 as month_num, t.ind_8 from t union all select t.year_num, 9 as month_num, t.ind_9 from t union all select t.year_num, 10 as month_num, t.ind_10 from t union all select t.year_num, 11 as month_num, t.ind_11 from t union all select t.year_num, 12 as month_num, t.ind_12 from t) ORDER BY year_num, month_num ") data <- fetch(rs) p_startYear <- 2000 p_startMonth <- 1 p_endYear <- 2008 p_endMonth <- 12 p_numOfPredictedMonths <- 12 dataTS <- ts(data$MEASURE, start=c(p_startYear, p_startMonth), end=c(p_endYear, p_endMonth), frequency=12) dataFit <- HoltWinters(dataTS) forecastFit <- forecast(dataFit, p_numOfPredictedMonths) plot(forecastFit)
где строка
con <- dbConnect(drv,"DWH","DWH","ORCL")содержит обращение к tns-записи ORCL из файла tnsnames.ora, соответствующего прилинкованной БД с помощью переменных окружения.
Замечу, что для обратной загрузки данных в Oracle следует выполнить следующие команды R:
forecastData <- data.frame(MEASURE = forecastFit$mean, MONTH_KEY = c(time(forecastFit$mean)) ) dbWriteTable(con, "TEMPERATURE_FORECAST", forecastData, append = TRUE)
Где первая команда генерирует набор данных (таблицу) из 2 столбцов – столбца MEASURE с прогнозными значениями вложенного ряда mean переменной forecastFit и столбца MONTH_KEY.
А вторая команда – загрузка набора данных в таблицу TEMPERATURE_FORECAST по открытому ранее соединению с БД с опцией Append (догрузки).
Создание PL/SQL пакета, формирующего R-скрипт как файл в файловой системе сервера БД, запускающего R-скрипт
Итак, теперь мы умеем из R среды обращаться к БД Oracle за данными, на основании полученных данных строить прогнозы временных рядов
(я специально не углубляюсь в детали библиотеки forecast – на сайте автора очень подробно все документировано) .
Но основной сущностью рассматриваемой здесь интеграции является БД Oracle.
Именно в ней хранятся исходные данные и туда же должны загружаться обработанные с помощью R (прогнозные) данные.
Значит, мы должны уметь из Oracle формировать R-скрипты с нужными наборами команд как файлы в файловой системе,
запускать эти скрипты на выполнение и удалять их по завершению работы.
Для этого я предлагаю использовать следующие пакеты:
PKG_UNLOADER (копия пакета Unloader Тома Кайта)
и
PKG_JAVA_COMMON_TOOLS (набор java stored procedure по java source fndjavafile)
Вот так будет выглядеть PL/SQL-код, генерирующий R-скрипт и запускающий его на выполнение:
declare l_dir_path varchar2(512); l_file_separator varchar2(4); l_rows number; l_query varchar2(32000); l_command varchar2(512); l_output DBMS_OUTPUT.chararr; l_lines INTEGER := 1000; BEGIN --вычислим путь к директории TEMP_DIR select DIRECTORY_PATH into l_dir_path from all_directories where DIRECTORY_NAME = 'TEMP_DIR'; l_file_separator := PKG_JAVA_COMMON_TOOLS.get_file_separator; --создадим универсальный R-скрипт для генерации прогнозных значений l_query := 'select '' library(ROracle) library(forecast) drv <- dbDriver("Oracle") con <- dbConnect(drv,"DWH","DWH","ORCL") rs <- dbSendQuery(con, " with t as (select t.* from TEMPERATURE_DATA t where t.observer_num = 26063 and t.year_num >= 2000) SELECT measure FROM (select t.year_num, 1 as month_num, t.ind_1 as measure from t union all select t.year_num, 2 as month_num, t.ind_2 from t union all select t.year_num, 3 as month_num, t.ind_3 from t union all select t.year_num, 4 as month_num, t.ind_4 from t union all select t.year_num, 5 as month_num, t.ind_5 from t union all select t.year_num, 6 as month_num, t.ind_6 from t union all select t.year_num, 7 as month_num, t.ind_7 from t union all select t.year_num, 8 as month_num, t.ind_8 from t union all select t.year_num, 9 as month_num, t.ind_9 from t union all select t.year_num, 10 as month_num, t.ind_10 from t union all select t.year_num, 11 as month_num, t.ind_11 from t union all select t.year_num, 12 as month_num, t.ind_12 from t) ORDER BY year_num, month_num ") data <- fetch(rs) p_startYear <- 2000 p_startMonth <- 1 p_endYear <- 2008 p_endMonth <- 12 p_numOfPredictedMonths <- 12 dataTS <- ts(data$MEASURE, start=c(p_startYear, p_startMonth), end=c(p_endYear, p_endMonth), frequency=12) dataFit <- HoltWinters(dataTS) forecastFit <- forecast(dataFit, p_numOfPredictedMonths) forecastData <- data.frame(MEASURE = forecastFit$mean, MONTH_KEY = c(time(forecastFit$mean)) ) dbWriteTable(con, "TEMPERATURE_FORECAST", forecastData, append = TRUE) '' as txt from dual'; l_rows := PKG_UNLOADER.run(p_query => l_query, p_town => '', p_tname => '', p_mode => 'truncate', p_dbdir => 'TEMP_DIR', p_filename => 'R_forecast', p_separator => '', p_enclosure => '', p_terminator => '', p_ctl => 'NO', p_header => 'NO'); --вызываем созданный ранее R-скрипт (необходимо включить путь до Rscript в PATH) l_command := 'Rscript "' || l_dir_path || l_file_separator || 'R_forecast.dat"'; PKG_JAVA_COMMON_TOOLS.host_command(l_command); --для отладки --DBMS_OUTPUT.get_lines(l_output, l_lines); --FOR i IN 1 .. l_lines LOOP -- DBMS_OUTPUT.put_line(l_output(i)); --END LOOP; --удалим R-скрипт l_rows := PKG_UNLOADER.remove(p_dbdir => 'TEMP_DIR', p_filename => 'R_forecast.dat'); end; /
После его выполнения запрос к таблице с прогнозными значениями возвращает следующие данные:
Создание отчета в Oracle BI Publisher, визуализирующего сформированные R-скриптом прогнозы
Наконец сведем все шаги воедино.
В виде отчета BI Publisher, который с помощью before report триггера будет выполнять PL/SQL код по генерации прогнозных данных;
визуализировать сформированные данные в виде графика;
с помощью after report триггера очищать сформированные данные.
Так как отчет может выполняться параллельно несколькими пользователями – следует разграничивать используемые в нем данные.
Для этого создадим sequence в БД.
create sequence R_FORECAST_SEQ;
Пересоздадим таблицу с результатами (порядок следования столбцов важен!):
create table TEMPERATURE_FORECAST ( measure VARCHAR2(50), month_key NUMBER, sess_key NUMBER, observer_num NUMBER )
Также немного усложним отчет – будем одним отчетом генерировать прогнозы для различных станций наблюдений за погодой.
Т.е. параметрами отчета будут станции наблюдения и год начала и окончания наблюдений.
Создадим PL/SQL пакет PKG_R_FORECAST с функциями before_report и after_report.
create or replace package PKG_R_FORECAST as P_YEAR_BEG varchar2(30); P_YEAR_END varchar2(30); P_OBSERVER_NUM varchar2(30); function get_sess_key return integer; function before_report(P_YEAR_BEG in varchar2, P_YEAR_END in varchar2, P_OBSERVER_NUM in varchar2) return boolean; function after_report return boolean; end PKG_R_FORECAST; / create or replace package body PKG_R_FORECAST as G_SESS_KEY integer; function get_sess_key return integer as begin return G_SESS_KEY; end get_sess_key; function before_report(P_YEAR_BEG in varchar2, P_YEAR_END in varchar2, P_OBSERVER_NUM in varchar2) return boolean as l_dir_path varchar2(512); l_file_separator varchar2(4); l_rows number; l_query varchar2(32000); l_command varchar2(512); l_output DBMS_OUTPUT.chararr; l_lines INTEGER := 1000; BEGIN G_SESS_KEY := R_FORECAST_SEQ.Nextval; --вычислим путь к директории TEMP_DIR select DIRECTORY_PATH into l_dir_path from all_directories where DIRECTORY_NAME = 'TEMP_DIR'; l_file_separator := PKG_JAVA_COMMON_TOOLS.get_file_separator; --создадим универсальный R-скрипт для генерации прогнозных значений l_query := 'select '' library(ROracle) library(forecast) drv <- dbDriver("Oracle") con <- dbConnect(drv,"DWH","DWH","ORCL") rs <- dbSendQuery(con, " with t as (select t.* from TEMPERATURE_DATA t where ((coalesce(null, ' || P_OBSERVER_NUM || ') is null) or (t.observer_num in (' || P_OBSERVER_NUM || '))) and t.year_num >= ' || P_YEAR_BEG || ' and t.year_num <= ' || P_YEAR_END || ') SELECT observer_num, measure FROM (select t.observer_num, t.year_num, 1 as month_num, t.ind_1 as measure from t union all select t.observer_num, t.year_num, 2 as month_num, t.ind_2 from t union all select t.observer_num, t.year_num, 3 as month_num, t.ind_3 from t union all select t.observer_num, t.year_num, 4 as month_num, t.ind_4 from t union all select t.observer_num, t.year_num, 5 as month_num, t.ind_5 from t union all select t.observer_num, t.year_num, 6 as month_num, t.ind_6 from t union all select t.observer_num, t.year_num, 7 as month_num, t.ind_7 from t union all select t.observer_num, t.year_num, 8 as month_num, t.ind_8 from t union all select t.observer_num, t.year_num, 9 as month_num, t.ind_9 from t union all select t.observer_num, t.year_num, 10 as month_num, t.ind_10 from t union all select t.observer_num, t.year_num, 11 as month_num, t.ind_11 from t union all select t.observer_num, t.year_num, 12 as month_num, t.ind_12 from t) ORDER BY observer_num, year_num, month_num ") data <- fetch(rs) p_startYear <- ' || P_YEAR_BEG || ' p_startMonth <- 1 p_endYear <- ' || P_YEAR_END || ' p_endMonth <- 12 p_numOfPredictedMonths <- 12 for (i in 1:length(unique(data$OBSERVER_NUM))) { iterObserver <- unique(data$OBSERVER_NUM)[i] iterObserverData <- data[data$OBSERVER_NUM == iterObserver,] iterObserverDataTS <- ts(iterObserverData$MEASURE, start=c(p_startYear, p_startMonth), end=c(p_endYear, p_endMonth), frequency=12) iterObserverDataFit <- HoltWinters(iterObserverDataTS) iterObserverForecastFit <- forecast(iterObserverDataFit, p_numOfPredictedMonths) iterObserverForecastData <- data.frame(MEASURE = iterObserverForecastFit$mean, MONTH_KEY = c(time(iterObserverForecastFit$mean)) ) iterObserverForecastData["SESS_KEY"] <- ' || G_SESS_KEY || ' iterObserverForecastData["OBSERVER_NUM"] <- iterObserver dbWriteTable(con, "TEMPERATURE_FORECAST", iterObserverForecastData, append = TRUE) } '' as txt from dual'; l_rows := PKG_UNLOADER.run(p_query => l_query, p_town => '', p_tname => '', p_mode => 'truncate', p_dbdir => 'TEMP_DIR', p_filename => 'R_forecast_' || G_SESS_KEY, p_separator => '', p_enclosure => '', p_terminator => '', p_ctl => 'NO', p_header => 'NO'); --вызываем созданный ранее R-скрипт (необходимо включить путь до Rscript в PATH) l_command := 'Rscript "' || l_dir_path || l_file_separator || 'R_forecast_' || G_SESS_KEY || '.dat"'; PKG_JAVA_COMMON_TOOLS.host_command(l_command); --для отладки --DBMS_OUTPUT.get_lines(l_output, l_lines); --FOR i IN 1 .. l_lines LOOP -- DBMS_OUTPUT.put_line(l_output(i)); --END LOOP; --удалим R-скрипт l_rows := PKG_UNLOADER.remove(p_dbdir => 'TEMP_DIR', p_filename => 'R_forecast_' || G_SESS_KEY || '.dat'); return true; END before_report; function after_report return boolean as BEGIN delete from TEMPERATURE_FORECAST t where t.sess_key = G_SESS_KEY; COMMIT; --!!!! return true; END after_report; end PKG_R_FORECAST; /
Создадим сам отчет в BI Publisher
Текст запроса набора данных MAIN:
with t as (select t.* from TEMPERATURE_DATA t where ((coalesce(null, :P_OBSERVER_NUM) is null) or (t.observer_num in (:P_OBSERVER_NUM))) and t.year_num >= :P_YEAR_BEG and t.year_num <= :P_YEAR_END), t_src as (SELECT observer_num, year_num || lpad(month_num, 2, '0') as month_key, measure FROM (select t.observer_num, t.year_num, 1 as month_num, t.ind_1 as measure from t union all select t.observer_num, t.year_num, 2 as month_num, t.ind_2 from t union all select t.observer_num, t.year_num, 3 as month_num, t.ind_3 from t union all select t.observer_num, t.year_num, 4 as month_num, t.ind_4 from t union all select t.observer_num, t.year_num, 5 as month_num, t.ind_5 from t union all select t.observer_num, t.year_num, 6 as month_num, t.ind_6 from t union all select t.observer_num, t.year_num, 7 as month_num, t.ind_7 from t union all select t.observer_num, t.year_num, 8 as month_num, t.ind_8 from t union all select t.observer_num, t.year_num, 9 as month_num, t.ind_9 from t union all select t.observer_num, t.year_num, 10 as month_num, t.ind_10 from t union all select t.observer_num, t.year_num, 11 as month_num, t.ind_11 from t union all select t.observer_num, t.year_num, 12 as month_num, t.ind_12 from t)), t_rslt as (select f.observer_num, trunc(f.month_key) || lpad(round((f.month_key - trunc(f.month_key)) * 12 + 1), 2, '0') as month_key, round(to_number(f.measure, '9999999999D99999999999999999999999999999', 'NLS_NUMERIC_CHARACTERS=''.,'''),2) as measure from TEMPERATURE_FORECAST f where f.sess_key = PKG_R_FORECAST.get_sess_key ) select s.observer_num, s.month_key, s.measure, 'Source' as measure_type from t_src s union all select to_char(r.observer_num), r.month_key, r.measure, 'Forecast' as measure_type from t_rslt r
Добрый день!
ОтветитьУдалитьЗанимаюсь попытками запустить предрассчет данных для отчета BIP.
Собственно, есть огромная проблема - не отрабатывает запуск пакетированной функции с параметрами.
Параметр описан, дефолт задан.
Собственно, не проходит view data в data model.
Говорит - Failed to load XML.
Внутри лога - PLS-00302: component 'PERIOD_DATE_START' must be declared
Очень нужна помощь знающего человека - в какую сторону копать.
Заранее огромное спасибо!
Добрый день!
ОтветитьУдалитьПопробуйте создать в спецификации пакета, который объявлен как defaulePackage для отчета, переменные для ВСЕХ параметров отчета.
Еще раз добрый день!
УдалитьОгромное спасибо за оперативный ответ!
К сожалению зря прождал все выходные пока включат сервер - не сработало. То есть пакетные переменные описал, описание вызова функции в BIP обновил, но снова ошибка:
Failed to load XML.
Правда, теперь в логе жалоба только на одну из двух переменных:
ORA-06550: line 4, column 42:
PLS-00201: identifier 'PERIOD_DATE_BEGIN' must be declared
А в прошлый раз(без задания пакетных переменных) было:
ORA-06550: line 2, column 18:
PLS-00302: component 'PERIOD_DATE_END' must be declared
ORA-06550: line 2, column 1:
PL/SQL: Statement ignored
ORA-06550: line 3, column 18:
PLS-00302: component 'PERIOD_DATE_BEGIN' must be declared
ORA-06550: line 3, column 1:
PL/SQL: Statement ignored
Что-то я совсем потерялся - сейчас все проходит, в смысле - без ошибки, но переменные пустые, хотя в BIP дефолты для переменных проставлены.
УдалитьОпределял - что с данными - простым селектом из дуала:
select 1 from dual
where :PERIOD_DATE_BEGIN > to_date('01.01.1990','dd.mm.yyyy')
and :PERIOD_DATE_END > to_date('01.01.1990','dd.mm.yyyy')
union all
select 2 from dual
where :PERIOD_DATE_BEGIN < to_date('01.01.1990','dd.mm.yyyy')
and :PERIOD_DATE_END < to_date('01.01.1990','dd.mm.yyyy')
union all
select 3 from dual
where :PERIOD_DATE_BEGIN is null
and :PERIOD_DATE_END is null
Собственно вопрос - куда копать?)
Вы не могли бы прислать мне на почту код пакета и скриншоты модели данных отчета, открытой в редакторе (скриншот вкладки "Набор данных", "Параметры" и общей вкладки) ?
Удалить1) Очень рекомендую создать в системе логирующий пакет - https://sites.google.com/site/obi2ru/PKG_LOG.zip?attredirects=0&d=1
УдалитьС его помощью можно отслеживать что именно происходит в PL/SQL пакете.
Пример вызова:
PKG_LOG.info('Мой супер пакет', 'Получено значение PERIOD_DATE_BEGIN равное ' || PERIOD_DATE_BEGIN);
2) Подозреваю, что вы определили в спецификации пакета переменные PERIOD_DATE_BEGIN и PERIOD_DATE_END с типом DATE.
КРАЙНЕ рекомендую определить их с типом varchar2(100), а также создать в спецификации пакета переменные
PERIOD_DATE_BEGIN_DT Date;
PERIOD_DATE_END_DT Date;
которые заполнять в процедуре пакета, являющейся beforeReport-триггером отчета:
PERIOD_DATE_BEGIN_DT := to_date(PERIOD_DATE_BEGIN, 'YYYY/MM/DD');
(форматную маску поймете когда будете логирующим пакетом писать в лог значения параметров)
1)
УдалитьК сожалению пока логирование не отладил - весь в мыле, сегодня на права в ГАИ сдаю)
Но увидел жалобу на RESULT_CACHE - ее надо прописать как глобальную?
2) Сделал, в смысле поменял тип на varchar2, добавил преобразование -
3) http://tempfile.ru/file/3095823
Вдогонку - третий слайд с результатами функции BEFORE_REPORT
Результат для BERFORE_REPORT_2 - такой же
С логирующим пакетом вам будет намного проще. поверьте!
УдалитьУдалите параметры ваших beforeReport-функций в пакете.
Внутри функций ссылайтесь на переменные в спецификации пакета - это и будут ваши параметры.
Не забудьте сделать to_date для строковых переменных.
Форматная маска будет, скорее всего, MM-dd-yyyy.
Прошу прошения, сидя в ГАИ совершенно забыл что база-то у нас десятка) Так что вариант с result_cache не подходит((
УдалитьНо логирование таки сделал для этой процедуры через таблицу, и проблему решил - оказывается дело было в том, что BIP передавал параметры следующим образом:
09-ОКТ-09 12.00.00,000000000 AM
И оттуда все проблемы и росли)
Огромное спасибо за помощь!)
С уважением, Иван.
Отлично, пожалуйста!
УдалитьЗдравствуйте, в нашей конторое только начинаем работать с ORACLE Publisher и не знаем многий функционал, подскажите пожалуйста есть ли какая возможнотьс генерить отчет и отправлять , только в том случае если есть данные, а если запрос ничего не возвращает ничего собственно и не делать ? или в паблишер в целом так невозможно работать
ОтветитьУдалитьТакая возможность есть (смотрите триггеры)
Удалить