Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курсовая_Рыбак.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
194.05 Кб
Скачать

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). В противном случае будут ограничиваться масштабируемость и конкурентный доступ.