|
Программирование >> Программирование баз данных
В этом очередном результирующем наборе присутствуют фактические результаты выполнения операторов UPDATE, INSERT и DELETE. В полном соответствии с задуманным удаляется строка с идентификатором SalesOrderlD, равным 43664, и вставляется новая строка с идентификатором SalesOrderlD, равным -99999. Такие данные обнаруживаются в таблице и отражают фактическое состояние дел, но курсор показывает сложившуюся ситуацию не столь точно. Следующий (и заключительный) результирующий набор позволяет выявить определенные различия между тем, что фактически представлено в курсоре, и тем, что обнаруживается после повторного запуска запроса. По стечению обстоятельств получено ровно пять строк, как и после первого вьшолнения запроса, и в соответствии с тем, что показал оператор SELECT, применяемый к реальной таблице. Но такое совпадение количества строк возникло чисто случайно. В действительности обнаруживается ряд существенных различий между тем, что показывает курсор, и тем, что находится в таблице. Первое различие проявляет себя вполне заметно, поскольку результирующий набор позволяет определить, что отсутствует одна строка. Дело в том, что ключевой курсор по-прежнему содержит сведения обо всех ключевых позициях в наборе ключей, но попытка вьшолнения поиска данных с помощью одной из ключевых позиций оканчивается неудачей. Переменной @@FETCH STATUS было присвоено значение -2, и мы имели возможность проверить наличие этого значения и сообщить о нем. Оператор SELECT показал, какие данные имеются, не сообщая ничего о том, что когда-то была строка, которая теперь исчезла. С другой стороны, с помощью оператора INSERT был вставлен объект, полностью недостижимый с помощью курсора. Вставленная строка не существовала в таблице ко времени создания курсора, поэтому курсор не содержит сведений о том, что эта строка появилась в таблице, и не выводит ее в результирующий набор. Ключевые курсоры могут оказаться очень удобными для использования в таких ситуациях, когда требуется обеспечить определенную чувствительность к изменениям в данных, но нет необходимости узнавать с точностью до минуты о том, что выполнена та или другая операция вставки. Кроме того, в зависимости от характера используемого результирующего набора и от особенностей создаваемого набора ключей, ключевые курсоры позволяют также добиться существенного сокращения объема данных, которые приходится дублировать и сохранять в базе данных tempdb, что может оказать положительное влияние на производительность всего сервера. Важное замечание. Если курсор определен как имеющий тип KEYSET, но задан на таблице без уникального индекса, то СУБД SQL Server неявно преобразует этот курсор в курсор типа STATIC. По-видимому, при этом достаточно неприятным сюрпризом для разработчика становится тот факт, что поведение курсора неожиданно изменяется, но на этом неприятности не заканчиваются, поскольку о происшедшем преобразовании не поступает никакой информации. И в этом нельзя упрекнуть создателей СУБД SQL Server, поскольку по умолчанию они не обязаны передавать какие-либо предупреждения о подобном преобразовании. К счастью, сам разработчик имеет возможность контролировать результаты создания курсора, используя в связи с выполнением этой операции такую опцию, как TYPE WARNING. Эта опция будет кратко описана в конце настоящей главы. Динамические курсоры Многие разработчики не сразу могут найти ответ на вопрос о том, в чем состоит основная особенность динамического курсора. Но чаще всего после небольшого раздумья они отвечают, будто такой основной особенностью динамического курсора является то, что он действительно динамический. Этот ответ является правильным. Вернее, почти правильным. Динамические курсоры не полностью соответствуют определению понятия динамичности, поскольку не позволяют заблаговременно узнавать об изменениях в основополагающих данных. Но, с другой стороны, динамические курсоры оправдывают свое название, поскольку являются чувствительными ко всем изменениям в тех данных, на которые распространяется их действие. Безусловно, как и за все хорошее в нашей жизни, за дополнительные преимущества динамичности приходится дорого платить. Это не означает, что работа с динамическими курсорами намного сложнее во всех отношениях. Например, если нужно обеспечить, чтобы вставленные строки добавлялись к курсору, никаких сложностей не возникает. Не возникает проблем и в том случае, если в курсоре должны правильно отображаться данные обновленных строк. Можно также без труда обеспечить, чтобы из набора данных курсора исключались удаленные строки (хотя фактически невозможно определить, что произошла операция удаления, поскольку полный просмотр курсора не позволяет даже обнаружить недостающую строку, в отличие от того, что происходит при использовании ключевого курсора). Но если требуется обеспечить в полном смысле параллельное выполнение операций с основополагающей таблицей и с курсором, обнаруживаются существенные затруднения (ведь при этом прргходится применять блокировки, действующие в течение более продолжительного времени, поэтому в многопользовательской базе данньгх конкуренция с другими пользователями становится почти неизбежной). А если необходимо также свести издержки к минимуму, то проблема становится еще более сложной (поскольку в процессе работы с динамическим курсором при выполнении каждого оператора FETCH вьщается еще один запрос к основополагающей таблице). Безусловно, разработчики знают об этих затруднениях в работе, возникающих при использовании динамических курсоров, но стараются смириться с ними, помня о том, что без преодоления препятствий в этой жизни нельзя ничего добиться. Тем не менее общепризнанной рекомендацией является то, при обычных обстоятельствах следует избегать использования динамических курсоров. Почему же нам приходится рассматривать динамические курсоры в настоящей книге? Дело в том, что мы можем понять, какие следствия влечет за собой решение о применении динамического курсора, лишь разобравшись в том, как работают эти курсоры. Прежде всего, динамические курсоры отличаются тем, что при выполнении каждого отдельно взятого оператора FETCH по существу происходит полная перестройка курсора. Под этим подразумевается то, что каждый раз снова и снова выполняется оператор SELECT, лежащий в основе запроса, наряду со связанной с ним конструкцией WHERE. Достаточно представить себе, к чему это приводит при использовании крупных наборов данных. Для оценки приложения с динамическим курсором, предназначенного для работы с огромной таблицей, нельзя найти другого определения, кроме как неэффективное. А в действительности подобные приложения не просто неэффективны, а крайне неэффективны. Одно из утверждений, касающихся использования реляционных СУБД, которое принято рассматривать как абсолютную истину, указывает, что динамические курсоры характеризуются крайне нижой производительностью, но практика показывает, что оно не всегда оправдывается. В частности, динамические курсоры являются довольно эффективными, если базовые таблицы имеют не очень крупные размеры. Причем даже краткие размышления на эту тему позволяют понять, почему динамические курсоры способны обеспечить более высокое быстродействие, чем принято считать, измеряемое как абсолютная производительность, особенно по сравнению с ключевыми курсорами. По мнению автора, не в пользу ключевых курсоров говорит то, что для работы с ними используется база данных tempdb. Разумеется, при использовании динамических курсоров в связи с ocyщecmвJleнueм каждой операции FETCH приходится выполнять намного больше работы, но все данные, выборка которых происходит при осередном запросе, чаще всего уже находятся полностью в кэше (что, конечно, зависит от объема оперативной памяти и загрузки системы). Это означает, что функционирование динамического курсора главным образом основано на применении оперативной памяти. С другой стороны, в большинстве систем набор ключей ключевого курсора хранится в базе данных tempdb, находящейся на диске (иначе говоря, на устройстве, характеризующимся гораздо более низким быстродействием по сравнению с оперативной памятью). Но по мере того, как увеличиваются размеры обрабатываемой таблицы, возрастает объем трафика, обрабатываемого сервером, повышается нагрузка на кэш, распределенный в оперативной памяти для СУБД SQL Seruer, и ключевые курсоры становятся более предпочтительными по сравнению с динамическими курсорами. Кроме того, абсолютное быстродействие- это не самый важный показатель, поскольку нельзя забывать и о проблемах обеспечения параллельной работы (тема, касающаяся распараллеливания операций с курсорами, будет рассматриваться ниже в данной главе), а динамические курсоры с точки зрения предоставления одновременного доступа к таблицам многочис-леннььч пользователям нельзя назвать самым лучшим средством. Таким образом, даже несмотря на то, что динамические курсоры, применяемые в серверных приложениях для работы с небольшими наборами данных, обеспечивают вполне приемлемое бьхтродейст-вие, нельзя не учитывать другие их недостатки. Перейдем к рассмотрению практического примера и снова вызовем на выполнение последний сценарий, внеся в него единственную модификацию - заменим слово KEYSET словом DYNAMIC: USE AdventureWorks /* Создается таблица, с которой будут проводиться эксперименты в этот раз */ SELECT SalesOrderlD, CustomerlD INTO CursorTable FROM Sales.SalesOrderHeader WHERE SalesOrderlD BETWEEN 43661 AND 43665 -- Создание уникального индекса в форме первичного ключа ALTER TABLE CursorTable ADD CONSTRAINT PKCursor PRIMARY KEY (SalesOrderlD) /* При выполнении оператора SELECT INTO было автоматичес1си сформировано значение ** IDENTITY, но решено использовать значение SalesOrderlD, сформированное в ** программе, поэтому необходимо ввести в действие параметр IDENTITY INSERT, ** чтобы можно было переопределить идентификационное значение
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |