Oracle DB, Oracle APEX, Linux etc.

среда, 27 января 2010 г.

Link Dump для разработчика Oracle APEX

Предыстория: множество появляющихся вопросов в этом форуме по Oracle APEX просто вынудили меня собрать несколько полезных ссылок для тех, кто вообще никогда не имел дело с Oracle Database, но неожиданно решил освоить разработку приложений Oracle APEX.

Возможно, для кого-то это станет открытием, но для того, чтобы разрабатывать, используя Oracle APEX, нужно знать Oracle SQL и PL/SQL хотя бы в пределах синтаксиса и основных функций. Мне кажется, это неправильно - разрабатывать приложения "наугад". Потому, господа и дамы, настоятельно рекомендую для прочтения и освоения:
  1. Oracle® Database SQL Reference и/или "Секреты Oracle SQL", Санжей Мишра, Алан Бьюли. Это поможет Вам избежать множества вопросов по SQL.

  2. Oracle® Database PL/SQL User's Guide and Reference и/или "Oracle PL/SQL для профессионалов", Стивен Фейерштейн, Билл Прибыл. Это поможет Вам избежать множества вопросов по PL/SQL.

  3. Oracle® Database 2 Day + Application Express Developer's Guide и Oracle® Application Express Advanced Tutorials. Это поможет Вам избежать множества вопросов по APEX и даст массу практического опыта. В принципе, здесь Вы можете остановиться, но если Вам стало интересно...

  4. Oracle® Database Application Developer's Guide - Fundamentals. Это снабдит Вас информацией о множестве способов разработки приложений, использующих Oracle Database.

  5. Ну, и помимо всего прочего, почитывайте Concepts, дядю Тома (1, 2, 3, 4) и другие источники, если Вам интересно, как всё это в принципе работает.


Всего!

PS: Хочу заметить, что упоминание вышеуказанных материалов ни в коей мере не является рекламой. Это просто ссылки на информацию, которую я считаю полезной.

Читать далее

воскресенье, 10 января 2010 г.

APEX: Описание новых возможностей версии 4.0

Я не буду растекаться мыслию по древу, потому что всё уже сделано до меня. Вместо этого я дам ссылку на довольно полезное приложение с описанием новых возможностей APEX 4.0, написанное на APEX 4.0: APEX 4.0 New Feature Descriptions.

Успехов!

PS: Мне кажется, или здесь действительно слишком часто звучит "APEX 4.0"?.. :)
Читать далее

суббота, 19 декабря 2009 г.

Oracle APEX: 4.0 Early Adopter Now!

Ну, кто ещё не слышал, для тех новость: попробовать APEX 4.0 можно уже сейчас - http://tryapexnow.com/.

Всем приятного тестирования!
Читать далее

четверг, 3 декабря 2009 г.

Oracle DB 11gR2: New features, new bugs...

Недавно наткнулся на новоприобретённый баг, появившийся в Oracle DB 11.2.0.1: MERGE позволяет обходить ограничения-проверки (CHECK CONSTRAINTS), изменяя данные.

Вот тестовый скрипт:

drop table test_merge;

create table test_merge as select 1 m, 2 n from dual;

alter table test_merge add constraint test_merge_chk check (m <= n);

update test_merge set n = m - 1;

rollback;

select * from test_merge;

merge into test_merge tgt
using (select m, m-1 n from test_merge) src
on (tgt.m = src.m)
when matched then update set tgt.n = src.n;

commit;

select * from test_merge;


И вот результат:


Таблица создана.


Таблица изменена.

update test_merge set n = m - 1
*
ошибка в строке 1:
ORA-02290: нарушено ограничение целостности CHECK(XXX.TEST_MERGE_CHK)



Откат завершен.


M N
---------- ----------
1 2


1 строка объединена.


Фиксация обновлений завершена.


M N
---------- ----------
1 0


Как оказалось, этот баг не проявляется, если указать ветку WHEN NOT MATCHED в операторе MERGE. Так что обход бага в случае, когда нужно только обновить данные, но не вставлять новые, прост:
merge into test_merge tgt 
using (select m, m-1 n from test_merge) src
on (tgt.m = src.m)
when matched then update set tgt.n = src.n
when not matched then insert (m) values(null) where 1 = 0;

Главное, чтобы никто не "соптимизировал" этот MERGE, удалив "лишнюю" ветку. ;)

Читать далее

вторник, 1 декабря 2009 г.

Oracle APEX: Скрывать регион с отчётом без данных

Итак, у Вас есть регион с отчётом, и Вы не хотите показывать этот регион, если в отчёте нет данных (то есть, запрос отчёта не вернул ни одной строки). Вроде всё просто:

0. Указываете Static ID в атрибутах региона с отчётом. Например, HIDING_REGION.
1. В атрибутах отчёта вставляете следующее в When No Data Found Message:




И всё! :) Скрипт вставится в получаемую страницу (а значит, и выполнится) только тогда, когда запрос для отчёта не вернёт строк.
Читать далее

суббота, 7 ноября 2009 г.

Oracle Dates: Поиск неправильных дат

Возможно, для кого-то это будет открытием, но Oracle Database допускает вставку таких дат, как 127-ое мая или 51.51.2009. :) Данные, которые передаются во внутреннем формате, например, через OCI или EXP/IMP, не проверяются. Но потом при использовании этих данных в приложении возникают ошибки вроде ORA-01801. И эти данные нужно найти и исправить.

В этом топике на sql.ru было найдено несколько следующих решений для задачи поиска.

Итак, данные:
CREATE TABLE tmp(ID NUMBER PRIMARY KEY, d DATE);

declare
d date;
BEGIN
INSERT INTO tmp VALUES(1, to_date('01.12.1900', 'dd.mm.yyyy'));
INSERT INTO tmp VALUES(2, to_date('01.12.-1900', 'dd.mm.syyyy'));
dbms_stats.convert_raw_value(hextoraw('7764057f7f77aa'), d);
INSERT INTO tmp VALUES(3, d);
dbms_stats.convert_raw_value(hextoraw('F7640d01010101'), d);
INSERT INTO tmp VALUES(4, d);
COMMIT;
end;/


Следующие решения основываются на формате хранения даты в Oracle Database. Если рассмотреть побайтно дамп даты, то видны следующие компоненты:
Номер байта слева  - Компонент
в функции DUMP

1 - столетие + 100
2 - год в столетии + 100
3 - месяц
4 - день
5 - час + 1
6 - минута + 1
7 - секунда + 1


Решение "в лоб", через указания границ для компонентов дампа даты:
WITH t AS 
(SELECT id, DUMP(d) AS dmp
FROM tmp),
comps as
(SELECT id, dmp
, substr(dmp, instr(dmp, ':', 1, 1)+1, instr(dmp, ',', 1, 1) - instr(dmp, ':') - 1) - 100 cc
, substr(dmp, instr(dmp, ',', 1, 1)+1, instr(dmp, ',', 1, 2) - instr(dmp, ',', 1, 1) - 1) - 100 yy
, substr(dmp, instr(dmp, ',', 1, 2)+1, instr(dmp, ',', 1, 3) - instr(dmp, ',', 1, 2) - 1) mm
, substr(dmp, instr(dmp, ',', 1, 3)+1, instr(dmp, ',', 1, 4) - instr(dmp, ',', 1, 3) - 1) dd
, substr(dmp, instr(dmp, ',', 1, 4)+1, instr(dmp, ',', 1, 5) - instr(dmp, ',', 1, 4) - 1) - 1 hh
, substr(dmp, instr(dmp, ',', 1, 5)+1, instr(dmp, ',', 1, 6) - instr(dmp, ',', 1, 5) - 1) - 1 mi
, substr(dmp, instr(dmp, ',', 1, 6)+1) - 1 ss
from t)
select id, dmp from comps
where cc not between -47 and 99
or (yy not between -99 and 99 or (cc = -47 and yy < -12))
or mm not between 1 and 12
or (mm in (1, 3, 5, 7, 8, 10, 12) and dd not between 1 and 31)
or (mm in (4, 6, 9, 11) and dd not between 1 and 30)
or ((mod(cc, 4) = 0 or (mod(cc, 4) <> 0 and mod(yy, 4) = 0)) and mm = 2 and dd not between 1 and 29)
or (mod(cc, 4) <> 0 and mod(yy, 4) <> 0 and mm = 2 and dd not between 1 and 28)
or hh not between 0 and 23
or mi not between 0 and 59
or ss not between 0 and 59;


Решение, использующее функцию IsDateValid, автор функции Elic:
create or replace function IsDateValid(aDate varchar2, aFormat varchar2) return int
is
d date;
begin
d := to_date(aDate, aFormat);
return 1;
exception
when others then
if sqlcode between -1899 and -1800 then
return 0;
else
raise;
end if;
end IsDateValid;
/

WITH t AS
(SELECT id, DUMP(d) AS dmp
FROM tmp),
comps as
(SELECT id, dmp
, substr(dmp, instr(dmp, ':', 1, 1)+1, instr(dmp, ',', 1, 1) - instr(dmp, ':') - 1) - 100 cc
, substr(dmp, instr(dmp, ',', 1, 1)+1, instr(dmp, ',', 1, 2) - instr(dmp, ',', 1, 1) - 1) - 100 yy
, substr(dmp, instr(dmp, ',', 1, 2)+1, instr(dmp, ',', 1, 3) - instr(dmp, ',', 1, 2) - 1) mm
, substr(dmp, instr(dmp, ',', 1, 3)+1, instr(dmp, ',', 1, 4) - instr(dmp, ',', 1, 3) - 1) dd
, substr(dmp, instr(dmp, ',', 1, 4)+1, instr(dmp, ',', 1, 5) - instr(dmp, ',', 1, 4) - 1) - 1 hh
, substr(dmp, instr(dmp, ',', 1, 5)+1, instr(dmp, ',', 1, 6) - instr(dmp, ',', 1, 5) - 1) - 1 mi
, substr(dmp, instr(dmp, ',', 1, 6)+1) - 1 ss
from t)
select id, dmp
from comps
where IsDateValid( to_char(dd, '900.')||to_char(mm, '900.')||to_char(cc*100+yy, '90000')||to_char(hh, '900')||':'||to_char(mi, '900')||':'||to_char(ss, '900')
, 'DD.MM.SYYYY HH24:MI:SS') = 0;


Решение, использующее собственную функцию IsDateDumpValid:
create or replace function IsDateDumpValid(p_date date)
return integer
is
l_date_str varchar2(32);
l_try_date date;
begin
select to_char(dd, '900.')||to_char(mm, '900.')||to_char(cc*100+yy, '90000')||to_char(hh, '900')||':'||to_char(mi, '900')||':'||to_char(ss, '900')
into l_date_str
from (SELECT substr(dmp, instr(dmp, ':', 1, 1)+1, instr(dmp, ',', 1, 1) - instr(dmp, ':') - 1) - 100 cc
, substr(dmp, instr(dmp, ',', 1, 1)+1, instr(dmp, ',', 1, 2) - instr(dmp, ',', 1, 1) - 1) - 100 yy
, substr(dmp, instr(dmp, ',', 1, 2)+1, instr(dmp, ',', 1, 3) - instr(dmp, ',', 1, 2) - 1) mm
, substr(dmp, instr(dmp, ',', 1, 3)+1, instr(dmp, ',', 1, 4) - instr(dmp, ',', 1, 3) - 1) dd
, substr(dmp, instr(dmp, ',', 1, 4)+1, instr(dmp, ',', 1, 5) - instr(dmp, ',', 1, 4) - 1) - 1 hh
, substr(dmp, instr(dmp, ',', 1, 5)+1, instr(dmp, ',', 1, 6) - instr(dmp, ',', 1, 5) - 1) - 1 mi
, substr(dmp, instr(dmp, ',', 1, 6)+1) - 1 ss
from (select dump(p_date) dmp from dual) t) comps;
begin
l_try_date := to_date(l_date_str, 'DD.MM.SYYYY HH24:MI:SS');
return 1;
exception
when others then
if sqlcode between -1899 and -1800 then
return 0;
else
raise;
end if;
end;
end;
/
select id from tmp where IsDateDumpValid(d) = 0;


Элегантное решение от Elic'а, использующее переполнение разрядов в дате. (К сожалению, им нельзя поймать случаи неправильных годов в датах.)
select id from tmp
where d <> d + numtodsinterval(trunc(1e8/3),'second') - numtodsinterval(trunc(1e8/3),'second');

Читать далее

понедельник, 5 октября 2009 г.

ApEx: обновление переменных в сессии без сабмита

Частенько так бывает, что нужно воспользоваться новым значением элемента на странице в скриптах или запросах без сабмита этой страницы. Например, обновить интерактивный отчёт или простой PPR-отчёт, выполнить процесс по требованию etc. Чтобы значение переменной, соответствующей этому элементу, изменилось в сессии, есть пара приёмов.

Чтобы ассинхронно обновить значение переменной в JavaScript, можно воспользоваться классом apex.ajax.ondemand:

/* Сохранить в сессии значение элемента асинхронно */
function saveThisItemAsync(p){
/* создаём пустой вызов к БД - без имени процесса и callback-функции */
var aget = new apex.ajax.ondemand();
/* добавляем нашу переменную-элемент */
aget.ajax.add(p.id, $v(p));
aget._get();
}


Для синхронного обновления можно воспользоваться им же, просто вызвав ajax.get(). :) Вот JS-функция для синхронного обновления значения переменной в сессии:

/* Сохранить в сессии значение элемента */
function saveThisItem(p){
var get = new apex.ajax.ondemand();
get.ajax.add(p.id, $v(p));
get.ajax.get();
}


Если кто-то хочет посмотреть, что там внутри apex.ajax.ondemand находится - пожалуйста:

apex.ajax = {
...
ondemand : function (pWidget,pReturn){
var that = this;
this.ajax = new htmldb_Get(null,$x('pFlowId').value,'APPLICATION_PROCESS='+pWidget,0);
this._get = _get;
this._set = _set;
this._return = !!pReturn?pReturn:_return;
return;
function _get(pValue){
that.ajax.GetAsync(that._return);
}
function _set(pValue){}
function _return(pValue){}
}
}

Таким образом, всё, что можно делать с htmldb_Get, можно делать и с apex.ajax.ondemand. Просто иногда короче. :)
Спасибо за информацию для размышления и написания этого поста kvad на sql.ru: Oracle APEX за этот пост
.


Читать далее