Будь умным!


У вас вопросы?
У нас ответы:) SamZan.net

темах принимаемая по умолчанию база данных может быть сформирована непосредственно в процессе установки и н.html

Работа добавлена на сайт samzan.net:

Поможем написать учебную работу

Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.

Предоплата всего

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 9.11.2024

PAGE  3

База данных

Создание базы данных

В различных СУБД процедура создания баз данных обычно закрепляется только за администратором баз данных. В однопользовательских системах принимаемая по умолчанию база данных может быть сформирована непосредственно в процессе установки и настройки самой СУБД. Стандарт SQL не определяет, как должны создаваться базы данных, поэтому в каждом из диалектов языка SQL обычно используется свой подход. В соответствии со стандартом SQL, таблицы и другие объекты базы данных существуют в некоторой среде. Помимо всего прочего, каждая среда состоит из одного или более каталогов, а каждый каталог – из набора схем. Схема представляет собой поименованную коллекцию объектов базы данных, некоторым образом связанных друг с другом (все объекты в базе данных должны быть описаны в той или иной схеме). Объектами схемы могут быть таблицы, представления, домены, утверждения, сопоставления, толкования и наборы символов. Все они имеют одного и того же владельца и множество общих значений, принимаемых по умолчанию.

Стандарт SQL оставляет за разработчиками СУБД право выбора конкретного механизма создания и уничтожения каталогов, однако механизм создания и удаления схем регламентируется посредством операторов CREATE SCHEMA и DROP SCHEMA. В стандарте также указано, что в рамках оператора создания схемы должна существовать возможность определения диапазона привилегий, доступных пользователям создаваемой схемы. Однако конкретные способы определения подобных привилегий в разных СУБД различаются.

В настоящее время операторы CREATE SCHEMA и DROP SCHEMA реализованы в очень немногих СУБД. В других реализациях, например, в СУБД MS SQL Server, используется оператор CREATE DATABASE.

Создание базы данных в среде MS SQL Server

Процесс создания базы данных в системе SQL-сервера состоит из двух этапов: сначала организуется сама база данных, а затем принадлежащий ей журнал транзакций. Информация размещается в соответствующих файлах, имеющих расширения *.mdf (для базы данных) и *.ldf. (для журнала транзакций). В файле базы данных записываются сведения об основных объектах (таблицах, индексах, просмотрах и т.д.), а в файле журнала транзакций – о процессе работы с транзакциями (контроль целостности данных, состояния базы данных до и после выполнения транзакций).

Создание базы данных в системе SQL-сервер осуществляется командой CREATE DATABASE. Следует отметить, что процедура создания базы данных в SQL-сервере требует наличия прав администратора сервера.

<определение_базы_данных> ::=

  CREATE DATABASE имя_базы_данных

  [ON [PRIMARY]

  [ <определение_файла>  [,...n] ]

  [,<определение_группы> [,...n] ] ]

  [ LOG ON {<определение_файла>[,...n] } ]

  [ FOR LOAD | FOR ATTACH ]

Рассмотрим основные параметры представленного оператора.

При выборе имени базы данных следует руководствоваться общими правилами именования объектов. Если имя базы данных содержит пробелы или любые другие недопустимые символы, оно заключается в ограничители (двойные кавычки или квадратные скобки). Имя базы данных должно быть уникальным в пределах сервера и не может превышать 128 символов.

При создании и изменении базы данных можно указать имя файла, который будет для нее создан, изменить имя, путь и исходный размер этого файла. Если в процессе использования базы данных планируется ее размещение на нескольких дисках, то можно создать так называемые вторичные файлы базы данных с расширением *.ndf. В этом случае основная информация о базе данных располагается в первичном (PRIMARY) файле, а при нехватке для него свободного места добавляемая информация будет размещаться во вторичном файле. Подход, используемый в SQL-сервере, позволяет распределять содержимое базы данных по нескольким дисковым томам.

Параметр ON определяет список файлов на диске для размещения информации, хранящейся в базе данных.

Параметр PRIMARY определяет первичный файл. Если он опущен, то первичным является первый файл в списке.

Параметр LOG ON определяет список файлов на диске для размещения журнала транзакций. Имя файла для журнала транзакций генерируется на основе имени базы данных, и в конце к нему добавляются символы _log.

При создании базы данных можно определить набор файлов, из которых она будет состоять. Файл определяется с помощью следующей конструкции:

<определение_файла>::=

  ([ NAME=логическое_имя_файла,]

  FILENAME='физическое_имя_файла'

  [,SIZE=размер_файла ]

  [,MAXSIZE={max_размер_файла |UNLIMITED } ]

  [, FILEGROWTH=величина_прироста ] )[,...n]

Здесь логическое имя файла – это имя файла, под которым он будет опознаваться при выполнении различных SQL-команд.

Физическое имя файла предназначено для указания полного пути и названия соответствующего физического файла, который будет создан на жестком диске. Это имя останется за файлом на уровне операционной системы.

Параметр SIZE определяет первоначальный размер файла; минимальный размер параметра – 512 Кб, если он не указан, по умолчанию принимается 1 Мб.

Параметр MAXSIZE определяет максимальный размер файла базы данных. При значении параметра UNLIMITED максимальный размер базы данных ограничивается свободным местом на диске.

При создании базы данных можно разрешить или запретить автоматический рост ее размера (это определяется параметром FILEGROWTH) и указать приращение с помощью абсолютной величины в Мб или процентным соотношением. Значение может быть указано в килобайтах, мегабайтах, гигабайтах, терабайтах или процентах (%). Если указано число без суффикса МБ, КБ или %, то по умолчанию используется значение MБ. Если размер шага роста указан в процентах (%), размер увеличивается на заданную часть в процентах от размера файла. Указанный размер округляется до ближайших 64 КБ.

Дополнительные файлы могут быть включены в группу:

<определение_группы>::=FILEGROUP имя_группы_файлов

   <определение_файла>[,...n]

Пример 3.1. Создать базу данных, причем для данных определить три файла на диске C, для журнала транзакций – два файла на диске C.

CREATE DATABASE Archive

ON PRIMARY ( NAME=Arch1,

 FILENAME=’c:\user\data\archdat1.mdf’,

SIZE=100MB, MAXSIZE=200, FILEGROWTH=20),

(NAME=Arch2,  

 FILENAME=’c:\user\data\archdat2.mdf’,

 SIZE=100MB, MAXSIZE=200, FILEGROWTH=20),

(NAME=Arch3,

 FILENAME=’c:\user\data\archdat3.mdf’,

 SIZE=100MB, MAXSIZE=200, FILEGROWTH=20)

LOG ON

(NAME=Archlog1,

 FILENAME=’c:\user\data\archlog1.ldf’,

   SIZE=100MB, MAXSIZE=200, FILEGROWTH=20),

(NAME=Archlog2,

 FILENAME=’c:\user\data\archlog2.ldf’,

   SIZE=100MB, MAXSIZE=200, FILEGROWTH=20)

Пример 3.1. Создание базы данных. (html, txt)

Изменение базы данных

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

<изменение_базы_данных> ::=  

 ALTER DATABASE имя_базы_данных

 { ADD FILE <определение_файла>[,...n]

   [TO FILEGROUP имя_группы_файлов ]

 | ADD LOG FILE <определение_файла>[,...n]

 | REMOVE FILE логическое_имя_файла

 | ADD FILEGROUP имя_группы_файлов

 | REMOVE FILEGROUP имя_группы_файлов

 | MODIFY FILE <определение_файла>

 | MODIFY FILEGROUP имя_группы_файлов

 <свойства_группы_файлов>}

Как видно из синтаксиса, за один вызов команды может быть изменено не более одного параметра конфигурации базы данных. Если необходимо выполнить несколько изменений, придется разбить процесс на ряд отдельных шагов.

В базу данных можно добавить (ADD) новые файлы данных (в указанную группу файлов или в группу, принятую по умолчанию) или файлы журнала транзакций.

Параметры файлов и групп файлов можно изменять (MODIFY).

Для удаления из базы данных файлов или групп файлов используется параметр REMOVE. Однако удаление файла возможно лишь при условии его освобождения от данных. В противном случае сервер не разрешит удаление.

В качестве свойств группы файлов используются следующие:

READONLY – группа файлов используется только для чтения; READWRITE – в группе файлов разрешаются изменения; DEFAULT – указанная группа файлов принимается по умолчанию.

Удаление базы данных

Удаление базы данных осуществляется командой:

DROP DATABASE имя_базы_данных [,...n]

Удаляются все содержащиеся в базе данных объекты, а также файлы, в которых она размещается. Для исполнения операции удаления базы данных пользователь должен обладать соответствующими правами.

Таблица

Создание таблицы

После создания общей структуры базы данных можно приступить к созданию таблиц, которые представляют собой отношения, входящие в состав проекта базы данных.

Таблица – основной объект для хранения информации в реляционной базе данных. Она состоит из содержащих данные строк и столбцов, занимает в базе данных физическое пространство и может быть постоянной или временной.

Поле, также называемое в реляционной базе данных столбцом, является частью таблицы, за которой закреплен определенный тип данных. Каждая таблица базы данных должна содержать хотя бы один столбец. Строка данных – это запись в таблице базы данных, она включает поля, содержащие данные из одной записи таблицы.

Приступая к созданию таблицы, необходимо иметь ответы на ряд вопросов:

  •  Как будет называться таблица?
  •  Как будут называться столбцы (поля) таблицы?
  •  Какие типы данных будут закреплены за каждым столбцом?
  •  Какой размер памяти должен быть выделен для хранения каждого столбца?
  •  Какие столбцы таблицы требуют обязательного ввода?
  •  Из каких столбцов будет состоять первичный ключ?

Базовый синтаксис оператора создания таблицы имеет следующий вид:

<определение_таблицы> ::=

  CREATE TABLE имя_таблицы

  (имя_столбца тип_данных

    [NULL | NOT NULL ] [,...n])

Приведенный стандарт совпадает с реализацией оператора создания таблицы в среде MS SQL Server.

Главное в команде создания таблицы – определение имени таблицы и описание набора имен полей, которые указываются в соответствующем порядке. Кроме того, этой командой оговариваются типы данных и размеры полей таблицы.

Ключевое слово NULL используется для указания того, что в данном столбце могут содержаться значения NULL. Значение NULL отличается от пробела или нуля – к нему прибегают, когда необходимо указать, что данные недоступны, опущены или недопустимы. Если указано ключевое слово NOT NULL, то будут отклонены любые попытки поместить значение NULL в данный столбец. Если указан параметр NULL, помещение значений NULL в столбец разрешено. По умолчанию стандарт SQL предполагает наличие ключевого слова NULL.

Мы использовали упрощенную версию оператора CREATE TABLE стандарта SQL. Его полная версия приводится при обсуждении вопросов обеспечения целостности данных.

Пример 3.2. Создать таблицу для хранения данных о товарах, поступающих в продажу в некоторой торговой фирме. Необходимо учесть такие сведения, как название и тип товара, его цена, сорт и город, где товар производится.

CREATE TABLE Товар

(Название       VARCHAR(50) NOT NULL,

   Цена        MONEY NOT     NULL,

   Тип         VARCHAR(50) NOT NULL,

   Сорт        VARCHAR(50),

   ГородТовара VARCHAR(50))

Пример 3.2. Создание таблицы для хранения данных о товарах, поступающих в продажу в некоторой торговой фирме. (html, txt)

Пример 3.3.Создать таблицу для сохранения сведений о постоянных клиентах с указанием названий города и фирмы, фамилии, имени и отчества клиента, номера его телефона.

CREATE TABLE  Клиент

   (Фирма       VARCHAR(50) NOT NULL,

   Фамилия      VARCHAR(50) NOT NULL,

   Имя          VARCHAR(50) NOT NULL,

   Отчество     VARCHAR(50),

   ГородКлиента VARCHAR(50),

   Телефон      CHAR(10) NOT NULL)

Пример 3.3. Создание таблицы для сохранения сведений о постоянных клиентах. (html, txt)

Изменение таблицы

Структура существующей таблицы может быть модифицирована с помощью команды ALTER TABLE, упрощенный синтаксис которой представлен ниже:

ALTER TABLE имя_таблицы

 {[ADD [COLUMN] имя_столбца тип_данных [

   NULL | NOT NULL ]]

 | [DROP [COLUMN] имя_столбца]}

В среде MS SQL Server упрощенный синтаксис команды модификации таблицы имеет вид:

ALTER TABLE имя_таблицы

 {[ALTER COLUMN имя_столбца

 {новый_тип_данных [(точность[,масштаб])]

   [ NULL | NOT NULL ]}]

 | ADD { [имя_столбца тип_данных]

 | имя_столбца AS выражение } [,...n]

 | DROP {COLUMN имя_столбца}[,...n]

 }

Команда позволяет добавлять и удалять столбцы, изменять их определения.

Одно из основных правил при добавлении столбцов в существующую таблицу гласит: когда в таблице уже содержатся данные, добавляемый столбец не может быть определен с атрибутом NOT NULL. Этот атрибут означает, что для каждой строки данных соответствующий столбец должен содержать некоторое значение, поэтому добавление столбца с атрибутом NOT NULL приводит к появлению противоречия – уже существующие строки данных таблицы не будут иметь в новом столбце ненулевых значений.

Тем не менее существует способ добавления обязательных полей в существующую таблицу. Для этого необходимо:

  •  добавить в таблицу новый столбец, определив его с атрибутом NULL (т.е. столбец не обязан содержать каких-либо значений);
  •  ввести в новый столбец какие-либо значения для каждой строки данных таблицы;
  •  убедившись, что новый столбец содержит ненулевые значения для каждой строки данных, изменить структуру таблицы, заменив атрибут этого столбца на NOT NULL.

При изменении определений столбцов следует принимать во внимание некоторые общепринятые правила:

  •  размер столбца может быть увеличен до максимального значения, допускаемого соответствующим типом данных;
  •  размер столбца может быть уменьшен только в том случае, если содержащееся в нем наибольшее значение не будет превосходить его нового размера;
  •  количество разрядов числового типа данных всегда может быть увеличено;
  •  количество разрядов числового типа данных может быть уменьшено только в том случае, если количество разрядов наибольшего значения в соответствующем столбце не будет превосходить нового числа разрядов, определенного для этого столбца;
  •  количество десятичных знаков числового типа данных может быть уменьшено или увеличено;
  •  тип данных столбца, как правило, может быть изменен.

Некоторые реализации фактически могут ограничить разработчика в использовании некоторых опций команды ALTER TABLE. Например, может оказаться недопустимым удаление столбцов из существующей таблицы. Чтобы добиться этого, сначала потребуется удалить саму таблицу и только потом заново ее построить с нужными столбцами. Причем уже внесенные в таблицу данные будут потеряны.

Возможны трудности, связанные с удалением из таблицы столбца, который зависит от некоторого столбца другой таблицы. В таком случае сначала придется удалить ограничение столбца, а затем сам столбец.

Пример 3.4. Добавить в таблицу Клиент поле для номера расчетного счета.

ALTER TABLE Клиент ADD Рас_счет CHAR(20)

Пример 3.4. Добавление в таблицу Клиент поля для номера расчетного счета. (html, txt)

Удаление таблицы

С течением времени структура базы данных меняется: создаются новые таблицы, а прежние становятся ненужными и удаляются из базы данных с помощью оператора:

DROP TABLE имя_таблицы [RESTRICT | CASCADE]

Следует отметить, что эта команда удалит не только указанную таблицу, но и все входящие в нее строки данных. Если требуется удалить из таблицы лишь данные, сохранив структуру таблицы, следует воспользоваться командой DELETE.

Оператор DROP TABLE дополнительно позволяет указывать, следует ли операцию удаления выполнять каскадно. Если в операторе указано ключевое слово RESTRICT, то при наличии в базе данных хотя бы одного объекта, существование которого зависит от удаляемой таблицы, выполнение оператора DROP TABLE будет отменено. Если указано ключевое слово CASCADE, автоматически удаляются и все прочие объекты базы данных, чье существование зависит от удаляемой таблицы, а также другие объекты, зависящие от удаляемых объектов. Общий эффект от выполнения оператора DROP TABLE с ключевым словом CASCADE может оказаться весьма ощутимым, поэтому подобные операторы следует использовать с максимальной осторожностью.

Чаще всего оператор DROP TABLE используется для исправления ошибок, допущенных при создании таблицы. Если таблица была создана с некорректной структурой, можно воспользоваться оператором DROP TABLE для ее удаления, после чего создать таблицу заново.

Индексы

Индексы в стандарте языка

Индексы представляют собой структуру, позволяющую выполнять ускоренный доступ к строкам таблицы на основе значений одного или более ее столбцов. Наличие индекса может существенно повысить скорость выполнения некоторых запросов и сократить время поиска необходимых данных за счет физического или логического их упорядочивания. Индекс – это набор ссылок, упорядоченных по определенному столбцу таблицы, который в данном случае будет называться индексированным столбцом. Хотя индекс и связан с конкретным столбцом (или столбцами) таблицы, все же он является самостоятельным объектом базы данных.

Физически индекс – всего лишь упорядоченный набор значений из индексированного столбца с указателями на места физического размещения исходных строк в структуре базы данных. Когда пользователь выполняет обращающийся к индексированному столбцу запрос, СУБД автоматически анализирует индекс для поиска требуемых значений.

Однако, поскольку индексы должны обновляться системой при каждом внесении изменений в их базовую таблицу, они создают дополнительную нагрузку на систему.

Индексы обычно создаются с целью удовлетворения определенных критериев поиска после того, как таблица уже находилась некоторое время в работе и увеличилась в размерах. Создание индексов не предусмотрено стандартом SQL, однако большинство диалектов поддерживают как минимум следующий оператор:

CREATE [ UNIQUE ] INDEX имя_индекса

 ON имя_таблицы(имя_столбца[ASC|DESC][,...n])

Указанные в операторе столбцы составляют ключ индекса. Индексы могут создаваться только для базовых таблиц, но не для представлений. Если в операторе указано ключевое слово UNIQUE, уникальность значений ключа индекса будет автоматически поддерживаться системой. Требование уникальности значений обязательно для первичных ключей, а также возможно и для других столбцов таблицы (например, для альтернативных ключей). Хотя создание индекса допускается в любой момент, при его построении для уже заполненной данными таблицы могут возникнуть проблемы, связанные с дублированием данных в различных строках. Следовательно, уникальные индексы (по крайней мере, для первичного ключа) имеет смысл создавать непосредственно при формировании таблицы. В результате система сразу возьмет на себя контроль за уникальностью значений данных в соответствующих столбцах.

Если созданный индекс впоследствии окажется ненужным, его можно удалить с помощью оператора

DROP INDEX имя_индекса

Индексы в среде MS SQL Server

Индекс представляет собой средство, помогающее ускорить поиск необходимых данных за счет физического или логического их упорядочивания. Индекс представляет собой набор ссылок, упорядоченных по определенному столбцу таблицы, который в данном случае будет называться индексированным столбцом. Индексы - это наборы уникальных значений для некоторой таблицы с соответствующими ссылками на данные. Они расположены в самой таблице и являются удобным внутренним механизмом системы SQL-сервера, с помощью которого осуществляется доступ к данным наиболее оптимальным способом. В среде SQL Server реализованы эффективные алгоритмы поиска нужного значения в строго определенной последовательности данных. Ускорение поиска достигается именно за счет того, что данные представляются упорядоченными (хотя физически, в зависимости от типа индекса, они могут храниться в соответствии с очередностью их добавления в таблицу). К настоящему времени разработаны эффективные математические алгоритмы поиска данных в упорядоченной последовательности. Наиболее эффективной структурой для поиска данных в машинном представлении являются B-деревья – многоуровневая иерархическая структура с переменным количеством элементов в каждом узле.

Создание индекса

Если выборка данных из таблицы требует значительного времени, это означает, что для нее необходимо создать индекс. Индексы могут существенно повысить производительность выполнения операций поиска и выборки данных. При выборе столбца для индекса следует проанализировать, какие типы запросов чаще всего выполняются пользователями и какие столбцы являются ключевыми, т.е. задающими критерии выборки данных, например, порядок сортировки.

В среде SQL Server реализовано несколько типов индексов:

  •  кластерные индексы;
  •  некластерные индексы;
  •  уникальные индексы.

Некластерный индекс

Некластерные индексы – наиболее типичные представители семейства индексов. В отличие от кластерных, они не перестраивают физическую структуру таблицы, а лишь организуют ссылки на соответствующие строки.

Для идентификации нужной строки в таблице некластерный индекс организует специальные указатели, включающие в себя:

  •  информацию об идентификационном номере файла, в котором хранится строка;
  •  идентификационный номер страницы соответствующих данных;
  •  номер искомой строки на соответствующей странице;
  •  содержимое столбца.

В большинстве случаев следует ограничиваться 4-5 индексами.

Кластерный индекс

Принципиальным отличием кластерного индекса от индексов других типов является то, что при его определении в таблице физическое расположение данных перестраивается в соответствии со структурой индекса. Логическая структура таблицы в этом случае представляет собой скорее словарь, чем индекс. Данные в словаре физически упорядочены, например по алфавиту.

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

Естественно, в таблице может быть определен только один кластерный индекс. В качестве такового следует выбирать наиболее часто используемые столбцы. При этом стоит следовать общим рекомендациям создания индексов и не индексировать слишком длинные столбцы.

Кластерный индекс может включать несколько столбцов. Однако количество таких столбцов рекомендуется по возможности свести к минимуму.

Необходимо избегать создания кластерного индекса для часто изменяемых столбцов, поскольку сервер должен будет выполнять физическое перемещение всех данных в таблице, чтобы они находились в упорядоченном состоянии, как того требует кластерный индекс. Для интенсивно изменяемых столбцов лучше подходит некластерный индекс.

При создании в таблице первичного ключа (PRIMARY KEY) сервер автоматически создает для него кластерный индекс, если его не существовало ранее или если при определении ключа не был явно указан другой тип индекса.

Когда же в таблице определен еще и некластерный индекс, то его указатель ссылается не на физическое положение строки в базе данных, а на соответствующий элемент кластерного индекса, описывающего эту строку, что позволяет не перестраивать структуру некластерных индексов всякий раз, когда кластерный индекс меняет физический порядок строк в таблице.

Уникальный индекс

Уникальность значений в индексируемом столбце гарантируют уникальные индексы. При их наличии сервер не разрешит вставить новое или изменить существующее значение таким образом, чтобы в результате этой операции в столбце появились два одинаковых значения.

Уникальный индекс является своеобразной надстройкой и может быть реализован как для кластерного, так и для некластерного индекса. В одной таблице может существовать один уникальный кластерный и множество уникальных некластерных индексов.

Уникальные индексы следует определять только тогда, когда это действительно необходимо. Для обеспечения целостности данных в столбце можно определить ограничение целостности UNIQUE или PRIMARY KEY, а не прибегать к уникальным индексам. Их использование только для обеспечения целостности данных является неоправданной тратой пространства в базе данных. Кроме того, на их поддержание тратится и процессорное время.

Средства языка SQL предлагают несколько способов определения индекса:

  •  автоматическое создание индекса при создании первичного ключа;
  •  автоматическое создание индекса при определении ограничения целостности UNIQUE;
  •  создание индекса с помощью команды CREATE INDEX.

Последняя команда имеет следующий формат:

<создание_индекса>::=

 CREATE [ UNIQUE ]

   [ CLUSTERED | NONCLUSTERED ]

 INDEX имя_индекса ON имя_таблицы(имя_столбца

   [ASC|DESC][,...n])

 [WITH [PAD_INDEX]

   [[,] FILLFACTOR=фактор_заполнения]

   [[,] IGNORE_DUP_KEY]

   [[,] DROP_EXISTING]

   [[,] STATISTICS_NORECOMPUTE] ]

   [ON имя_группы_файлов ]

Рассмотрим некоторые параметры приведенной команды.

Имя индекса должно быть уникальным в пределах таблицы, а сам индекс создается исключительно для таблицы текущей базы данных.

Параметр UNIQUE используется при необходимости ввода в определенное поле только уникальных значений. При указании этого ключевого слова будет создан уникальный индекс. В индексируемом столбце желательно запретить хранение значений NULL, чтобы избежать проблем, связанных с уникальностью значений. После того как для столбца появится уникальный индекс, сервер не разрешит выполнение команд INSERT и UPDATE, которые приведут к появлению дублирующих значений.

Параметр CLUSTERED использует возможность физического индексирования данных и позволяет произвести так называемое кластерное индексирование, в результате чего будут отсортированы данные в самой таблице согласно порядку этого индекса, а вся добавляемая информация станет приводить к изменению физического порядка данных. Кластерным может быть только один индекс в таблице.

Параметр NONCLUSTERED позволяет создавать некластерные индексы.

Параметр FILLFACTOR осуществляет настройку разбиения индекса на страницы и заметно оптимизирует работу SQL-сервера. Коэффициент FILLFACTOR определяет в процентном соотношении размер создаваемых индексных страниц. При этом имеется обратно пропорциональная зависимость частоты работы с таблицей и коэффициента FILLFACTOR.

Параметр PAD_INDEX определяет заполнение внутреннего пространства индекса и применяется совместно с FILLFACTOR.

Параметр DROP_EXISTING при использовании кластерного индекса определяет его повторное создание, что позволяет предотвратить нежелательное обновление кластерных индексов.

Параметр STATISTICS_NORECOMPUTE определяет функции автоматического обновления статистики для таблицы.

Параметр имя_группы_файлов позволяет осуществить выбор файловой группы, в которой будет находиться создаваемый индекс. Использование индекса из другой файловой группы повышает производительность некластерных индексов в связи с параллельностью выполнения процессов ввода/вывода и работы с самим индексом.

Удаление индекса

Удаление индекса выполняется командой

DROP INDEX 'имя_индекса'[,...n]

Пример 3.5. Создать уникальный кластерный индекс для таблицы Клиент по столбцу Фамилия в первичной группе файлов.

CREATE UNIQUE CLUSTERED INDEX index_klient1

 ON Клиент (Фамилия)

 WITH DROP_EXISTING

 ON PRIMARY

Пример 3.5. Создание уникального кластерного индекса. (html, txt)

Пример 3.6. Создать уникальный некластерный индекс для таблицы Клиент по столбцам Фамилия и Имя в первичной группе файлов. Кроме того, элементы индекса будут упорядочены по убыванию. Также запретим автоматическое обновление статистики при изменении данных в таблице и установим фактор заполнения индексных страниц на уровне 30%.

CREATE UNIQUE NONCLUSTERED INDEX index_klient2

 ON Клиент (Фамилия DESC,Имя DESC)

 WITH FILLFACTOR=30,

 STATISTICS_NORECOMPUTE

 ON PRIMARY

Пример 3.6. Создание уникального некластерного индекса. (html, txt)

Лекция: Функции пользователя

1 Понятие функции пользователя

При реализации на языке SQL сложных алгоритмов, которые могут потребоваться более одного раза, сразу встает вопрос о сохранении разработанного кода для дальнейшего применения. Эту задачу можно было бы реализовать с помощью хранимых процедур, однако их архитектура не позволяет использовать процедуры непосредственно в выражениях, т.к. они требуют промежуточного присвоения возвращенного значения переменной, которая затем и указывается в выражении. Естественно, подобный метод применения программного кода не слишком удобен. Многие разработчики уже давно хотели иметь возможность вызова разработанных алгоритмов непосредственно в выражениях.

Возможность создания пользовательских функций была предоставлена в среде MS SQL Server 2000. В других реализациях SQL в распоряжении пользователя имеются только встроенные функции, которые обеспечивают выполнение наиболее распространенных алгоритмов: поиск максимального или минимального значения и др.

Функции пользователя представляют собой самостоятельные объекты базы данных, такие, например, как хранимые процедуры или триггеры. Функция пользователя располагается в определенной базе данных и доступна только в ее контексте.

В SQL Server имеются следующие классы функций пользователя:

  •  Scalarфункции возвращают обычное скалярное значение, каждая может включать множество команд, объединяемых в один блок с помощью конструкции BEGIN...END;
  •  Inlineфункции содержат всего одну команду SELECT и возвращают пользователю набор данных в виде значения типа данных TABLE;
  •  Multi-statementфункции также возвращают пользователю значение типа данных TABLE, содержащее набор данных, однако в теле функции находится множество команд SQL (INSERT, UPDATE и т.д.). Именно с их помощью и формируется набор данных, который должен быть возвращен после выполнения функции.

Пользовательские функции сходны с хранимыми процедурами, но, в отличие от них, могут применяться в запросах так же, как и системные встроенные функции. Пользовательские функции, возвращающие таблицы, могут стать альтернативой просмотрам. Просмотры ограничены одним выражением SELECT, а пользовательские функции способны включать дополнительные выражения, что позволяет создавать более сложные и мощные конструкции.

2 Функции Scalar

Создание и изменение функции данного типа выполняется с помощью команды:

<определение_скаляр_функции>::=

{CREATE | ALTER } FUNCTION [владелец.]

   имя_функции

( [ { @имя_параметра скаляр_тип_данных

   [=default]}[,...n]])

RETURNS скаляр_тип_данных

[WITH {ENCRYPTION | SCHEMABINDING}

   [,...n] ]

[AS]

BEGIN

<тело_функции>

RETURN скаляр_выражение

END

Рассмотрим назначение параметров команды.

Функция может содержать один или несколько входных параметров либо не содержать ни одного. Каждый параметр должен иметь уникальное в пределах создаваемой функции имя и начинаться с символа "@". После имени указывается тип данных параметра. Дополнительно можно указать значение, которое будет автоматически присваиваться параметру (DEFAULT), если пользователь явно не указал значение соответствующего параметра при вызове функции.

С помощью конструкции RETURNS скаляр_тип_данных указывается, какой тип данных будет иметь возвращаемое функцией значение.

Дополнительные параметры, с которыми должна быть создана функция, могут быть указаны посредством ключевого слова WITH. Благодаря ключевому слову ENCRYPTION код команды, используемый для создания функции, будет зашифрован, и никто не сможет просмотреть его. Эта возможность позволяет скрыть логику работы функции. Кроме того, в теле функции может выполняться обращение к различным объектам базы данных, а потому изменение или удаление соответствующих объектов может привести к нарушению работы функции. Чтобы избежать этого, требуется запретить внесение изменений, указав при создании этой функции ключевое слово SCHEMABINDING.

Между ключевыми словами BEGIN...END указывается набор команд, они и будут являться телом функции.

Когда в ходе выполнения кода функции встречается ключевое слово RETURN, выполнение функции завершается и как результат ее вычисления возвращается значение, указанное непосредственно после слова RETURN. Отметим, что в теле функции разрешается использование множества команд RETURN, которые могут возвращать различные значения. В качестве возвращаемого значения допускаются как обычные константы, так и сложные выражения. Единственное условие – тип данных возвращаемого значения должен совпадать с типом данных, указанным после ключевого слова RETURNS.

Пример 11.1. Создать и применить функцию скалярного типа для вычисления суммарного количества товара, поступившего за определенную дату. Владелец функции – пользователь с именем user1.

CREATE FUNCTION

   user1.sales(@data DATETIME)

RETURNS INT

AS

BEGIN

DECLARE @c INT

SET @c=(SELECT SUM(количество)

       FROM Сделка

       WHERE дата=@data)

RETURN (@c)

END

Пример 11.1. Создание функции скалярного типа для вычисления суммарного количества товара, поступившего за определенную дату. (html, txt)

В качестве входного параметра используется дата. Функция возвращает значение целого типа, полученное из оператора SELECT путем суммирования количества товара из таблицы Сделка. Условием отбора записей для суммирования является равенство даты сделки значению входного параметра функции.

Проиллюстрируем обращение к функции пользователя: определим количество товара, поступившего за 02.11.01:

DECLARE @kol INT

SET @kol=user1.sales ('02.11.01')

SELECT @kol

3 Функции Inline

Создание и изменение функции этого типа выполняется с помощью команды:

<определение_табл_функции>::=

{CREATE | ALTER } FUNCTION [владелец.]

   имя_функции

( [ { @имя_параметра скаляр_тип_данных

   [=default]}[,...n]])

RETURNS TABLE

[ WITH {ENCRYPTION | SCHEMABINDING}

   [,...n] ]

[AS]

RETURN [(] SELECT_оператор [)]

Основная часть параметров, используемых при создании табличных функций, аналогична параметрам скалярной функции. Тем не менее создание табличных функций имеет свою специфику.

После ключевого слова RETURNS всегда должно указываться ключевое слово TABLE. Таким образом, функция данного типа должна строго возвращать значение типа данных TABLE. Структура возвращаемого значения типа TABLE не указывается явно при описании собственно типа данных. Вместо этого сервер будет автоматически использовать для возвращаемого значения TABLE структуру, возвращаемую запросом SELECT, который является единственной командой функции.

Особенность функции данного типа заключается в том, что структура значения TABLE создается автоматически в ходе выполнения запроса, а не указывается явно при определении типа после ключевого слова RETURNS.

Возвращаемое функцией значение типа TABLE может быть использовано непосредственно в запросе, т.е. в разделе FROM.

Пример 11.2. Создать и применить функцию табличного типа для определения двух наименований товара с наибольшим остатком.

CREATE FUNCTION user1.itog()

RETURNS TABLE

AS

RETURN (SELECT TOP 2 Товар.Название

  FROM Товар INNER JOIN Склад  

  ON Товар.КодТовара=Склад.КодТовара

  ORDER BY Склад.Остаток DESC)

Пример 11.2. Создание функции табличного типа для определения двух наименований товара с наибольшим остатком. (html, txt)

Использовать функцию для получения двух наименований товара с наибольшим остатком можно следующим образом:

SELECT Название

FROM user1.itog()

4 Функции Multi-statement

Создание и изменение функций типа Multi-statement выполняется с помощью следующей команды:

<определение_мульти_функции>::=

{CREATE | ALTER }FUNCTION [владелец.]

   имя_функции

( [ { @имя_параметра скаляр_тип_данных

   [=default]}[,...n]])

RETURNS @имя_параметра TABLE

   <определение_таблицы>

[WITH {ENCRYPTION | SCHEMABINDING}

   [,...n] ]

[AS]

BEGIN

<тело_функции>

RETURN

END

Использование большей части параметров рассматривалось при описании предыдущих функций.

Отметим, что функции данного типа, как и табличные, возвращают значение типа TABLE. Однако, в отличие от табличных функций, при создании функций Multi-statement необходимо явно задать структуру возвращаемого значения. Она указывается непосредственно после ключевого слова TABLE и, таким образом, является частью определения возвращаемого типа данных. Синтаксис конструкции <определение_таблицы> полностью соответствует одноименным структурам, используемым при создании обычных таблиц с помощью команды CREATE TABLE.

Набор возвращаемых данных должен формироваться с помощью команд INSERT, выполняемых в теле функции. Кроме того, в теле функции допускается использование различных конструкций языка SQL, которые могут контролировать значения, размещаемые в выходном наборе строк. При работе с командой INSERT требуется явно указать имя того объекта, куда необходимо вставить строки. Поэтому в функциях типа Multi-statement, в отличие от табличных, необходимо присвоить какое-то имя объекту с типом данных TABLE – оно и указывается как возвращаемое значение.

Завершение работы функции происходит в двух случаях: если возникают ошибки выполнения и если появляется ключевое слово RETURN. В отличие от функций скалярного типа, при использовании команды RETURN не нужно указывать возвращаемое значение. Сервер автоматически возвратит набор данных типа TABLE, имя и структура которого была указана после ключевого слова RETURNS. В теле функции может быть указано более одной команды RETURN.

Необходимо отметить, что работа функции завершается только при наличии команды RETURN. Это утверждение верно и в том случае, когда речь идет о достижении конца тела функции – самой последней командой должна быть команда RETURN.

Пример 11.3. Создать и применить функцию (типа multi-statement), которая для некоторого сотрудника выводит список всех его подчиненных (подчиненных как непосредственно ему, так и опосредствованно через других сотрудников).

Список сотрудников с указанием каждого руководителя представлен в таблице emp_mgr со следующей структурой:

CREATE TABLE emp_mgr

(emp CHAR(2) PRIMARY KEY,-- сотрудник

mgr CHAR(2))             -- руководитель

Пример данных в таблице emp_mgr показан ниже. Для упрощения иллюстрации имена сотрудников и их начальников представлены буквами латинского алфавита. У директора организации начальника нет (NULL).

emp     mgr

---------

a     NULL

b     a

c     a

d     a

e     f

f     b

g     b

i     c

k     d

 

CREATE FUNCTION fn_findReports(@id_emp

   CHAR(2))

RETURNS @report TABLE(empid CHAR(2)

   PRIMARY KEY,

   mgrid CHAR(2))

AS

BEGIN

   DECLARE @r INT

   DECLARE @t TABLE(empid CHAR(2)

       PRIMARY KEY,

       mgrid CHAR(2),

          pr   INT DEFAULT 0)

INSERT @t SELECT emp,mgr,0

       FROM emp_mgr

       WHERE emp=@id_emp

SET @r=@@ROWCOUNT

WHILE @r>0

BEGIN

   UPDATE @t   SET pr=1   WHERE pr=0

   INSERT @t SELECT e.emp, e.mgr,0  

       FROM  emp_mgr e, @t t

       WHERE e.mgr=t.empid

           AND t.pr=1

   SET @r=@@ROWCOUNT

   UPDATE @t SET pr=2 WHERE pr=1

END

INSERT @report SELECT empid, mgrid

   FROM @t

RETURN

END

Пример 11.3. Создание функции, которая для некоторого сотрудника выводит список всех его подчиненных. (html, txt)

Применим созданную функцию для определения списка подчиненных сотрудника ‘b’:

SELECT * FROM fn_findReports('b')

Оператор возвращает следующие значения:

emp     mgr

-----------

b     a

e     f

f     b

g     b

Список подчиненных сотрудника ‘a’ создается с помощью оператора

SELECT * FROM fn_findReports('a')

emp     mgr

---------

a     NULL

b     a

c     a

d     a

e     f

f     b

g     b

i     c

k     d

Другой оператор формирует список подчиненных сотрудника ‘e’:

SELECT * FROM fn_findReports('e')

emp     mgr

--------

e     f

Список подчиненных сотрудника ‘c’ создает следующий оператор:

SELECT * FROM fn_findReports('c')

emp     mgr

--------

c     a

i     c

Удаление любой функции осуществляется командой:

DROP FUNCTION {[ владелец.] имя_функции }

   [,...n]

5 Встроенные функции

Встроенные функции, имеющиеся в распоряжении пользователей при работе с SQL, можно условно разделить на следующие группы:

  •  математические функции;
  •  строковые функции;
  •  функции для работы с датой и временем;
  •  функции конфигурирования;
  •  функции системы безопасности;
  •  функции управления метаданными;
  •  статистические функции.

Математические функции

Краткий обзор математических функций представлен в таблице.

Таблица 11.1.

ABS

вычисляет абсолютное значение числа

ACOS

вычисляет арккосинус

ASIN

вычисляет арксинус

ATAN

вычисляет арктангенс

ATN2

вычисляет арктангенс с учетом квадратов

CEILING

выполняет округление вверх

COS

вычисляет косинус угла

COT

возвращает котангенс угла

DEGREES

преобразует значение угла из радиан в градусы

EXP

возвращает экспоненту

FLOOR

выполняет округление вниз

LOG

вычисляет натуральный логарифм

LOG10

вычисляет десятичный логарифм

PI

возвращает значение "пи"

POWER

возводит число в степень

RADIANS

преобразует значение угла из градуса в радианы

RAND

возвращает случайное число

ROUND

выполняет округление с заданной точностью

SIGN

определяет знак числа

SIN

вычисляет синус угла

SQUARE

выполняет возведение числа в квадрат

SQRT

извлекает квадратный корень

TAN

возвращает тангенс угла

SELECT Товар.Название, Сделка.Количество,

Round(Товар.Цена*Сделка.Количество

   *0.05,1)

AS Налог

FROM Товар INNER JOIN Сделка

   ON Товар.КодТовара=

   Сделка.КодТовара

Пример 11.4. Использование функции округления до одного знака после запятой для расчета налога. (html, txt)

Строковые функции

Краткий обзор строковых функций представлен в таблице.

Таблица 11.2.

ASCII

возвращает код ASCII левого символа строки

CHAR

по коду ASCII возвращает символ

CHARINDEX

определяет порядковый номер символа, с которого начинается вхождение подстроки в строку

DIFFERENCE

возвращает показатель совпадения строк

LEFT

возвращает указанное число символов с начала строки

LEN

возвращает длину строки

LOWER

переводит все символы строки в нижний регистр

LTRIM

удаляет пробелы в начале строки

NCHAR

возвращает по коду символ Unicode

PATINDEX

выполняет поиск подстроки в строке по указанному шаблону

REPLACE

заменяет вхождения подстроки на указанное значение

QUOTENAME

конвертирует строку в формат Unicode

REPLICATE

выполняет тиражирование строки определенное число раз

REVERSE

возвращает строку, символы которой записаны в обратном порядке

RIGHT

возвращает указанное число символов с конца строки

RTRIM

удаляет пробелы в конце строки

SOUNDEX

возвращает код звучания строки

SPACE

возвращает указанное число пробелов

STR

выполняет конвертирование значения числового типа в символьный формат

STUFF

удаляет указанное число символов, заменяя новой подстрокой

SUBSTRING

возвращает для строки подстроку указанной длины с заданного символа

UNICODE

возвращает Unicode-код левого символа строки

UPPER

переводит все символы строки в верхний регистр

SELECT Фирма, [Фамилия]+""

   +Left([Имя],1)+"."

   +Left([Отчество],1)

   +"." AS ФИО

FROM Клиент

Пример 11.5. Использование функции LEFT для получения инициалов клиентов. (html, txt)

Функции для работы с датой и временем

Краткий обзор основных функций для работы с датой и временем представлен в таблице.

Таблица 11.3.

DATEADD

добавляет к дате указанное значение дней, месяцев, часов и т.д.

DATEDIFF

возвращает разницу между указанными частями двух дат

DATENAME

выделяет из даты указанную часть и возвращает ее в символьном формате

DATEPART

выделяет из даты указанную часть и возвращает ее в числовом формате

DAY

возвращает число из указанной даты

GETDATE

возвращает текущее системное время

ISDATE

проверяет правильность выражения на соответствие одному из возможных форматов ввода даты

MONTH

возвращает значение месяца из указанной даты

YEAR

возвращает значение года из указанной даты

SELECT Year(Дата) AS Год, Month(Дата)

  AS Месяц,

  Sum(Количество) AS Общ_Количество

FROM Сделка

GROUP BY Year(Дата), Month(Дата)

Пример 11.6. Использование функций YEAR и MONTH для определения общего количества товара, проданного за каждый месяц каждого года. (html, txt)

DECLARE @d DATETIME

DECLARE @y INT

SET @d=’29.10.03’

SET @y=DATEPART(yy,@d)

SELECT @y

Пример 11.7. Пример выделения из даты значения года. (html, txt)


Лекция: Хранимые процедуры

1 Понятие хранимой процедуры

Хранимые процедуры представляют собой группы связанных между собой операторов SQL, применение которых делает работу программиста более легкой и гибкой, поскольку выполнить хранимую процедуру часто оказывается гораздо проще, чем последовательность отдельных операторов SQL. Хранимые процедуры представляют собой набор команд, состоящий из одного или нескольких операторов SQL или функций и сохраняемый в базе данных в откомпилированном виде. Выполнение в базе данных хранимых процедур вместо отдельных операторов SQL дает пользователю следующие преимущества:

  •  необходимые операторы уже содержатся в базе данных;
  •  все они прошли этап синтаксического анализа и находятся в исполняемом формате; перед выполнением хранимой процедуры SQL Server генерирует для нее план исполнения, выполняет ее оптимизацию и компиляцию;
  •  хранимые процедуры поддерживают модульное программирование, так как позволяют разбивать большие задачи на самостоятельные, более мелкие и удобные в управлении части;
  •  хранимые процедуры могут вызывать другие хранимые процедуры и функции;
  •  хранимые процедуры могут быть вызваны из прикладных программ других типов;
  •  как правило, хранимые процедуры выполняются быстрее, чем последовательность отдельных операторов;
  •  хранимые процедуры проще использовать: они могут состоять из десятков и сотен команд, но для их запуска достаточно указать всего лишь имя нужной хранимой процедуры. Это позволяет уменьшить размер запроса, посылаемого от клиента на сервер, а значит, и нагрузку на сеть.

Хранение процедур в том же месте, где они исполняются, обеспечивает уменьшение объема передаваемых по сети данных и повышает общую производительность системы. Применение хранимых процедур упрощает сопровождение программных комплексов и внесение изменений в них. Обычно все ограничения целостности в виде правил и алгоритмов обработки данных реализуются на сервере баз данных и доступны конечному приложению в виде набора хранимых процедур, которые и представляют интерфейс обработки данных. Для обеспечения целостности данных, а также в целях безопасности, приложение обычно не получает прямого доступа к данным – вся работа с ними ведется путем вызова тех или иных хранимых процедур.

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

Хранимые процедуры существуют независимо от таблиц или каких-либо других объектов баз данных. Они вызываются клиентской программой, другой хранимой процедурой или триггером. Разработчик может управлять правами доступа к хранимой процедуре, разрешая или запрещая ее выполнение. Изменять код хранимой процедуры разрешается только ее владельцу или члену фиксированной роли базы данных. При необходимости можно передать права владения ею от одного пользователя к другому.

2 Хранимые процедуры в среде MS SQL Server

При работе с SQL Server пользователи могут создавать собственные процедуры, реализующие те или иные действия. Хранимые процедуры являются полноценными объектами базы данных, а потому каждая из них хранится в конкретной базе данных. Непосредственный вызов хранимой процедуры возможен, только если он осуществляется в контексте той базы данных, где находится процедура.

Типы хранимых процедур

В SQL Server имеется несколько типов хранимых процедур.

  •  Системные хранимые процедуры предназначены для выполнения различных административных действий. Практически все действия по администрированию сервера выполняются с их помощью. Можно сказать, что системные хранимые процедуры являются интерфейсом, обеспечивающим работу с системными таблицами, которая, в конечном счете, сводится к изменению, добавлению, удалению и выборке данных из системных таблиц как пользовательских, так и системных баз данных. Системные хранимые процедуры имеют префикс sp_, хранятся в системной базе данных и могут быть вызваны в контексте любой другой базы данных.
  •  Пользовательские хранимые процедуры реализуют те или иные действия. Хранимые процедуры – полноценный объект базы данных. Вследствие этого каждая хранимая процедура располагается в конкретной базе данных, где и выполняется.
  •  Временные хранимые процедуры существуют лишь некоторое время, после чего автоматически уничтожаются сервером. Они делятся на локальные и глобальные. Локальные временные хранимые процедуры могут быть вызваны только из того соединения, в котором созданы. При создании такой процедуры ей необходимо дать имя, начинающееся с одного символа #. Как и все временные объекты, хранимые процедуры этого типа автоматически удаляются при отключении пользователя, перезапуске или остановке сервера. Глобальные временные хранимые процедуры доступны для любых соединений сервера, на котором имеется такая же процедура. Для ее определения достаточно дать ей имя, начинающееся с символов ##. Удаляются эти процедуры при перезапуске или остановке сервера, а также при закрытии соединения, в контексте которого они были созданы.

3 Создание, изменение и удаление хранимых процедур

Создание хранимой процедуры предполагает решение следующих задач:

  •  определение типа создаваемой хранимой процедуры: временная или пользовательская. Кроме этого, можно создать свою собственную системную хранимую процедуру, назначив ей имя с префиксом sp_ и поместив ее в системную базу данных. Такая процедура будет доступна в контексте любой базы данных локального сервера;
  •  планирование прав доступа. При создании хранимой процедуры следует учитывать, что она будет иметь те же права доступа к объектам базы данных, что и создавший ее пользователь;
  •  определение параметров хранимой процедуры. Подобно процедурам, входящим в состав большинства языков программирования, хранимые процедуры могут обладать входными и выходными параметрами;
  •  разработка кода хранимой процедуры. Код процедуры может содержать последовательность любых команд SQL, включая вызов других хранимых процедур.

Создание новой и изменение имеющейся хранимой процедуры осуществляется с помощью следующей команды:

<определение_процедуры>::=

{CREATE | ALTER } [PROCEDURE] имя_процедуры

   [;номер]

[{@имя_параметра тип_данных } [VARYING ]

 [=default][OUTPUT] ][,...n]

[WITH { RECOMPILE | ENCRYPTION | RECOMPILE,

ENCRYPTION }]

[FOR REPLICATION]

AS

sql_оператор [...n]

Рассмотрим параметры данной команды.

Используя префиксы sp_, #, ##, создаваемую процедуру можно определить в качестве системной или временной. Как видно из синтаксиса команды, не допускается указывать имя владельца, которому будет принадлежать создаваемая процедура, а также имя базы данных, где она должна быть размещена. Таким образом, чтобы разместить создаваемую хранимую процедуру в конкретной базе данных, необходимо выполнить команду CREATE PROCEDURE в контексте этой базы данных. При обращении из тела хранимой процедуры к объектам той же базы данных можно использовать укороченные имена, т. е. без указания имени базы данных. Когда же требуется обратиться к объектам, расположенным в других базах данных, указание имени базы данных обязательно.

Номер в имени – это идентификационный номер хранимой процедуры, однозначно определяющий ее в группе процедур. Для удобства управления процедурами логически однотипные хранимые процедуры можно группировать, присваивая им одинаковые имена, но разные идентификационные номера.

Для передачи входных и выходных данных в создаваемой хранимой процедуре могут использоваться параметры, имена которых, как и имена локальных переменных, должны начинаться с символа @. В одной хранимой процедуре можно задать множество параметров, разделенных запятыми. В теле процедуры не должны применяться локальные переменные, чьи имена совпадают с именами параметров этой процедуры.

Для определения типа данных, который будет иметь соответствующий параметр хранимой процедуры, годятся любые типы данных SQL, включая определенные пользователем. Однако тип данных CURSOR может быть использован только как выходной параметр хранимой процедуры, т.е. с указанием ключевого слова OUTPUT.

Наличие ключевого слова OUTPUT означает, что соответствующий параметр предназначен для возвращения данных из хранимой процедуры. Однако это вовсе не означает, что параметр не подходит для передачи значений в хранимую процедуру. Указание ключевого слова OUTPUT предписывает серверу при выходе из хранимой процедуры присвоить текущее значение параметра локальной переменной, которая была указана при вызове процедуры в качестве значения параметра. Отметим, что при указании ключевого слова OUTPUT значение соответствующего параметра при вызове процедуры может быть задано только с помощью локальной переменной. Не разрешается использование любых выражений или констант, допустимое для обычных параметров.

Ключевое слово VARYING применяется совместно с параметром OUTPUT, имеющим тип CURSOR. Оно определяет, что выходным параметром будет результирующее множество.

Ключевое слово DEFAULT представляет собой значение, которое будет принимать соответствующий параметр по умолчанию. Таким образом, при вызове процедуры можно не указывать явно значение соответствующего параметра.

Так как сервер кэширует план исполнения запроса и компилированный код, при последующем вызове процедуры будут использоваться уже готовые значения. Однако в некоторых случаях все же требуется выполнять перекомпиляцию кода процедуры. Указание ключевого слова RECOMPILE предписывает системе создавать план выполнения хранимой процедуры при каждом ее вызове.

Параметр FOR REPLICATION востребован при репликации данных и включении создаваемой хранимой процедуры в качестве статьи в публикацию.

Ключевое слово ENCRYPTION предписывает серверу выполнить шифрование кода хранимой процедуры, что может обеспечить защиту от использования авторских алгоритмов, реализующих работу хранимой процедуры.

Ключевое слово AS размещается в начале собственно тела хранимой процедуры, т.е. набора команд SQL, с помощью которых и будет реализовываться то или иное действие. В теле процедуры могут применяться практически все команды SQL, объявляться транзакции, устанавливаться блокировки и вызываться другие хранимые процедуры. Выход из хранимой процедуры можно осуществить посредством команды RETURN.

Удаление хранимой процедуры осуществляется командой:

DROP PROCEDURE {имя_процедуры} [,...n]

4 Выполнение хранимой процедуры

Для выполнения хранимой процедуры используется команда:

[[ EXEC [ UTE] имя_процедуры [;номер]

[[@имя_параметра=]{значение | @имя_переменной}

 [OUTPUT ]|[DEFAULT ]][,...n]

Если вызов хранимой процедуры не является единственной командой в пакете, то присутствие команды EXECUTE обязательно. Более того, эта команда требуется для вызова процедуры из тела другой процедуры или триггера.

Использование ключевого слова OUTPUT при вызове процедуры разрешается только для параметров, которые были объявлены при создании процедуры с ключевым словом OUTPUT.

Когда же при вызове процедуры для параметра указывается ключевое слово DEFAULT, то будет использовано значение по умолчанию. Естественно, указанное слово DEFAULT разрешается только для тех параметров, для которых определено значение по умолчанию.

Из синтаксиса команды EXECUTE видно, что имена параметров могут быть опущены при вызове процедуры. Однако в этом случае пользователь должен указывать значения для параметров в том же порядке, в каком они перечислялись при создании процедуры. Присвоить параметру значение по умолчанию, просто пропустив его при перечислении нельзя. Если же требуется опустить параметры, для которых определено значение по умолчанию, достаточно явного указания имен параметров при вызове хранимой процедуры. Более того, таким способом можно перечислять параметры и их значения в произвольном порядке.

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

Пример 12.1. Процедура без параметров. Разработать процедуру для получения названий и стоимости товаров, приобретенных Ивановым.

CREATE PROC my_proc1

AS

SELECT Товар.Название,

      Товар.Цена*Сделка.Количество

AS Стоимость, Клиент.Фамилия

 FROM Клиент INNER JOIN

 (Товар INNER JOIN Сделка

 ON Товар.КодТовара=Сделка.КодТовара)

 ON Клиент.КодКлиента=Сделка.КодКлиента

 WHERE Клиент.Фамилия=’Иванов’

Пример 12.1. Процедура для получения названий и стоимости товаров, приобретенных Ивановым. (html, txt)

Для обращения к процедуре можно использовать команды:

EXEC my_proc1 или my_proc1

Процедура возвращает набор данных.

Пример 12.2. Процедура без параметров. Создать процедуру для уменьшения цены товара первого сорта на 10%.

CREATE PROC my_proc2

AS

UPDATE Товар SET Цена=Цена*0.9

 WHERE Сорт=’первый

Пример 12.2. Процедура для уменьшения цены товара первого сорта на 10%. (html, txt)

Для обращения к процедуре можно использовать команды:

EXEC my_proc2 или my_proc2

Процедура не возвращает никаких данных.

Пример 12.3. Процедура с входным параметром. Создать процедуру для получения названий и стоимости товаров, которые приобрел заданный клиент.

CREATE PROC my_proc3

 @k VARCHAR(20)

AS

SELECT Товар.Название,

      Товар.Цена*Сделка.Количество

 AS Стоимость, Клиент.Фамилия

 FROM Клиент INNER JOIN

 (Товар INNER JOIN Сделка

 ON Товар.КодТовара=Сделка.КодТовара)

 ON Клиент.КодКлиента=Сделка.КодКлиента

 WHERE Клиент.Фамилия=@k

Пример 12.3. Процедура для получения названий и стоимости товаров, которые приобрел заданный клиент. (html, txt)

Для обращения к процедуре можно использовать команды:

EXEC my_proc3 'Иванов' или

my_proc3 @k='Иванов'

Пример 12.4. Процедура с входными параметрами. Создать процедуру для уменьшения цены товара заданного типа в соответствии с указанным %.

CREATE PROC my_proc4

 @t VARCHAR(20), @p FLOAT

AS

UPDATE Товар SET Цена=Цена*(1-@p)

 WHERE Тип=@t

Пример 12.4. Процедура для уменьшения цены товара заданного типа в соответствии с указанным %. (html, txt)

Для обращения к процедуре можно использовать команды:

EXEC my_proc4 'Вафли',0.05 или

EXEC my_proc4 @t='Вафли', @p=0.05

Пример 12.5. Процедура с входными параметрами и значениями по умолчанию. Создать процедуру для уменьшения цены товара заданного типа в соответствии с указанным %.

CREATE PROC my_proc5

 @t VARCHAR(20)=’Конфеты',

 @p FLOAT=0.1

AS

UPDATE Товар SET Цена=Цена*(1-@p)

 WHERE Тип=@t

Пример 12.5. Процедура с входными параметрами и значениями по умолчанию. Создать процедуру для уменьшения цены товара заданного типа в соответствии с указанным %. (html, txt)

Для обращения к процедуре можно использовать команды:

EXEC my_proc5 'Вафли',0.05 или

EXEC my_proc5 @t='Вафли', @p=0.05 или

EXEC my_proc5 @p=0.05

В этом случае уменьшается цена конфет (значение типа не указано при вызове процедуры и берется по умолчанию).

EXEC my_proc5

В последнем случае оба параметра (и тип, и проценты) не указаны при вызове процедуры, их значения берутся по умолчанию.

Пример 12.6. Процедура с входными и выходными параметрами. Создать процедуру для определения общей стоимости товаров, проданных за конкретный месяц.

CREATE PROC my_proc6

 @m INT,

 @s FLOAT OUTPUT

AS

SELECT @s=Sum(Товар.Цена*Сделка.Количество)

 FROM Товар INNER JOIN Сделка

 ON Товар.КодТовара=Сделка.КодТовара

 GROUP BY Month(Сделка.Дата)

 HAVING Month(Сделка.Дата)=@m

Пример 12.6. Процедура с входными и выходными параметрами. Создать процедуру для определения общей стоимости товаров, проданных за конкретный месяц. (html, txt)

Для обращения к процедуре можно использовать команды:

DECLARE @st FLOAT

EXEC my_proc6 1,@st OUTPUT

SELECT @st

Этот блок команд позволяет определить стоимость товаров, проданных в январе (входной параметр месяц указан равным 1).

Создать процедуру для определения общего количества товаров, приобретенных фирмой, в которой работает заданный сотрудник.

Сначала разработаем процедуру для определения фирмы, где работает сотрудник.

CREATE PROC my_proc7

 @n VARCHAR(20),

 @f VARCHAR(20) OUTPUT

AS

SELECT @f=Фирма

 FROM Клиент

 WHERE Фамилия=@n

Пример 12.7. Использование вложенных процедур. Создать процедуру для определения общего количества товаров, приобретенных фирмой, в которой работает заданный сотрудник.

Затем создадим процедуру, подсчитывающую общее количество товара, который закуплен интересующей нас фирмой.

CREATE PROC my_proc8

 @fam VARCHAR(20),

 @kol INT OUTPUT

AS

DECLARE @firm VARCHAR(20)

EXEC my_proc7 @fam,@firm OUTPUT

SELECT @kol=Sum(Сделка.Количество)

 FROM Клиент INNER JOIN Сделка

 ON Клиент.КодКлиента=Сделка.КодКлиента

 GROUP BY Клиент.Фирма

 HAVING Клиент.Фирма=@firm

Пример 12.7. Создание процедуры для определения общего количества товаров, приобретенных фирмой, в которой работает заданный сотрудник. (html, txt)

Вызов процедуры осуществляется с помощью команды:

DECLARE @k INT

EXEC my_proc8 ‘Иванов’,@k OUTPUT

SELECT @k


Лекция: Курсоры: принципы работы

1 Понятие курсора

Запрос к реляционной базе данных обычно возвращает несколько рядов (записей) данных, но приложение за один раз обрабатывает лишь одну запись. Даже если оно имеет дело одновременно с несколькими рядами (например, выводит данные в форме электронных таблиц), их количество по-прежнему ограничено. Кроме того, при модификации, удалении или добавлении данных рабочей единицей является ряд. В этой ситуации на первый план выступает концепция курсора, и в таком контексте курсор – указатель на ряд.

Курсор в SQL – это область в памяти базы данных, которая предназначена для хранения последнего оператора SQL. Если текущий оператор – запрос к базе данных, в памяти сохраняется и строка данных запроса, называемая текущим значением, или текущей строкой курсора. Указанная область в памяти поименована и доступна для прикладных программ.

Обычно курсоры используются для выбора из базы данных некоторого подмножества хранимой в ней информации. В каждый момент времени прикладной программой может быть проверена одна строка курсора. Курсоры часто применяются в операторах SQL, встроенных в написанные на языках процедурного типа прикладные программы. Некоторые из них неявно создаются сервером базы данных, в то время как другие определяются программистами.

В соответствии со стандартом SQL при работе с курсорами можно выделить следующие основные действия:

  •  создание или объявление курсора;
  •  открытие курсора , т.е. наполнение его данными, которые сохраняются в многоуровневой памяти;
  •  выборка из курсора и изменение с его помощью строк данных;
  •  закрытие курсора, после чего он становится недоступным для пользовательских программ;
  •  освобождение курсора, т.е. удаление курсора как объекта, поскольку его закрытие необязательно освобождает ассоциированную с ним память.

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

В некоторых случаях применение курсора неизбежно. Однако по возможности этого следует избегать и работать со стандартными командами обработки данных: SELECT, UPDATE, INSERT, DELETE. Помимо того, что курсоры не позволяют проводить операции изменения над всем объемом данных, скорость выполнения операций обработки данных посредством курсора заметно ниже, чем у стандартных средств SQL.

2 Реализация курсоров в среде MS SQL Server

SQL Server поддерживает три вида курсоров:

  •  курсоры SQL применяются в основном внутри триггеров, хранимых процедур и сценариев;
  •  курсоры сервера действуют на сервере и реализуют программный интерфейс приложений для ODBC, OLE DB, DB_Library;
  •  курсоры клиента реализуются на самом клиенте. Они выбирают весь результирующий набор строк из сервера и сохраняют его локально, что позволяет ускорить операции обработки данных за счет снижения потерь времени на выполнение сетевых операций.

Различные типы многопользовательских приложений требуют и различных типов организации параллельного доступа к данным. Некоторым приложениям необходим немедленный доступ к информации об изменениях в базе данных. Это характерно для систем резервирования билетов. В других случаях, например, в системах статистической отчетности, важна стабильность данных, ведь если они постоянно модифицируются, программы не смогут эффективно отображать информацию. Различным приложениям нужны разные реализации курсоров.

В среде SQL Server типы курсоров различаются по предоставляемым возможностям. Тип курсора определяется на стадии его создания и не может быть изменен. Некоторые типы курсоров могут обнаруживать изменения, сделанные другими пользователями в строках, включенных в результирующий набор. Однако SQL Server отслеживает изменения таких строк только на стадии обращения к строке и не позволяет отслеживать изменения, когда строка уже считана.

Курсоры делятся на две категории: последовательные и прокручиваемые. Последовательные позволяют выбирать данные только в одном направлении – от начала к концу. Прокручиваемые же курсоры предоставляют большую свободу действий – допускается перемещение в обоих направлениях и переход к произвольной строке результирующего набора курсора.Если программа способна модифицировать данные, на которые указывает курсор, он называется прокручиваемым и модифицируемым. Говоря о курсорах, не следует забывать об изолированности транзакций. Когда один пользователь модифицирует запись, другой читает ее при помощи собственного курсора, более того, он может модифицировать ту же запись, что делает необходимым соблюдение целостности данных.

SQL Server поддерживает курсоры статические, динамические, последовательные и управляемые набором ключей.

В схеме со статическим курсором информация читается из базы данных один раз и хранится в виде моментального снимка (по состоянию на некоторый момент времени), поэтому изменения, внесенные в базу данных другим пользователем, не видны. На время открытия курсора сервер устанавливает блокировку на все строки, включенные в его полный результирующий набор. Статический курсор не изменяется после создания и всегда отображает тот набор данных, который существовал на момент его открытия.

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

В статический курсор внести изменения невозможно, поэтому он всегда открывается в режиме "только для чтения".

Динамический курсор поддерживает данные в "живом" состоянии, но это требует затрат сетевых и программных ресурсов. При использовании динамических курсоров не создается полная копия исходных данных, а выполняется динамическая выборка из исходных таблиц только при обращении пользователя к тем или иным данным. На время выборки сервер блокирует строки, а все изменения, вносимые пользователем в полный результирующий набор курсора, будут видны в курсоре. Однако если другой пользователь внес изменения уже после выборки данных курсором, то они не отразятся в курсоре.

Курсор, управляемый набором ключей, находится посередине между этими крайностями. Записи идентифицируются на момент выборки, и тем самым отслеживаются изменения. Такой тип курсора полезен при реализации прокрутки назад – тогда добавления и удаления рядов не видны, пока информация не обновится, а драйвер выбирает новую версию записи, если в нее были внесены изменения.

Последовательные курсоры не разрешают выполнять выборку данных в обратном направлении. Пользователь может выбирать строки только от начала к концу курсора. Последовательный курсор не хранит набор всех строк. Они считываются из базы данных, как только выбираются в курсоре, что позволяет динамически отражать все изменения, вносимые пользователями в базу данных с помощью команд INSERT, UPDATE, DELETE. В курсоре видно самое последнее состояние данных.

Статические курсоры обеспечивают стабильный взгляд на данные. Они хороши для систем "складирования" информации: приложений для систем отчетности или для статистических и аналитических целей. Кроме того, статический курсор лучше других справляется с выборкой большого количества данных. Напротив, в системах электронных покупок или резервирования билетов необходимо динамическое восприятие обновляемой информации по мере внесения изменений. В таких случаях используется динамический курсор. В этих приложениях объем передаваемых данных, как правило, невелик, а доступ к ним осуществляется на уровне рядов (отдельных записей). Групповой доступ встречается очень редко.

3 Управление курсором в среде MS SQL Server

Управление курсором реализуется путем выполнения следующих команд:

  •  DECLARE – создание или объявление курсора;
  •  OPENоткрытие курсора, т.е. наполнение его данными;
  •  FETCHвыборка из курсора и изменение строк данных с помощью курсора;
  •  CLOSEзакрытие курсора;
  •  DEALLOCATEосвобождение курсора, т.е. удаление курсора как объекта.

Объявление курсора

В стандарте SQL для создания курсора предусмотрена следующая команда:

<создание_курсора>::=

 DECLARE имя_курсора

   [INSENSITIVE][SCROLL] CURSOR

 FOR SELECT_оператор

 [FOR { READ_ONLY | UPDATE

   [OF имя_столбца[,...n]]}]

При использовании ключевого слова INSENSITIVE будет создан статический курсор. Изменения данных не разрешаются, кроме того, не отображаются изменения, сделанные другими пользователями. Если ключевое слово INSENSITIVE отсутствует, создается динамический курсор.

При указании ключевого слова SCROLL созданный курсор можно прокручивать в любом направлении, что позволяет применять любые команды выборки. Если этот аргумент опускается, то курсор окажется последовательным, т.е. его просмотр будет возможен только в одном направлении – от начала к концу.

SELECT-оператор задает тело запроса SELECT, с помощью которого определяется результирующий набор строк курсора.

При указании аргумента FOR READ_ONLY создается курсор "только для чтения", и никакие модификации данных не разрешаются. Он отличается от статического, хотя последний также не позволяет менять данные. В качестве курсора "только для чтения" может быть объявлен динамический курсор, что позволит отображать изменения, сделанные другим пользователем.

Создание курсора с аргументом FOR UPDATE позволяет выполнять в курсоре изменение данных либо в указанных столбцах, либо, при отсутствии аргумента OF имя_столбца, во всех столбцах.

В среде MS SQL Server принят следующий синтаксис команды создания курсора:

<создание_курсора>::=

 DECLARE имя_курсора CURSOR [LOCAL | GLOBAL]

 [FORWARD_ONLY | SCROLL]

 [STATIC | KEYSET | DYNAMIC | FAST_FORWARD]

 [READ_ONLY | SCROLL_LOCKS | OPTIMISTIC]

 [TYPE_WARNING]

 FOR SELECT_оператор

 [FOR UPDATE [OF имя_столбца[,...n]]]

При использовании ключевого слова LOCAL будет создан локальный курсор, который виден только в пределах создавшего его пакета, триггера, хранимой процедуры или пользовательской функции. По завершении работы пакета, триггера, процедуры или функции курсор неявно уничтожается. Чтобы передать содержимое курсора за пределы создавшей его конструкции, необходимо присвоить его параметру аргумент OUTPUT.

Если указано ключевое слово GLOBAL, создается глобальный курсор; он существует до закрытия текущего соединения.

При указании FORWARD_ONLY создается последовательный курсор; выборку данных можно осуществлять только в направлении от первой строки к последней.

При указании SCROLL создается прокручиваемый курсор; обращаться к данным можно в любом порядке и в любом направлении.

При указании STATIC создается статический курсор.

При указании KEYSET создается ключевой курсор.

При указании DYNAMIC создается динамический курсор.

Если для курсора READ_ONLY указать аргумент FAST_FORWARD, то созданный курсор будет оптимизирован для быстрого доступа к данным. Этот аргумент не может быть использован совместно с аргументами FORWARD_ONLY и OPTIMISTIC.

В курсоре, созданном с указанием аргумента OPTIMISTIC, запрещается изменение и удаление строк, которые были изменены после открытия курсора.

При указании аргумента TYPE_WARNING сервер будет информировать пользователя о неявном изменении типа курсора, если он несовместим с запросом SELECT.

Открытие курсора

Для открытия курсора и наполнения его данными из указанного при создании курсора запроса SELECT используется следующая команда:

OPEN {{[GLOBAL]имя_курсора }

    |@имя_переменной_курсора}

После открытия курсора происходит выполнение связанного с ним оператора SELECT, выходные данные которого сохраняются в многоуровневой памяти.

Выборка данных из курсора

Сразу после открытия курсора можно выбрать его содержимое (результат выполнения соответствующего запроса) посредством следующей команды:

FETCH [[NEXT | PRIOR | FIRST | LAST

 | ABSOLUTE {номер_строки

 | @переменная_номера_строки}

 | RELATIVE {номер_строки |

   @переменная_номера_строки}]

 FROM ]{{[GLOBAL ]имя_курсора }|

   @имя_переменной_курсора }

 [INTO @имя_переменной [,...n]]

При указании FIRST будет возвращена самая первая строка полного результирующего набора курсора, которая становится текущей строкой.

При указании LAST возвращается самая последняя строка курсора. Она же становится текущей строкой.

При указании NEXT возвращается строка, находящаяся в полном результирующем наборе сразу же после текущей. Теперь она становится текущей. По умолчанию команда FETCH использует именно этот способ выборки строк.

Ключевое слово PRIOR возвращает строку, находящуюся перед текущей. Она и становится текущей.

Аргумент ABSOLUTE {номер_строки | @переменная_номера_строки} возвращает строку по ее абсолютному порядковому номеру в полном результирующем наборе курсора. Номер строки можно задать с помощью константы или как имя переменной, в которой хранится номер строки. Переменная должна иметь целочисленный тип данных. Указываются как положительные, так и отрицательные значения. При указании положительного значения строка отсчитывается от начала набора, отрицательного – от конца. Выбранная строка становится текущей. Если указано нулевое значение, строка не возвращается.

Аргумент RELATIVE {кол_строки | @переменная_кол_строки} возвращает строку, находящуюся через указанное количество строк после текущей. Если указать отрицательное значение числа строк, то будет возвращена строка, находящаяся за указанное количество строк перед текущей. При указании нулевого значения возвратится текущая строка. Возвращенная строка становится текущей.

Чтобы открыть глобальный курсор, перед его именем требуется указать ключевое слово GLOBAL. Имя курсора также может быть указано с помощью переменной.

В конструкции INTO @имя_переменной [,...n] задается список переменных, в которых будут сохранены соответствующие значения столбцов возвращаемой строки. Порядок указания переменных должен соответствовать порядку столбцов в курсоре, а тип данных переменной – типу данных в столбце курсора. Если конструкция INTO не указана, то поведение команды FETCH будет напоминать поведение команды SELECT – данные выводятся на экран.

Изменение и удаление данных

Для выполнения изменений с помощью курсора необходимо выполнить команду UPDATE в следующем формате:

UPDATE имя_таблицы SET {имя_столбца={

 DEFAULT | NULL | выражение}}[,...n]

 WHERE CURRENT OF {{[GLOBAL] имя_курсора}

 |@имя_переменной_курсора}

За одну операцию могут быть изменены несколько столбцов текущей строки курсора, но все они должны принадлежать одной таблице.

Для удаления данных посредством курсора используется команда DELETE в следующем формате:

DELETE имя_таблицы

 WHERE CURRENT OF {{[GLOBAL] имя_курсора}

 |@имя_переменной_курсора}

В результате будет удалена строка, установленная текущей в курсоре.

Закрытие курсора

CLOSE {имя_курсора | @имя_переменной_курсора}

После закрытия курсор становится недоступным для пользователей программы. При закрытии снимаются все блокировки, установленные в процессе его работы. Закрытие может применяться только к открытым курсорам. Закрытый, но не освобожденный курсор может быть повторно открыт. Не допускается закрывать неоткрытый курсор.

Освобождение курсора

Закрытие курсора необязательно освобождает ассоциированную с ним память. В некоторых реализациях нужно явным образом освободить ее с помощью оператора DEALLOCATE. После освобождения курсора освобождается и память, при этом становится возможным повторное использование имени курсора.

DEALLOCATE { имя_курсора |

 @имя_переменной_курсора }

Для контроля достижения конца курсора рекомендуется применять функцию: @@FETCH_STATUS

Функция @@FETCH_STATUS возвращает:

0, если выборка завершилась успешно;

-1, если выборка завершилась неудачно вследствие попытки выборки строки, находящейся за пределами курсора;

-2, если выборка завершилась неудачно вследствие попытки обращения к удаленной или измененной строке.

DECLARE abc CURSOR SCROLL FOR

SELECT * FROM Клиент

Пример 13.1. Объявление курсора. (html, txt)

DECLARE @MyCursor CURSOR

SET @MyCursor=CURSOR LOCAL SCROLL FOR

SELECT * FROM Клиент

Пример 13.2. Использование переменной для объявления курсора. (html, txt)

DECLARE abc CURSOR GLOBAL SCROLL FOR

SELECT * FROM Клиент

OPEN abc

Пример 13.3. Объявление и открытие курсора. (html, txt)

DECLARE @MyCursor CURSOR

SET @MyCursor=abc

Пример 13.4. Использование переменной для переприсваивания курсора. (html, txt)

Пример 13.5. Разработать курсор для вывода списка фирм и клиентов из Москвы.

DECLARE  @firm    VARCHAR(50),

        @fam     VARCHAR(50),

        @message VARCHAR(80)

PRINT ' Список клиентов'

DECLARE klient_cursor CURSOR LOCAL FOR

   SELECT Фирма, Фамилия

   FROM Клиент

   WHERE Город='Москва'

   ORDER BY Фирма, Фамилия

OPEN klient_cursor

FETCH NEXT FROM klient_cursor INTO @firm, @fam

WHILE @@FETCH_STATUS=0

BEGIN

   SELECT @message='Клиент '+@fam+

                   ' Фирма '+ @firm

   PRINT @message

-- переход к следующему клиенту--

   FETCH NEXT FROM klient_cursor

     INTO @firm, @fam

END

CLOSE klient_cursor

DEALLOCATE klient_cursor

Пример 13.5. Курсор для вывода списка фирм и клиентов из Москвы. (html, txt)

Пример 13.6. Разработать курсор для вывода списка приобретенных клиентами из Москвы товаров и их общей стоимости. В один курсор заносятся все московские клиенты, затем для каждой строки курсора, т.е. для каждого клиента, определяется и распечатывается другой курсор – его покупки. Подсчитывается общая стоимость покупок клиента.

DECLARE @id_kl    INT,

       @firm     VARCHAR(50),

       @fam      VARCHAR(50),       

       @message  VARCHAR(80),

       @nam      VARCHAR(50),

       @d        DATETIME,

       @p        INT,

       @s        INT

SET @s=0

PRINT '  Список покупок'

DECLARE klient_cursor CURSOR LOCAL FOR

   SELECT КодКлиента, Фирма, Фамилия

   FROM Клиент

   WHERE Город='Москва'

   ORDER BY Фирма, Фамилия

OPEN klient_cursor

FETCH NEXT FROM klient_cursor

INTO @id_kl, @firm, @fam

WHILE @@FETCH_STATUS=0

BEGIN

   SELECT @message='Клиент '+@fam+

       ' Фирма '+ @firm

   PRINT @message

   SELECT @message='Наименование товара Дата

       покупки Стоимость'

   PRINT @message

   DECLARE tovar_cursor CURSOR FOR

       SELECT Товар.Название, Сделка.Дата,

           Товар.Цена*Сделка.Количество AS

   Стоимость

       FROM Товар INNER JOIN Сделка ON Товар.

   КодТовара=Сделка.КодТовара

       WHERE Сделка.КодКлиента=@id_kl

   OPEN tovar_cursor

   FETCH NEXT FROM tovar_cursor

     INTO @nam, @d, @p

   IF @@FETCH_STATUS<>0

       PRINT ' Нет покупок'

   WHILE @@FETCH_STATUS=0

   BEGIN

       SELECT @message='   '+@nam+'   '+

          CAST(@d AS CHAR(12))+'  '+

          CAST(@p AS CHAR(6))

       PRINT @message

       SET @s=@s+@p

       FETCH NEXT FROM tovar_cursor

       INTO @nam, @d, @p  

   END

   CLOSE tovar_cursor

   DEALLOCATE tovar_cursor

 

   SELECT @message='Общая стоимость '+

    CAST(@s AS CHAR(6))

   PRINT @message    

-- переход к следующему клиенту--

   FETCH NEXT FROM klient_cursor

   INTO @id_kl, @firm, @fam

END

CLOSE klient_cursor

DEALLOCATE klient_cursor

Пример 13.6. Курсор для вывода списка приобретенных клиентами из Москвы товаров и их общей стоимости. (html, txt)

Пример 13.7. Разработать прокручиваемый курсор для клиентов из Москвы. Если номер телефона начинается на 1, удалить клиента с таким номером и в первой записи курсора заменить первую цифру в номере телефона на 4.

DECLARE @firm     VARCHAR(50),

       @fam      VARCHAR(50),

       @tel      VARCHAR(8),

       @message  VARCHAR(80)

PRINT '  Список клиентов'

DECLARE klient_cursor CURSOR GLOBAL SCROLL

KEYSET FOR

       SELECT Фирма, Фамилия, Телефон

       FROM Клиент

       WHERE Город='Москва'

       ORDER BY Фирма, Фамилия

FOR UPDATE

OPEN klient_cursor

FETCH NEXT FROM klient_cursor

   INTO  @firm, @fam, @tel

WHILE @@FETCH_STATUS=0

BEGIN         

   SELECT @message='Клиент '+@fam+

       '  Фирма  '+@firm '  Телефон '+ @tel

   PRINT @message

-- если номер телефона начинается на 1,

-- удалить клиента с таким номером

   IF @tel LIKE ‘1%’

      DELETE Клиент

      WHERE CURRENT OF klient_cursor

   ELSE

-- переход к следующему клиенту

   FETCH NEXT FROM klient_cursor

   INTO @firm, @fam, @tel

END

FETCH ABSOLUTE 1 FROM klient_cursor

   INTO @firm, @fam, @tel

-- в первой записи заменить первую цифру в

-- номере телефона на 4

  UPDATE Клиент SET Телефон=’4’ +     

  RIGHT(@tel,LEN(@tel)-1))

       WHERE CURRENT OF klient_cursor

SELECT @message='Клиент '+@fam+'  Фирма  '+

   @firm '  Телефон '+ @tel

   PRINT @message

CLOSE klient_cursor

DEALLOCATE klient_cursor

Пример 13.7. Прокручиваемый курсор для клиентов из Москвы. (html, txt)

Пример 13.8. Использование курсора как выходного параметра процедуры. Процедура возвращает набор данных – список товаров.

CREATE PROC my_proc

@cur CURSOR VARYING OUTPUT

AS

SET @cur=CURSOR FORWARD_ONLY STATIC FOR

SELECT Название FROM Товар

OPEN @cur

Пример 13.8. Использование курсора как выходного параметра процедуры. (html, txt)

Вызов процедуры и вывод на печать данных из выходного курсора осуществляется следующим образом:

DECLARE @my_cur CURSOR

DECLARE @n VARCHAR(20)

EXEC my_proc @cur=@my_cur OUTPUT

 FETCH NEXT FROM @my_cur INTO @n

 SELECT @n

WHILE (@@FETCH_STATUS=0)

BEGIN

 FETCH NEXT FROM @my_cur INTO @n

 SELECT @n

END

CLOSE @my_cur

DEALLOCATE @my_cur


Лекция: Триггеры: создание и применение

1 Определение триггера в стандарте языка SQL

Триггеры являются одной из разновидностей хранимых процедур. Их исполнение происходит при выполнении для таблицы какого-либо оператора языка манипулирования данными (DML). Триггеры используются для проверки целостности данных, а также для отката транзакций.

Триггер – это откомпилированная SQL-процедура, исполнение которой обусловлено наступлением определенных событий внутри реляционной базы данных. Применение триггеров большей частью весьма удобно для пользователей базы данных. И все же их использование часто связано с дополнительными затратами ресурсов на операции ввода/вывода. В том случае, когда тех же результатов (с гораздо меньшими непроизводительными затратами ресурсов) можно добиться с помощью хранимых процедур или прикладных программ, применение триггеров нецелесообразно.

Триггеры – особый инструмент SQL-сервера, используемый для поддержания целостности данных в базе данных. С помощью ограничений целостности, правил и значений по умолчанию не всегда можно добиться нужного уровня функциональности. Часто требуется реализовать сложные алгоритмы проверки данных, гарантирующие их достоверность и реальность. Кроме того, иногда необходимо отслеживать изменения значений таблицы, чтобы нужным образом изменить связанные данные. Триггеры можно рассматривать как своего рода фильтры, вступающие в действие после выполнения всех операций в соответствии с правилами, стандартными значениями и т.д.

Триггер представляет собой специальный тип хранимых процедур, запускаемых сервером автоматически при попытке изменения данных в таблицах, с которыми триггеры связаны. Каждый триггер привязывается к конкретной таблице. Все производимые им модификации данных рассматриваются как одна транзакция. В случае обнаружения ошибки или нарушения целостности данных происходит откат этой транзакции. Тем самым внесение изменений запрещается. Отменяются также все изменения, уже сделанные триггером.

Создает триггер только владелец базы данных. Это ограничение позволяет избежать случайного изменения структуры таблиц, способов связи с ними других объектов и т.п.

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

В отличие от обычной подпрограммы, триггер выполняется неявно в каждом случае возникновения триггерного события, к тому же он не имеет аргументов. Приведение его в действие иногда называют запуском триггера. С помощью триггеров достигаются следующие цели:

  •  проверка корректности введенных данных и выполнение сложных ограничений целостности данных, которые трудно, если вообще возможно, поддерживать с помощью ограничений целостности, установленных для таблицы;
  •  выдача предупреждений, напоминающих о необходимости выполнения некоторых действий при обновлении таблицы, реализованном определенным образом;
  •  накопление аудиторской информации посредством фиксации сведений о внесенных изменениях и тех лицах, которые их выполнили;
  •  поддержка репликации.

Основной формат команды CREATE TRIGGER показан ниже:

<Определение_триггера>::=

 CREATE TRIGGER имя_триггера

 BEFORE | AFTER <триггерное_событие>

 ON <имя_таблицы>

 [REFERENCING

   <список_старых_или_новых_псевдонимов>]

 [FOR EACH { ROW | STATEMENT}]

 [WHEN(условие_триггера)]

 <тело_триггера>

триггерные события состоят из вставки, удаления и обновления строк в таблице. В последнем случае для триггерного события можно указать конкретные имена столбцов таблицы. Время запуска триггера определяется с помощью ключевых слов BEFORE (триггер запускается до выполнения связанных с ним событий) или AFTER (после их выполнения).

Выполняемые триггером действия задаются для каждой строки (FOR EACH ROW), охваченной данным событием, или только один раз для каждого события (FOR EACH STATEMENT).

Обозначение <список_старых_или_новых_псевдонимов> относится к таким компонентам, как старая или новая строка (OLD / NEW) либо старая или новая таблица (OLD TABLE / NEW TABLE). Ясно, что старые значения не применимы для событий вставки, а новые – для событий удаления.

При условии правильного использования триггеры могут стать очень мощным механизмом. Основное их преимущество заключается в том, что стандартные функции сохраняются внутри базы данных и согласованно активизируются при каждом ее обновлении. Это может существенно упростить приложения. Тем не менее следует упомянуть и о присущих триггеру недостатках:

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

Неправильно написанные триггеры могут привести к серьезным проблемам, таким, например, как появление "мертвых" блокировок. Триггеры способны длительное время блокировать множество ресурсов, поэтому следует обратить особое внимание на сведение к минимуму конфликтов доступа.

2 Реализация триггеров в среде MS SQL Server

В реализации СУБД MS SQL Server используется следующий оператор создания или изменения триггера:

<Определение_триггера>::=

{CREATE | ALTER} TRIGGER имя_триггера

ON {имя_таблицы | имя_просмотра }

[WITH ENCRYPTION ]

{

{ { FOR | AFTER | INSTEAD OF }

{ [ DELETE] [,] [ INSERT] [,] [ UPDATE] }

[ WITH APPEND ]

[ NOT FOR REPLICATION ]

AS

  sql_оператор[...n]

} |

{ {FOR | AFTER | INSTEAD OF } { [INSERT] [,]

 [UPDATE] }

[ WITH APPEND]

[ NOT FOR REPLICATION]

AS

{ IF UPDATE(имя_столбца)

[ {AND | OR} UPDATE(имя_столбца)] [...n]

|

IF (COLUMNS_UPDATES(){оператор_бит_обработки}

 бит_маска_изменения)

{оператор_бит_сравнения }бит_маска [...n]}

sql_оператор [...n]

}

}

Триггер может быть создан только в текущей базе данных, но допускается обращение внутри триггера к другим базам данных, в том числе и расположенным на удаленном сервере.

Рассмотрим назначение аргументов из команды CREATE | ALTER TRIGGER.

Имя триггера должно быть уникальным в пределах базы данных. Дополнительно можно указать имя владельца.

При указании аргумента WITH ENCRYPTION сервер выполняет шифрование кода триггера, чтобы никто, включая администратора, не мог получить к нему доступ и прочитать его. Шифрование часто используется для скрытия авторских алгоритмов обработки данных, являющихся интеллектуальной собственностью программиста или коммерческой тайной.

Типы триггеров

В SQL Server существует два параметра, определяющих поведение триггеров:

  •  AFTER. Триггер выполняется после успешного выполнения вызвавших его команд. Если же команды по какой-либо причине не могут быть успешно завершены, триггер не выполняется. Следует отметить, что изменения данных в результате выполнения запроса пользователя и выполнение триггера осуществляется в теле одной транзакции: если произойдет откат триггера, то будут отклонены и пользовательские изменения. Можно определить несколько AFTER-триггеров для каждой операции (INSERT, UPDATE, DELETE). Если для таблицы предусмотрено выполнение нескольких AFTER-триггеров, то с помощью системной хранимой процедуры sp_settriggerorder можно указать, какой из них будет выполняться первым, а какой последним. По умолчанию в SQL Server все триггеры являются AFTER-триггерами.
  •  INSTEAD OF. Триггер вызывается вместо выполнения команд. В отличие от AFTER-триггера INSTEAD OF-триггер может быть определен как для таблицы, так и для просмотра. Для каждой операции INSERT, UPDATE, DELETE можно определить только один INSTEAD OF-триггер.

Триггеры различают по типу команд, на которые они реагируют.

Существует три типа триггеров:

  •  INSERT TRIGGER – запускаются при попытке вставки данных с помощью команды INSERT.
  •  UPDATE TRIGGER – запускаются при попытке изменения данных с помощью команды UPDATE.
  •  DELETE TRIGGER – запускаются при попытке удаления данных с помощью команды DELETE.

Конструкции [ DELETE] [,] [ INSERT] [,] [ UPDATE] и FOR | AFTER | INSTEAD OF } { [INSERT] [,] [UPDATE] определяют, на какую команду будет реагировать триггер. При его создании должна быть указана хотя бы одна команда. Допускается создание триггера, реагирующего на две или на все три команды.

Аргумент WITH APPEND позволяет создавать несколько триггеров каждого типа.

При создании триггера с аргументом NOT FOR REPLICATION запрещается его запуск во время выполнения модификации таблиц механизмами репликации.

Конструкция AS sql_оператор[...n] определяет набор SQL- операторов и команд, которые будут выполнены при запуске триггера.

Отметим, что внутри триггера не допускается выполнение ряда операций, таких, например, как:

  •  создание, изменение и удаление базы данных;
  •  восстановление резервной копии базы данных или журнала транзакций.

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

3 Программирование триггера

При выполнении команд добавления, изменения и удаления записей сервер создает две специальные таблицы: inserted и deleted. В них содержатся списки строк, которые будут вставлены или удалены по завершении транзакции. Структура таблиц inserted и deleted идентична структуре таблиц, для которой определяется триггер. Для каждого триггера создается свой комплект таблиц inserted и deleted, поэтому никакой другой триггер не сможет получить к ним доступ. В зависимости от типа операции, вызвавшей выполнение триггера, содержимое таблиц inserted и deleted может быть разным:

  •  команда INSERT – в таблице inserted содержатся все строки, которые пользователь пытается вставить в таблицу; в таблице deleted не будет ни одной строки; после завершения триггера все строки из таблицы inserted переместятся в исходную таблицу;
  •  команда DELETE – в таблице deleted будут содержаться все строки, которые пользователь попытается удалить; триггер может проверить каждую строку и определить, разрешено ли ее удаление; в таблице inserted не окажется ни одной строки;
  •  команда UPDATE – при ее выполнении в таблице deleted находятся старые значения строк, которые будут удалены при успешном завершении триггера. Новые значения строк содержатся в таблице inserted. Эти строки добавятся в исходную таблицу после успешного выполнения триггера.

Для получения информации о количестве строк, которое будет изменено при успешном завершении триггера, можно использовать функцию @@ROWCOUNT; она возвращает количество строк, обработанных последней командой. Следует подчеркнуть, что триггер запускается не при попытке изменить конкретную строку, а в момент выполнения команды изменения. Одна такая команда воздействует на множество строк, поэтому триггер должен обрабатывать все эти строки.

Если триггер обнаружил, что из 100 вставляемых, изменяемых или удаляемых строк только одна не удовлетворяет тем или иным условиям, то никакая строка не будет вставлена, изменена или удалена. Такое поведение обусловлено требованиями транзакции – должны быть выполнены либо все модификации, либо ни одной.

Триггер выполняется как неявно определенная транзакция, поэтому внутри триггера допускается применение команд управления транзакциями. В частности, при обнаружении нарушения ограничений целостности для прерывания выполнения триггера и отмены всех изменений, которые пытался выполнить пользователь, необходимо использовать команду ROLLBACK TRANSACTION.

Для получения списка столбцов, измененных при выполнении команд INSERT или UPDATE, вызвавших выполнение триггера, можно использовать функцию COLUMNS_UPDATED(). Она возвращает двоичное число, каждый бит которого, начиная с младшего, соответствует одному столбцу таблицы (в порядке следования столбцов при создании таблицы). Если бит установлен в значение "1", то соответствующий столбец был изменен. Кроме того, факт изменения столбца определяет и функция UPDATE (имя_столбца).

Для удаления триггера используется команда

DROP TRIGGER {имя_триггера} [,...n]

Приведем примеры использования триггеров.

Пример 14.1. Использование триггера для реализации ограничений на значение. В добавляемой в таблицу Сделка записи количество проданного товара должно быть не больше, чем его остаток из таблицы Склад.

Команда вставки записи в таблицу Сделка может быть, например, такой:

INSERT INTO Сделка 

 VALUES (3,1,-299,'01/08/2002')

Создаваемый триггер должен отреагировать на ее выполнение следующим образом: необходимо отменить команду, если в таблице Склад величина остатка товара оказалась меньше продаваемого количества товара с введенным кодом (в примере код товара=3). Во вставляемой записи количество товара указывается со знаком "+", если товар поставляется, и со знаком "-", если он продается. Представленный триггер настроен на обработку только одной добавляемой записи.

CREATE TRIGGER Триггер_ins

ON Сделка FOR INSERT

AS

IF @@ROWCOUNT=1

BEGIN

 IF NOT EXISTS(SELECT *

     FROM inserted

     WHERE -inserted.количество<=ALL(SELECT

       Склад.Остаток

   FROM Склад,Сделка

   WHERE Склад.КодТовара=

     Сделка.КодТовара))

   BEGIN

     ROLLBACK TRAN

   PRINT

  'Отмена поставки: товара на складе нет'

   END

END

Пример 14.1. Использование триггера для реализации ограничений на значение. (html, txt)

Пример 14.2. Использования триггера для сбора статистических данных.

Создать триггер для обработки операции вставки записи в таблицу Сделка, например, такой команды:

INSERT INTO Сделка 

 VALUES (3,1,200,'01/08/2002')

поставляется товар с кодом 3 от клиента с кодом 1 в количестве 200 единиц.

При продаже или получении товара необходимо соответствующим образом изменить количество его складского запаса. Если товара на складе еще нет, необходимо добавить соответствующую запись в таблицу Склад. Триггер обрабатывает только одну добавляемую строку.

ALTER TRIGGER Триггер_ins

ON Сделка FOR INSERT

AS

DECLARE @x INT, @y INT

IF @@ROWCOUNT=1

--в таблицу Сделка добавляется запись

--о поставке товара

BEGIN

--количество проданного товара должно быть не

--меньше, чем его остаток из таблицы Склад

IF NOT EXISTS(SELECT *

          FROM inserted

          WHERE -inserted.количество<

 =ALL(SELECT Склад.Остаток

        FROM Склад,Сделка

        WHERE Склад.КодТовара=

             Сделка.КодТовара))

   BEGIN

      ROLLBACK TRAN

        PRINT 'откат товара нет '

    END

--если записи о поставленном товаре еще нет,

--добавляется соответствующая запись

--в таблицу Склад

 IF NOT EXISTS ( SELECT *

          FROM Склад С, inserted i

          WHERE С.КодТовара=i.КодТовара )

     INSERT INTO Склад (КодТовара,Остаток)

 ELSE

--если запись о товаре уже была в таблице

--Склад, то определяется код и количество

--товара из добавленной в таблицу Сделка записи

 BEGIN

   SELECT @y=i.КодТовара, @x=i.Количество

   FROM Сделка С, inserted i

   WHERE С.КодТовара=i.КодТовара

--и производится изменения количества товара в

--таблице Склад

        UPDATE Склад

        SET Остаток=остаток+@x

        WHERE КодТовара=@y

   END

END

Пример 14.2. Использования триггера для сбора статистических данных. (html, txt)

Пример 14.3. Создать триггер для обработки операции удаления записи из таблицы Сделка, например, такой команды:

DELETE FROM Сделка WHERE КодСделки=4

Для товара, код которого указан при удалении записи, необходимо откорректировать его остаток на складе. Триггер обрабатывает только одну удаляемую запись.

CREATE TRIGGER Триггер_del

ON Сделка FOR DELETE

AS

IF @@ROWCOUNT=1 -- удалена одна запись

BEGIN

 DECLARE @y INT,@x INT

--определяется код и количество товара из

--удаленной из таблицы Склад записи

 SELECT @y=КодТовара, @x=Количество

 FROM deleted

--в таблице Склад корректируется количество

--товара

    UPDATE Склад

    SET Остаток=Остаток-@x

    WHERE КодТовара=@y

END

Пример 14.3. Триггер для обработки операции удаления записи из таблицы (html, txt)

Пример 14.4. Создать триггер для обработки операции изменения записи в таблице Сделка, например, такой командой:

UPDATE Сделка SET количество=количество-10

WHERE КодТовара=3

во всех сделках с товаром, имеющим код, равный 3, уменьшить количество товара на 10 единиц.

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

CREATE TRIGGER Триггер_upd

ON Сделка FOR UPDATE

AS

DECLARE @x INT, @x_old INT, @y INT, @y_old INT

-- курсор с новыми значениями

DECLARE CUR1 CURSOR FOR

   SELECT КодТовара,Количество

   FROM inserted

-- курсор со старыми значениями

DECLARE CUR2 CURSOR FOR

   SELECT КодТовара,Количество

   FROM deleted

OPEN CUR1

OPEN CUR2

-- перемещаемся параллельно по обоим курсорам

   FETCH NEXT FROM CUR1 INTO @x, @y

   FETCH NEXT FROM CUR2 INTO @x_old, @y_old

   WHILE @@FETCH_STATUS=0

     BEGIN

--для старого кода товара уменьшается его

--количество на складе

     UPDATE Склад

     SET Остаток=Остаток-@y_old

     WHERE КодТовара=@x_old

--для нового кода товара, если такого товара

--еще нет на складе, вводится новая запись

     IF NOT EXISTS (SELECT * FROM Склад

        WHERE КодТовара=@x)

     INSERT INTO Склад(КодТовара,Остаток)

       VALUES (@x,@y)

     ELSE

--иначе для нового кода товара увеличивается

--его количество на складе

       UPDATE Склад

       SET Остаток=Остаток+@y

       WHERE КодТовара=@x

     FETCH NEXT FROM CUR1 INTO @x, @y

   FETCH NEXT FROM CUR2 INTO @x_old, @y_old

END

CLOSE CUR1

CLOSE CUR2

DEALLOCATE CUR1

DEALLOCATE CUR2

Пример 14.4. триггер для обработки операции изменения записи в таблице (html, txt)

В рассмотренном триггере отсутствует сравнение количества товара при изменении записи о сделке с его остатком на складе.

Пример 14.5. Исправим этот недостаток. Для генерирования сообщения об ошибке используем в теле триггера команду MS SQL Server RAISERROR, аргументами которой являются текст сообщения, уровень серьезности и статус ошибки.

ALTER TRIGGER Триггер_upd

ON Сделка FOR UPDATE

AS

DECLARE @x INT, @x_old INT, @y INT,

       @y_old INT ,@o INT

DECLARE CUR1 CURSOR FOR

   SELECT КодТовара,Количество

   FROM inserted

DECLARE CUR2 CURSOR FOR

   SELECT КодТовара,Количество

   FROM deleted

OPEN CUR1

OPEN CUR2

   FETCH NEXT FROM CUR1 INTO @x, @y

   FETCH NEXT FROM CUR2 INTO @x_old, @y_old

   WHILE @@FETCH_STATUS=0

     BEGIN

       SELECT @o=остаток

       FROM Склад

       WHERE кодтовара=@x

       IF @o<-@y

       BEGIN

         RAISERROR('откат',16,10)

         CLOSE CUR1

         CLOSE CUR2

         DEALLOCATE CUR1

         DEALLOCATE CUR22

         ROLLBACK TRAN

         RETURN

       END

         UPDATE Склад

       SET Остаток=Остаток-@y_old

       WHERE КодТовара=@x_old

       IF NOT EXISTS (SELECT * FROM Склад

         WHERE КодТовара=@x)

       INSERT INTO Склад(КодТовара,Остаток)

         VALUES (@x,@y)

     ELSE

       UPDATE Склад

       SET Остаток=Остаток+@y

       WHERE КодТовара=@x

   FETCH NEXT FROM CUR1 INTO @x, @y

   FETCH NEXT FROM CUR2 INTO @x_old, @y_old

END

CLOSE CUR1

CLOSE CUR2

DEALLOCATE CUR1

DEALLOCATE CUR2

Пример 14.5. Исправленный вариант триггера для обработки операции изменения записи в таблице (html, txt)

Пример 14.6. В примере 14.5 происходит отмена всех изменений при невозможности реализовать хотя бы одно из них. Создадим триггер, позволяющий отменять изменение только некоторых записей и выполнять изменение остальных.

В этом случае триггер выполняется не после изменения записей, а вместо команды изменения.

ALTER TRIGGER Триггер_upd

ON Сделка INSTEAD OF UPDATE

AS

DECLARE @k INT, @k_old INT

DECLARE @x INT, @x_old INT, @y INT

DECLARE @y_old INT ,@o INT

DECLARE CUR1 CURSOR FOR

   SELECT КодСделки, КодТовара,Количество

   FROM inserted

DECLARE CUR2 CURSOR FOR

   SELECT КодСделки, КодТовара,Количество

   FROM deleted

OPEN CUR1

OPEN CUR2

   FETCH NEXT FROM CUR1 INTO @k,@x, @y

   FETCH NEXT FROM CUR2 INTO @k_old,@x_old,

     @y_old

   WHILE @@FETCH_STATUS=0

     BEGIN

       SELECT @o=остаток

       FROM Склад

       WHERE КодТовара=@x

       IF @o>=-@y

       BEGIN

       RAISERROR('изменение',16,10)

       UPDATE Сделка SET количество=@y,

    КодТовара=@x

       WHERE КодСделки=@k

       UPDATE Склад

       SET Остаток=Остаток-@y_old

       WHERE КодТовара=@x_old

       IF NOT EXISTS (SELECT * FROM Склад

         WHERE КодТовара=@x)

       INSERT INTO Склад(КодТовара, Остаток)

         VALUES (@x,@y)

       ELSE

       UPDATE Склад

       SET Остаток=Остаток+@y

       WHERE КодТовара=@x

     END

     ELSE

       RAISERROR('запись не изменена',16,10)

     FETCH NEXT FROM CUR1 INTO @k,@x, @y

     FETCH NEXT FROM CUR2 INTO @k_old,@x_old,

       @y_old

END

CLOSE CUR1

CLOSE CUR2

DEALLOCATE CUR1

DEALLOCATE CUR2

Лекция: Триггеры в рекурсивных структурах

1 Введение в рекурсивные структуры

Рассмотрим создание таблицы, реализующей рекурсивную иерархию, на примере данных, описывающих отношения подчиненности между сотрудниками. В таблице emp_mgr необходимо задать как имя сотрудника (emp), так и имя его начальника (mgr). Для рекурсивной связи одна и та же сущность является и родительской, и дочерней. При задании рекурсивной связи атрибут первичного ключа мигрирует в качестве внешнего ключа в состав неключевых атрибутов той же сущности (атрибуты emp – сотрудник и mgr – начальник таблицы emp_mgr). Информация о руководителе содержится в той же сущности, поскольку руководитель – сотрудник той же организации. Связь руководит/подчиняется (fk_emp) позволяет хранить древовидную иерархию подчиненности. Такой вид рекурсивной связи называется иерархической рекурсией и задает связь, когда руководитель (экземпляр родительской сущности) может иметь множество подчиненных (экземпляров дочерней сущности), но подчиненный – только одного руководителя. В среде MS SQL Server создадим таблицу emp_mgr:

CREATE TABLE emp_mgr

 (emp CHAR(2) PRIMARY KEY,

 mgr CHAR(2) NULL,

 NoOfReports INT DEFAULT 0,

 CONSTRAINT fk_emp FOREIGN KEY (mgr)

   REFERENCES emp_mgr (emp) )

В таблицу введено поле NoOfReports, в котором для каждого сотрудника определено количество его подчиненных.

Для удобства иллюстрации в качестве имени сотрудника и его начальника будут использоваться латинские буквы. Например, ввод данных в таблицу осуществляется операторами:

INSERT INTO emp_mgr(emp,mgr) VALUES('a',NULL)

INSERT INTO emp_mgr(emp,mgr) VALUES('b','a')

INSERT INTO emp_mgr(emp,mgr) VALUES('c','a')

INSERT INTO emp_mgr(emp,mgr) VALUES('d','a')

INSERT INTO emp_mgr(emp,mgr) VALUES('e','b')

INSERT INTO emp_mgr(emp,mgr) VALUES('f','b')

INSERT INTO emp_mgr(emp,mgr) VALUES('g','b')

INSERT INTO emp_mgr(emp,mgr) VALUES('i','c')

INSERT INTO emp_mgr(emp,mgr) VALUES('k','d')

После ввода данных в таблицу emp_mgr оператор SELECT * FROM emp_mgr возвращает следующий результат:

emp    mgr    NoOfReports

-------------------------

a     NULL   3

b     a      3

c     a      1

d     a      1

e     b      0

f     b      0

g     b      0

i     c      0

 k     d      0

2 Реализация правил целостности данных

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

  1.  Каждый сотрудник имеет только одного руководителя.
  2.  Каждый сотрудник не является сам себе руководителем.
  3.  Каждый руководитель в первую очередь сотрудник.
  4.  Имеется только один сотрудник (директор организации), который никому не подчиняется.
  5.  Правило 2 необходимо усилить. Каждый сотрудник не должен находиться в роли собственного руководителя не только непосредственно, но и опосредствованно, через других сотрудников.

Выполнение правила 1 обеспечивается ограничением первичного ключа и не требует дополнительных SQL-операторов.

Рассмотрим правило 2. Имена сотрудника и его начальника в одной записи не должны совпадать. При добавлении и изменении записи в таблице emp_mgr это требование предъявляется к новой записи, которая до подтверждения транзакции располагается во временной таблице с именем inserted. Этому правилу соответствуют следующие SQL-операторы:

IF EXISTS (SELECT * FROM inserted

          WHERE mgr=emp)

BEGIN

 ROLLBACK TRAN

 RAISERROR('САМ СЕБЕ НАЧАЛЬНИК',16,10)

 RETURN

END

Правило 3 говорит о том, что именем начальника может быть только уже внесенное в таблицу имя сотрудника. Это требование представляет собой декларативную ссылочную целостность и обеспечивается ограничением внешнего ключа. Однако, чтобы запустить механизм триггеров, придется удалить ограничение внешнего ключа и его функцию возложить на триггер.

В новой или измененной записи имя начальника должно быть указано и уже присутствовать в таблице в качестве имени сотрудника, что может быть записано следующими SQL-операторами:

IF EXISTS(SELECT * FROM inserted

   WHERE mgr IS NOT NULL) AND

   NOT EXISTS(SELECT * FROM inserted,emp_mgr

   WHERE emp_mgr.emp=inserted.mgr)

BEGIN

 RAISERROR('НЕТ НАЧАЛЬНИКА',16,10)

 ROLLBACK TRAN

 RETURN

END

или (что эквивалентно)

IF NOT EXISTS(SELECT * FROM emp_mgr, inserted

   WHERE emp_mgr.emp=inserted.mgr

   OR inserted.mgr IS NULL)

BEGIN

 RAISERROR('НЕТ НАЧАЛЬНИКА',16,10)

 ROLLBACK TRAN

 RETURN

END

В соответствии с правилом 4 необходимо проверить, введена ли запись о директоре (сотруднике, у которого нет начальника). Если такая запись уже есть, ввод нового директора запрещается с помощью следующих SQL-операторов:

IF EXISTS (SELECT * FROM inserted

          WHERE mgr IS NULL)

   AND EXISTS

    (SELECT * FROM emp_mgr,inserted

   WHERE emp_mgr.mgr IS NULL

   AND emp_mgr.emp<>inserted.emp)

BEGIN

 ROLLBACK TRAN

 RAISERROR('ОДИН ДИРЕКТОР УЖЕ ЕСТЬ',16,10)

 RETURN

END

Оператор UPDATE может изменить иерархическую структуру таким образом, что возникает ситуация, когда сотрудник становится начальником самому себе через других сотрудников, т.е. в иерархии подчиненности возникает петля. Для исключения подобных преобразований используем SQL-операторы:

IF UPDATE(mgr)--изменился начальник

BEGIN

 DECLARE @x CHAR(2), @y CHAR(2), @xx CHAR(2)

--узнали имя сотрудника,

--у которого изменился начальник

 SELECT @xx=inserted.emp FROM inserted

 SELECT @x=@xx

 SELECT @y='*'

 WHILE @y IS NOT NULL

--пока не дошли до директора

 BEGIN

--запомнили имя начальника

 SELECT @y=mgr FROM emp_mgr

 WHERE emp=@x

 IF @xx=@y

--имя сотрудника и его начальника совпали

 BEGIN

 RAISERROR('транзитивное замыкание',16,10)

   ROLLBACK TRAN

   RETURN

 END

 ELSE

--далее начальник становится сотрудником,

--и в цикле будем искать его начальника

   SELECT @x=@y

 END

END

Чтобы сработали триггеры, необходимо удалить ограничение внешнего ключа:

ALTER TABLE emp_mgr DROP CONSTRAINT fk_emp

Ниже приведен текст триггеров, поддерживающих целостность данных в иерархических структурах. Предполагается, что триггеры обрабатывают ввод, изменение или удаление одной записи.

3 Добавление записи в рекурсивную структуру

ALTER TRIGGER emp_ins

ON emp_mgr FOR INSERT

AS

--Правило 2

IF EXISTS (SELECT * FROM inserted WHERE mgr=emp)

BEGIN

   ROLLBACK TRAN

   RAISERROR('САМ СЕБЕ НАЧАЛЬНИК',16,10)

   RETURN

END

--Правило 4

IF EXISTS (SELECT * FROM inserted WHERE mgr IS NULL) AND

   EXISTS (SELECT * FROM emp_mgr,inserted

       WHERE emp_mgr.mgr IS NULL

 AND emp_mgr.emp<>inserted.emp)

BEGIN

   ROLLBACK TRAN

   RAISERROR('ОДИН ДИРЕКТОР УЖЕ ЕСТЬ',16,10)

   RETURN

END

--Правило 3

IF EXISTS(SELECT * FROM inserted

   WHERE mgr IS NOT NULL) AND

NOT EXISTS(SELECT * FROM inserted,emp_mgr

   WHERE emp_mgr.emp=inserted.mgr)

BEGIN

 RAISERROR('НЕТ ТАКОГО НАЧАЛЬНИКА',16,10)

 ROLLBACK TRAN

 RETURN

END

--Пересчет числа подчиненных у начальника

--добавленного подчиненного

DECLARE @e CHAR(2), @m CHAR(2)

 SELECT @e=emp_mgr.emp FROM emp_mgr, inserted

 WHERE emp_mgr.emp=inserted.mgr

 UPDATE emp_mgr

 SET emp_mgr.NoOfReports=emp_mgr.NoOfReports+1

 WHERE emp_mgr.emp=@e

Пример 15.1. Триггер для добавления записи в таблицу. (html, txt)

Изменение записи в рекурсивной структуре

CREATE TRIGGER emp_upd ON emp_mgr

FOR UPDATE

AS

IF UPDATE(mgr)

BEGIN

--Правило 5

DECLARE @x CHAR(2), @y CHAR(2), @xx CHAR(2)

SELECT @xx=inserted.emp FROM inserted

SELECT @x=@xx

SELECT @y='*'

WHILE @y IS NOT NULL

 BEGIN

 SELECT @y=mgr FROM emp_mgr WHERE emp=@x

 IF @xx=@y

   BEGIN

     RAISERROR('транзитивное замыкание',16,10)

     ROLLBACK TRAN

     RETURN

     END

   ELSE

   SELECT @x=@y

 END

END

--Правило 2

IF EXISTS (SELECT * FROM inserted WHERE mgr=emp)

BEGIN

 ROLLBACK TRAN

 RAISERROR('САМ СЕБЕ НАЧАЛЬНИК',16,10)

 RETURN

END

--Правило 4

IF EXISTS (SELECT * FROM inserted WHERE mgr

 IS NULL) AND EXISTS (SELECT *

 FROM emp_mgr,inserted WHERE emp_mgr.mgr IS NULL

 AND emp_mgr.emp<>inserted.emp)

BEGIN

 ROLLBACK TRAN

 RAISERROR('ОДИН ДИРЕКТОР УЖЕ ЕСТЬ',16,10)

 RETURN

END

--Правило 3

IF UPDATE(mgr)

 IF NOT EXISTS(SELECT * FROM emp_mgr, inserted

   WHERE emp_mgr.emp=inserted.mgr

   OR inserted.mgr IS NULL)

BEGIN

 RAISERROR('НЕТ ТАКОГО НАЧАЛЬНИКА',16,10)

 ROLLBACK TRAN

 RETURN

END

IF UPDATE(mgr)

--пересчет числа подчиненных у старого и нового

--начальников

BEGIN

 UPDATE emp_mgr

   SET emp_mgr.NoOfReports=emp_mgr.NoOfReports+1

   FROM inserted WHERE emp_mgr.emp=inserted.mgr

   UPDATE emp_mgr

   SET emp_mgr.NoOfReports=emp_mgr.NoOfReports-1

   FROM deleted WHERE emp_mgr.emp=deleted.mgr

END

IF UPDATE(emp)

--если изменилось имя сотрудника, следует изменить

--имя начальника у всех его подчиненных

UPDATE emp_mgr SET emp_mgr.mgr=inserted.emp

FROM emp_mgr, inserted, deleted WHERE

emp_mgr.mgr=deleted.emp

Пример 15.2. Триггер для изменения записи в таблице. (html, txt)

Попытка подчинить сотрудника с именем ‘b’ начальнику с именем ‘e’ будет сервером отвергнута, иначе в организации сложилась бы такая ситуация: сотрудник ‘e’ подчинятся сотруднику ‘b’, а сотрудник ‘b’ подчиняется сотруднику ‘e’.

UPDATE emp_mgr SET mgr='e' WHERE emp='b'

Server: Msg 50000, Level 16, State 10,

 Procedure emp_upd,

 Line 15 транзитивное замыкание

Выполнение команды

UPDATE emp_mgr SET mgr='f' WHERE emp='e'

и команды

UPDATE emp_mgr SET mgr='a' WHERE emp='g'

приведет к следующему изменению первоначальной иерархической структуры:

emp    mgr    NoOfReports

-------------------------

a      NULL   4

b      a      1

c      a      1

d      a      1

e      f      0

f      b      1

g      a      0

i      c      0

k      d      0

Удаление записи из рекурсивной структуры

ALTER TRIGGER emp_del

ON emp_mgr

FOR DELETE

AS

DECLARE @e CHAR(2), @m CHAR(2), @r INT

SELECT @e=emp,@m=mgr,@r=NoOfReports FROM deleted

IF @m IS NOT NULL

-- удаляется сотрудник, не являющийся директором

 BEGIN

  IF @r=0

-- удаляется сотрудник, у которого нет подчиненных

-- уменьшается число подчиненных у начальника

-- удаляемого сотрудника

  UPDATE emp_mgr SET NoOfReports=

                     NoOfReports-1

  WHERE emp=@m

 ELSE

   BEGIN

-- удаляется сотрудник, у которого есть подчиненные

-- переподчиним его подчиненных его начальнику,

-- т.е. начальником подчиненных удаляемого сотрудника

-- становится его начальник

  UPDATE emp_mgr SET NoOfReports=

                     NoOfReports+@r-1

  WHERE emp=@m

       UPDATE emp_mgr SET mgr=@m

       WHERE mgr=@e

   END

END

ELSE

-- Правило 4

  IF EXISTS(SELECT * FROM emp_mgr)

    BEGIN

       ROLLBACK TRAN

                RAISERROR('НЕЛЬЗЯ УДАЛЯТЬ

                           ДИРЕКТОРА',16,10)

       RETURN

    END

Пример 15.3. Триггер для удаления записи из таблицы. (html, txt)

Попытка удаления записи о директоре будет отвергнута сервером:

DELETE FROM emp_mgr WHERE emp='a'

Server: Msg 50000, Level 16, State 10,

 Procedure emp_del, Line 24

НЕЛЬЗЯ УДАЛЯТЬ ДИРЕКТОРА

В результате удаления рядового сотрудника с именем b его подчиненные e, f и g станут подчиненными сотрудника с именем a.

DELETE FROM emp_mgr WHERE emp='b'

Первоначальное содержимое таблицы emp_mgr изменится следующим образом:

emp    mgr    NoOfReports

-------------------------

a      NULL   5

c      a      1

d      a      1

e      a      0

f      a      0

g      a      0

i      c      0

k      d      0

Лекция: Транзакции и блокировки

1 Введение в транзакции

Концепция транзакций – неотъемлемая часть любой клиент-серверной базы данных.

Под транзакцией понимается неделимая с точки зрения воздействия на БД последовательность операторов манипулирования данными (чтения, удаления, вставки, модификации), приводящая к одному из двух возможных результатов: либо последовательность выполняется, если все операторы правильные, либо вся транзакция откатывается, если хотя бы один оператор не может быть успешно выполнен. Обработка транзакций гарантирует целостность информации в базе данных. Таким образом, транзакция переводит базу данных из одного целостного состояния в другое.

Поддержание механизма транзакций – показатель уровня развитости СУБД. Корректное поддержание транзакций одновременно является основой обеспечения целостности БД. Транзакции также составляют основу изолированности в многопользовательских системах, где с одной БД параллельно могут работать несколько пользователей или прикладных программ. Одна из основных задач СУБД – обеспечение изолированности, т.е. создание такого режима функционирования, при котором каждому пользователю казалось бы, что БД доступна только ему. Такую задачу СУБД принято называть параллелизмом транзакций.

Большинство выполняемых действий производится в теле транзакций. По умолчанию каждая команда выполняется как самостоятельная транзакция. При необходимости пользователь может явно указать ее начало и конец, чтобы иметь возможность включить в нее несколько команд.

При выполнении транзакции система управления базами данных должна придерживаться определенных правил обработки набора команд, входящих в транзакцию. В частности, разработано четыре правила, известные как требования ACID, они гарантируют правильность и надежность работы системы.

2 ACID-свойства транзакций

Характеристики транзакций описываются в терминах ACID (Atomicity, Consistency, Isolation, Durability – неделимость, согласованность, изолированность, устойчивость).

  •  Транзакция неделима в том смысле, что представляет собой единое целое. Все ее компоненты либо имеют место, либо нет. Не бывает частичной транзакции. Если может быть выполнена лишь часть транзакции, она отклоняется.
  •  Транзакция является согласованной, потому что не нарушает бизнес-логику и отношения между элементами данных. Это свойство очень важно при разработке клиент-серверных систем, поскольку в хранилище данных поступает большое количество транзакций от разных систем и объектов. Если хотя бы одна из них нарушит целостность данных, то все остальные могут выдать неверные результаты.
  •  Транзакция всегда изолирована, поскольку ее результаты самодостаточны. Они не зависят от предыдущих или последующих транзакций – это свойство называется сериализуемостью и означает, что транзакции в последовательности независимы.
  •  Транзакция устойчива. После своего завершения она сохраняется в системе, которую ничто не может вернуть в исходное (до начала транзакции) состояние, т.е. происходит фиксация транзакции, означающая, что ее действие постоянно даже при сбое системы. При этом подразумевается некая форма хранения информации в постоянной памяти как часть транзакции.

Указанные выше правила выполняет сервер. Программист лишь выбирает нужный уровень изоляции, заботится о соблюдении логической целостности данных и бизнес-правил. На него возлагаются обязанности по созданию эффективных и логически верных алгоритмов обработки данных. Он решает, какие команды должны выполняться как одна транзакция, а какие могут быть разбиты на несколько последовательно выполняемых транзакций. Следует по возможности использовать небольшие транзакции, т.е. включающие как можно меньше команд и изменяющие минимум данных. Соблюдение этого требования позволит наиболее эффективным образом обеспечить одновременную работу с данными множества пользователей.

3 Блокировки

Повышение эффективности работы при использовании небольших транзакций связано с тем, что при выполнении транзакции сервер накладывает на данные блокировки.

Блокировкой называется временное ограничение на выполнение некоторых операций обработки данных. Блокировка может быть наложена как на отдельную строку таблицы, так и на всю базу данных. Управлением блокировками на сервере занимается менеджер блокировок, контролирующий их применение и разрешение конфликтов. Транзакции и блокировки тесно связаны друг с другом. Транзакции накладывают блокировки на данные, чтобы обеспечить выполнение требований ACID. Без использования блокировок несколько транзакций могли бы изменять одни и те же данные.

Блокировка представляет собой метод управления параллельными процессами, при котором объект БД не может быть модифицирован без ведома транзакции, т.е. происходит блокирование доступа к объекту со стороны других транзакций, чем исключается непредсказуемое изменение объекта. Различают два вида блокировки:

  •  блокировка записи – транзакция блокирует строки в таблицах таким образом, что запрос другой транзакции к этим строкам будет отменен;
  •  блокировка чтения – транзакция блокирует строки так, что запрос со стороны другой транзакции на блокировку записи этих строк будет отвергнут, а на блокировку чтения – принят.

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

  •  транзакция, результатом действия которой на строку данных в таблице является ее извлечение, обязана наложить блокировку чтения на эту строку;
  •  транзакция, предназначенная для модификации строки данных, накладывает на нее блокировку записи;
  •  если запрашиваемая блокировка на строку отвергается из-за уже имеющейся блокировки, то транзакция переводится в режим ожидания до тех пор, пока блокировка не будет снята;
  •  блокировка записи сохраняется вплоть до конца выполнения транзакции.

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

Если в системе управления базами данных не реализованы механизмы блокирования, то при одновременном чтении и изменении одних и тех же данных несколькими пользователями могут возникнуть следующие проблемы одновременного доступа:

  •  проблема последнего изменения возникает, когда несколько пользователей изменяют одну и ту же строку, основываясь на ее начальном значении; тогда часть данных будет потеряна, т.к. каждая последующая транзакция перезапишет изменения, сделанные предыдущей. Выход из этой ситуации заключается в последовательном внесении изменений;
  •  проблема "грязного" чтения возможна в том случае, если пользователь выполняет сложные операции обработки данных, требующие множественного изменения данных перед тем, как они обретут логически верное состояние. Если во время изменения данных другой пользователь будет считывать их, то может оказаться, что он получит логически неверную информацию. Для исключения подобных проблем необходимо производить считывание данных после окончания всех изменений;
  •  проблема неповторяемого чтения является следствием неоднократного считывания транзакцией одних и тех же данных. Во время выполнения первой транзакции другая может внести в данные изменения, поэтому при повторном чтении первая транзакция получит уже иной набор данных, что приводит к нарушению их целостности или логической несогласованности;
  •  проблема чтения фантомов появляется после того, как одна транзакция выбирает данные из таблицы, а другая вставляет или удаляет строки до завершения первой. Выбранные из таблицы значения будут некорректны.

Для решения перечисленных проблем в специально разработанном стандарте определены четыре уровня блокирования. Уровень изоляции транзакции определяет, могут ли другие (конкурирующие) транзакции вносить изменения в данные, измененные текущей транзакцией, а также может ли текущая транзакция видеть изменения, произведенные конкурирующими транзакциями, и наоборот. Каждый последующий уровень поддерживает требования предыдущего и налагает дополнительные ограничения:

  •  уровень 0 – запрещение "загрязнения" данных. Этот уровень требует, чтобы изменять данные могла только одна транзакция; если другой транзакции необходимо изменить те же данные, она должна ожидать завершения первой транзакции;
  •  уровень 1 – запрещение "грязного" чтения. Если транзакция начала изменение данных, то никакая другая транзакция не сможет прочитать их до завершения первой;
  •  уровень 2 – запрещение неповторяемого чтения. Если транзакция считывает данные, то никакая другая транзакция не сможет их изменить. Таким образом, при повторном чтении они будут находиться в первоначальном состоянии;
  •  уровень 3 – запрещение фантомов. Если транзакция обращается к данным, то никакая другая транзакция не сможет добавить новые или удалить имеющие строки, которые могут быть считаны при выполнении транзакции. Реализация этого уровня блокирования выполняется путем использования блокировок диапазона ключей. Подобная блокировка накладывается не на конкретные строки таблицы, а на строки, удовлетворяющие определенному логическому условию.
  •  

4  Управление транзакциями

Под управлением транзакциями понимается способность управлять различными операциями над данными, которые выполняются внутри реляционной СУБД. Прежде всего, имеется в виду выполнение операторов INSERT, UPDATE и DELETE. Например, после создания таблицы (выполнения оператора CREATE TABLE) не нужно фиксировать результат: создание таблицы фиксируется в базе данных автоматически. Точно так же с помощью отмены транзакции не удастся восстановить только что удаленную оператором DROP TABLE таблицу.

После успешного выполнения команд, заключенных в тело одной транзакции, немедленного изменения данных не происходит. Для окончательного завершения транзакции существуют так называемые команды управления транзакциями, с помощью которых можно либо сохранить в базе данных все изменения, произошедшие в ходе ее выполнения, либо полностью их отменить.

Существуют три команды, которые используются для управления транзакциями:

  •  COMMIT – для сохранения изменений;
  •  ROLLBACK – для отмены изменений;
  •  SAVEPOINT – для установки особых точек возврата.

После завершения транзакции вся информация о произведенных изменениях хранится либо в специально выделенной оперативной памяти, либо во временной области отката в самой базе данных до тех пор, пока не будет выполнена одна из команд управления транзакциями. Затем все изменения или фиксируются в базе данных, или отбрасываются, а временная область отката освобождается.

Команда COMMIT предназначена для сохранения в базе данных всех изменений, произошедших в ходе выполнения транзакции. Она сохраняет результаты всех операций, которые имели место после выполнения последней команды COMMIT или ROLLBACK.

Команда ROLLBACK предназначена для отмены транзакций, еще не сохраненных в базе данных. Она отменяет только те транзакции, которые были выполнены с момента выдачи последней команды COMMIT или ROLLBACK.

Команда SAVEPOINT (точка сохранения) предназначена для установки в транзакции особых точек, куда в дальнейшем может быть произведен откат (при этом отката всей транзакции не происходит). Команда имеет следующий вид:

SAVEPOINT имя_точки_сохранения

Она служит исключительно для создания точек сохранения среди операторов, предназначенных для изменения данных. Имя точки сохранения в связанной с ней группе транзакций должно быть уникальным.

Для отмены действия группы транзакций, ограниченных точками сохранения, используется команда ROLLBACK со следующим синтаксисом:

ROLLBACK TO имя_точки_сохранения

Поскольку с помощью команды SAVEPOINT крупное число транзакций может быть разбито на меньшие и поэтому более управляемые группы, ее применение является одним из способов управления транзакциями.

5 Управление транзакциями в среде MS SQL Server

Определение транзакций

SQL Server предлагает множество средств управления поведением транзакций. Пользователи в основном должны указывать только начало и конец транзакции, используя команды SQL или API (прикладного интерфейса программирования). Транзакция определяется на уровне соединения с базой данных и при закрытии соединения автоматически закрывается. Если пользователь попытается установить соединение снова и продолжить выполнение транзакции, то это ему не удастся. Когда транзакция начинается, все команды, выполненные в соединении, считаются телом одной транзакции, пока не будет достигнут ее конец.

SQL Server поддерживает три вида определения транзакций:

  •  явное;
  •  автоматическое;
  •  подразумеваемое.

По умолчанию SQL Server работает в режиме автоматического начала транзакций, когда каждая команда рассматривается как отдельная транзакция. Если команда выполнена успешно, то ее изменения фиксируются. Если при выполнении команды произошла ошибка, то сделанные изменения отменяются и система возвращается в первоначальное состояние.

Когда пользователю понадобится создать транзакцию, включающую несколько команд, он должен явно указать транзакцию.

Сервер работает только в одном из двух режимов определения транзакций: автоматическом или подразумевающемся. Он не может находиться в режиме исключительно явного определения транзакций. Этот режим работает поверх двух других.

Для установки режима автоматического определения транзакций используется команда:

SET IMPLICIT_TRANSACTIONS  OFF

При работе в режиме неявного (подразумевающегося) начала транзакций SQL Server автоматически начинает новую транзакцию, как только завершена предыдущая. Установка режима подразумевающегося определения транзакций выполняется посредством другой команды:

SET IMPLICIT_TRANSACTIONS  ON

Явные транзакции

Явные транзакции требуют, чтобы пользователь указал начало и конец транзакции, используя следующие команды:

  •  начало транзакции: в журнале транзакций фиксируются первоначальные значения изменяемых данных и момент начала транзакции;
  •  BEGIN TRAN[SACTION]
  •   [имя_транзакции |
  •   @имя_переменной_транзакции
  •  [WITH MARK [‘описание_транзакции’]]]
  •  конец транзакции: если в теле транзакции не было ошибок, то эта команда предписывает серверу зафиксировать все изменения, сделанные в транзакции, после чего в журнале транзакций помечается, что изменения зафиксированы и транзакция завершена;
  •  COMMIT [TRAN[SACTION]
  •   [имя_транзакции |
  •   @имя_переменной_транзакции]]
  •  создание внутри транзакции точки сохранения: СУБД сохраняет состояние БД в текущей точке и присваивает сохраненному состоянию имя точки сохранения;
  •  SAVE TRAN[SACTION]
  •    {имя_точки_сохранеия |
  •    @имя_переменной_точки_сохранения}
  •  прерывание транзакции; когда сервер встречает эту команду, происходит откат транзакции, восстанавливается первоначальное состояние системы и в журнале транзакций отмечается, что транзакция была отменена. Приведенная ниже команда отменяет все изменения, сделанные в БД после оператора BEGIN TRANSACTION или отменяет изменения, сделанные в БД после точки сохранения, возвращая транзакцию к месту, где был выполнен оператор SAVE TRANSACTION.
  •  ROLLBACK [TRAN[SACTION]
  •    [имя_транзакции |
  •    @имя_переменной_транзакции
  •    | имя_точки_сохранения
  •   |@имя_переменной_точки_сохранения]]

Функция @@TRANCOUNT возвращает количество активных транзакций.

Функция @@NESTLEVEL возвращает уровень вложенности транзакций.

BEGIN TRAN

SAVE TRANSACTION  point1

Пример 16.1. Использование точек сохранения (html, txt)

В точке point1 сохраняется первоначальное состояние таблицы Товар

DELETE FROM Товар WHERE КодТовара=2

 SAVE TRANSACTION  point2

В точке point2 сохраняется состояние таблицы Товар без товаров с кодом 2.

DELETE FROM Товар WHERE КодТовара=3

 SAVE TRANSACTION  point3

В точке point3 сохраняется состояние таблицы Товар без товаров с кодом 2 и с кодом 3.

DELETE FROM Товар WHERE КодТовара<>1

 ROLLBACK TRANSACTION point3

Происходит возврат в состояние таблицы без товаров с кодами 2 и 3, отменяется последнее удаление.

SELECT * FROM Товар

Оператор SELECT покажет таблицу Товар без товаров с кодами 2 и 3.

ROLLBACK TRANSACTION point1

Происходит возврат в первоначальное состояние таблицы.

SELECT * FROM Товар

COMMIT

Первоначальное состояние сохраняется.

6 Вложенные транзакции

Вложенными называются транзакции, выполнение которых инициируется из тела уже активной транзакции.

Для создания вложенной транзакции пользователю не нужны какие-либо дополнительные команды. Он просто начинает новую транзакцию, не закрыв предыдущую. Завершение транзакции верхнего уровня откладывается до завершения вложенных транзакций. Если транзакция самого нижнего (вложенного) уровня завершена неудачно и отменена, то все транзакции верхнего уровня, включая транзакцию первого уровня, будут отменены. Кроме того, если несколько транзакций нижнего уровня были завершены успешно (но не зафиксированы), однако на среднем уровне (не самая верхняя транзакция) неудачно завершилась другая транзакция, то в соответствии с требованиями ACID произойдет откат всех транзакций всех уровней, включая успешно завершенные. Только когда все транзакции на всех уровнях завершены успешно, происходит фиксация всех сделанных изменений в результате успешного завершения транзакции верхнего уровня.

Каждая команда COMMIT TRANSACTION работает только с последней начатой транзакцией. При завершении вложенной транзакции команда COMMIT применяется к наиболее "глубокой" вложенной транзакции. Даже если в команде COMMIT TRANSACTION указано имя транзакции более высокого уровня, будет завершена транзакция, начатая последней.

Если команда ROLLBACK TRANSACTION используется на любом уровне вложенности без указания имени транзакции, то откатываются все вложенные транзакции, включая транзакцию самого высокого (верхнего) уровня. В команде ROLLBACK TRANSACTION разрешается указывать только имя самой верхней транзакции. Имена любых вложенных транзакций игнорируются, и попытка их указания приведет к ошибке. Таким образом, при откате транзакции любого уровня вложенности всегда происходит откат всех транзакций. Если же требуется откатить лишь часть транзакций, можно использовать команду SAVE TRANSACTION , с помощью которой создается точка сохранения.

BEGIN TRAN

INSERT Товар (Название, остаток)

VALUES ('v',40)

  BEGIN TRAN

  INSERT Товар (Название, остаток)

  VALUES ('n',50)

  BEGIN TRAN

 INSERT Товар (Название, остаток)

 VALUES ('m',60)

ROLLBACK TRAN

Пример 16.2. Вложенные транзакции. (html, txt)

Здесь происходит возврат на начальное состояние таблицы, поскольку выполнение команды ROLLBACK TRAN без указания имени транзакции откатывает все транзакции.

7 Блокировки в среде MS SQL Server

Управление блокировками

Пользователю чаще всего не нужно предпринимать никаких действий по управлению блокировками. Всю работу по установке, снятию и разрешению конфликтов выполняет специальный компонент сервера, называемый менеджером блокировок. MS SQL Server поддерживает различные уровни блокирования объектов (или детализацию блокировок), начиная с отдельной строки таблицы и заканчивая базой данных в целом. Менеджер блокировок автоматически оценивает, какое количество данных необходимо блокировать, и устанавливает соответствующий тип блокировки. Это позволяет поддерживать равновесие между производительностью работы системы блокирования и возможностью пользователей получать доступ к данным. Блокирование на уровне строки позволяет наиболее точно управлять таким доступом, поскольку блокируются только действительно изменяемые строки. Множество пользователей могут одновременно работать с данными с минимальными задержками. Платой за это является увеличение числа операций установки и снятия блокировок, а также большое количество служебной информации, которое приходится хранить для отслеживания установленных блокировок. При блокировке на уровне таблицы производительность системы блокирования резко увеличивается, так как необходимо установить лишь одну блокировку и снять ее только после завершения транзакции. Пользователь при этом имеет максимальную скорость доступа к данным. В то же время они не доступны никому другому, потому что вся таблица заблокирована. Приходится ожидать, пока текущий пользователь завершит работу.

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

  •  Коллективные блокировки. Они накладываются при выполнении операций чтения данных (например, SELECT). Если сервер установил на ресурс коллективную блокировку, то пользователь может быть уверен, что уже никто не сможет изменить эти данные.
  •  Блокировка обновления. Если на ресурс установлена коллективная блокировка и для этого ресурса устанавливается блокировка обновления, то никакая транзакция не сможет наложить коллективную блокировку или блокировку обновления.
  •  Монопольная блокировка. Этот тип блокировок используется, если транзакция изменяет данные. Когда сервер устанавливает монопольную блокировку на ресурс, то никакая другая транзакция не может прочитать или изменить заблокированные данные. Монопольная блокировка не совместима ни с какими другими блокировками, и ни одна блокировка, включая монопольную, не может быть наложена на ресурс.
  •  Блокировка массивного обновления. Накладывается сервером при выполнении операций массивного копирования в таблицу и запрещает обращение к таблице любым другим процессам. В то же время несколько процессов, выполняющих массивное копирование, могут одновременно вставлять строки в таблицу.

Помимо перечисленных основных типов блокировок SQL Server поддерживает ряд специальных блокировок, предназначенных для повышения производительности и функциональности обработки данных. Они называются блокировками намерений и используются сервером в том случае, если транзакция намеревается получить доступ к данным вниз по иерархии и для других транзакций необходимо установить запрет на наложение блокировок, которые будут конфликтовать с блокировкой, накладываемой первой транзакцией.

Ранее рассмотренные блокировки относятся к данным. Помимо перечисленных в среде SQL Server существует два других типа блокировок: блокировка диапазона ключей и блокировка схемы (метаданных, описывающих структуру объекта).

Блокировка диапазона ключей решает проблему возникновения фантомов и обеспечивает требования сериализуемости транзакции. Блокировки этого типа устанавливаются на диапазон строк, соответствующих определенному логическому условию, с помощью которого осуществляется выборка данных из таблицы.

Блокировка схемы используется при выполнении команд модификации структуры таблиц для обеспечения целостности данных.

"Мертвые" блокировки

"Мертвые", или тупиковые, блокировки характерны для многопользовательских систем. "Мертвая" блокировка возникает, когда две транзакции блокируют два блока данных и для завершения любой из них нужен доступ к данным, заблокированным ранее другой транзакцией. Для завершения каждой транзакции необходимо дождаться, пока блокированная другой транзакцией часть данных будет разблокирована. Но это невозможно, так как вторая транзакция ожидает разблокирования ресурсов, используемых первой.

Без применения специальных механизмов обнаружения и снятия "мертвых" блокировок нормальная работа транзакций будет нарушена. Если в системе установлен бесконечный период ожидания завершения транзакции (а это задано по умолчанию), то при возникновении "мертвой" блокировки для двух транзакций вполне возможно, что, ожидая освобождения заблокированных ресурсов, в тупике окажутся и новые транзакции. Чтобы избежать подобных проблем, в среде MS SQL Server реализован специальный механизм разрешения конфликтов тупикового блокирования.

Для этих целей сервер снимает одну из блокировок, вызвавших конфликт, и откатывает инициализировавшую ее транзакцию. При выборе блокировки, которой необходимо пожертвовать, сервер исходит из соображений минимальной стоимости.

Полностью избежать возникновения "мертвых" блокировок нельзя. Хотя сервер и имеет эффективные механизмы снятия таких блокировок, все же при написании приложений следует учитывать вероятность их возникновения и предпринимать все возможные действия для предупреждения этого. "Мертвые" блокировки могут существенно снизить производительность, поскольку системе требуется достаточно много времени для их обнаружения, отката транзакции и повторного ее выполнения.

Для минимизации возможности образования "мертвых" блокировок при разработке кода транзакции следует придерживаться следующих правил:

  •  выполнять действия по обработке данных в постоянном порядке, чтобы не создавать условия для захвата одних и тех же данных;
  •  избегать взаимодействия с пользователем в теле транзакции;
  •  минимизировать длительность транзакции и выполнять ее по возможности в одном пакете;
  •  применять как можно более низкий уровень изоляции.

8 Уровни изоляции SQL Server

Уровень изоляции определяет степень независимости транзакций друг от друга. Наивысшим уровнем изоляции является сериализуемость, обеспечивающая полную независимость транзакций друг от друга. Каждый последующий уровень соответствует требованиям всех предыдущих и обеспечивает дополнительную защиту транзакций.

SQL Server поддерживает все четыре уровня изоляции, определенные стандартом ANSI. Уровень изоляции устанавливается командой:

SET TRANSACTION ISOLATION LEVEL

{ READ COMMITTED

| READ UNCOMMITTED

| REPEATABLE READ

| SERIALIZABLE }

  •  READ UNCOMMITEDнезавершенное чтение, или допустимо черновое чтение. Низший уровень изоляции, соответствующий уровню 0. Он гарантирует только физическую целостность данных: если несколько пользователей одновременно изменяют одну и ту же строку, то в окончательном варианте строка будет иметь значение, определенное пользователем, последним изменившим запись. По сути, для транзакции не устанавливается никакой блокировки, которая гарантировала бы целостность данных. Для установки этого уровня используется команда:
  •  SET TRANSACTION ISOLATION
  •  LEVEL READ UNCOMMITTED
  •  READ COMMITTEDзавершенное чтение, при котором отсутствует черновое, "грязное" чтение. Тем не менее в процессе работы одной транзакции другая может быть успешно завершена и сделанные ею изменения зафиксированы. В итоге первая транзакция будет работать с другим набором данных. Это проблема неповторяемого чтения. Данный уровень изоляции установлен в SQL Server по умолчанию и устанавливается посредством команды:
  •   
  •  SET TRANSACTION ISOLATION
  •  LEVEL READ COMMITTED
  •  REPEATABLE READповторяющееся чтение. Повторное чтение строки возвратит первоначально считанные данные, несмотря на любые обновления, произведенные другими пользователями до завершения транзакции. Тем не менее на этом уровне изоляции возможно возникновение фантомов. Его установка реализуется командой:
  •  SET TRANSACTION ISOLATION
  •  LEVEL REPEATABLE READ
  •  SERIALIZABLEсериализуемость. Чтение запрещено до завершения транзакции. Это максимальный уровень изоляции, который обеспечивает полную изоляцию транзакций друг от друга. Он устанавливается командой:
  •  SET TRANSACTION ISOLATION
  •  LEVEL SERIALIZABLE

В каждый момент времени возможен только один уровень изоляции.

Таблица 16.1. Уровень изоляции конкурирующей транзакции принят по умолчанию (READ COMMITTED). В примере шаги 4, 6 и 8 демонстрируют черновое чтение. Шаги 9 и 10 блокируются, потому что данные захвачены конкурирующей транзакцией.

Пользователь user1 Конкурирующая транзакция

Пользователь user2 Текущая транзакция

USE basa_user2 

BEGIN TRANSACTION TRA

USE basa_user2 

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

BEGIN TRANSACTION TRB

1.SELECT * FROM Товар

2.SELECT * FROM Товар

3.UPDATE Товар SET остаток=остаток+10 WHERE КодТовара=4

4.SELECT * FROM Товар (читает измененные неподтвержденные данные)

5.DELETE FROM Товар WHERE КодТовара=4

6.SELECT * FROM Товар (читает измененные неподтвержденные данные)

7.INSERT Товар (Название, остаток) VALUES ('SS',999)

8.SELECT * FROM Товар (читает измененные неподтвержденные данные)

12.ROLLBACK TRANSACTION TRA

9.UPDATE Товар SET остаток=остаток+10 WHERE КодТовара=4 (блокируется до окончания конкурирующей транзакции)

10.DELETE FROM Товар WHERE КодТовара=4 (блокируется до окончания конкурирующей транзакции)

11.INSERT Товар(Название, остаток) VALUES ('SS',999) (выполняется)

13.ROLLBACK TRANSACTION TRB SELECT @@TRANCOUNT

Таблица 16.2. Уровень изоляции конкурирующей транзакции принят по умолчанию (READ COMMITTED). В примере шаги 4, 6 и 8 демонстрируют блокировку данных, захваченных другой транзакцией, в то время как работа с другими данными разрешается (шаг 10).

Пользователь user1 Конкурирующая транзакция

Пользователь user2 Текущая транзакция

USE basa_user2 

BEGIN TRANSACTION TRA

USE basa_user2 

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

BEGIN TRANSACTION TRB

1.SELECT * FROM Товар

2.SELECT * FROM Товар

3.UPDATE Товар SET остаток=остаток+10 (захватывает данные)

4.SELECT * FROM Товар WHERE КодТовара=4 (блокируется до окончания конкурирующей транзакции)

5.DELETE FROM Товар WHERE КодТовара=4

6.UPDATE Товар SET остаток=остаток+10 WHERE КодТовара=4 (блокируется до окончания конкурирующей транзакции)

7.UPDATE Товар SET остаток=остаток+10 WHERE КодТовара=4 (выполняется той транзакцией, которая первой захватила данные на изменение или удаление)

8.DELETE FROM Товар WHERE КодТовара=4 (блокируется до окончания конкурирующей транзакции)

9.INSERT Товар (Название, остаток) VALUES ('SS',999) 

10.INSERT Товар(Название, остаток) VALUES ('SS',999) (выполняется)

11.ROLLBACK TRANSACTION TRA SELECT @@TRANCOUNT

12.ROLLBACK TRANSACTION TRB SELECT @@TRANCOUNT

Таблица 16.3. Уровень изоляции конкурирующей транзакции принят по умолчанию (READ COMMITTED). На шаге 2 транзакция захватила данные чтением и блокирует работу с ними со стороны конкурирующей транзакции (шаги 3, 5), которая может лишь добавлять записи (шаг 7).

Пользователь user1 Конкурирующая транзакция

Пользователь user2 Текущая транзакция

USE basa_user2 

BEGIN TRANSACTION TRA

USE basa_user2 

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ

BEGIN TRANSACTION TRB

1.SELECT * FROM Товар

2.SELECT * FROM Товар (захватывает данные)

3.UPDATE Товар SET остаток=остаток+10 WHERE КодТовара=4 (блокируется)

4.SELECT * FROM Товар (блокируется до окончания конкурирующей транзакции)

5.DELETE FROM Товар WHERE КодТовара=4 (блокируется)

6.UPDATE Товар SET остаток=остаток+10 WHERE КодТовара=4 (выполняется, т.к. данные захвачены текущей транзакцией)

7.INSERT Товар (Название, остаток) VALUES ('SS',999) (выполняется)

8.DELETE FROM Товар WHERE КодТовара=4 (выполняется, т.к. данные захвачены текущей транзакцией)

10.ROLLBACK TRANSACTION TRA SELECT @@TRANCOUNT

9.INSERT Товар(Название, остаток) VALUES ('SS',999) (выполняется)

11.ROLLBACK TRANSACTION TRB SELECT @@TRANCOUNT

Таблица 16.4. Уровень изоляции конкурирующей транзакции принят по умолчанию (READ COMMITTED). Пример демонстрирует, что текущая транзакция захватила данные чтением (шаг 2) и блокирует любые действия с ними со стороны конкурирующей транзакции вплоть до вставки данных (шаг 7).

Пользователь user1 Конкурирующая транзакция

Пользователь user2 Текущая транзакция

USE basa_user2 

BEGIN TRANSACTION TRA

USE basa_user2 

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

BEGIN TRANSACTION TRB

1.SELECT * FROM Товар

2.SELECT * FROM Товар (захватывает данные)

3.UPDATE Товар SET остаток=остаток+10 WHERE КодТовара=4 (блокируется)

4.SELECT * FROM Товар (выполняется)

5.DELETE FROM Товар WHERE КодТовара=4 (блокируется)

6.UPDATE Товар SET остаток=остаток+10 WHERE КодТовара=4 (выполняется, т.к. данные захвачены текущей транзакцией)

7.INSERT Товар (наименование, остаток) VALUES ('SS',999) (блокируется)

8.DELETE FROM Товар WHERE КодТовара=4 (выполняется, т.к. данные захвачены текущей транзакцией)

10.ROLLBACK TRANSACTION TRA SELECT @@TRANCOUNT

9.INSERT Товар(Название, остаток) VALUES ('SS',999) (выполняется)

11.ROLLBACK TRANSACTION TRB SELECT @@TRANCOUNT

Лекция: Основные методы защиты данных. Управление пользователями

1 Управление пользователями базы данных

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

2 Управление пользователями в среде MS SQL Server

Рассмотрим вопрос создания пользователей в среде MS SQL Server.

После проектирования логической структуры базы данных, связей между таблицами, ограничений целостности и других структур необходимо определить круг пользователей, которые будут иметь доступ к базе данных.

В системе SQL-сервер организована двухуровневая настройка ограничения доступа к данным. На первом уровне необходимо создать так называемую учетную запись пользователя (login), что позволяет ему подключиться к самому серверу, но не дает автоматического доступа к базам данных. На втором уровне для каждой базы данных SQL-сервера на основании учетной записи необходимо создать запись пользователя. На основе прав, выданных пользователю как пользователю базы данных (user), его регистрационное имя (login) получает доступ к соответствующей базе данных. В разных базах данных login одного и того же пользователя может иметь одинаковые или разные имена user с разными правами доступа. Иначе говоря, с помощью учетной записи пользователя осуществляется подключение к SQL-серверу, после чего определяются его уровни доступа для каждой базы данных в отдельности.

В системе SQL-сервер существуют дополнительные объекты – роли, которые определяют уровень доступа к объектам SQL-сервера. Они разделены на две группы: назначаемые для учетных записей пользователя сервера и используемые для ограничения доступа к объектам базы данных.

Итак, на уровне сервера система безопасности оперирует следующими понятиями:

  •  аутентификация;
  •  учетная запись;
  •  встроенные роли сервера.

На уровне базы данных применяются следующие понятия;

  •  пользователь базы данных;
  •  фиксированная роль базы данных;
  •  пользовательская роль базы данных.

Режимы аутентификации

SQL Server предлагает два режима аутентификации пользователей:

  •  режим аутентификации средствами Windows NT/2000;
  •  смешанный режим аутентификации (Windows NT Authentication and SQL Server Authentication).

Администрирование системы безопасности

Для создания пользователя в среде MS SQL Server следует предпринять следующие шаги:

  1.  Создать в базе данных учетную запись пользователя, указав для него пароль и принятое по умолчанию имя базы данных (процедура sp_addlogin).
  2.  Добавить этого пользователя во все необходимые базы данных (процедура sp_adduser).
  3.  Предоставить ему в каждой базе данных соответствующие привилегии (команда GRANT) .

Создание новой учетной записи может быть произведено с помощью системной хранимой процедуры:

sp_addlogin

[@login=] 'учетная_запись'

[, [@password=] 'пароль']

[, [@defdb=] 'база_данных_по_умолчанию']

После завершения аутентификации и получения идентификатора учетной записи (login ID) пользователь считается зарегистрированным, и ему предоставляется доступ к серверу. Для каждой базы данных, к объектам которой он намерен получить доступ, учетная запись пользователя (login) ассоциируется с пользователем (user) конкретной базы данных, что осуществляется посредством процедуры:

sp_adduser

[@loginame=] 'учетная_запись'

[, [@name_in_db=] 'имя_пользователя']

[, [@grpname=] 'имя_роли']

Отобразить учетную запись Windows NT в имя пользователя позволяет хранимая процедура:

sp_grantdbaccess

[@login=] ‘учетная_запись’

[, [@name_in_db=]‘имя_пользователя’]

Пользователь, который создает объект в базе данных (таблицу, хранимую процедуру, просмотр), становится его владельцем. Владелец объекта (database object owner dbo) имеет все права доступа к созданному им объекту. Чтобы пользователь мог создать объект, владелец базы данных (dbo) должен предоставить ему соответствующие права. Полное имя создаваемого объекта включает в себя имя создавшего его пользователя.

Владелец объекта не имеет специального пароля или особых прав доступа. Он неявно имеет полный доступ, но должен явно предоставить доступ другим пользователям.

SQL Server позволяет передавать права владения от одного пользователя другому с помощью процедуры:

sp_changeobjectowner

[@objname=] ‘имя_объекта’

[@newowner=] ‘имя_владельца’

Роль позволяет объединить в одну группу пользователей, выполняющих одинаковые функции.

В SQL Server реализовано два вида стандартных ролей: на уровне сервера и на уровне баз данных. При установке SQL Server создаются фиксированные роли сервера (например, sysadmin с правом выполнения любых функций SQL-сервера) и фиксированные роли базы данных (например, db_owner с правом полного доступа к базе данных или db_accessadmin с правом добавления и удаления пользователей). Среди фиксированных ролей базы данных существует роль public, которая имеет специальное назначение, поскольку ее членами являются все пользователи, имеющие доступ к базе данных.

Можно включить любую учетную запись SQL Server (login) или учетную запись Windows NT в любую роль сервера.

Роли базы данных позволяют объединять пользователей в одну административную единицу и работать с ней как с обычным пользователем. Можно назначить права доступа к объектам базы данных для конкретной роли, при этом автоматически все члены этой роли наделяются одинаковыми правами.

В роль базы данных можно включить пользователей SQL Server, роли SQL Server, пользователей Windows NT.

Различные действия по отношению к роли осуществляются при помощи специальных процедур:

  •  создание новой роли:
  •  sp_addrole
  •  [@rolename=] 'имя_роли'
  •  [, [@ownername=] 'имя_владельца']
  •  добавление пользователя к роли:
  •  sp_addrolemember
  •  [@rolename=] 'имя_роли',
  •  [@membername=] 'имя_пользователя'
  •  удаление пользователя из роли:
  •  sp_droprolemember
  •  [@rolename=] 'имя_роли',
  •  [@membername=] 'имя_пользователя'
  •  удаление роли:
  •  sp_droprole
  •  [@rolename=] 'имя_роли'

3 Управление доступом к данным

Определение привилегий в стандарте языка

Каждая СУБД должна поддерживать механизм, гарантирующий, что доступ к базе данных смогут получить только те пользователи, которые имеют соответствующее разрешение. Язык SQL включает операторы GRANT и REVOKE, предназначенные для организации защиты таблиц в базе данных. Механизм защиты построен на использовании идентификаторов пользователей, предоставляемых им прав владения и привилегий.

Идентификатором пользователя называется обычный идентификатор языка SQL, применяемый для обозначения некоторого пользователя базы данных. Каждому пользователю должен быть назначен собственный идентификатор, присваиваемый администратором базы данных. Из очевидных соображений безопасности идентификатор пользователя, как правило, связывается с некоторым паролем. Каждый выполняемый СУБД SQL-оператор выполняется от имени какого-либо пользователя. Идентификатор пользователя определяет, на какие объекты базы данных пользователь может ссылаться и какие операции с этими объектами он имеет право выполнять.

Каждый созданный в среде SQL объект имеет своего владельца, который изначально является единственной персоной, знающей о существовании данного объекта и имеет право выполнять с ним любые операции.

Привилегиями, или правами, называются действия, которые пользователь имеет право выполнять в отношении данной таблицы базы данных или представления. В стандарте SQL определяется следующий набор привилегий:

  •  SELECTправо выбирать данные из таблицы;
  •  INSERTправо вставлять в таблицу новые строки;
  •  UPDATEправо изменять данные в таблице;
  •  DELETEправо удалять строки из таблицы;
  •  REFERENCESправо ссылаться на столбцы указанной таблицы в описаниях требований поддержки целостности данных;
  •  USAGEправо использовать домены, проверки и наборы символов.

Привилегии INSERT и UPDATE могут ограничиваться лишь отдельными столбцами таблицы, в этом случае пользователю разрешается модифицировать значения только указанных столбцов. Аналогичным образом привилегия REFERENCES может распространяться исключительно на отдельные столбцы таблицы, что позволит использовать их имена в формулировках требований защиты целостности данных – например, в предложениях CHECK и FOREIGN KEY, входящих в определение других таблиц, тогда как применение для подобных целей остальных столбцов будет запрещено.

Когда пользователь с помощью оператора CREATE TABLE создает новую таблицу, он автоматически становится ее владельцем и получает по отношению к ней полный набор привилегий, которых остальные пользователи исходно не имеют. Чтобы обеспечить им доступ, владелец должен явным образом предоставить необходимые права, для чего используется оператор GRANT.

Создавая представление с помощью оператора CREATE VIEW, пользователь автоматически становится владельцем этого представления и также получает полный набор прав. Для создания представления пользователю достаточно иметь привилегию SELECT для всех входящих в него таблиц и привилегию REFERENCES для всех столбцов, упоминаемых в определении этого представления. Привилегии INSERT, UPDATE и DELETE в отношении созданного представления пользователь получит только в том случае, если имеет соответствующие привилегии в отношении всех используемых в представлении таблиц.

Предоставление привилегий пользователям

Оператор GRANT применяется для предоставления привилегий в отношении поименованных объектов базы данных указанным пользователям. Обычно его использует владелец таблицы с целью предоставления доступа к ней другим пользователям. Оператор GRANT имеет следующий формат:

<предоставление_привилегий>::=

GRANT {<привилегия>[,...n] |

   ALL PRIVILEGES}

ON имя_объекта

TO {<идентификатор_пользователя>

   [,...n]| PUBLIC}

[ WITH GRANT OPTION]

Параметр <привилегия> представляет собой:

<привилегия>::=

{SELECT | DELETE | INSERT

   [(имя_столбца[,...n])]

| UPDATE [(имя_столбца[,...n])]}

| REFERENCES [(имя_столбца[,...n])] |

   USAGE }

Из соображений упрощения в операторе GRANT можно указать ключевое слово ALL PRIVILEGES, что позволит предоставить указанному пользователю все существующие привилегии без необходимости их перечисления. Кроме того, в этом операторе может указываться ключевое слово PUBLIC, означающее предоставление доступа указанного типа не только всем существующим пользователям, но также и всем тем, кто будет определен в базе данных впоследствии.

Параметр имя_объекта может использоваться как имя таблицы базы данных, представления, домена, набора символов, проверки.

Благодаря параметру WITH GRANT OPTION, указанные в операторе GRANT пользователи имеют право передавать все предоставленные им в отношении указанного объекта привилегии другим пользователям, которые, в свою очередь, будут наделены точно таким же правом передачи своих полномочий. Если данный параметр не будет указан, получатель привилегии не сможет передать свои права другим пользователям. Таким образом, владелец объекта может четко контролировать, кто получил право доступа к объекту и какие полномочия ему предоставлены.

Отмена предоставленных пользователям привилегий

В языке SQL для отмены привилегий, предоставленных пользователям посредством оператора GRANT, используется оператор REVOKE. С помощью этого оператора могут быть отменены все или некоторые из привилегий, полученных указанным пользователем раньше. Оператор REVOKE имеет следующий формат:

<отмена_привилегий>::=

REVOKE[GRANT OPTION FOR]

   {<привилегия>[,...n]

   | ALL PRIVILEGES}

ON имя_объекта

FROM {<идентификатор_пользователя>

   [,...n]| PUBLIC}

[RESTRICT | CASCADE]

Ключевое слово ALL PRIVILEGES означает, что для указанного пользователя отменяются все привилегии, предоставленные ему ранее тем пользователем, который ввел данный оператор. Необязательная фраза GRANT OPTION FOR позволяет для всех привилегий, переданных в исходном операторе GRANT фразой WITH GRANT OPTION, отменять возможность их передачи независимо от самих привилегий.

Если в операторе указано ключевое слово RESTRICT, успешное выполнение команды REVOKE возможно лишь в том случае, когда перечисленные в операторе привилегии не могут послужить причиной появления у каких-либо других пользователей так называемых "оставленных" привилегий. С помощью параметра CASCADE удаляются все привилегии, которые иначе могли бы остаться у других пользователей.

"Оставленными" являются привилегии, сохранившиеся у пользователя, которому они в свое время были предоставлены с помощью параметра GRANT OPTION.

Поскольку наличие привилегии необходимо для создания определенных объектов, вместе с ее удалением можно лишиться права, за счет использования которого был образован тот или иной объект (подобные объекты называются "брошенными"). Если в результате выполнения оператора REVOKE могут появиться брошенные объекты (например, представления), право будет отменено при условии, что в нем не указывается ключевое слово CASCADE. Если ключевое слово CASCADE в операторе присутствует, то для любых брошенных объектов, возникающих при выполнении исходного оператора REVOKE, будут автоматически выданы операторы DROP.

Привилегии, которые были предоставлены указанному пользователю другими пользователями, не могут быть затронуты оператором REVOKE. Следовательно, если другой пользователь также предоставил данному пользователю удаляемую привилегию, то право доступа к соответствующей таблице у указанного пользователя сохранится. Например, пусть пользователь A и пользователь Е имели право INSERT на таблицу Товар. Пользователь А предоставил пользователю B привилегию INSERT для таблицы Товар, причем с указанием WITH GRANT OPTION (этап 1). Пользователь B передал эту привилегию пользователю C (этап 2). Затем пользователь C получил ее же от пользователя E (этап 3). Далее пользователь C предоставил упомянутую привилегию пользователю D (этап 4). Когда пользователь A отменяет привилегию INSERT для пользователя B, она не может быть отменена и для пользователя C, поскольку ранее он уже получил ее от пользователя E. Если бы пользователь E не предоставил данной привилегии пользователю C, то удаление привилегии пользователя B имело бы следствием каскадное удаление привилегий для пользователей C и D (см. таблицу 17.1).

4 Реализация прав на доступ к объектам баз данных в среде MS SQL Server

Категории прав в среде MS SQL Server

При подключении к SQL Server все возможные действия пользователей определяются правами (привилегиями, разрешениями), выданными их учетной записи, группе или роли, в которых они состоят.

Права можно разделить на три категории:

  •  права на доступ к объектам;
  •  права на выполнение команд;
  •  неявные права.

Таблица 17.1.

Пользователь A

Пользователь B

Пользователь C

Пользователь D

Пользователь E

GRANT INSERT ON Товар TO B WITH GRANT OPTION

Получение права

GRANT INSERT ON Товар TO C WITH GRANT OPTION

Получение права от B. Получение права от E

GRANT INSERT ON Товар TO C WITH GRANT OPTION

GRANT INSERT ON Товар TO D

Получение права

REVOKE INSERT ON Товар TO B CASCADE

Отмена права

Сохранение права

Сохранение права

Сохранение права

Работа с данными и выполнение хранимых процедур требуют наличия класса доступа, называемого правами на доступ к объектам баз данных. Под объектами подразумеваются таблицы, столбцы таблиц, представления, хранимые процедуры.

Для различных объектов применяются разные наборы прав доступа к ним:

  •  SELECT, INSERT, UPDATE, DELETE, REFERENCESдля таблицы или представления;
  •  SELECT, UPDATE – для конкретного столбца таблицы или представления;
  •  EXECUTE – для хранимых процедур и функций.

Право INSERT позволяет вставлять новые строки в таблицу или представление и выдается только на уровне таблицы или представления; оно не может быть выдано на уровне столбца.

Право UPDATE выдается либо на уровне таблицы, что позволяет изменять в ней все данные, либо на уровне отдельного столбца, что разрешает изменять данные только в его пределах.

Право DELETE позволяет удалять строки из таблицы или представления, выдается только на уровне таблицы или представления, но не может быть выдано на уровне столбца.

Право SELECT разрешает выборку данных и может выдаваться как на уровне таблицы, так и на уровне отдельного столбца.

Право REFERENCES предоставляет возможность ссылаться на указанный объект. Применительно к таблицам разрешает создавать внешние ключи, ссылающиеся на первичный ключ или уникальный столбец этой таблицы.

Предоставление прав

Для управления разрешением пользователя на доступ к объектам базы данных используется команда:

<предоставление_привилегий>::=

GRANT { ALL [ PRIVILEGES] | <привилегия>

   [,...n]}

{ [(    имя_столбца [,...n])]

   ON { имя_таблицы |

       имя_просмотра} |

           ON {имя_таблицы |

       имя_просмотра }

           ([имя_столбца

               [,...n])]

   | ON {имя_хранимой_процедуры |

       имя_внешней_процедуры}}

TO {    имя_пользователя | имя_группы |

       имя_роли} [,...n]

[WITH GRANT OPTION ]

[AS {имя_группы | имя_роли }]

Параметр <привилегия> представляет собой следующую конструкцию:

<привилегия>::=

{SELECT | DELETE | INSERT |

UPDATE | EXECUTE | REFERENCES }

Параметр WITH GRANT OPTION поможет пользователю, которому вы предоставляете права, назначить права на доступ к объекту другим пользователям. Его использование требует особой осторожности, поскольку при этом владелец теряет контроль над предоставлением прав на доступ другим пользователям. Лучше всего ограничить круг пользователей, обладающих возможностью управлять назначением прав.

Необязательный параметр AS {имя_группы | имя_роли } позволяет указать участие пользователя в роли, обеспечивающей предоставление прав другим пользователям.

Единственное право доступа, которое может быть предоставлено для хранимой процедуры, – право на ее выполнение (EXECUTE). Естественно, кроме этого владелец хранимой процедуры может просматривать и изменять ее код.

Для функции можно выдать право на ее выполнение, а кроме того, выдать право REFERENCES, что обеспечит возможность связывания функции с объектами, на которые она ссылается. Такое связывание позволит запретить внесение изменений в структуру объектов, способных привести к нарушению работы функции.

Права на выполнение команд SQL

Этот класс прав контролирует возможность создания объектов в базе данных, самой базы данных и выполнения процедуры резервного копирования. Можно использовать следующую команду для предоставления права на выполнение команд SQL:

<предоставление_права_выполнения>::=

GRANT {ALL | <команда>[,...n]}

TO {имя_пользователя | имя_группы |

   имя_роли} [,...n]

Параметр <команда> представляет собой следующую конструкцию:

<команда>::=

{CREATE DATABASE | CREATE TABLE |

   CREATE VIEW | CREATE DEFAULT |

   CREATE RULE | CREATE PROCEDURE

       | BACKUP DATABASE |

BACKUP LOG | ALL }

Таким образом, можно предоставить право на создание базы данных, таблицы, просмотра, умолчания, правила, хранимой процедуры, резервной копии базы данных и журнала транзакций или предоставить сразу все вышеперечисленные права.

Неявные права

Выполнение некоторых действий не требует явного разрешения и доступно по умолчанию. Эти действия могут быть выполнены только членами ролей сервера или владельцами объектов в базе данных.

Неявные права не предоставляются пользователю непосредственно, он получает их лишь при определенных обстоятельствах. Например, пользователь может стать владельцем объекта базы данных, только если сам создаст объект либо если кто-то другой передаст ему право владения своим объектом. Таким образом владелец объекта автоматически получит права на выполнение любых действий с объектом, в том числе и на предоставление доступа к объекту другим пользователям. Эти права нигде не указываются, выполнять любые действия позволяет только факт владения объектом.

Запрещение доступа

Система безопасности SQL Server имеет иерархическую структуру, и поэтому роли базы данных включают в себя учетные записи и группы Windows NT, пользователей и роли SQL Server. Пользователь же, в свою очередь, может участвовать в нескольких ролях и одновременно иметь разные права доступа для разных ролей. Когда одна из ролей, в которых состоит пользователь, имеет разрешение на доступ к данным, он автоматически имеет аналогичные права. Тем не менее, если возникает необходимость, пользователю можно запретить доступ к данным или командам, тогда аннулируются все разрешения на доступ, полученные им на любом уровне иерархии. При этом гарантируется, что доступ останется запрещенным независимо от разрешений, предоставленных на более высоком уровне.

Для запрещения доступа к объектам базы данных используется команда:

<запрещение_доступа>::=

DENY {ALL [PRIVILEGES]| | <привилегия>

   [,...n]}

{ [(имя_столбца [,...n])]

   ON { имя_таблицы |

   имя_просмотра}

| ON {имя_таблицы | имя_просмотра }

   [имя_столбца [,...n])]

| ON {имя_хранимой_процедуры |

   имя_внешней_процедуры}}

TO {имя_пользователя | имя_группы |

   имя_роли}

   [,...n]

[CASCADE ]

Параметр CASCADE позволяет отзывать права не только у конкретного пользователя, но также и у всех тех, кому он предоставил аналогичные права.

Для запрещения выполнения команд SQL применяется оператор:

<запрещение_выполнения>::=

DENY {ALL | <команда>[,...n]}

TO {имя_пользователя | имя_группы |

   имя_роли} [,...n]

Неявное отклонение доступа

Неявное отклонение подобно запрещению доступа с тем отличием, что оно действует только на том уровне, на котором определено. Если пользователю на определенном уровне неявно отклонен доступ, он все же может получить его на другом уровне иерархии через членство в роли, имеющей право просмотра. По умолчанию доступ пользователя к данным неявно отклонен. Для неявного отклонения доступа к объектам базы данных используется команда:

<неявное_отклонение_доступа>::=

REVOKE [GRANT OPTION FOR]

{ALL [ PRIVILEGES]| | <привилегия>

   [,...n]}

{ [(имя_столбца [,...n])] ON

   { имя_таблицы | имя_просмотра}

   | ON {имя_таблицы |

       имя_просмотра }

   [имя_столбца [,...n])]

   | ON {имя_хранимой_процедуры |

   имя_внешней_процедуры}}

TO | FROM {имя_пользователя |

   имя_группы |

   имя_роли}[,...n]

[CASCADE ]

[AS {имя_группы | имя_роли }]

Для неявного отклонения разрешения на выполнение команд SQL используется следующая команда:

<неявное_отклонение_разрешения>::=

REVOKE {ALL | <команда>[,...n]}

FROM {имя_пользователя | имя_группы |

   имя_роли}[,...n]

 

Смысл параметров аналогичен параметрам команд GRANT и DENY. Параметр GRANT OPTION FOR используется, когда необходимо отозвать право, предоставленное параметром WITH GRANT OPTION команды GRANT. Пользователь сохраняет разрешение на доступ к объекту, но теряет возможность предоставлять это разрешение другим пользователям.

Конфликты доступа

Разрешения, предоставленные роли или группе, наследуются их членами. Хотя пользователю может быть предоставлен доступ через членство в одной роли, роль другого уровня может иметь запрещение на действие с объектом. В таком случае возникает конфликт доступа.

При разрешении конфликтов доступа SQL Server руководствуется следующим принципом: разрешение на предоставление доступа имеет самый низкий приоритет, а на запрещение доступа – самый высокий. Это значит, что доступ к данным может быть получен только явным его предоставлением при отсутствии запрещения доступа на любом другом уровне иерархии системы безопасности. Если доступ явно не предоставлен, пользователь не сможет работать с данными.

Пример 17.1. Создать новую базу данных, нового пользователя для этой базы данных, предоставив ему все права.

-- создание администратором новой

-- базы данных

CREATE DATABASE basa_user

-- создание нового пользователя с

-- именем UserA и паролем ‘123’

-- базой данных по умолчанию для

-- пользователя UserA будет база

-- с именем basa_user.

sp_addlogin 'UserA','123','basa_user'

-- переход в базу данных basa_user

USE basa_user

-- добавление в текущую базу данных

-- (basa_user) пользователя с именем

-- userA

sp_adduser 'UserA'

-- предоставление пользователю userA

-- в базе данных basa_user всех прав

GRANT ALL TO UserA

Пример 17.1. Создание новой базы данных, нового пользователя для этой базы данных, с предоставлением ему всех прав. (html, txt)

Пример 17.2. Использование ролей.

Создадим роль stud и включим в эту роль двух пользователей user1 и user2:

sp_addrole 'stud'

sp_addrolemember 'stud','user1'

sp_addrolemember 'stud','user2'

Предоставим права роли stud и непосредственно пользователю user2:

GRANT SELECT, INSERT ON Товар TO stud

GRANT SELECT, INSERT ON Товар TO user2

После выполнения этих команд пользователи user1 и user2 могут выполнять команды выборки и добавления записи в таблицу Товар.

Приостановим право на выполнение вставки в таблицу Товар для роли stud:

REVOKE INSERT ON Товар TO stud

После выполнения предыдущей команды пользователь user1 теряет право вставки записи, а user2 сохраняет это право, поскольку право вставки предоставлено ему явно.

Выполним команду

DENY INSERT ON Товар TO stud.

После выполнения этой команды оба пользователя лишаются права вставки в таблицу Товар.




1. Задание 1 Найти длину и направляющие косинусы вектора если заданы векторы
2. Научные факты гипотезы теории как формы научного познания
3. Обґрунтування ролі соціального капіталу у формуванні засад концепції людського розвитку на прикладі Полтавської області
4. Римский театр
5. Человек который обманывал смерть чаще чем его знаменитое альтер эго пока только в начале своего пути
6. 30е годы В начале 20х гг
7. Розвиток і виховання дітей раннього віку 1965 та ін
8. применение ЭВМ для автоматизации вычислений.
9. спортивного праздника среди военнопатриотических клубов и объединений посвященного Дню Защитника Отеч
10. Рынок ценных бумаг тенденции и перспективы
11. тематики и информационных технологий ОТЧЕТ по учебноознакомительной практике На тему
12. ЮрИнфоР 1997 Содержание Введение
13. на тему- Изучение мнения учащихся о деятельности СНО ВШНИ Научноисследовательский и творческий союз студе
14. Underworld The homosexul subculture of theEighteenth Century mixed with the gypsies trmps thieves of populr song to produce rich crossfertilistion of customs phrses nd trditions
15. Отправление за неделю до выезда
16. ИстокФарм Дорогие Лебедянцы и гости города Мы рады сообщить вам о появлении в нашем городе филиала м
17. Системы телеизмерения
18. Министерство внутренних дел Республики Беларусь
19. Виды массовой коммуникации
20. Impaled Nazarene