Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Metodichka_konspekt_2.doc
Скачиваний:
2
Добавлен:
01.03.2025
Размер:
752 Кб
Скачать
☆

Домашнее задание

1. Ознакомится с теоретическими сведениями лекции №23.

2. Ответить на контрольные вопросы.

Контрольные вопросы

  1. Определение БД.

  2. Определение СУБД.

  3. Что такое ключ в терминологии БД.

  4. Внешний ключ.

  5. Простой ключ.

  6. Составной ключ.

  7. Первичный ключ.

  8. Определение генератора.

  9. Синтаксис представления генератора.

  10. Определение триггера.

  11. Синтаксис представления триггера.

Лекция № 24 (2часа)

Тема: Хранимые процедуры. Использование генераторов в триггерах и хранимых процедурах.

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

Литература:

  1. “Реляционные, древовидные и объектно-ориентированные базы данных” Артур б. Смит m. Computing May/June 1996, V.4 n.2

  2. “Проектирование реляционных баз данных. Просто и доступно” Джен л.Харрингтон – издательство “Лори”, 2000.-230 с. Содержание

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

Хранимая процедура — объект базы данных, представляющий собой набор SQL-инструкций, который компилируется один раз и хранится на сервере. Хранимые процедуры очень похожи на обыкновенные процедуры языков высокого уровня, у них могут быть входные и выходные параметры и локальные переменные, в них могут производиться числовые вычисления и операции над символьными данными, результаты которых могут присваиваться переменным и параметрам. В хранимых процедурах могут выполняться стандартные операции с базами данных (как DDL, так и DML). Кроме того, в хранимых процедурах возможны циклы и ветвления, то есть в них могут использоваться инструкции управления процессом исполнения.

Сохраняемые процедуры похожи на определяемые пользователем функции (UDF). Основное различие заключается в том, что пользовательские функции можно использовать как и любое другое выражение в SQL запросе, в то время как сохраняемые процедуры должны быть вызваны с помощью функции CALL:

CALL процедура(...)

или

EXECUTE процедура(...)

Сохраняемые процедуры могут возвращать множества результатов, т.е. результаты запроса SELECT. Такие множества результатов могут обрабатываться, используя курсоры, другими сохраненными процедурами, возвращая указатель результирующего множества, либо же приложениями. Сохраняемые процедуры могут также содержать объявленные переменные для обработки данных и курсоров, которые позволяют организовать цикл по нескольким строкам в таблице. Стандарт SQL предоставляет для работы выражения IF, LOOP, REPEAT, CASE и многие другие. Сохраняемые процедуры могут принимать переменные, возвращать результаты или изменять переменные и возвращать их, в зависимости от того, где переменная объявлена.

Реализация хранимых процедур варьируется от одной СУБД к другой. Большинство крупных поставщиков баз данных поддерживают их в той или иной форме. В зависимости от СУБД, хранимые процедуры могут быть реализованы на различных языках программирования, таких, как SQL, Java, C или C++. Сохраняемые процедуры, написанные не на SQL, могут самостоятельно выполнять SQL-запросы, а могут и не выполнять. Все более широкое использование сохраняемых процедур привело к появлению процедурных элементов в языке SQL стандарта SQL:1999 и SQL: 2003 в части SQL/PSM. Это сделало SQL императивным языком программирования. Большинство СУБД предлагают собственные расширения производителя, сверх SQL.

CREATE PROCEDURE T_SPECIALNOST_INSERT (ID_SPEC INTEGER,SPEC_NAME VARCHAR(30),PAY SMALLINT)

AS BEGIN

INSERT INTO T_SPECIALNOST(ID_SPEC,SPEC_NAME,PAY)

VALUES(:ID_SPEC,:SPEC_NAME,:PAY);

END*

Использование генераторов в триггерах и хранимых процедурах

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

создадим генератор для уникальной идентификации клиентов:

CREATE GENERATOR NEWCLIENT;

создадим триггер для таблицы CLIENTS :

CREATE TRIGGER TBI_CLIENTS FOR CLIENTS

ACTIVE BEFORE INSERT POSITION 0

AS

BEGIN

NEW.CLIENT_ID = GEN_ID(NEWCLIENT, 1);

END

В результате при создании новой записи полю CLIENT_ID будет автоматически присваиваться новое значение.

Однако при использовании генератора в триггере возникает проблема на клиентской стороне (например в BDE, используемом в Delphi, C++Builder ...), когда клиентское приложение пытается перечитать только-что вставленную запись. Поскольку триггер меняет значение первичного ключа вставляемой записи, BDE "теряет" такую запись и чаще всего выдает сообщение "Record/Key deleted". Поскольку SQL-сервер не может сообщить клиентскому приложению о новом значении ключевого поля, необходимо сначала запросить уникальное значение с сервера, и только затем использовать его во вставляемой записи. Сделать это можно при помощи хранимой процедуры

CREATE PROCEDURE GETNEWCLIENT

RETURNS (NID INTEGER)

AS

BEGIN

NID = GEN_ID(NEWCLIENT, 1);

END

В Delphi, можем поместить компонент TStoredProc на форму, подсоединить его к данной процедуре, и например в методе таблицы BeforePost написать следующее

begin

if DataSource.State = dsInsert then

begin

StoredProc1.ExecProc;

ClientTable.FieldByName('CLIENT_ID').asInteger:=

StoredProc1.Params[0].asInteger;

end;

end;

После этого вышеприведенный триггер TBI_CLIENTS можно либо удалить, либо переписать так, чтобы генератор использовался только когда поле первичного ключа случайно приобрело значение NULL (например когда к таблице CLIENTS доступ осуществляется не через ваше приложение):

ALTER TRIGGER TBI_CLIENTS

AS

BEGIN

IF (NEW.CLIENT_ID IS NULL) THEN

NEW.CLIENT_ID = GEN_ID(NEWCLIENT, 1);

END

Однако использование хранимой процедуры не всегда удобно - BDE может решить, что процедура вероятно изменяет какие-то данные на сервере, и в режиме autocommit завершит текущую транзакцию, что вызовет прочитывание данных TTable и TQuery. Более простым способом является получение значения генератора при помощи запроса:

SELECT GEN_ID(NEWCLIENT, 1) FROM RDB$DATABASE

При этом, если запрос помещен например в Query2, текст в BeforePost будет следующим:

begin

if DataSource.State = dsInsert then

begin

Query2.Open;

ClientTable.FieldByName('CLIENT_ID').asInteger:=

Query2.Fields[0].asInteger;

Query2.Close;

end;

end;

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

Изменение значения генератора

Значение генератора можно переустановить при помощи оператора DDL

SET GENERATOR generatorname TO value;

Однако не сможем использовать такое выражение в теле триггера или хранимой процедуры, т.к. там можно использовать только операторы DML (а не DDL).

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

(В данном примере генератор NEWCLIENT увеличивается на свое-же значение с отрицательным знаком.)

...

TEMPVAR = GEN_ID(NEWCLIENT, -GEN_ID(NEWCLIENT, 0);

...

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

Получение текущего значения генераторов

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

SELECT GEN_ID(NEWCLIENT, 0) FROM RDB$DATABASE

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

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

Удаление генераторов

В языке DDL Borland Interbase нет оператора для удаления генератора. Запись о генераторе создается в таблице RDB$GENERATORS. Эту запись, безусловно, можно удалить. Однако место, распределенное на странице генераторов, освобождено не будет. Оно будет освобождено только после того, как сделаем текущей БД BACKUP/RESTORE.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]