Форма для работы с тарифами и билетами - динамическое формирование SQL запроса
На этой форме (FormTicket) покажем три списка (рис. 24). Первый предназначен для отображения всех имеющихся тарифов. Второй показывает даты вылета и суммарное количество билетов по выбранному тарифу на указанную дату. Третий список выводит данные по всем билетам за указанное число по выбранному тарифу.
Рисунок 24 Форма для работы с тарифами и билетами
На этой же форме расположим кнопку, при помощи которой будет продаваться новый билет.
На форме размещаются следующие компоненты для работы с данными
Назначение запроса | Компонент доступа к данным (IBQuery) | Компонент модификации данных (IBUpdateSQL) | Компонент источник данных DataSource |
Выводит данные по всем тарифам (номер авиалиний, название аэропортов откуда и куда, тип салона, цена билета) | IBQueryTarif | DataSourceTarif | IBUpdateSQLTarif |
Даты вылета и количество проданных билетов по указанному тарифу | IBQueryCntTick | DataSourceCntTick | DataSourceCntTick |
Извлекает данные по билетам (ФИО пассажира, номер билета и место) | IBQueryPass | DataSourcePass | DataSourcePass |
Для динамического создания запросов | IBQueryAsc |
Первый список (тарифы – IBQueryTarif) выводится при помощи запроса
select TR_AL_NUM, TR_AP_FROM, TR_AP_TO, TR_CODE, TR_COST, TR_SL_TYPE from TARIFF
Второй список (даты вылета – IBQueryCntTick) использует левое объединение, так как возможно, что по этому тарифу за это число не было продано ни одного билета.
SELECT count (TC.TC_NUM), f.fl_date FROM TARIFF TR
INNER JOIN flight f on f.fl_num = TR.tr_al_num
LEFT JOIN TICKET TC ON (TC.TC_TR_CODE = TR.tr_code AND TC.tc_fl_date = f.fl_date)
WHERE TR.TR_CODE = :tr
В качестве параметра запроса подставляется код тарифа при помощи события
procedure TFormTicket.DataSourceTarifDataChange(Sender: TObject;
Field: TField);
Begin
IBQueryCntTick.Close;
IBQueryCntTick.ParamByName('tr').AsInteger := IBQueryTarifTR_CODE.Value;
IBQueryCntTick.Open;
end;
Данные по проданным билетам получаем при помощи следующего запроса:
select P.pr_name, P.pr_name2, P.pr_name3, T.tc_fl_num, Cast(T.tc_row as char(2))||tc_lit from person P
inner join ticket T on T.tc_pr_code = P.pr_code
where T.tc_tr_code = :TR and T.tc_fl_date = :dt
В этот запрос нужно подставить два параметра – код тарифа и дату вылета. Эта подстановка производиться событием onDataChange для компонента DataSourceCntTick
procedure TFormTicket.DataSourceCntTickDataChange(Sender: TObject;
Field: TField);
Begin
IBQueryPass.Close;
//билеты по тарифу на день вылета
IBQueryPass.ParamByName('TR').AsInteger := IBQueryTarifTR_CODE.Value;
IBQueryPass.ParamByName('DT').AsDate := IBQueryCntTickFL_DATE.Value;
IBQueryPass.Open;
end;
Покупка нового билета
Рассмотрим, как наше приложение позволяет осуществлять покупку нового билета.
Прежде всего, нам нужно будет создать вспомогательную форму (FormNewTicket), при помощи которой мы сможем задать все атрибуты для нового билета (рис. 25). На форме будут располагаться компоненты TDBEdit отображающие сведенья о приобретаемом билете, полученные из запроса тарифы – IBQueryTarif. Отображаются следующие поля: номер авиалиний, название аэропортов откуда и куда, тип салона, цена билета. Редактор дат (DateTimePickerTicket) предназначен для задания даты вылета в билете. Три однострочных редактора (EditName, EditName2, EditName3) предназначены для задания фамилии, имени, отчества пассажира. Эти поля используются для поиска личности при помощи запроса с параметрами.
Решетка (DBGridPRS) предназначена для вывода списка личностей, из которых можно выбрать покупателя билета.
Под решеткой располагаются два обычных однострочных редактора, в которые выводятся номер билета и номер места.
Кроме визуальных компонент на форме размещаются компоненты для работы с данными
Назначение запроса | Компонент доступа к данным (IBQuery) | Компонент источник данных DataSource |
Извлекает данные личности (код и ФИО) для продажи билета | IBQueryPRS | DataSourcePRS |
Для подстановки динамически сгенерированного SQL запроса | IBQueryAny | |
Находит номер для вновь продаваемого билета | IBQueryNewNum |
В нижней части формы располагаются две кнопки (TBitBtn). У одной из них свойство Kind равно bkOK – при нажатии на эту кнопку форма закроется (свойство ModalResult для формы будет присвоено mrOk) и будет создан новый билет. Эта кнопка имеет надпись «ОК». У второй кнопки свойство Kind установлено bkCancel – эта кнопка просто закрывает форму (свойство ModalResult для формы будет присвоено mrCancel).
Рисунок 25 Форма для покупки нового билета
Обсудим использование компонент для работы с данными детально.
Компонент IBQueryPRS выводит список личностей в зависимости от условия отбора, которые задаются при помощи параметров. Сам SQL запрос имеет вид:
SELECT PR_NAME, PR_NAME2, PR_NAME3, PR_CODE FROM PERSON
WHERE PR_NAME LIKE :NAME AND PR_NAME2 LIKE :NAME2 AND PR_NAME3 LIKE :NAME3
ORDER BY PR_NAME, PR_NAME2, PR_NAME3
Параметры запроса подставляются при любом изменении в заданной фамилии, имени или отчестве, т.е. в компоненте EditName, EditName2 или EditName3. Соответствующее событие имеет вид
procedure TFormNewTicket.EditNameChange(Sender: TObject);
Begin
IBQueryPRS.Close;
IBQueryPRS.ParamByName('NAME').AsString := EditName.Text + '%';
IBQueryPRS.ParamByName('NAME2').AsString := EditName2.Text + '%';
IBQueryPRS.ParamByName('NAME3').AsString := EditName3.Text + '%';
IBQueryPRS.Open;
end;
После отработки этого события в решетке DBGridPRS будет отображен список личностей, соответствующий введенным ограничениям. Чтобы выбрать одного человека, которому будет продан билет нужно щелкнуть по выбранной строке в решетке, при этом его фамилия, имя, отчество будут скопированы в EditName, EditName2, EditName3.
procedure TFormNewTicket.DBGridPRSDblClick(Sender: TObject);
Begin
EditName.Text := IBQueryPRSPR_NAME.Value;
EditName2.Text := IBQueryPRSPR_NAME2.Value;
EditName3.Text := IBQueryPRSPR_NAME3.Value;
end;
Компоненты IBQueryAny, IBQueryNewNum задействованы при первичном заполнении полей для нового билета.
Будем считать, что нумерация билетов на каждую авиалинию идет строго по нарастающей. Тогда IBQueryNewNum будет находить номер для вновь продаваемого билета при помощи SQL запроса
select Max(TC_NUM) + 1 from TICKET
WHERE TC_FL_NUM = :fl
Номер места в новом билете тоже будем проставлять автоматически. Если место последнего проданного билета на данный рейс и в данном салоне не последнее в ряду, то следующее место будет отмечаться следующей литерой, иначе следующее место будет в следующем ряду и обозначаться литерой 'А'. Выполняем этот алгоритм при помощи компонента IBQueryAny путем динамического создания текста запроса в зависимости от заданных параметров. Найденные номер ряда и литера места для нового билета будут помещены в публичные члены класса TFormNewTicket.
Public
sLit :String;
sNum :Integer;
Все требуемые поля задаются после нажатия на кнопку «Купить билет» при показе формы FormNewTicket. Вот событие OnShow для этой формы
procedure TFormNewTicket.FormShow(Sender: TObject);
Var
inRow : Integer;
Begin
with FormTicket do begin //данные передаются из формы FormTicket
//подсчитаем новый номер билета
IBQueryNewNum.Close;
IBQueryNewNum.ParamByName('fl').AsInteger :=
IBQueryTarifTR_AL_NUM.AsInteger;
IBQueryNewNum.Open;
EditNum.Text := IBQueryNewNum.Fields[0].AsString;
//подсчитаем новое место
//Ряд
IBQueryAny.SQL.Text := 'SELECT MAX(ticket.tc_row) FROM tariff '
+'INNER JOIN ticket ON (tariff.tr_code = ticket.tc_tr_code) '
+'WHERE tariff.tr_al_num = ' + IBQueryTarifTR_AL_NUM.AsString
+'AND tariff.tr_sl_type = ' + IBQueryTarifTR_SL_TYPE.AsString
+'AND ticket.tc_fl_date = ''' + DateToStr(DateTimePickerTicket.Date)
+'''';
IBQueryAny.Open;
sNum := IBQueryAny.Fields[0].AsInteger;
//Литера
IBQueryAny.SQL.Text := 'SELECT MAX(ticket.tc_lit)FROM tariff '
+'INNER JOIN ticket ON (tariff.tr_code = ticket.tc_tr_code) '
+'WHERE tariff.tr_al_num = ' + IBQueryTarifTR_AL_NUM.AsString
+'AND tariff.tr_sl_type = ' + IBQueryTarifTR_SL_TYPE.AsString
+'AND ticket.tc_fl_date = ''' + DateToStr(DateTimePickerTicket.Date)
+ '''' + ' AND ticket.tc_row = ' + IntToStr(sNum);
IBQueryAny.Open;
sLit := IBQueryAny.Fields[0].AsString;
//мест в ряду
IBQueryAny.SQL.Text := 'SELECT SP.SP_INROW FROM SALON_IN_PLANE SP '
+'INNER JOIN TARIFF T '
+'ON T.TR_SL_TYPE = SP.SP_SL_TYPE INNER JOIN AIRLINE AL '
+'ON AL.AL_NUM = T.TR_AL_NUM AND SP.SP_PL_CODE = AL.AL_PL_CODE '
+'WHERE T.TR_CODE = ' + IBQueryTarifTR_CODE.AsString;
IBQueryAny.Open;
inRow := IBQueryAny.Fields[0].AsInteger;
if sNum = 0 then begin
sNum := 1; //Первый билет
sLit := 'А';