Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Laby / ЛР2 / БД_2(неоконченная) от 10 окт.doc
Скачиваний:
22
Добавлен:
12.04.2015
Размер:
177.15 Кб
Скачать

Insert into Имя_таблицы( Имя_столбца_1, Имя_столбца_2, и т.Д.)

SELECT далее_синтаксис_запроса

Например, для копирования из таблицы Заказы( Номер, Дата, КодСотрудника) всех заказов, сделанных в 2006 году, в таблицу Заказы2006( Номер, Дата, КодСотрудника), выполняется запрос:

INSERT INTO “Заказы2006”( “Номер”, “Дата”, “КодСотрудника”)

SELECT “Номер”, “Дата”, “КодСотрудника”

FROM “Заказы”

WHERE “Дата” BETWEEN ’01-01-2006’ AND ’31-12-2006’;

Стандартом SQL1 запрещается использовать в многострочной инструкции INSERT оператор сортировки ORDER BY, объединения строк UNION, и добавление строк в таблицу из самой себя.

Удаление строк

Инструкция DELETE удаляет из таблицы одну и более строк, удовлетворяющих условию.

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

WHERE условие_отбора_строк

Условие может отсутствовать – тогда удаляются все строки таблицы. Например,

DELETE FROM “Сотрудники”; // вычищает всю таблицу

Пример другой: удалить из таблицы Заказы строки, обслуживаемые сотрудниками 13 и 14.

DELETE FROM “Заказы”

WHERE “КодСотрудника”=13 OR “КодСотрудника”=14;

В условии отбора строк можно использовать запросы из других таблиц.

Например, имеются две связанные таблицы Заказы(Номер, Дата, КодСотрудника(FK)) и Сотрудники( КодСотрудника(PK), ФИО). Требуется удалить из таблицы Заказы строки, относящиеся к сотруднику “Петров Иван Иванович”.

DELETE FROM “Заказы”

WHERE “КодСотрудника” IN (SELECT “КодСотрудника

FROM “Сотрудники

WHERE “Имя”=’Петров Иван Иванович’);

Изменение строк

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

UPDATE Имя_таблицы

SET Имя_столбца_1=выражение, Имя_столбца_2=выражение, и т.д.

WHERE условие_отбора_строк

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

Пример: в таблице Сотрудники( КодСотрудника, Имя) изменить имя “Бринёва Лариса Николаевна” на “Медведева Лариса Николаевна”:

UPDATE “Поставщики”

SET “Имя”=’Медведева Лариса Николаевна

WHERE “Имя”=’Бринёва Лариса Николаевна’;

В выражении можно использовать операции + – * /, значения других столбцов текущей строки, имена переменных, вызывать функции. Пример: увеличить значение скидки на 0,05 у всех клиентов, имеющих сумму заказов свыше 3000.

UPDATE “Клиенты”

SET “Скидка”=”Скидка”+0.05

WHERE “СуммаЗаказов”>3000;

Стандарт SQL1 разрешает использовать в условии WHERE выборку из других таблиц. Например, у сотрудников, имеющих более 10 заказов, установить в таблице Сотрудники( КодСотрудника, ФИО, Статус) поле Статус=2.

UPDATE “Сотрудники”

SET “Статус”=2

WHERE EXISTS( SELECT COUNT(*)

FROM “Заказы”

WHERE “Заказы”.”КодСотрудника”=”Сотрудники”.”КодСотрудника”

HAVING COUNT(*)>10);

Запрос будет работать так: сервер по очереди перебирает все строки таблицы Сотрудники. Для каждой строки вычисляет подзапрос SELECT. Если подзапрос SELECT содержит хотя бы одну строку результата, EXISTS возвращает TRUE и Статус меняется; если подзапрос пустой, то EXISTS возвращает FALSE, и данная строка остается прежней. Оператор COUNT(*) подсчитывает количество строк (=количество заказов), у которых поле КодСотрудника равно коду сотрудника текущей строки. Условие HAVING – то же, что WHERE.

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

Нередко случается, что элементарное с точки зрения пользователя действие по изменению данных в базе затрагивает сразу несколько таблиц. Например, удаление заказа с заданным номером вызывает удаление строк из двух таблиц: сначала из таблицы СодержимоеЗаказов( НомерЗаказа(FK), КодТовара(FK), Количество, Цена) удаляются все строки, относящихся к этому заказу, а потом удаляется одна строка из таблицы Заказы( НомерЗаказа(PK), Дата, КодПокупателя(FK)).

Другой пример: оператор регистрирует нового покупателя, а такой, возможно, уже существует в базе. Чтобы не было повторений, надо сначала проверить, есть ли покупатель с таким именем; если да, то далее ссылаться на него; если нет – то добавить новую строку в таблицу Покупатели( Код(PK), ФИО, Адрес).

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

Процедуры, реализующие действия пользователя (бизнес-логику), можно писать на языке высокого уровня – приложение будет посылать СУБД последовательность простых SQL-команд: SELECT, INSERT, UPDATE, DELETE и пр. (такая технология называется «толстый клиент»). При изменении бизнес-логики придется переписывать пользовательское приложение, и «раздавать» пользователям новые экземпляры. А можно бизнес-логику написать на SQL – это будут хранимые процедуры, хранящиеся на сервере СУБД; пользовательская программы посылает на сервер SQL-команды вызова нужных процедур. При изменении алгоритма обработки данных процедуру переписывают на сервере один раз, а пользовательское приложение не меняется (если входные и выходные параметры процедуры те же). Вывод: везде, где можно, обработку данных оформлять в виде хранимых процедур.

К сожалению, хранимые процедуры реализованы не во всех СУБД (в MySQL до версии 4.х их нет).

Хранимая процедура – это процедура, написанная на языке SQL, имеющая имя, входные и выходные параметры, и выполняющая определенные действия с базой данных. Её тело хранится на сервере СУБД. Наиболее «продвинутые» СУБД, такие как Oracle, Microsoft SQL Server, автоматически оптимизируют скорость выполнения хранимых процедур, строя план выполнения. В теле хранимых процедур можно использовать конструкции «расширенного SQL»: IF–THEN–ELSE, циклы WHILE, FOR, объединять несколько инструкций в блоки BEGIN–END, объявлять локальные переменные, вызывать другие хранимые процедуры.

Хранимая процедура создается SQL-инструкцией CREATE PROCEDURE. В разных СУБД её синтаксис может отличаться.

MySQL 3.x хранимые процедуры не поддерживает.

В Firebird/Interbase процедура создается инструкцией:

CREATE PROCEDURE ИмяПроцедуры

(ИмяПараметра1 ТИП, ИмяПараметра2 ТИП, и т.д.) /*входные параметры*/

RETURNS

(ИмяПараметра1 ТИП, ИмяПараметра2 ТИП, и т.д.) /*выходные параметры*/

AS

/* объявление локальных переменных */

DECLARE VARIABLE ИмяЛокальнойПеременной ТИП;

DECLARE VARIABLE ИмяЛокальнойПеременной ТИП;

и т.д.

BEGIN

/* тело процедуры */

инструкция SQL;

инструкция SQL;

...

END

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

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

В Firebird/Interbase существует два типа хранимых процедур: исполняемые (возвращают обычные, одиночные, значения параметров, или не имеют выходных параметров вообще) и процедуры–выборки (возвращают данные в форме таблицы, выходные параметры соответствуют столбцам). Перед выходом из процедуры-выборки обязательно должна быть SQL-команда SUSPEND. Она формирует одну (очередную) строку таблицы-результата. Далее в этой методичке будут упоминаться исполняемые процедуры, а процедуры–выборки планируется рассмотреть в ЛР №3.

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

EXECUTE PROCEDURE ИмяПроцедуры( список фактических параметров );

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

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

ИмяПеременной_или_параметра = арифметическое_выражение;

В выражении можно использовать знаки + – * / , параметры и переменные, вызов функций. Двоеточие перед именами здесь не ставится.

Оператор IF

IF (условие)

THEN

BEGIN /* BEGIN–END нужен, если SQL-инструкций несколько*/

инструкция SQL;

...

инструкция SQL;

END

ELSE /* раздел ELSE может отсутствовать */

BEGIN

инструкция SQL;

...

инструкция SQL;

END

Обратите внимание, что точка с запятой ставится после каждой SQL-инструкции, а после END нет.

Цикл WHILE

WHILE (условие) DO

BEGIN /* BEGIN–END нужен, если SQL-инструкций несколько*/

инструкция SQL;

...

инструкция SQL;

END

Цикл FOR SELECT…DO

FOR SELECT список_столбцов

FROM таблицы ... далее как в обычном запросе SELECT

INTO :ИмяПеременной1, :ИмяПеременной2, и т.д.

DO

BEGIN /* BEGIN–END нужен, если SQL-инструкций несколько*/

инструкция SQL;

...

инструкция SQL;

END

Этот цикл организует построчную обработку выборки из таблицы. Каждая строка выборки сначала записывается в локальные переменные (число переменных в списке INTO… должно равняться числу столбцов в SELECT…). Затем над выбранной строкой выполняются действия между BEGIN…END.

Хранимая процедура удаляется инструкцией

DROP PROCEDURE ИмяПроцедуры

Если нужно изменить хранимую процедуру, то ее удаляют, а потом создают заново.

В Microsoft SQL Server 2000 хранимая процедура создается инструкцией:

CREATE PROCEDURE ИмяПроцедуры

/*входные параметры*/

@ИмяПараметра1 ТИП =значение_по_умолчанию OUTPUT,

/* слова OUTPUT и «=значение_по_умолчанию» не обязательные */

@ИмяПараметра2 ТИП VARYING =значение_по_умолчанию OUTPUT,

... и т.д.

WITH ENCRYPTION /* необязательный параметр, вызывается,

если тело процедуры нужно зашифровать */

AS

BEGIN

/* тело процедуры */

инструкция SQL;

инструкция SQL;

...

END

Имена параметров должны начинаться с символа @.

Значение по умолчанию указывается, если при вызове процедуры вы часто используете одно и то же значение параметра – тогда его можно не прописываться всякий раз, а указать DEFAULT. Слово OUTPUT пишется, если параметр возвращает результат.

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

DECLARE @ИмяПеременной ТИП;

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

SET @ИмяПеременной = выражение;

Досрочный выход из процедуры даёт команда

RETURN.

Оператор IF

IF условие

BEGIN /* BEGIN–END нужен, если SQL-инструкций несколько*/

инструкция SQL;

...

инструкция SQL;

END

ELSE /* раздел ELSE может отсутствовать */

BEGIN

инструкция SQL;

...

инструкция SQL;

END

Цикл WHILE

WHILE условие

BEGIN /* BEGIN–END нужен, если SQL-инструкций несколько*/

инструкция SQL;

...

инструкция SQL;

END

Оператор BREAK немедленно выходит из цикла, оператор CONTINUE заставляет немедленно переходить на начало цикла. Эти операторы часто применяются вместе с IF:

WHILE условие

BEGIN

инструкция SQL;

...

IF условие_проверки

BREAK

ELSE

CONTINUE

END

Модифицированный UPDATE

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

UPDATE Имя_таблицы

SET Имя_столбца_1=выражение, Имя_столбца_2=выражение, и т.д.

FROM список таблиц

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

Например, имеются две связанные таблицы: Работники(ID, ФИО, СуммаЗаказов) и Заказы(Номер, Дата, Сумма, ID_Работника (FK)). Требуется обновить столбец СуммаЗаказов работников, добавив стоимость заказов за январь 2008 г.

UPDATE [Работники]

SET [СуммаЗаказов]=[СуммаЗаказов]+Z.[СуммаЯнварь]

FROM [Работники] R,

(select [ID_Работника], SUM([Сумма]) as [СуммаЯнварь]

FROM dbo.[Заказы]

WHERE [Дата] BETWEEN '2008.01.01' AND '2008.01.31'

GROUP BY [ID_Работника]) Z

WHERE R.[ID]=Z.[ID_Работника]

Но здесь есть одна тонкость: каждая строка обновляемой таблицы ([Работники]) соединяется ровно с одной строкой другой таблицы (в данном примере это подвыборка Z). Если есть несколько строк для соединения, то в обновлении будет использована только первая из них, а остальные отбрасываются. То есть такой запрос сработает неправильно:

UPDATE [Работники]

SET [СуммаЗаказов]=[СуммаЗаказов]+Z.[Сумма]

FROM [Работники] R, [Заказы] Z

WHERE R.[ID]=Z.[ID_Работника]

AND Z.[Дата] BETWEEN ‘2008.01.01’ AND ‘2008.01.31’

Вызов хранимой процедуры

EXEC ИмяПроцедуры @ИмяПараметра1=значение, @Имяпараметра2=значение,...

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

Хранимая процедура удаляется инструкцией

DROP PROCEDURE ИмяПроцедуры

Если нужно изменить хранимую процедуру, то ее удаляют, а потом создают заново.

Триггеры

Триггеры представляют собой особый вид хранимых процедур, которые выполняются автоматически при операциях insert, update, delete таблицы или представления (view). Каждый триггер «цепляется» только к одной таблице, и указываются операции (insert, update или delete), на которые он должен «срабатывать».

Триггеры – мощный механизм, который, однако, следует использовать с осторожностью. C помощью триггеров организуют проверки вставляемых строк на какое-то «хитрое» условие, которое невозможно проверить с помощью обычного CHECK; для автоматического заполнения полей в таблицах; для синхронного обновления данных из нескольких таблиц и пр. Недостатки триггеров проявляются, когда в теле триггера имеются команды модификации других таблиц – может возникнуть неоднозначность с транзакциями, и так называемые гонки триггеров.

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

В Firebird/Interbase существует шесть видов триггеров: для каждой операции (INSERT, UPDATE, DELETE) два варианта вызова триггера (непосредственно до – BEFORE, или сразу после – AFTER, операции) – итого шесть комбинаций. Триггер создается инструкцией:

CREATE TRIGGER ИмяТриггера ON ИмяТаблицы

{ACTIVE|INACTIVE} {BEFORE|AFTER} {INSERT|UPDATE|DELETE} POSITION n

AS

BEGIN

инструкция SQL;

...

инструкция SQL;

END

Здесь через вертикальную черту записываются альтернативы.

Опция ACTIVE указывает, что триггер активный, INACTIVE – триггер временно отключён. Если триггеров, срабатывающих на одну и ту же операцию, несколько, POSITION n (n=0,1,2,…) задает очередность их срабатывания.

Если одним оператором (например, updatewhere условие) модифицируется несколько строк, то триггер, привязанный к операции UPDATE, будет вызываться для каждой строки. То же касается вставки и удаления группы строк.

Включить или отключить триггер можно инструкцией

ALTER TRIGGER ИмяТриггера {ACTIVE|INACTIVE};

Триггер удаляется оператором

DROP TRIGGER ИмяТриггера;

Контекстные переменные представляют собой структуру (аналог struc на С), и называются NEW – содержит значение новой (вставляемой) строки, и OLD – старой (удаляемой) строки. Поля структур NEW и OLD называются так же, как столбцы таблицы.

Приведем примеры триггеров: 1) Триггер создается для добавления новой строки в таблицу Сотрудники( КодСотрудника(PK), ФИО). Столбец КодСотрудника авто-инкрементный. Если его значение явно не задано, то выбирается значение генератора GEN_STUFF_ID (о генераторах см. методичку по ЛР №1, с.57-59).

CREATE TRIGGER STUFF_BI_0 FOR “Сотрудники”

BEFORE INSERT

POSITION 0

AS BEGIN

IF (NEW.“КодСотрудника” IS NULL)

THEN NEW.“КодСотрудника” = GEN_ID(GEN_STUFF_ID, 1);

END

2) Имеются две связанные таблицы Работники(ID, ФИО, СуммаЗаказов) и Заказы(Номер, Дата, Сумма, ID_Работника (FK)). Требуется при добавлении нового заказа или изменении поля Сумма заказа автоматически обновлять СуммуЗаказов работника, курирующего этот заказ. Будет два триггера (на INSERT и UPDATE) таблицы Заказы, срабатывающих безразлично когда – до или после – вставки. Логичнее после.

CREATE TRIGGER ORDER_AI_0 FOR “Заказы”

AFTER INSERT

POSITION 0

AS BEGIN

UPDATE “Работники”

SET “СуммаЗаказов” = “СуммаЗаказов” + NEW.“Сумма”

WHERE “ID” = NEW.“ID_Работника”

END

CREATE TRIGGER ORDER_AU_0 FOR “Заказы”

AFTER UPDATE

POSITION 0

AS BEGIN

UPDATE “Работники”

SET “СуммаЗаказов” = “СуммаЗаказов” + (NEW.“Сумма” - OLD.“Сумма”)

WHERE “ID” = NEW.“ID_Работника”

END

Есть два подхода к решению этой задачи. Можно создать триггеры, как описано выше, и добавлять заказ обычным запросом INSERT, а менять сумму обычным запросом UPDATE. Другой вариант – триггеры не использовать, а добавление заказа и изменение суммы сделать хранимыми процедурами, и обновлять таблицу Работники именно там. Второй вариант предпочтительнее из-за гонок триггеров. Где можно, триггеры лучше не использовать.

В Microsoft SQL Server 2000 существует два типа триггеров: обычные, вызываемые после операции (AFTER), и замещающие, вызываемые вместо операции (INSTEAD OF). Последние используются для обновления представлений (view – см. ЛР №3). Операции (insert, update, delete) на которые вызывается триггер, могут комбинироваться в любом сочетании (в отличие от Firebird, где триггер привязывался только к одной операции).

Триггер создается инструкцией

CREATE TRIGGER ИмяТриггера

ON ИмяТаблицы

WITH ENCRYPTION /* необязательный параметр, вызывается,

если тело триггера нужно зашифровать */

{FOR | INSTEAD OF} {INSERT,UPDATE,DELETE} /*перечисляются операции*/

AS

BEGIN

инструкция SQL;

...

инструкция SQL;

END

Удаляется инструкцией

DROP PROCEDURE ИмяТриггера

Включение/отключение триггеров реализуется оператором ALTER TABLE:

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

{ENABLE|DISABLE} TRIGGER {Список_имен_триггеров |ALL}

Контекстные переменные в теле триггера представляют собой таблицы с именами INSERTED и DELETED. Столбцы этих таблиц именуются так же, как и столбцы таблицы, к которой привязан триггер. Если одним оператором (например, insert), добавляется группа строк, то триггер FOR INSERT будет вызван один раз после вставки всех строк, и таблица INSERTED будет содержать добавленные строки. Аналогично таблица DELETED содержит удаленные строки. При обновлении строк инструкцией update таблица DELETED содержит старые значения модифицированных строк, а INSERTED – новые значения (эти же значения сейчас сохранены в таблице). Триггеры операций UPDATE и DELETE вызываются даже в том случае, если операция не удалила и не обновила ни одной строки, так как не нашлось строк, соответствующих условию WHERE.

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

Имеются две связанные таблицы Работники(ID, ФИО, СуммаЗаказов) и Заказы(Номер, Дата, Сумма, ID_Работника (FK)). Требуется при добавлении нового заказа автоматически увеличивать СуммуЗаказов соответствующего работника, при удалении заказа автоматически уменьшать, а при изменении поля Сумма заказа автоматически корректировать СуммуЗаказов.

CREATE TRIGGER TR_ORDERS_IUD

ON Заказы

FOR INSERT, UPDATE, DELETE

AS

BEGIN

DECLARE @InsertedCount INT;

DECLARE @DeletedCount INT;

SET @InsertedCount = (SELECT COUNT(*) FROM INSERTED);

SET @DeletedCount = (SELECT COUNT(*) FROM DELETED);

IF (@DeletedCount > 0)

/* строки удалили или обновили */

IF (@InsertedCount > 0) AND UPDATE(“Сумма”)

/* было обновление строк, изменился столбец “Сумма” */

UPDATE [Работники]

SET [СуммаЗаказов]=[СуммаЗаказов]+(ZNew.[СуммаОбщ]- ZOld.[СуммаОбщ])

FROM [Работники] R,

(SELECT [ID_Работника], SUM([Сумма]) as [СуммаОбщ]

FROM DELETED

GROUP BY [ID_Работника]) ZOld,

(SELECT [ID_Работника], SUM([Сумма]) as [СуммаОбщ]

FROM INSERTED

GROUP BY [ID_Работника]) ZNew

WHERE R.[ID]=ZOld.[ID_Работника] AND R.[ID]=ZNew.[ID_Работника]

ELSE

/* было удаление строк */

UPDATE [Работники]

SET [СуммаЗаказов]=[СуммаЗаказов]-Z.[СуммаОбщ]

FROM [Работники] R,

(SELECT [ID_Работника], SUM([Сумма]) as [СуммаОбщ]

FROM DELETED

GROUP BY [ID_Работника]) Z

WHERE R.[ID]=Z.[ID_Работника]

ELSE IF (@InsertedCount > 0)

/* была вставка строк */

UPDATE [Работники]

SET [СуммаЗаказов]=[СуммаЗаказов]+Z.[СуммаОбщ]

FROM [Работники] R,

(SELECT [ID_Работника], SUM([Сумма]) as [СуммаОбщ]

FROM INSERTED

GROUP BY [ID_Работника]) Z

WHERE R.[ID]=Z.[ID_Работника]

END

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

Транзакции и конфликты совместного доступа

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

Блок операций, которые либо должны быть выполнены все, либо не выполнена ни одна из них, называется транзакцией. Если какая-то операция по ходу транзакции не сможет выполниться или приведет к ошибке, следует отменить все изменения данных, произошедшие с начала транзакции, и вернуть базу в то состояние, которое она имела на момент начала этой транзакции. Отмена изменений называется откатом транзакции (rollback). Если все операции по ходу транзакции прошли успешно, без ошибок, то производится фиксация транзакции (commit), после чего отменить изменения уже нельзя.

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

Atomicity (Атомарность) – выполняемые в транзакции изменения будут либо выполнены все, либо не будут выполнены совсем.

Consistency (Согласованность) – данные в базе до и после выполнения транзакции должны находиться в согласованном состоянии; внутри транзакции возможна временная несогласованность. Под согласованностью подразумевается соблюдение логического состояния данных. Например, имеются таблицы Заказы(Номер (PK), Дата, СуммаКОплате) и СодержимоеЗаказов( НомерЗаказа(FK), КодТовара (FK), Цена, Количество). Вся процедура оформления нового заказа представляет собой транзакцию: сначала добавляется новая строка в Заказы, при этом СуммаКОплате=0. Потом набираются товары – добавляется несколько строк в СодержимоеЗаказов. Последним шагом происходит расчет СуммыКОплате, исходя из содержимого заказа. В данном примере согласованность данных – это равенство СуммыКОплате =  Цена*Количество товаров, входящих в заказ. Пока добавляются товары в заказ, поле СуммаКОплате=0 – временная несогласованность данных.

Isolation (Изолированность) – изменения, сделанные одной транзакцией, не должны зависеть от изменений, сделанных другой транзакцией. То есть работа каждой транзакции должна происходить так, как будто эта транзакция работает с базой данных монопольно. Она должна видеть только те изменения, которая сделала сама (а фактически в то же самое время другие транзакции могут обновлять эти же данные, но СУБД так «подтасовывает» данные в таблицах, что транзакции видят только свои изменения).

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

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

В лабораторной работе «элементарные» действия пользователя следует оформлять в виде транзакций. По длительности транзакции условно можно разделить на короткие (последовательность SQL-команд, выполняемых друг за другом без задержек – порядка от микросекунд до секунд) и длинные, или бизнес-транзакции1 (связаны с ожиданием действий пользователя, как, например, добавление товаров в заказ – могут длиться минуты, часы, а могут прерваться из-за обрыва связи).

Для коротких транзакций:

в Firebird/Interbase ничего специально делать не надо – каждая хранимая процедура автоматически является транзакцией.

В Microsoft SQL Server 2000 управлять транзакциями (начинать, завершать) приходится вручную – с помощью SQL-инструкций.

  • начать транзакцию:

BEGIN TRAN ИмяТранзакции

  • фиксировать транзакцию:

COMMIT TRAN ИмяТранзакции

  • откатить транзакцию:

ROLLBACK TRAN ИмяТранзакции

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

Грубо говоря, в теле хранимой процедуры, оформляемой как транзакция, первым оператором стоит BEGIN TRAN, последним COMMIT TRAN, или ROLLBACK TRAN, если процедура завершилась ошибкой.

Еще есть возможность – в теле транзакции объявлять «точки сохранения» и откатывать транзакцию не полностью, а до точки сохранения.

  • Объявить точку останова:

SAVE TRAN ИмяТочки

  • Откатить транзакцию до точки сохранения

ROLLBACK TRAN ИмяТочки

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

1) пессимистический. Заключается в том, что клиентское приложение передает на сервер SQL-команду BEGIN TRAN – и сервер будет воспринимать все дальнейшие команды от приложения в контексте текущей транзакции (со всеми вытекающими последствиями: будет блокировать таблицы, к которым обратилась ваша программа, тормозя работу других пользователей). Как только приложение посылает команду COMMIT или ROLLBACK, блокировки снимаются. То есть транзакция живет, как эгоист – сама выполняется без ошибок, но осложняет жизнь другим.

2) оптимистический. Алгоритмы бизнес-логики делаются вообще без транзакций, либо в транзакцию оборачивают самый критический участок минимальной длительности. Идея оптимистического подхода основана на предположении, что «авось», пока шла многоэтапная обработка данных, никто из других пользователей эти данные не менял. Если все-таки меняли, то «начинай алгоритм бизнес-логики сначала» – пользователю предлагается обновить данные и повторить попытку.

Пример:

Т. Полезны из-за сериализуемости. Перейти на конфликты доступа.

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

Чтобы

Конфликты совместного доступа.

Транзакции. Уровни изолированности. Установка уровня в СУБД.

Пример ЛР2 для примера из ЛР1.

Пример отката в триггере.

Обработка ошибок (RAISERROR, EXCEPTIONS)

Обращение к полям IDENTITY из других таблиц (свойство @@identity), создание глобальных уникальных полей (уникальных по нескольким таблицам) – см. раздел IDENTITY property\Creating and modifying identifying columns. Функция NEWID( ) возвращает GUID. Используется для того, чтобы при объединении таблиц, созданных в нескольких базах данных, не было конфликтов из-за повторений внешних ключей.

Таблица- образец

п/п

Описание действия

Входные параметры (имя, тип)

Выходные параметры (имя, тип)

Алгоритм выполнения

ioliuli