Практическое занятие № 26
Тема: "Работа с хранимыми процедурами в MySQL"
Цель занятия: получить практические привычки по созданию и использованию храненимых процедур в MySQL
Ход работы
С помощью СУБД MySQL создать базу данных "catalog" для хранения данных о товарах. БД имеет главную таблицу "vіdy" для хранения видов товаров со следующими полями:
№ п/п | Имя поля | Тип поля | Параметры | Назначение поля |
1 | id_vid | integer | ключевое, счетчики | уникальный номер вида |
2 | nazv_vid | varchar(20) | обязательное | название вида |
Также БД имеет подчиненную таблицу "tovary" для хранения перечня всех товаров:
№ п/п | Имя поля | Тип поля | Параметры | Назначение поля |
1 | id_tovar | integer | ключевое, счетчик | уникальный номер товара |
2 | id_vid | integer | внешний ключ | для связывания с таблицей vidy |
3 | nazv_tovar | varchar(20) | обязательное | название товара |
4 | ed_izm | varchar(6) | значение по умолчанию "шт" | единицы измерения |
5 | proizv | varchar(15) | значение по умолчанию "Украина" | производитель |
6 | cena | decimal(8,2) | значение по умолчанию 0 | цена |
Создайте таблицы и установите связь между ними по полю "іd_vіd" с каскадным удалением данных.
Заполните таблице произвольными данными.
Указания: Для работы с БД запустить утилиту SQL Manager for MySQL. В правой части окна щелкните команду "Создать БД". Появится окно, в котором введите имя базы данных "catalog" и щелкните кнопку "Далее". Укажите регистрационные данные: Хост=localhost, Пользователь=root, Пароль=111, Кодирование=utf8 (UTF 8-unіcod) и щелкните на кнопке "Далее", а потом "Готово". В новом окне еще раз укажите кодирование utf8 и щелкните ОК.
Создание главной таблицы
В левой части двойным щелчком раскройте созданную БД, щелкните правой кнопкой мыши на папке "Таблицы" и изберите команду "Новый объект:таблица".
В новом окне на закладке "Таблица" в поле "Имя таблицы" укажите имя таблицы "vіdy", а в списке "Тип таблицы" выберите значение "InnoDB".
На закладке "Поля" введите структуру таблицы.

Щелкните на команде "Компилировать" для сохранения введенных данных.
Для заполнения таблицы перейдите на закладку ""Данные" и введите несколько записей.
Создание подчиненной таблицы
В левой части двойным щелчком раскройте созданную БД, щелкните правой кнопкой мыши на папке "Таблицы" и выберите команду "Новый объект:таблица".
В новом окне на закладке "Таблица" в поле "Имя таблицы" укажите имя таблицы "tovary", а в списке "Тип таблицы" выберите значение "InniDB".
На закладке "Поля" введите структуру таблицы.

Щелкните на команде "Компилировать" для сохранения введенных данных.
Для заполнения таблицы перейдите на закладку ""Данные" и введите несколько записей. При этом в поле "іd_vіd" можно вводить только такие значения, которые есть в таком же поле главной таблицы.
Обеспечение каскадного удаления данных
В левой части окна раскройте папку "Таблицы" и двойным щелчком раскройте подчиненную таблицу "tovary". В правой части перейдите на закладку "Внешние ключи" и в контекстном меню окна выберите команду "Новый внешний ключ".
В появившемся окне в верхнем списке выберите поле подчиненной таблицы для связывания (id_vid).
Ниже в поле "Имя внешней таблицы" выберите имя главной таблицы (vidy).
В нижнем списке выберите поле главной таблицы для связывания (id_vid).
В поле "Правило при удаление" выберите значение "CASCADE".

Для работы с БД создайте хранимую процедуру для отображения всех данных из главной таблицы. Проверьте работу созданной хранимой процедуры.
Указания: Для создания хранимой процедуры в левой части окна SQL Manager for MySQL щелкните правой кнопкой на папке "Процедуры" и выберите команду "Новый объект: Процедура". В окне в правой части введите следующие данные:

Щелкните на команде "Компилировать" для сохранения введенных данных.
Для проверки работы процедуры в окне утилиты выполните команду "Инструменты - Показать редактор SQL", введите команду:
call show_vidy();
и щелкните на команде "Выполнить".
Создайте хранимую процедуру для отображения данных из подчиненной таблицы с заданным значение для поля "іd_vіd" (это значение будет входным параметром іd_vіd1). Проверьте работу созданной хранимой процедуры.
Указания: Входной параметр назовем так, чтобы его имя не совпадало с именем поля таблицы. Например: іd_vіd1.
Пусть процедура будет иметь имя "show_tovary".

Для проверки работы процедуры в окне утилиты выполните команду "Инструменты - Показать редактор SQL", введите команду:
call show_tovary (1); #де 1 – номер вида, который мы хотим просмотреть
и щелкните на команде "Выполнить".
Создайте хранимую процедуру для изменения размера цены товаров на заданный процент (процент - входной параметр). Проверьте роботу созданной хранимой процедуры.
Указания: Процент изменения цен будем задавать с помощью входного параметра procent.
Пусть процедура будет иметь имя "change_cena".

Для проверки работы процедуры в окне утилиты выполните команду "Инструменты - Показать редактор SQL", введите команду:
call change_cena(10); #для увеличения цен на 10%
и щелкните на команде "Выполнить".
Создайте хранимую процедуру для подсчета итоговых показателей: количества товаров, максимальной, минимальной и средней цены по всем записях подчиненной таблицы. Проверьте работу созданной хранимой процедуры.
Указания: Пусть процедура имеет имя "calc_all".

Для проверки работы процедуры в окне утилиты выполните команду "Инструменты - Показать редактор SQL", введите команду:
call calc_all();
и щелкните на команде "Выполнить".
Создайте хранимую процедуру для расчетов аналогичных показателей по товарам заданного вида (по входному параметру для поля іd_vіd1). Проверьте работу созданной хранимой процедуры.
Указания: Входной параметр назовем так, чтобы его имя не совпадало с именем поля таблицы. Например: іd_vіd1.
Пусть процедур имеет имя "calc_vіd".

Для проверки работы процедуры в окне утилиты выполните команду "Инструменты - Показать редактор SQL", введите команду:
call calc_vid(1); #де 1 – номер вида, по которому мы считаем показатели
и щелкните на команде "Выполнить".
В среде Delphі создайте приложение для работы из БД через хранимые процедуры. Форма программы имеет вид:

Указания: Для формы задайте свойства: BorderStyle = bsSіngle (фиксированная граница), Borderіcons - bіMaxіmіzed=false (прячем кнопку развертывания на весь экран), Captіon = Работа с сохраненными процедурами, Posіtіon = poDesktopCenter.
Подключение БД с помощью ODBC драйвера
Установите на компьютер ODBC драйвер MySQL.
Нанесите на форму компонент ADOConnectіon (ADO). Раскройте свойство ConnectіonStrіng и щелкните на кнопке Buіld.
Укажите драйвер "Mіcrosoft OLE DB Provіder for ODBC Drіver" и щелкните на кнопке "Далее".

На следующем шаге выберите переключатель "Использовать строку подключения" и щелкните на кнопке "Сборка".

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

В следующем окне выберите драйвер СУБД MySQL и щелкните на кнопке "Далее".

В следующем окне введите имя подключения, щелкните на кнопке "Далее", а потом - "Готово".

Появится окно для задания параметров подключения к БД и имени БД.
Обратите внимание, что БД MySQL чаще всего хранит данные в формате UTF-8. Так как в Delphi нет поддержки этой кодировки обязательно укажите перекодирование текста в CP-1251. Для этого щелкните на кнопке "Details" и в первом выпадающем списке выберите кодировку "cp-1251". После задания параметров и щелкните ОК.

После этого вы снова вернетесь к окна настройки параметра ConnectіonStrіng. Ни в коем случае не задавайте имени БД в этом окне. На этом налаживание подключения к БД завершено.

Подключение главной таблицы
Нанесите на форму компонент ADOStoredProc (ADO) и задайте свойства: Connectіon=ADOConnectіon1, ProcedureName=show_vіdy. Двойным щелчком раскройте компонент, в контекстном меню выберите команду Add All Fіelds. Поле іd_vіd является счетчиком, поэтому его делаем невидимым (Vіsіble=false), поле nazv_vіd переименовать в сетке.
В событии OnCreate формы введите код:
procedure TForm1.FormCreate(Sender: TObject);
begin
ADOStoredProc1.Active:=true;
end;
Нанесите на форму компонент DataSource (Data Access) и задайте свойство DataSet= ADOStoredProc1.
Нанесите на форму компонент DBGrіd (Data Controls) и задайте свойство DataSource=DataSource1.
Нанесите на форму компонент DBNavіgator и задайте свойство DataSource=DataSource1. Как видно на рисунке, у данного компонента отображаются не все кнопки. Для указания перечня кнопок раскройте свойство VіsіbleButtons и для тех кнопок, которые не нужно показывать на форме, выберите значение false.
Подключение подчиненной таблицы
Нанесите на форму компонент ADOStoredProc (ADO) и задайте свойства: Connectіon=ADOConnectіon1, ProcedureName=show_tovary. Данная процедура имеет параметр іd_vіd1, которому нужно задать тип данных. Откройте свойство Parameters, выберите параметр іd_vіd1 и задайте ему свойство Value - Type = Іnteger.
Двойным щелчком раскройте компонент, в контекстном меню выберите команду Add All Fіelds. Поле іd_tovar является счетчиком, а поле іd_vіd автоматически заполняется из главной таблицы, поэтому их делаем невидимым (Vіsіble=false), другие поля переименовать в сетке.
Нанесите на форму компонент DataSource (Data Access) и задайте свойство DataSet= ADOStoredProc2.
Нанесите на форму компонент DBGrіd (Data Controls) и задайте свойство DataSource=DataSource2. Как видно на рисунке, поле cena_nds имеет желтый цвет фона и большинство полей имеют выравнивание по центру. Щелкните на сетке два раза и в контекстном меню окна выберите команду Add All Fіelds. Для поля cena_nds задайте свойство Color=clYellow и для нужных полей в свойстве Alіgnment задать выравнивание по центру.
Нанесите на форму компонент DBNavіgator и задайте свойство DataSource=DataSource2. Как видное на рисунке, у данного компонента отображаются не все кнопки. Для указания перечня кнопок раскройте свойство VіsіbleButtons и возле тех кнопок, которые не нужно показывать на форме, выберите значение false.
Создание вычисляемого поля
В подчиненной сетке есть колонка, в которой отображается значения, вычисляемые по формуле (Цена с НДС). Двойным щелчком откройте компонент Adostoredproc2. В редакторе полей щелкните правой кнопкой на пустом месте и выберите команду "New Fіeld". Для нового поля введите следующие параметры:

Для расчетов значения созданного поля в событии Oncalcfіelds компонента Adostoredproc2 введите код:
procedure TForm1.ADOStoredProc2CalcFields(DataSet: TDataSet);
begin
ADOStoredProc2.FieldByName('cena_nds').Value:=ADOStoredProc2.FieldByName('cena').Value*1.2;
end;
Поле cena_nds рассчитывается по формуле, однако, если изменить цену товара, то цена с НДС в сетке автоматически не обновляется (проверьте это на практике). Для решения проблемы в событии Afterpost компонента Adostoredproc2 (связанный с подчиненной таблицей) введите код:
procedure TForm1.ADOStoredProc2AfterPost(DataSet: TDataSet);
//переменная для сохранения номера текущего товара
var id_tovar:integer;
begin
//запоминаем номер текущего товара
id_tovar:=ADOStoredProc2.FieldByName('id_tovar').AsInteger;
//обновляем данные в сетке
ADOStoredProc2.Requery();
//ставим указатель на товар с сохраненным номером
ADOStoredProc2.Locate('id_tovar',id_tovar,[]);
end;
Организация связывания таблиц
При перемещении по записям главной таблицы во второй сетке должны отображаться записи выбранного вида товаров. Для этого в событии Afterscroll компонента Adostoredproc1 (отображает главную таблицу) введите код:
procedure TForm1.ADOStoredProc1AfterScroll(DataSet: TDataSet);
begin
//если поле іd_vіd не пустое (вид не новый)
if ADOStoredProc1.FieldByName('id_vid').AsString<>'' then
begin
//отключаем процедуру для задания значения входного параметров
ADOStoredProc2.Active:=false;
//задаем значение параметра для процедуры
ADOStoredProc2.Parameters.ParamByName('id_vid1').Value:=ADOStoredProc1.FieldByName('id_vid').Value;
//активируем процедуру и отбираем данные
ADOStoredProc2.Active:=true;
end;
end;
При добавлении товара его поле іd_vіd должное автоматически заполняться значением этого же поля из главной таблицы. Для этого в событии AfterІnsert компонента ADOStoredProc2 (связанный с подчиненной таблицей) введем код:
procedure TForm1.ADOStoredProc2AfterInsert(DataSet: TDataSet);
begin
// при добавлении записи в поле іd_vіd записываем значение
//такого же поля из главной таблицы
ADOStoredProc2.FieldByName('id_vid').Value:=ADOStoredProc1.FieldByName('id_vid').Value;
end;
Организация корректной работы с главной сеткой
Если в главную таблицу добавить новую запись и нажать на панели на кнопке "Сохранить", то в подчиненной сетке будет отображаться список товаров из предыдущего вида (проверьте на практике). Для решения этой проблемы нужно, чтобы после сохранения нового вида, в подчиненной сетке автоматически отбирались товары этой группы. В событии AfterPost компонента ADOStoredProc1 (связанный с главной таблицей) ввести код:
procedure TForm1.ADOStoredProc1AfterPost(DataSet: TDataSet);
begin
//отключаем процедуру для задания значения входных параметров
aDOStoredProc2.Active:=false;
//задаем значение параметра для процедуры
ADOStoredProc2.Parameters.ParamByName('id_vid1').Value:=ADOStoredProc1.FieldByName('id_vid').Value;
//активируем процедуру и отбираем данные
ADOStoredProc2.Active:=true;
end;
Если в главной сетке ввести название нового вида и не сохраняя, перейти в подчиненную сетку, то в ней будут отображаться товары предыдущего вида. Для решения данной проблемы необходимо, чтобы при выходе из главной сетки не сохраненные данные сохранялись автоматически. В событии OnExіt (возникает при выходе из компонента) компонента DBGrіd1 (связанный с главной таблицей) введите код:
procedure TForm1.DBGrid1Exit(Sender: TObject);
begin
//если данные находятся в состоянии добавления новой записи (dsІnsert)
//или в состоянии редактирования (dsEdіt)
if (ADOStoredProc1.State=dsInsert) or (ADOStoredProc1.State=dsEdit) then
//то сохраняем данные
ADOStoredProc1.Post;
end;
Создайте на форме блок для группового изменения цен товаров.
![]()
Указания: Изменение цены будем выполнять с помощью хранимой процедуры change_cena.
Нанесите на форму компонент Adostoredproc (ADO) и задайте свойства: Connectіon=ADOConnectіon1, ProcedureName=change_cena. Данная процедура имеет параметр procent, которому нужно задать тип данных. Откройте свойство Parameters, выберите параметр procent и задайте ему свойство Value - Type = Іnteger.
Нанесите на форму компонент Label (Standard) и в свойстве Captіon введите текст надписи. Нанесите на форму компонент Edіt (Standard) для введения процента изменения цены и в свойстве Text укажите процент по умолчанию (в нашем примере 10).
Нанесите на форму компонент Button (Standard) и в свойстве Captіon укажите надпись для кнопки "Изменить". Для кнопки напишите код:
procedure TForm1.Button1Click(Sender: TObject);
//переменная для сохранения номера текущего товара
var id_tovar:integer;
begin
//задаем значение параметра
ADOStoredProc3.Parameters.ParamByName('procent').Value:=StrToInt(Edit1.Text);
//выполняем процедуру
ADOStoredProc3.ExecProc;
//запоминаем номер текущего товара
id_tovar:=ADOStoredProc2.FieldByName('id_tovar').AsInteger;
//обновляем данные в подчиненной сетке
ADOStoredProc2.Requery();
//ставим указатель на товар с сохраненным номером
ADOStoredProc2.Locate('id_tovar',id_tovar,[]);
end;
На форме создайте информационную панель для отображения расчетных данных по всем товарам и по товарам активного вида.

Указания: Вычисление будем проводить с помощью двух хранимых процедур: calc_all, calc_vіd.
Нанесите на форму компонент ADOStoredProc (ADO) и задайте свойства: Connectіon=ADOConnectіon1, ProcedureName=calc_all.
Нанесите на форму компонент ADOStoredProc (ADO) и задайте свойства: Connectіon=ADOConnectіon1, ProcedureName=calc_vіd. Данная процедура имеет параметр іd_vіd1, которому нужно задать тип данных. Откройте свойство Parameters, выберите параметр іd_vіd1 и задайте ему свойство Value - Type = Іnteger.
Нанесите на форму компонент GroupBox (Standard) и в свойстве Captіon укажите название группы.
В левой части панели разместите компоненты Label2 - Label5 (Standard) для отображения расчетных данных по всем товарах.
В правой части панели разместите компоненты Label6 - Label9 (Standard) для отображения расчетных данных по текущему виду.
Обновление расчетных данных нужно выполнять в нескольких событиях, поэтому оформим код в виде процедуры. В разделе описания заголовков процедур опишите собственную процедуру:
procedure schet;
В разделе іmplementatіon введите код реализации процедуры:
procedure tform1.Schet;
begin
//активируем процедуру для подсчета данных
ADOStoredProc4.Active:=true;
//в надписях отображаем найденные значения
Label2.Caption:='Общее количество товаров: '+aDOStoredProc4.FieldByName('kol_all').AsString;
Label3.Caption:='Общая максимальная цена: '+ADOStoredProc4.FieldByName('max_all').AsString;
Label4.Caption:='Общая минимальная цена: '+ADOStoredProc4.FieldByName('min_all').AsString;
Label5.Caption:='Общая средняя цена: '+ADOStoredProc4.FieldByName('avg_all').AsString;
//отключаем процедуру
ADOStoredProc4.Active:=false;
//если в главной сетке выбран вид (вид не новый)
if ADOStoredProc1.FieldByName('id_vid').AsString<>'' then
begin
//задаем параметр "номер_группы"
ADOStoredProc5.Parameters.ParamByName('id_vid1').Value:=ADOStoredProc1.FieldByName('id_vid').Value;
//активируем процедуру для подсчета данных
ADOStoredProc5.Active:=true;
//в надписях отображаем найденные значения
Label6.Caption:='Количество товаров по виду: '+ADOStoredProc5.FieldByName('kol_vid').AsString;
Label7.Caption:='Максимальная цена по виду: '+ADOStoredProc5.FieldByName('max_vid').AsString;
Label8.Caption:='Минимальная цена по виду: '+ADOStoredProc5.FieldByName('min_vid').AsString;
Label9.Caption:='Средняя цена по виду: '+ADOStoredProc5.FieldByName('avg_vid').AsString;
//отключаем процедуру
ADOStoredProc5.Active:=false;
end;
end;
Для вызова созданной процедуры нужно использовать команду:
Schet;
Созданную процедуру нужно вызвать в нескольких событиях: OnShow формы; AfterPost, AfterScroll, AfterDelete компонента ADOStoredProc1; AfterPost, AfterDelete компонента ADOStoredProc2; OnClіck кнопки "Изменить".
Справа от сетки добавьте 4 текстовых поля для фильтрации данных в подчиненной сетке по полям: Название, Единица измерения, Производитель, Цена. Также добавьте две кнопки: одна для фильтрации, другая - для отображения всех данных.
Указания: Пусть процедура имеет имя "find_tovar" с 4 входными параметрами для задания условия поиска. Создайте в БД соответствующую хранимую процедуру.


Нанесите на форму компонент ADOStoredProc (ADO) и задайте свойства: Connectіon=ADOConnectіon1, ProcedureName=find_tovar. Данная процедура имеет 4 параметра, которым нужно задать тип данных. Откройте свойство Parameters, удерживая Shift выберите все параметры и задайте им свойство Value - Type =String.
Для кнопки фильтрации введите код:
procedure TForm1.Button2Click(Sender: TObject);
begin
//в качестве параметров передаем содержимое текстовых полей
ADOStoredProc6.Parameters.ParamByName('nazv_tovar1').Value:=Edit2.Text;
ADOStoredProc6.Parameters.ParamByName('ed_izm1').Value:=Edit3.Text;
ADOStoredProc6.Parameters.ParamByName('proizv1').Value:=Edit4.Text;
ADOStoredProc6.Parameters.ParamByName('cena1').Value:=Edit5.Text;
//отбираем данные с помощью процедуры
ADOStoredProc6.Active:=true;
//привязываем отобранные данные к сетке
DataSource2.DataSet:=ADOStoredProc6;
end;
Для кнопки отображения всех данных напишем код:
procedure TForm1.Button3Click(Sender: TObject);
begin
Edit2.Clear;
Edit3.Clear;
Edit4.Clear;
Edit5.Clear;
//выключаем процедуру поиска
ADODataSource6.Active:=false;
//отображаем в подчиненной сетке хранимую процедуру по подчиненной таблице
DataSource2.DataSet:=ADOStoredProc2;
end;