Практическое занятие № 21

Тема: "Обработка таблиц с помощью запросов"

Цель работы: получить практические навыки по использованию запросов разных типов при работе с БД

 

Ход работы

 

Скачайте БД в формате Microsoft Access. , которая содержит таблицы grupy и students, связанные по полю id_grup.

 

Таблица grupy имеет структуру:

Имя поля

Тип

Размер

id_grup

integer

ключевое, счетчик

grup_sokr

varchar

3

grup

varchar

20

klruk

varchar

50

kurs

integer

 

Таблица students имеет структуру:

 

Имя поля

Тип

Размер

id_stud

integer

ключевое, счетчик

inn

integer

уникальное

fam

varchar

15

imya

varchar

15

otch

varchar

15

datar

date

 

srbal

decimal

2 знака после запятой

id_grup

integer

внешний ключ для связывания

 

В среде Delphi создайте программу для работы с данными таблиц. При этом форма должна иметь вид:

 

 

 

Указания: для формы задайте свойства: Caption=Работа с запросами, Position=DesktopCenter, Borderstyle=bsSingle, BorderIcons-biMaximized=false (скрывает кнопку разворачивания на весь экран).

 

Подключение главной таблицы

 

На форму нанесите компонент ADOConnection (ADO). В свойстве ConnectionString укажите параметры подключения к БД Access. В свойстве LoginPrompt укажите false, чтобы программа не запрашивала логин и пароль при соединении с БД.

Нанесить на форму компонент ADOTable (ADO). В свойстве Connection укажите соединение с БД ADOConnection1, в свойстве TableName укажите таблицу grupy.

 

Для того, чтобы программа корректно подключала таблицу в событии формы OnCreate введите код:

 

procedure TForm1.FormCreate(Sender: TObject);

begin

    ADOTable1.Active:=true;

end;

 

Для того, чтобы программа корректно отключала таблицу в свойстве формы OnDestroy введите код:

 

procedure TForm1.FormDestroy(Sender: TObject);

begin

    ADOConnection1.Connected:=false;

end;

 

Для отображения данных в событии формы OnCreate введите код:

 

procedure Tform1.Formcreate(Sender: TObject);

begin

    ADOTable1.Active:=true;

end;  

 

Для того, чтобы  программа корректно отключалась от БД , в событии формы OnDestroy введите код:

 

procedure Tform1.FormDestroy(Sender: TObject);

begin

    ADOConnection1.Connected:=false;

end;

 

Нанесите на форму компонент DataSource (DataAccess). Установите его свойство DataSet =ADOTable1.

Нанесите на форму компонент DBGrid (DataControls). В свойстве DataSource укажите имя компонента DataSource1.

 

Подключение подчиненной таблицы

 

По умолчанию подчиненная таблица отображает только те записи, которые соответствую группе, выбранной в главной таблице. Для реализации загрузки только нужного набора данных (что соответствует принципу работы по технологии клиент/сервер), будем отображать данные с помощью запроса.

На форму нанесите компонент ADOQuery (ADO). Для компонента задайте свойства: Connection=ADOConnection1, SQL = select * from students where id_grup=:id_grup.

Запрос имеет параметр :id_grup, для которого нужно задать правильный тип данных. Для этого откройте свойство Parameters компонента ADOQuery, выберите нужный параметр и укажите тип Integer в свойствах DataType и Value - Type.

Добавьте в запрос все поля, переименуйте и задайте нужную ширину. Поле id_grup связано с полем главной таблицы  (заполняется автоматически), поэтому его можно не показывать (Visible=false). Аналогично скройте поле id_stud, которое как счетчик также заполняется автоматически.

Нанесите на форму компонент DataSource (DataAccess). Установите его свойство DataSet =ADOQuery1.

Нанесите на форму компонент DBGrid (DataControls). В свойстве DataSource укажите имя компонента DataSource2.

 

При переходе по группам в главной таблице в подчиненной нужно обновлять запрос так, чтобы из БД загружался тот список студентов, который соответствует текущей группе.

В событии AfterScroll для компонента ADOTable1 введите код:

 

procedure TForm1.ADOTable1AfterScroll(DataSet: TDataSet);

begin

    //выключаем запрос

    ADOQuery1.Active:=false;

    //в текст запроса передаем новый номер группы из главной таблицы

    ADOQuery1.Parameters.ParamByName('id_grup').Value:=AdoTable1.FieldByName('id_grup').Value;

    //выключаем запрос

    ADOQuery1.Active:=true;

end;

 

Каждая сетка DBGrid по умолчанию поддерживает специальные клавиши: Insert - додавить новую запись над текущей, Ctrl+Delete - удалить текущую запись (можете проверить эти возможности). Нужно заблокировать эти клавиши:

 

Указания: для того, чтобы форма могла реагировать и обрабатывать нажатия клавиш установите для формы свойство KeyPreview=true.

Для формы в событии OnKeyDown напишем код:

 

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    begin
        //если в момент нажатия на клавиши одна из сеток активна

    if (DBGrid1.Focused) or (DBGrid2.Focused) then
            //если нажата клавиша Insert

        //или клавиши Ctrl+Insert

        if (Key=VK_Insert) or (Key=VK_Delete) and (sSctrl in Shift) then
                //то это нажатие игнорируется         

            Abort;
    end;

 

Под каждой из сеток добавим кнопки для добавления и удаления данных.

 

Указания: под главной сеткой разместите два компонента Button (Standard) и в свойстве Caption введите текст надписей на кнопках.

Для кнопки "Добавить" введите код:

 

procedure TForm1.Button1Click(Sender: TObject);

begin

    //добавляем новую запись

    ADOTable1.Append;

    //ставим курсор в поле grup_sokr (первое поле в сетке)   

    ADOTable1.FieldByName('grup_sokr').FocusControl;

end;

 

Для кнопки "Удалить" напишем код:

 

procedure TForm1.Button2Click(Sender: TObject);

begin

    //если данных для удаления нет, то выход

    if ADOTable1.RecordCount=0 then Exit;

 

   //показываем запрос на удаление

   if Application.MessageBox(PChar('Удалить группу '''+

                                    ADOTable1.FieldByName('grup').AsString+'''?'),

                                   'Подтвердите',MB_YesNo+MB_IconQuestion)=IdYes then

        //если ответ положительный, то удаляем запись

        ADOTable1.Delete;

end;

 

Под подчиненной сеткой разместите два компонента Button (Standard) и в свойстве Caption введите текст надписей на кнопках.

Для кнопки "Добавить" введите код:

 

procedure TForm1.Button3Click(Sender: TObject);
    begin
        //добавляем новую запись

    ADOQuery1.Append;

    //ставим курсор в поле inn (первое поле в сетке)   

    ADOQuery1.FieldByName('inn').FocusControl;


    end;

 

Так как подчиненная таблица строится на основании запроса и не связана с главной таблицей с помощью свойств Master Source, Master Fields, то перед сохранением записи в подчиненной таблице нужно программно заполнять поле id_grup номером группы из главной сетки.

Для компонента ADOQuery1 в с обытии BeforPost введите код:

 

procedure TForm1.ADOQuery1BeforePost(DataSet: TDataSet);

begin

    ADOQuery1.FieldByName('id_grup').Value:=ADOTable1.FieldByName('id_grup').Value;

end;

 

Для кнопки "Удалить" напишем код:

 

procedure TForm1.Button4Click(Sender: TObject);
    begin

    //если данных для удаления нет, то выход

    if ADOQuery1.RecordCount=0 then Exit;

    

    //показываем запрос на удаление
        if Application.MessageBox(PChar('Удалить студента '''+
                                         ADOQuery1.FieldByName('fam').AsString+' '+
                                         
ADOQuery1.FieldByName('imya').AsString+' '+
                                         
ADOQuery1.FieldByName('otch').AsString+'''?'),

                              'Подтвердите',MB_YesNo+mb_IconQuestion)=IdYes then

         //если ответ положительный, то удаляем запись
             
ADOQuery1.Delete;
    end;

 

Реализуем просмотр данных по группам или целым списком. На форме создайте группу переключателей.

 

 

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

 

Указания: для отображения всех данных из подчиненной таблицы нанесите на форму компонент ADOTable и задайте свойства: Connection = ADOConnection1, TableName = students.

добавьте на форму компонент RadioGroup (Standard) і задайте свойства Caption=Режим просмотра, Columns=2 (количество колонок), Items - названия переключателей, ItemIndex=0 (первый переключатель включен по умолчанию).В событии OnClick компонента напишем код:

 

procedure TForm1.RadioGroup1Click(Sender: TObject);
    begin
               

    case RadioGroup1.ItemIndex of
            //если выбран первый переключатель (по группам)

        //подключаем источник DataSource подчиненной сетки к запросу

        0: begin

            ADOTable2.Active:=false;

            DataSource2.DataSet:=ADOQuery1;

           end;
            //если выбран второй переключатель (общим списком)

        //подключаем источник DataSource подчиненной сетки к запросу

        1: begin

            ADOTable2.Active:=true;

            DataSource2.DataSet:=ADOTable2;

           end;  

    end;
    end;

 

Реализуем сортировку данных в подчиненной сетке при щелчке на заголовках сетки.

 

Указания: в практическом занятии № 11 был показан пример сортировки данных с помощью свойства ADOTable.IndexFieldNames. В нашем примере данные в подчиненной сетке отображаются с помощью компонента ADOQuery (при просмотре по группам) или с помощью компонента ADOTable (при просмотре общим списком). Компонент ADOQuery не имеет свойства IndexFieldNames, но имеет похожее свойство Sort.

В событии OnTitleClick подчиненной сетки введите код:

 

procedure TForm1.DBGrid1TitleClick(Column: TColumn);

begin

//если источник данных для подчиненной сетки - ADOQuery1

if DataSource2.DataSet=ADOQuery1 then

    //сортируем данные в ADOQuery по возрастанию или по убыванию   

    if ADOQuery1.Sort=Column.FieldName + ' DESC' then

        ADOQuery1.Sort:=Column.FieldName

    else

        ADOQuery1.Sort:=Column.FieldName+' DESC'

//иначе, если источник данных для подчиненной сетки - ADOTable1

else

   //сортируем данные в ADOTable по возрастанию или по убыванию   

    if ADOTable2.IndexFieldNames=Column.FieldName + ' DESC' then

        ADOTable2.IndexFieldNames:=Column.FieldName

    else

        ADOTable2.IndexFieldNames:=Column.FieldName+' DESC';

end;

 

Реализуем функцию увеличения или уменьшения среднего балла студентов на заданный процент с помощью запроса на обновление данных. При этом на форме нужно создать конструкцию вида:

 

Пользователь в поле вводит нужный процент.

 

Указания: разместите на форме нужные компоненты.

Для обновления данных будем использовать запрос, задаваемый через запрос. нанесите на форму компонент ADOQuery (ADO) и задайте свойство Connection = ADOConnection1. 

Для кнопки введите код:

 

procedure TForm1.Button5Click(Sender: TObject);

begin

    //выдаем запрос на обновление

    if Application.MessageBox('Ви подтверждаете обновление данных?',

                                                                           'Подтвердите',MB_YesNo+MB_IconQuestion)=IdYes then

    //если пользователь ответил утвердительно

    begin

        //формируем запрос на обновления поля srbal

        ADOQuery2.Sql.Text:='update students set srbal = srbal + srbal * '+Edit2.Text+' / 100';

        //выполняем запрос

        ADOQuery2.ExecSql;

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

        ADOQuery1.Requery();

    end;

end;

 

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

 

 

Указания: нанесите на форму компонент Groupbox (Standard) и в свойстве Caption введите название панели.

На панель нанесите 4 компонента Label2 - Label5 (Standard), разместите ее левее на панели и в свойстве Font задайте синий цвет текста.

На панель нанесите 4 компонента Label6 - Label9 (Standard), разместите ее правее на панели и в свойстве Font задайте красный цвет текста.

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

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

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

 

 procedure Schet;

 

После ключевого слова Implementation введите код процедуры:

 

procedure TForm1.Schet;

begin

 

    //выполняем расчет по всем записям таблицы

    ADOQuery2.Active:=false;;

    //формируем запрос на расчет показателей

    //обратите внимание на округление каждой функции в запросе

    //с помощью стандартной функции round

    ADOQuery2.Sql.Text:='select count(inn) as kolvo, round(max(srbal),2) as maxbal, round(min(srbal),2) as minbal, round(avg(srbal),2) as sredbal from students';

 

    //активируем запрос

    ADOQuery2.Active:=true;

 

    //в надписях на панели отображаем найденные значения

    Label2.Caption:='Общее число студентов: '+

                     VarToStr(ADOQuery2.FieldByName('kolvo').Value);

    Label3.Caption:='Общий максимальный балл: '+

                     VarToStr(ADOQuery2.FieldByName('maxbal').Value);

    Label4.Caption:='Общий минимальный балл: '+

                     VarToStr(ADOQuery2.FieldByName('minbal').Value);

    Label5.Caption:='Общий средний балл: '+

                     VarToStr(ADOQuery2.FieldByName('sredbal').Value);

 

    //================================================   

    //выполняем расчет по студентам отдельной группы

    ADOQuery2.Active:=false;

    //если номер активной группы не пустой (группа не новая)

    if ADOTable1.FieldByName('id_grup').AsString<>'' then

        //формируем запрос на расчет показателей текущей группы

        //обратите внимание на округление каждой функции в запросе

        //с помощью стандартной функции round

        ADOQuery2.Sql.Text:='select count(inn) as kolvo, round(max(srbal),2) as maxbal, round(min(srbal),2) as minbal, round(avg(srbal),2) as sredbal from students where id_grup='+ADOTable1.FieldByName('id_grup').AsString

    //если номер группы пустой (группа нова)

    else

        //формируем запрос, который отберет пустые значения

        //и на форме ничего не отобразиться

        ADOQuery2.Sql.Text:='select null as kolvo, null as maxbal, null as minbal, null as sredbal from students';

 

    //активируем запрос и отбираем данные

    ADOQuery2.Active:=true;

 

    //в надписях на панели отображаем найденные значения

    Label6.Caption:='Общее число студентов в группе: '+

                     VarToStr(ADOQuery2.FieldByName('kolvo').Value);

    Label7.Caption:='Общий максимальный балл в группе: '+

                     VarToStr(ADOQuery2.FieldByName('maxbal').Value);

    Label8.Caption:='Общий минимальный балл в группе: '+

                     VarToStr(ADOQuery2.FieldByName('minbal').Value);

    Label9.Caption:='Общий средний балл в группе: '+

                      VarToStr(adoquery2.FieldByName('sredbal').Value);

end;

 

Для вызова созданной процедуры используют команду:

 

Schet;

 

Созданную процедуру нужно вызывать в нескольких событиях:

- для формы: OnShow (при открытии формы);

- для компонента ADOTable1 - главная таблица: AfterDelete (после удаления группы удаляются и студенты, значит показатели нужно пересчитать показатели), AfterScroll (после перехода по группам в подчиненной сетке меняется список студентов, значит показатели нужно пересчитать показатели);

- для компонента ADOQuery1 - подчиненная таблица: AfterPost (после добавления или изменения данных о студенте), AfterDelete (после удаления данных о студенте);

- для кнопки "Изменить" после обновления поля srbal нужно пересчитать показатели.