Программирование >>  Oracle 

1 ... 11 12 13 [ 14 ] 15 16 17 ... 469


insert into t values (x.username, x.user id, x.created); end loop ;

Результат выполнения оператора SELECT * FROM T предопределен в момент начала выполнения запроса. Оператор SELECT не будет видеть новых данных, генери-руем1х операторами INSERT. Представьте себе, что было бы в противном случае: оператор превратился бы в бесконечный цикл. Если бы по мере генерации оператором INSERT дополнительных строк в таблице Т, оператор SELECT мог видеть эти вставляемые строки, представленный выше фрагмент кода создал бы неизвестное количество строк. Если бы в таблице Т первоначально б1ло 10 строк, в результате могло бы получиться 20, 21, 23 или бесконечное количество строк. Точно предсказать результат было бы невозможно. Согласованность по чтению обеспечивается для всех операторов, так что операторы INSERT, вроде представленного ниже, тоже работают предсказуемо:

insert into t select * from t;

Оператор INSERT получит согласованное по чтению представление таблицы Т - он не увидит строки, которые сам же только что вставил, и будет вставлять только строки, существовавшие на момент начала его выполнения. Во многих СУБД подобные рекурсивные операторы просто не разрешены, поскольку они не могут определить, сколько строк вообще будет вставлено.

Поэтому если вы привыкли к реализации согласованности и одновременности запросов в других СУБД или просто никогда не сталкивались с такими понятиями (не имеете реального опыта работы с СУБД), то теперь понимаете, насколько важно для вашей работы их понимание. Чтобы максимально использовать потенциальные возможности СУБД Oracle, необходимо понимать эти проблемы и способы их решения именно в Oracle, а не в других СУБД.

Независимость от СУБД?

Вы, наверное, уже поняли направление моей мысли. Я сс1лался на другие СУБД и описывал различия реализации одних и тех же возможностей в каждой из них. Я убежден: за исключением некоторых приложений, исключительно читающих из базы данных, создать полностью независимое от СУБД и при этом масштабируемое приложение крайне сложно и даже практически невозможно, не зная особенностей работы всех

СУБД.

Например, давайте вернемся к первому примеру планировщика ресурсов (до добавления конструкции FOR UPDATE). Предположим, это приложение было разработано на СУБД с моделью блокирования/обеспечения одновременного доступа, полностью отличающейся от принятой в Oracle. Я собираюсь продемонстрировать, что при переводе приложения с одной СУБД на другую необходимо проверять, работает ли оно корректно в новой среде.

Предположим, что первоначально приложение по планированию ресурсов работало в СУБД, использующей блокирование на уровне страниц и блокировку чтения (чтение блокируется при изменении считываемых данных), и для таблицы SCHEDULES б1л создан индекс:

create index schedules idx on schedules(resource name, start time);



Предположим также, что бизнес-правило было реализовано с помощью триггера (после выполнения оператора INSERT, но перед фиксацией транзакции мы проверяем, что для указанного временного интервала в базе данных имеется только наша, только что вставленная строка). В системе с блокированием на уровне страниц, из-за изменения страницы индекса по столбцам RESOURCE NAME и START TIME, очень вероятно, что транзакции будут выполняться строго последовательно. Система будет выполнять вставки поочередно, поскольку страница индекса блокируется (все близкие значения по полю STARTTIME для одного ресурса RESOURCE NAME будут находиться на той же странице). В такой СУБД с блокированием на уровне страниц наше приложение, вероятно, будет работать нормально, так как перекрытие выделяемых ресурсов будет проверяться последовательно, а не одновременно.

Если просто перенести это приложение в СУБД Oracle, исходя из предположения, что она работает точно так же, можно получить шок. В СУБД Oracle, выполняющей блокирование на уровне строк и не блокирующей чтения, оно окажется некорректным. Как уже было показано, необходимо использовать конструкцию FOR UPDATE для упорядочения доступа. Без этой конструкции два пользователя могут зарезервировать ресурс на одно и то же время. Это будет прямым следствием непонимания особенностей работы используемой СУБД в многопользовательской среде.

С подобными проблемами я сталкивался многократно при переносе приложений из СУБД А в СУБД Б. Когда приложение, без проблем работавшее в СУБД А, не работает или работает весьма странно в СУБД Б, сразу же возникает мысль, что СУБД Б - плохая . Правда, однако, в том, что СУБД Б работает иначе. Ни одна из СУБД не ошибается и не является плохой - они просто разные. Знание и понимание особенностей их работы поможет успешно решить подобные проблемы.

Совсем недавно я помогал перевести код с языка Transact SQL (язык создания хранимых процедур для СУБД SQL Server) на PL/SQL. Разработчик, занимавшийся переводом, жаловался, что SQL-запросы в Oracle возвращают неправильный ответ. Запросы выглядели следующим образом:

declare

l soma variable varchar2(25);

begin

if (sorae condition) then

l some variable : =f (...); end if;

for x in (select * from T where x = l some variable) loop

Целью является получение всех строк таблицы Т, которые в столбце X имеют пустое значение, если некоторое условие не выполнено, или определенное значение, если это условие выполнено.

Суть жалобы состояла в том, что, в Oracle этот запрос не возвращал данных, если переменная L SOME VARIABLE не получала значения явно (когда у нее оставалось



значение NULL). В СУБД Sybase или SQL Server все было не так - запрос находил строки с пустым (NULL) значением в столбце X. Я встречался с этим практически при любом переводе приложения с СУБД Sybase или SQL Server на Oracle. Язык SQL предполагает использование трехзначной логики, и СУБД Oracle реализует пустые значения так, как того требует стандарт ANSI SQL. По этим правилам сравнение столбца X со значением NULL не дает ни True, ни False - результат фактически неизвестен. Следующий пример показывает, что я имею в виду:

ops$tkyte@ORA8I.WORLD> select * from dual;

ops$tkyte@ORA8I.WORLD> select * from dual where null=null; no rows selected

ops$tkyte@ORA8I.WORLD> select

no rows selected

from dual where null<>null;

В первый раз это может показаться странным: в Oracle NULL не равен и не не равен NULL. СУБД SQL Server по умолчанию ведет себя не так: в SQL Server и Sybase NULL равен NULL. Ни Oracle, ни Sybase, ни SQL Server не выполняет операторы SQL неправильно - они просто делают это по-разному. Все эти СУБД якобы соответствуют стандарту ANSI, но все равно работают по-разному. Есть неоднозначности, проблемы совместимости с прежними версиями и так далее, которые необходимо решать. Например, СУБД SQL Server поддерживает метод сравнения со значением NULL, диктуемый стандартом ANSI, но не по умолчанию (это нарушило бы работу тысяч уже существующих приложений, созданных для этой СУБД).

Одним из решений проблемы могло быть переформулирование запроса следующим образом:

select * irom t where (x

l some variable OR (x is null and l some variable is NULL))

Однако это привело бы к еще одной проблеме. В СУБД SQL Server при выполнении этого запроса использовался бы индекс по столбцу X. В СУБД Oracle индекс на основе В*-дерева (подробнее о методах индексирования читайте в главе 7) не позволяет индексировать значения ключа NULL. Поэтому, если необходимо найти пустые значения, индексы на основе В*-деревьев не сильно помогут.

В рассматриваемом случае, чтобы свести к минимуму изменения в коде, столбцу X присваивалось значение, которого не могло быть в реальных данных. Так, X, по определению, б]л числом положительным, поэтому было выбрано значение -1. Запрос приобрел следующий вид:

select * irom t where nvl(x,-l) = nvl(l some variable,-1)



1 ... 11 12 13 [ 14 ] 15 16 17 ... 469

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика