Этот триггер закреплён за таблицей «PUBLISHERS», и задан как триггер для изменения и удаления записей. Для создания триггера выберем Triggers/NewTrigger...(Триггер/Новый триггер). Появится окно создания триггера, в котором укажем:
· Имя – «PUBLISHERS_CASCADE_BOOKS»;
· Для таблицы – «PUBLISHERS»;
· Тип – «BEFORE», т. е. выполняется прежде операции;
· Укажем UPDATE, DELETE.
Полный текст триггера будет следующий:
SET TERM ^ ;
CREATE OR ALTER TRIGGER PUBLISHERS_CASCADE_BOOKS FOR PUBLISHERS
ACTIVE BEFORE UPDATE OR DELETE POSITION 0
AS
begin
IF (updating) THEN
begin
update books C
set C.id_publishers = new.id_publishers
where c.id_publishers = old.id_publishers;
end
else
begin
delete from books C
where c.id_publishers = old.id_publishers;
end
end
^
SET TERM ; ^
Так как данный триггер выполняется при изменении и удалении записей из таблицы «PUBLISHERS», то в нём задано условие, проверяющее, какая операция над записями этой таблицы производится. IF (updating) THEN= ИСТИНА, если операция изменения (UPDATE), иначе =ЛОЖЬ (для данного триггера это операция удаления). В первом случае производится каскадное обновление записей при помощи оператора изменения данных UPDATE, во втором каскадное удаление при помощи операции DELETEFROM. В обоих случаях условием отбора записей на изменение или удаление является равенство значений поля «ID_PUBLISHERS» таблицы «BOOKS» и старым значением поля «ID_PUBLISHERS» таблицы «PUBLISHERS». В случае изменения данных этим полям присваиваются новые значения записей.
Аналогично создадим триггер каскадного обновления и удаления записей таблицы «BOOKS» при обновлении и удалении соответствующих им записей в таблице «READERS» – «READERS_CASCADE_BOOKS». Разница здесь в том, что триггер будет задан для таблицы «READERS» и в записях сравниваются значения полей «ID_ABONENT».
Создадим три хранимых процедуры.
Первая процедура будет выводить список книг, находящихся в библиотеке. Для этого зададим запрос на выборку списка книг, в поле «BOOKS.ID_ABONENT» которых стоит значение NULL.
Для создания новой процедуры выберем Procedures\NewProcedure(Процедуры\Новая процедура). Выберем OutputParameters (Выходные Параметры) затем Insertparameter/variable (Добавить параметр/переменную). Добавим параметр «NAME_BOOKS VARCHAR(30)» под название книги, а также ещё два – под наименование и город издательства – соответственно «NAME_PUBLISHERS VARCHAR(20)» и «CITY_PUBLISHERS VARCHAR(20)».
Листинг первой процедуры «BOOKS_LIBRARY»:
CREATE PROCEDURE BOOKS_LIBRARY
returns (
city_publishers varchar(20) character set win1251,
name_publishers varchar(20) character set win1251,
name_books varchar(30) character set win1251)
as
begin
for
select distinct books.name, publishers.name, publishers.city
from books inner join publishers on books.id_publishers = publishers.id_publishers
where books.id_abonent iS NULL
into: name_books, : name_publishers, : city_publishers
dosuspend;
end
В данной процедуре вместо обычного запроса на выборку SELECT использована конструкция «FOR SELECT ... INTO ... DO ...», которая производит обработку возвращаемого набора записей в цикле. Иначе если SELECT возвратит более одной строки, то возникнет ошибка «multiplerowsinsingletonselect». Здесь же после каждой возвращаемой строки производится принудительная выдача параметров, после чего они принимают новые значения при следующей итерации цикла и т. д. пока не будут выданы все строки, удовлетворяющие условию запроса.
Вторая процедура будет выводить список книг, выданных за указанный период (входные параметры – начальная и конечная дата). Зададим входные параметры «DATE_1 DATE» и «DATE_2 DATE». Зададим выходные параметры: «NAME_BOOKS VARCHAR (30)», «NAME_PUBLISHERS VARCHAR(20)» и «CITY_PUBLISHERS VARCHAR(20)».
Листинг процедуры «BOOKS_LIST_PERIOD»:
CREATE PROCEDURE BOOKS_LIST_PERIOD (
date_1 date,
date_2 date)
returns (
city_publishers varchar(20) character set win1251,
name_publishers varchar(20) character set win1251,
name_books varchar(30) character set win1251)
as
declare variable var_date date;
begin
for
select distinct books.name, publishers.name, publishers.city, books.date_issue
from books inner join publishers on books.id_publishers = publishers.id_publishers
into: name_books, : name_publishers, : city_publishers, :var_date
do if (var_date between date_1 and date_2) then suspend;
end
В этой процедуре в операторе циклической выборки для каждой записи, полученной в результате выполнения оператора выборки «SELECT», проверяется, лежит ли значение одной из возвращаемых SELECT-ом переменной var_date между двумя введёнными, заданными входными параметрами date_1 и date_2. Если да, то производится выдача процедурой значений выходных параметров при помощи оператора suspend.
Создадим третью процедуру, которая будет выводить количество книг и средний тираж по издательствам. Здесь выходные данные будут представлять собой значения функций агрегирования, вычисляемые для каждой группы по значению поля «PUBLISHERS. NAME».
Листинг процедуры «KOL_BOOKS_TIRAZ»:
CREATE PROCEDURE KOL_BOOKS_TIRAZ
returns (
publishers_name varchar(20) character set win1251,
avg_tiraz integer,
count_ integer)
as
begin
for
select distinct publishers.name, count(publishers.id_publishers), AVG(books.tiraz)
from books inner join publishers on books.id_publishers = publishers.id_publishers
group by publishers.name
into: publishers_name, : count_, : avg_tiraz
dosuspend;
end
Выходными параметрами процедуры являются:
· publishers_namevarchar(20) charactersetwin1251 – строковой параметр для значений имени издательства;
· avg_tirazinteger – параметр для среднего значения тиража книги каждой группы.
· count_ integer – параметр для выдачи значения количества строк каждой группы.
В данной процедуре осуществляется выборка значений поля «PUBLISHERS.NAME», количества возвращаемых строк и среднего значения по полю «BOOKS.TIRAZ» для каждого значения поля «PUBLISHERS.NAME».
Теперь структура базы данных готова. Заполним таблицы некоторыми записями:
Таблица «Издательства» (Publishers)
ID_Publishers | Name | City |
1 | Питер | С.- Петербург |
2 | ДиаСофт | Киев |
3 | КОРОНА принт | С.- Петербург |
4 | Финансы и статистика | Москва |
Таблица «Читатели» (Readers)
ID_ abonent | FIO | Telephone | Address |
1 | Иванов Вадим | 12345678 | Москва ул. 1 д.1 кв. 1 |
2 | Петров Борис | 11111111 | Орел ул. 1 д.1 кв. 1 |
3 | Сидоров Иван | 22222222 | Курск ул. 1 д.1 кв. 1 |
4 | Кузнецов Артем | 12121212 | Воронеж ул.1 д.1 кв.1 |
Таблица «Книги» (Books)
ID_ Books | Name | ID_ Publishers | K_ pages | Cover | Tiraz | ID_ abonent | Date_issue |
1 | Эффективная работа с СУБД | 1 | 704 | твёрдый | 6000 | 1 | 01.01.2009 |
2 | Delphi. Разработка баз данных | 1 | 477 | твёрдый | 5000 | 1 | 01.01.2009 |
3 | Базы данных и приложения | 2 | 592 | твёрдый | 7000 | 2 | 11.01.2009 |
4 | Базы данных | 2 | 416 | твёрдый | 5000 | NULL | NULL |
Создадим клиентское приложение в ИСР Delphi, используя технологию доступа к данным InterBaseeXpress (IBX).
Выберем File/New/Application (Файл/Новое/Приложение), затем добавим модуль данных для компонентов доступа к данным – File/New/DataModule (Файл/Новый/Модуль данных). Компоненты доступа к данным расположены на страницеDataAccessПалитры компонентов. Компоненты отображения данных расположены на страницеDataControls Палитры компонентов. Компоненты, используемые в технологии InterBaseeXpress распологаются на странице InterBase, а компоненты для создания отчётов – QReport.
Поместим на модуль данных компонент TIBDatabase. Укажем в свойстве DatabaseName полный путь (включая имя сервера) к выбранному файлу БД – «C:\01\LIBRARY.FDB».
Поместим следующие компоненты на форму модуля данных:
· компонент IBTransaction
· три IBDataSet,
· три DataSource
Подключимся к базе данных. Выделим компонент TIBDatabase и выберем из контекстного меню Database Editor.… В этом окне укажем User Name = SYSDBA, Character Set = WIN1251. Затем установим свойство Connected компонента IBDatabase1 равным True и свойство DefaultTransaction компонента IBDatabase1 равным IBTransaction1.
Зададим управление транзакциями. Сделаем активным компонент IBTransaction1, для чего его свойству Active придадим значение True. Вызовем редактор TransactionEditor..., и в появившемся диалоговом окне выберем уровень изоляции транзакций – ReadCommitted.
Установим значения свойств:
· DefaultAction – TACommitRetaining
· DefaultDatabase – IBDatabase1
· Params – read_committed
rec_version
nowait
· Active – True
Перейдём к компоненту IBDataSet1. Переименуем его на BOOKS_DataSet (свойство Name). Укажем базу данных – DataBase= IBDataBase1 и компонент обработки транзакций – Transaction = IBTransaction1. Укажем в свойстве SelectSQL текст основного запроса: «select* from BOOKS». При помощи свойства GeneratorField выбираем поле, значение которого присваивается генератором и сам генератор. Активируем компонент: Active – True. Вызовем редактор компонента DatasetEditor.... Выберем из списка TableName таблицу и нажмём кнопку GetTableFields (Получить поля таблицы). В списке KeyFields (Ключевые поля) выделим поле «ID_BOOKS», которое будут формировать условие WHERE в запросах. После нажатия на кнопку GenerateSQL автоматически сгенерируются значения свойств DeleteSQL, InsertSQL, ModifySQL, RefreshSQL. Эти значения станут равны:
DeleteSQL:
delete from BOOKS
where
ID_BOOKS = :OLD_ID_BOOKS
InsertSQL:
insert into BOOKS
(ID_BOOKS, NAME, ID_PUBLISHERS, K_PAGES, COVER, TIRAZ, ID_ABONENT, DATE_ISSUE)
values
(:ID_BOOKS, :NAME, :ID_PUBLISHERS, :K_PAGES, :COVER, :TIRAZ, :ID_ABONENT,
:DATE_ISSUE)
ModifySQL:
update BOOKS
set
ID_BOOKS = :ID_BOOKS,
NAME = :NAME,
ID_PUBLISHERS = :ID_PUBLISHERS,
K_PAGES = :K_PAGES,
COVER = :COVER,
TIRAZ = :TIRAZ,
ID_ABONENT = :ID_ABONENT,
DATE_ISSUE = :DATE_ISSUE
where
ID_BOOKS = :OLD_ID_BOOKS
RefreshSQL:
Select
ID_BOOKS,
NAME,
ID_PUBLISHERS,
K_PAGES,
COVER,
TIRAZ,
ID_ABONENT,
DATE_ISSUE
from BOOKS
where
ID_BOOKS = :ID_BOOKS
Аналогично зададим значения свойств двум остальным компонентам IBDataSet.
У каждого компонента DataSource в свойстве Dataset укажем название соответствующего ему компонента IBDataSet.
Создадим три формы для отображения таблиц. На каждую форму поместим компоненты DBGrid и DBNavigator. У компонентов DBGrid и DBNavigator в свойстве DataSource укажем соответствующий компонент DataSource.
Для поиска данных используется функция
function LocateNext(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions): Boolean;
где KeyFields – список полей, по которым производится поиск (имена полей разделяются точкой с запятой), KeyValues – список значений, сравниваемых со значениями этих полей (значения разделяются запятой), TLocateOptions – параметры поиска, где loCaselnsensitive означает поиск без учета регистра (заглавные и малые символы), а loPartialKey. – значения полей для поиска даны не полностью. Функция LocateNext позволяет находить несколько записей, удовлетворяющих условиям поиска, для отображения очередной записи следует вызвать функцию ещё раз. Функция возвращает значение логического типа, равного TRUE (ИСТИНА), если найдена подходящая запись, и FALSE (ЛОЖЬ) в противном случае.