Практическое занятие № 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;