
- •Глава 1. Изучение Системы управления базами данных и приложения субд.
- •1.1. Разработка клиентских приложений.
- •Классификация средств разработки приложений.
- •2.1.Классификация приложений.
- •2.2.Средства разработки, ориентированные на конкретные субд.
- •2.3.Средства разработки, универсальные по отношению к субд
- •2.4.Приложения в архитектуре "клиент-сервер"
- •2.5.Распределенные приложения
- •2.6.Разработка успешных приложений для субд Oracle
- •Влияние стандартов
- •Функциональные возможности
- •Заключение
2.6.Разработка успешных приложений для субд Oracle
В разных СУБД реализация функциональных возможностей различна. Создать приложение, полностью независимое от СУБД, чрезвычайно тяжело (исключая некоторые приложения, предназначенные только для чтения баз данных), а без детального понимания особенностей конкретной СУБД фактически невозможно.
Первоначально разработали и внедрили приложение планирования ресурсов в СУБД, в которой используется блокирование на уровне страниц и блокирование операций чтения (читатели блокируются писателями), кроме того, был создан индекс столбцов (resource_name и start_time) таблицы SCHEDULES:
create index schedules_idx on schedules( resource_name, start_time );
Также предположим, что бизнес-правило было реализовано с помощью триггера базы данных (после выполнения оператора INSERT, но перед фиксацией транзакции мы будем проверять, что для данного временного интервала в таблице существует только одна наша строка). В системе с блокированием страниц очень вероятно, что из-за необходимости обновления индексной страницы эти транзакции будут выполняться последовательно. При вставке новых значений столбцов RESOURCE_NAME и START_TIME индексная страница блокируется (то есть блокируются все близлежащие значения этих столбцов, находящиеся в данной странице). В такой системе наше приложение, несомненно, будет работать корректно, так как наши проверки перекрытия временных интервалов будут выполняться последовательно.
Если перенесем это приложение в среду СУБД Oracle и просто предположим, что оно будет работать так же корректно, то будем поражены. В среде СУБД Oracle, в которой реализовано блокирование на уровне строк, приложение будет работать "неправильно". Как говорили раньше, для сериализации доступа нужно было использовать предложение FOR UPDATE. Без него два пользователя могли одновременно зарезервировать один и тот же ресурс. Это непосредственное следствие непонимания функционирования СУБД в многопользовательском режиме.
Многие сталкивались с подобными проблемами столько раз, сколько приложения переносились из среды СУБД A в среду СУБД B. Когда приложение безупречно работает в СУБД A, но не работает или работает странно в СУБД B, первая мысль: СУБД B – "плохая" СУБД. Истинная правда заключается в том, что СУБД B выполняет это приложение по-другому – никакая СУБД не является неправильной или "плохой", они просто отличаются друг от друга. Знание и понимание механизмов их работы существенно помогает в разрешении подобных проблем.
Например, задача конвертировать некоторый код, написанный на языке Transact SQL (язык хранимых процедур в СУБД SQL Server) в код на языке PL/SQL. Разработчик, занимавшийся этим преобразованием, были недовольны, что SQL-запросы в СУБД Oracle возвращают "неправильный" ответ. Запросы выглядели примерно так:
declare
l_некоторая_переменная varchar2(25);
begin
if ( некоторое_условие )
then
l_некоторая_переменная := f( … );
end if;
for x in ( select * from T where x = l_некоторая_переменная )
loop ...
Здесь нужно было найти все строки таблицы T, значение столбца X в которых равнялось значению Null, если не выполнялось "некоторое условие", или значение X равнялось конкретному значению "некоторой переменной", если "некоторое условие" выполнялось. Жалоба состояла в том, что в СУБД Oracle этот запрос не возвращал никаких данных, если не устанавливалось какое-либо конкретное значение переменной L_НЕКОТОРАЯ_ПЕРЕМЕННАЯ (то есть у нее сохранялось значение Null). В СУБД Sybase и SQL Server это было не так – запрос находил строки, значение X в которых было равно Null. Я сталкиваюсь с этим почти при каждом переносе приложений из СУБД Sybase или SQL Server в СУБД Oracle. В языке SQL подразумевается использование трехзначной логики, и в СУБД Oracle реализация значений Null соответствуют стандарту ANSI SQL. В соответствии с этим стандартом результатом сравнения X со значением Null будет не значение True ("истина") или False ("ложь"), а – неизвестное значение. Следующий фрагмент показывает, что я имею в виду:
ops$tkyte@ORA8I.WORLD> select * from dual;
D
-
X
ops$tkyte@ORA8I.WORLD> select * from dual where null=null;
no rows selected
ops$tkyte@ORA8I.WORLD> select * from dual where null<>null;
no rows selected
Пример подтверждает, в СУБД Oracle значение Null ни равно, ни не равно значению Null. СУБД SQL Server по умолчанию так не работает: в СУБД SQL Server и Sybase значение Null равно значению Null. Ни в СУБД Oracle, ни в СУБД Sybase или SQL Server обработка операторов SQL не является неправильной – они просто отличаются друг от друга. Обе эти СУБД фактически отвечают требованиям стандарта ANSI, но, тем не менее, они работают по – разному. Существуют проблемы неоднозначности, обратной совместимости и т.п., которые приходится преодолевать. Например, в СУБД SQL Server поддерживается сравнение значений Null в соответствии со стандартом ANSI, но только не по умолчанию (в противном случае тысячи существующих унаследованных приложений этой СУБД не будут работать).
В данном случае одним из решений этой проблемы является замена исходного запроса на другой запрос:
select *
from t
where ( x = l_some_variable OR (x is null and l_some_variable is NULL ))
Однако это приводит к появлению другой проблемы. В СУБД SQL Server в этом запросе использовался индекс столбца Х. Но в СУБД Oracle это не так, поскольку в ней используются индексы типа B*-деревьев (более подробно о методах индексирования см. в главе 7), в которых значения Null не индексируются. Следовательно, для поиска значений Null индексы типа B*-деревьев не очень полезны.
В таком случае, чтобы минимизировать влияние на код, мы можем присвоить Х значение, которое никогда не встретится в действительности. Здесь значение Х по определению должно быть положительным числом, поэтому мы выбираем число, равное –1. Таким образом, запрос становится следующим:
select * from t where nvl(x,-1) = nvl(l_some_variable,-1)
И создаем индекс на базе функции (function - based index):
create index t_idx on t( nvl(x,-1) );
С минимальными изменениями достигаем того же конечного результата. Из выше изложенного важно понимать следующее:
СУБД отличаются друг от друга. Опыт работы с одной из них может быть частично использован в другой, но вы должны быть готовы к каким-то существенным различиям так же, как и к некоторым незначительным различиям;
незначительные различия (такие, как обработка значений Nulls) могут влиять на значительные различия (такие, как механизм управления конкурентным доступом);
единственный путь решения таких проблем – знать СУБД: и как она работает, и как реализованы ее функциональные возможности.
Разработчики часто спрашивают, как сделать что-то в среде конкретной СУБД. Например, они задают вопрос: "Как создать временную таблицу в хранимой процедуре?" Можно задать встречный вопрос: "Почему вы хотите сделать это?" Ответ был следующим: "В СУБД SQL Server мы создавали временные таблицы в наших хранимых процедурах, нам нужно сделать то же самое в СУБД Oracle."В таком случае ответ на вопрос прост: "Вам не нужно создавать временные таблицы в хранимых процедурах СУБД Oracle (вы только думаете, что это нужно сделать)." Фактически, делать это в СУБД Oracle не рекомендуется, в противном случае вы обнаружите, что:
выполнение операций DDL препятствует масштабируемости;
операции DDL выполняются медленно;
при выполнении операции DDL ваша транзакция фиксируется;
для доступа к таким таблицам необходимо использовать динамический SQL, а не статический;
динамический SQL в PL/SQL выполняется не так быстро или оптимизируется не так хорошо, как статический SQL.
Практический результат: вам не нужно делать точно тоже самое, что вы делали в СУБД SQL Server (если даже вам нужны временные таблицы в СУБД Oracle). В СУБД Oracle нужно делать то, что наилучшим образом подходит для СУБД Oracle. То же самое можно сказать про перенос приложений из СУБД Oracle в СУБД SQL Server, в которой не нужно создавать одну таблицу для совместного хранения временных данных всеми пользователями (именно так это делается в СУБД Oracle). В противном случае будут ограничиваться масштабируемость и конкурентный доступ.