Разработка баз данных в Delphi. 11 Уроков

         

Компоненты для построения отчетов


На странице палитры компонентов QReport расположено более двух десятков компонентов, применяемых для построения отчетов.

Центральным компонентом является TQuickRep, определяющий поведение отчета в целом. С помощью других компонентов создаются составные части отчета.

TQRBand – заготовка для расположения данных, заголовков, титула отчета и др. Отчет, в основном, строится из компонентов TQRBand, которые реализуют:

· область заголовка отчета;

·        область заголовка страницы;

·        область заголовка группы;

·        область названий столбцов отчета;

·        область детальных данных, предназначенную для отображения данных самого нижнего уровня детализации;

·        область подвала группы;

·        область подвала страницы;

·        область подвала отчета.

TQRStringsBand – имеет то же назначение, что и TQRBand. Отличается встроенным списком строк Items, содержимое которого становится видным в режиме печати и предварительного просмотра, если на компонент TQRStringsBand положен компонент TQRExpr. Для каждой строки в Items выводится своя полоса TQRStringsBand.

TQRSubDetail – дочерняя полоса. Привязывается к родительской полосе и служит для ее расширения. Любая полоса может стать родительской с помощью установки значения True в ее свойство HasChild.

TQRGroup – применяется для группировок данных в отчетах.

TQRLabel – позволяет разместить в отчете произвольную текстовую строку.

TQRDBText – служит для вывода в отчет содержимого текстового поля набора данных.

TQRExpr – применяется для вывода значений, являющихся результатом вычислений выражений. Алгоритм вычисления выражений строится при помощи редактора формул данного компонента.



TQRSysData – служит для вывода в отчете системной величины: даты, времени, номера страницы и т.п.

TQRMemo – вставляет в отчет многостраничный текст.

TQRExprMemo – используется для создания многострочных вычисляемых полей.

TQRRichText – вставляет в отчет многострочный текст в формате RTF.

TQRDBRichText – служит для вывода в отчете полей НД, содержащих многострочный текст в формате RTF.

TQRShape – служит для вывода в отчете графических фигур, например, прямоугольников.

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

TQRPreview – базовый компонент для создания нестандартных окон предварительного просмотра. Стандартное окно реализуется с помощью метода Preview компонента TQuickRep.

TQRXXXFilter – фильтрующие компоненты для преобразования отчета в текст, страницу HTML и т.п. при печати отчета.

TQRChart – служит для встраивания в отчет графиков.



Компонент TQuickRep


При размещении этого компонента на форме в ней появляется сетка отчета (рис.1). В дальнейшем в этой сетке располагаются составные части отчета, например, полосы TQRBand (рис.2).

Рис. 1. Пустая сетка отчета. Образуется после размещения на форме компонента TQuickRep.

Рис. 2. Сетка отчета с размещенными в ней компонентами отчета.

Перечислим важнейшие свойства, методы и события компонента TQuickRep.



Компонент TQRBand


Компоненты TQRBand являются основными частями отчета и используются для размещения на них отображающих компонентов, таких как TQRLabel, TQRDBText, TQRImage и т.п.

Свойства компонента:

Свойство

Назначение

property AlignToBottom: boolean;

Если имеет значение True полоса печатается непосредственно над подвалом страницы вместо обычного расположения справа/снизу от предыдущей полосы.

type TQRBandType = (rbTitle, rbPageHeader, rbDetail, rbPageFooter, rbSummary, rbGroupHeader, rbGroupFooter, rbSubDetail, rbColumnHeader);

property BandType: TQRBandType;

Указывает назначение полосы: rbTitle – содержит заголовок отчета; rbPageHeader – содержит заголовок страницы (на первой странице печатается под rbTitle); rbDetaul – содержит информацию из НД; выводится всякий раз при переходе на новую запись НД; эта полоса повторяется для всех записей DataSet, начиная с первой записи и заканчивая последней; позицирование на первую запись и последовательный их перебор осуществляется компонентом TQuickRep автоматически; rbPageFooter – содержит подвал страницы; выводится в конце каждой страницы отчета после всех других полос; rbSummary – подвал отчета; выводится на последней странице отчета после всей иной информации, но перед подвалом последней страницы; rbGroupHeader – содержит заголовок группы; применяется при группировках информации в отчете и выводится всякий раз при выводе новой группы; rbGroupFooter – содержит подвал группы; выводится всякий раз при окончании вывода группы, после всех данных группы; rbSubDetail – содержит детальную информацию из подчиненного НД при выводе в отчете информации из двух или более наборов данных, связанных в приложении как главный-подчиненный; этот тип назначается полосе автоматически при размещении на форме компонента TQRSubDetail; rbColumnHeader – содержит заголовки столбцов; размещается на каждой странице отчета после заголовка страницы.

property Enabled: boolean;

Разрешает/запрещает печать полосы.

property ForceNewColumn: boolean;

Если содержит True, полоса печатается в следующей колонке.

property ForceNewPage: boolean;

Если содержит True, полоса печатается на новой странице.

property HasChild: boolean;

Если содержит True, полоса имеет дочернюю полосу TChildBand. Установка True в это свойство автоматически создает в отчете дочернюю полосу.


События
property AfterPrint: TQRAfterPrintEvent;
и
property BeforePrint: TQRBeforePrintEvent;
наступают соответственно до и после печати полосы. Метод
function AddPrintable(PrintableClass: TQRNewComponentClass): TQRPrintable;
используется для вставки в полосу отображающего компонента в процессе прогона программы. Он автоматически устанавливает между полосами отношение собственности. Два следующих фрагмента выполняют одинаковую работу:
with DetailBand1.AddPrintable(TQRLabel) do
begin
  Size.Left := 20;
  Size.Top := 5;
  Caption := ‘Новая полоса’;
end;
var
  aLabel : TQRLabel;
begin
  aLabel := TQRLabel.Create(ReportForm);
  aLabel.Parent := DetailBand1;
  with aLabel do
  begin
    Size.Left := 20;
    Size.Top := 5;
    Caption := ‘Новая полоса’;
  end;
end;

Создание простейшего отчета


Компоненты TQuickRep и TQRBand являются минимально достаточными для создания простейшего отчета, не содержащего внутри себя группировок информации.

Пусть имеется таблица БД Rashod.DB, содержащая сведения об отпуске материалов со склада. В состав ТБД входят поля

· N_RASH – уникальный номер события отпуска товара;

·        DEN – номер дня;

·        MES – номер месяца;

·        GOD – номер года;

·        TOVAR – наименование отпущенного товара;

·        POKUP – наименование покупателя;

·        KOLVO – количество единиц отпущенного товара.

Заметим, что дата отпуска товара хранится в разбивке на день, год и месяц. Сделано так специально, с целью показать, как в отчетах используются выражения и вычисляемые поля.

Создадим простейший отчет, состоящий из заголовка и сведений об отпуске товара. В отчет включаются все факты отпуска товара. Сортировка производится по номеру события отпуска товара. Для этого разместим на форме компонент TTable, свяжем его с таблицей Rashod.DB и откроем (Active = True). Разместим на форме компонент TQuickRep. Поместим в его свойство DataSet значение Table1, назначив таким образом отчету НД, записи которого будут выводиться в отчете. Добавим в отчет компонент TQRBand. В его свойство BandType компонента QRBand1 по умолчанию будет установлено значение rbTitle, то есть компонент QRBand1 определяет заголовок отчета Разместим на QRBand1 компонент TQRLabel. Установим в свойство Caption этого компонента значение Отпуск товаров со склада и выберем в свойстве Font жирный наклонный шрифт высотой 16 пунктов. Вид формы отчета к этому моменту показан на рис.5.

Рис. 5. В отчете определен только его заголовок.

Теперь разместим в отчете данные, соответствующие текущей записи таблицы Rashod. Для этого поместим в отчет новый компонент TQRBand (имя QRBand2) и установим в его свойство BandType значение rbDetail. Затем разместим на полосе QRBand2 шесть компонентов TQRDBText. Свяжем эти компоненты с полями НД – N_RASH, TOVAR, KOLVO, DEN, MES, GOD. Для этого в свойство DataSet каждого компонента QRDBText установим значение Table1, а в свойство DataField – имя соответствующего поля. Вид отчета к этому моменту показан на рис.6.



Рис. 6. Отчет с заголовком и группой детальной информации.
Для просмотра получившегося отчета щелкнем по нему правой кнопкой мыши и из всплывающего меню выберем элемент Preview. Окно предварительного просмотра отчета показано на рис. 7.

Рис. 7. Содержимое отчета в окне предварительного просмотра.
Чтобы окно предварительного просмотра открывалось при активизации формы, создадим такой обработчик события OnActivate формы:
 
procedure TForm1.FormActivate(Sender: TObject);
begin
  QuickRep1.Preview;
end;
а чтобы после выхода из окна предварительного просмотра закрывалась бы форма, на которой расположен текст, используем такой обработчик события AfterPreview:
 
procedure TForm1.QuickRep1AfterPreview(Sender: TObject);
begin
  Form1.Close;
end;

Использование компонента TQREXPR


Из рис.7 видно, что в простейшем отчете выводится дата, составленная из трех полей – DEN, MES, GOD. Объединим значения из этих полей в одно значение, являющееся результатом вычисления выражения. Выражение в отчетах формируется при помощи компонента TQRExpr. Удалим из компонента QRBand2 компоненты QRDBText4, QRDBText5 и QRDBText6, связанные с полями DEN, MES, GOD. Вместо них разместим в отчете компонент TQRExpr (имя QRExpr1).

Выражения в TQRExpr формируются с помощью специального редактора, который вызывается в окне инспектора объектов кнопкой в поле данных свойства Expression этого компонента (рис.8).

Рис. 8. Окно редактора формул компонента TQRExpr.

В поле Enter expression можно ввести или отредактировать выражение, которое обычно состоит из имен полей НД, преобразующих функций и переменных, связанных операциями отношения. Имена полей НД добавляются в текущее положение курсора (поле Enter expression) с помощью вспомогательного окна, связанного с кнопкой Function, а переменные – с кнопкой Variable.

Нажмите кнопку Function, в левом окне выберите категорию Other (другие) и функцию STR в правом окне – эта функция преобразует числовое значение в строковое. Нажмите Continue, чтобы перейти к вводу параметров (рис.9). Надпись над строкой ввода окна Expression Wizard напоминает о том, что выбранная нами функция имеет один числовой параметр.

Рис. 9. Формирование части выражения.

Для его ввода нажмите кнопку справа от строки ввода – на экране вновь появится начальное окно редактора формул. Поскольку мы хотим преобразовать в строку номер дня, нажмите кнопку Database field и выберите поле DEN в списке полей таблицы Table1. Нажмите OK, чтобы завершить ввод параметра. В поле Enter expression будет сформирована часть формулы – STR(Table1.DEN). На панели Insert at cursor position нажмем кнопку «+» и вручную введем разделитель ‘.’ (рис.10).

Рис. 10. Создание части формулы выражения.

Продолжите формировать выражение так, чтобы в конце концов оно приобрело такой вид:


STR(Table1.DEN) + ‘.’ + STR(Table1.MES) + ‘.’ + STR(Table1.GOD)
(возможно проще ввести его вручную). Затем нажмите кнопку OK, чтобы закрыть окно редактора формул. С помощью Инспектора объектов установите в свойство AutoSize компонента QRExpr1 значение False, измените размеры компонента так, чтобы он мог отображать примерно 10 символов, и установите выравнивание вправо (свойство Alignment = taRightJustify). Запустите режим предварительного просмотра содержимого отчета (рис.11). Как видим, дата отпуска товара приобрела более привычный вид.

Рис. 11. Результат вычисления выражения появился в отчете.
Замечание.
Другим способом составления значения даты из трех полей могло бы быть создание вычисляемого поля (например, SumData) и определение алгоритма вычисления его значения в таком обработчике события OnCalcFields:
procedure TForm1.TableCalcFields(DataSet: TDataSet);
begin
  Table1SumData.Value := Table1DEN.AsString + ‘.’ +
  Table1MES.AsString + ‘.’ + Table1GOD.AsString;
end;
 
 

Использование TQRBand для представления заголовков столбцов


Компонент TQRBand, у которого в свойство BandType установлено значение rbColumnHeader, используется для размещения заголовков столбцов. Собственно заголовки столбцов формируются при помощи компонентов TQRLabel.

В рассмотренном в предыдущих разделах отчете разместим компонент TQRBand (имя QRBand3) и установим в свойства Caption этих компонентов соответственно значения №№, Товар, Количество, Дата. В свойствах Font компонентов выберем наклонный и подчеркнутый шрифт. Вызовем окно предварительного просмотра отчета – для каждой страницы отчета теперь будут выводиться названия столбцов (рис.12).

Рис.12. В отчете появились заголовки столбцов.



Использование TQRBand для показа заголовка и подвала страницы.


Компонент TQRBand, у которого в свойство BandType установлено значение rbPageHeader, используется для показа заголовка страницы, а если это свойство установлено в rbPageFooter, – для показа подвала страницы. Заголовок выводится в начале каждой страницы, а подвал – в ее конце. Информация в заголовке и подвале страницы может формироваться на основе статического текста (компоненты TQRLabel), значений полей (компоненты TQRDBText) и результатов вычислений выражений (компоненты TQRExpr).

Вернувшись к предыдущему примеру, разместим в отчете компонент TQRBand (имя QRBand4) и установим в его свойство BandType значение rbPageHeader. Не будем размещать в заголовке никакого текста, просто отчеркнем линию вверху страницы. Для этого установим в свойство компонента страницы Frame.DrawTop значение True, что обеспечивает вывод линии по верхнему краю области, занимаемой компонентом. Аналогичным образом определим в отчете компонент подвала страницы (имя QRBand5) и установим в его свойство Frame.DrawBottom значение True, что обеспечивает вывод линии по нижнему краю области, занимаемой компонентом.

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



Использование компонента TQRSysData


Компонент TQRSysData используется для показа вспомогательной и системной информации. Вид показываемой информации определяется свойством

property Data: TQRSysDataType;

 

Ниже указаны возможные значения этого свойства.

Значение

Что выводится

qrsColumnNo

Номер текущей колонки отчета (для одноколоночного отчета всегда 1).

qrsDate

Текущая дата.

qrsDateTime

Текущие дата и время.

qrsDetailCount

Количество записей в НД, а при использовании нескольких НД – количество записей в главном наборе. Для случая, когда НД представлен компонентом TQuery, эта возможность может быть недоступной, что связано с характером работы компонента TQuery, который возвращает столько записей, сколько необходимо для использования в текущий момент, а остальные предоставляет по мере надобности.

qrsDetailNo

Номер текущей записи в НД.

qrsPageNumber

Номер текущей страницы отчета.

qrsPageCount

Общее количество страниц отчета.

qrsReportTitle

Заголовок отчета.

qrsTime

Текущее время

Разместим в компоненте QRBand5 подвала отчета два компонента TQRSysData. В свойство Data первого из них установим значение qrsDate, второго – qrsPageNumber. В режиме предварительного просмотра увидим, что теперь в подвале страницы выводятся номер страницы и текущая дата (рис.13)

Рис. 13. Показ номера страницы и текущей даты в подвале страницы.



Группировки данных


Для группировок информации используется компонент TQRGroup. Его свойство Expression указывает некоторое выражение, которое используется для группировки, иными словами, в группу входят записи, удовлетворяющие условию этого выражения. При смене выражения происходит смена группы.

Для каждой группы выводятся ее заголовок и подвал. В качестве заголовка группы используется компонент TQRBand со значением свойства BandType, равным rbColumnHeader, а в качестве подвала – со значением rbGroupFooter. Свойство FooterBand компонента TQRGroup должно содержать ссылку на компонент подвала группы. В заголовке группы, как правило, выводится группирующее выражение, а в подвале группы – агрегированная информация: суммарные, средние и т.п. значения по группе в целом.

 

Пример.

Построим отчет о расходе товара со склада, в котором информация группируется по наименованию товара. Для этого определим набор данных отчета (компонент TTable, имя Table1). Установим у НД текущим индекс по полю TOVAR (в свойстве FieldIndexNames или IndexName). Разместим в отчете:

·        заголовок отчета – компонент TQRBand с именем QRBand1, свойство BandType=rbTitle;

·        заголовок столбцов – компонент TQRBand с именем QRBand2, свойство BandType=rbColumnHeader;

·        группу – компонент TQRGroup с именем QRGroup1;

·        область детальной информации – TQRBand с именем QRBand3, свойство BandType=rbDetail;

·        подвал группы – TQRBand с именем QRBand4, свойство BandType=rbGroupFooter;

В компоненте QRGroup1 установим:

·        в свойство FooterBand значение QRBand4;

·        в свойство Expression значение Table1.TOVAR, которое является формулой и строится в редакторе формул.

Поскольку свойство Expression не визуализирует значения выражения, необходимо разместить в группе компонент TQRExpr (имя QRExpr1) и определить значение его свойства Expression
так, чтобы оно содержало Table1.TOVAR.
В компоненте подвала группы QRBand4 будем подсчитывать сумму по полю KOLVO (сумму отпущенного конкретного товара). Для этого разместим в подвале группы компонент TQRExpr (имя QRExpr2) и определить значение его свойства Expression так, чтобы оно содержало формулу SUM(Table1.TOVAR).
В группе детальной информации разместим компоненты TQRDBText, связанные с полями Pokup и Kolvo. Заполним области отчета статическим текстом, как это показано на рис.14.


Рис. 14. Макет отчета с группировкой по товару.

Рис. 15. Отчет с группировкой по товару в окне предварительного прсмотра.

Множественная группировка данных


Часто внутри группы должны содержаться другие группы, например, по названию товара и внутри каждой группы – по покупателям. В этом случае внутри одной группы определяют другую посредством дополнительных компонентов TQRGroup.

Пусть требуется представить в отчете сведения о расходе товаров со склада группируя данные по товарам, а внутри группы – по покупателям. Установим текущий индекс по полям TOVAR, POKUP. Общий вид отчета на этапе разработки приводится на рис.17, а в окне предварительного просмотра – на рис.18.

Рис.17. Макет отчета с вложенными группами.

 

Рис. 18. Отчет с вложенными группами.



Построение отчета главный-детальный


Если необходимо построить отчет на основе более чем одной ТБД, можно поступить двумя способами:

1.     с помощью компонента TQuery произвести соединение данных из нескольких таблиц БД в один НД, после чего определить в отчете нужные группировки;

2.     создать в приложении по одному НД на каждую таблицу, соединить эти наборы между собой связью главный-детальный (используя свойства MasterSource, MasterFields набора данных) и применить в отчете компонент (или несколько компонентов) TQRSubDetail для вывода информации из детального НД (или группы детальных НД); для вывода информации из главного НД, как и в обычных отчетах, применяется компонент TQRBand, у которого в свойстве BandType установлено значение rbDetail.

Построение отчета для первого случая осуществляется аналогично тому, как это описано выше. Построение отчета для второго случая имеет некоторые отличительные особенности. Рассмотрим второй способ.

Компонент TQRSubDetail предназначен для показа в отчете информации из детального НД. Его свойство

 

Property DataSet: TDataSet;

указывает имя детального НД, информация из которого будет выводиться в пространстве компонента TQRSubDetail. В остальном использование этого компонента аналогично использованию компонента TQRBand, у которого в свойство BandType установлено значение rbDetail.

Пусть имеется таблица БД TOVARY.DB, содержащая помимо прочих поле TOVAR (название товара). Пусть также имеется таблица БД RASHOD.DB, содержащая сведения об отпуске материалов со склада. В ее состав входят поля N_RASH (уникальный номер события отпуска товара), DEN (номер дня), MES (номер месяца), GOD (номер года), TOVAR (наименование отпущенного товара), POKUP (наименование покупателя) и KOLVO (количество единиц отпущенного товара).

Таблицы TOVARY.DB и RASHOD.DB находятся в отношении один-ко-многим, то есть одному товару может соответствовать более одного факта отпуска товара со склада.

Разместим на форме компонент TTable (им TovaryTable), ассоциированный с ТБД TOVARY.DB, и связанный с ним компонент TDataSource
(имя DS_TovaryTable). Разместим также еще один компонент TTable (им RashodTable), ассоциированный с ТБД RASHOD.DB, и установим между НД связь главный-детальный. Для этого установим в свойство RashodTable.MasterSource значение DS_TovaryTable, а в свойство RashodTable.MasterFields значение TOVAR (рис.19).


Рис. 19. Установка связи главный-детальный.
Заметим, что после установления связей НД и НД RashodTable текущим индексом должен быть индекс по полю Tovar (свойство RashodTable.IndexFieldNames).
Приступим к разработке отчета. Определим заголовок отчета – компонент TQRBand с именем QRBand1, в свойство BandType которого установлено значение rbTitle. Установим в качестве основного НД отчета TovaryTable, указав QuickRep1.DataSet = TovaryTable. Разместим в отчете компонент TQRBand с именем QRBand2 и установим в его свойство BandType значение rbDetail. Этот компонент будет использоваться для отображения детальной информации из НД TovaryTable.
Разместим в отчете компонент TQRSubTetail (имя QRSubDetail). Установим в его свойство DataSet значение RashodTable, связав таким образом данный компонент с подчиненным НД. Разместим в области компонента QRSubDetail три компонента TQRDBText и свяжем их соответственно с полями Pokup, Kolvo и D НД RashodTable (поле D определено в НД RashodTable  как вычисляемое по значениям полей DEN, MES, GOD). Разместим в области компонента QRBand2 заголовки столбцов.
Вид формы отчета показан на рис.20.

Рис. 20. Макет отчета, в котором показываются записи из связанных наборов данных.
В результирующем отчете (рис.21) для каждой записи НД TovaryTable выводятся подчиненные ей записи из НД RashodTable.

Рис. 21. Отчет, в котором показываются записи из связанных наборов данных.
Замечание.
Если необходимо определить заголовок и подвал для информации, группируемой в компоненте TQRSubDetail, следует воспользоваться свойством
property Bands: TQRSubDetailGroupBands;
 
этого компонента, которое имеет два логических подсвойства (HasHeader и HasFooter), указывающих на наличие или отсутствие соответственно заголовка и подвала.

Построение композитного отчета


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

Композитный отчет реализуется при помощи компонента TQRCompositeReport. В его обработчике события OnAddReport ранее определенные простые отчеты добавляются в списковое свойство Report. Например, так:

property TCompositnyjOtchet.QRCompositeReport1AddReports(Sender: TObject);

begin

  with QRCompositeReport1 do

  begin

    Reports.Add(ManyGroup.QuickRep1);

    Reports.Add(Prostoj.QuickRep1);

  end

end;

 

В этом примере композитный отчет составляется из двух отчетов: QuickRep1 (определенный в форме ManyGroup) и QuickRep1 (определенный в форме Prostoj). Почать композитного отчета или его предварительный просмотр осуществляется так же, как для простых отчетов, например

QRCompositeReport1.Preview;

На рис.22 показан композитный отчет, построенный из двух ранее разработанных нами отчетов – простейшего отчета и отчета с группировками данных.

Рис. 22. Композитный отчет, составленный из двух простых отчетов.

 Урок 2: Создание таблиц с помощью Database Desktop

Содержание урока 2:

Обзор........................................................................................................................................... 2

Утилита Database Desktop...................................................................................................... 2

Заключение................................................................................................................................. 9




 Агрегатные функции


К агрегирующим функциям относятся функции вычисления суммы (SUM), максимального (SUM) и минимального (MIN) значений столбцов, арифметического среднего (AVG), а также количества строк, удовлетворяющих заданному условию (COUNT).

SELECT count(*), sum (budget), avg (budget),

       min (budget), max (budget)

FROM department

WHERE head_dept = 100  вычислить: количество отделов,
                                                                являющихся подразделениями
                                                                отдела 100 (Маркетинг и продажи),
                                                                их суммарный, средний, мини-    мальный и максимальный бюджеты

 COUNT         SUM        AVG        MIN         MAX

====== =========== ========== ========== ===========

     5  3800000.00  760000.00  500000.00  1500000.00



 Алиасы


Таблицы сохраняются в базе данных. Некоторые СУБД сохраняют базу данных в виде нескольких отдельных файлов, представляющих собой таблицы (в основном, все локальные СУБД), в то время как другие состоят из одного файла, который содержит в себе все таблицы и индексы (InterBase). Например, таблицы dBase и Paradox всегда сохраняются в отдельных файлах на диске. Каталог, содержащий dBase .DBF файлы или Paradox .DB файлы, рассматривается как база данных. Другими словами, любой каталог, содержащий файлы в формате Paradox или dBase, рассматривается Delphi как единая база данных. Для переключения на другую базу данных нужно просто переключиться на другой каталог. Как уже было указано выше, InterBase сохраняет все таблицы в одном файле, имеющем расширение .GDB, поэтому этот файл и есть база данных InterBase.

Удобно не просто указывать путь доступа к таблицам базы данных, а использовать для этого некий заменитель - псевдоним, называемый алиасом. Он сохраняется в отдельном конфигурационном файле в произвольном месте на диске и позволяет исключить из программы прямое указание пути доступа к базе данных. Такой подход дает возможность располагать данные в любом месте, не перекомпилируя при этом программу. Кроме пути доступа, в алиасе указываются тип базы данных, языковый драйвер и много другой управляющей информации. Поэтому использование алиасов позволяет легко переходить от локальных баз данных к SQL-серверным базам (естественно, при выполнении требований разделения приложения на клиентскую и серверную части).

Для создания алиаса запустите утилиту конфигурации BDE (программу bdeadmin.exe), находящуюся в каталоге, в котором располагаются динамические библиотеки BDE.

Рис. 1: Главное окно утилиты конфигурации BDE


Главное окно утилиты настройки BDE имеет вид, изображенный на рис.1. Для создания алиаса выберите в меню “Object” пункт “New”. В появившемся диалоговом окне выберите имя драйвера базы данных. Тип алиаса может быть стандартным (STANDARD) для работы с локальными базами в формате dBase или Paradox или соответствовать наименованию SQL-сервера (InterBase, Sybase, Informix, Oracle и т.д.).

Рис. 2: В диалоговом окне добавления нового алиаса можно указать тип базы данных


После создания нового алиаса следует дать ему имя. Это можно сделать с помощью подпункта “Rename” меню “Object”. Однако просто создать алиас не достаточно. Вам нужно указать дополнительную информацию, содержание которой зависит от типа выбранной базы данных. Например, для баз данных Paradox и dBase (STANDARD) требуется указать лишь путь доступа к данным, имя драйвера и флаг ENABLE BCD, который определяет, транслирует ли BDE числа в двоично-десятичном формате (значения двоично-десятичного кода устраняют ошибки округления):

TYPE

STANDARD

DEFAULT DRIVER

PARADOX

ENABLE BCD

FALSE

PATH

c:\users\data

SQL-сервер InterBase и другие типы баз данных требуют задания большого количества параметров, многие из которых можно оставить установленными по умолчанию.


 BETWEEN


Предикат BETWEEN задает диапазон значений, для которого выражение принимает значение true. Разрешено также использовать конструкцию  NOT  BETWEEN.

SELECT first_name, last_name, salary

FROM employee

WHERE salary BETWEEN 20000 AND 30000
                          получить список сотрудников,
                                                                       годовая зарплата которых
                                                                       больше 20000 и меньше 30000

FIRST_NAME      LAST_NAME           SALARY

=============== ========== ===============

Ann             Bennet            22935.00

Kelly           Brown             27000.00

Тот же запрос с использованием операторов сравнения будет выглядеть следующим образом:

SELECT first_name, last_name, salary

FROM employee

WHERE salary >= 20000

  AND salary <= 30000     получить список сотрудников,
                                                                       годовая зарплата которых
                                                                       больше 20000 и меньше 30000

FIRST_NAME      LAST_NAME           SALARY

=============== ========== ===============

Ann             Bennet            22935.00

Kelly           Brown             27000.00

Запрос с предикатом BETWEEN может иметь следующий вид:

SELECT first_name, last_name, salary

FROM employee

WHERE last_name BETWEEN "Nelson" AND "Osborne"
                           получить список сотрудников,
                                                                         фамилии которых начинаются
                                                                         с “Nelson”
                                                                         и заканчиваются “Osborne”

FIRST_NAME      LAST_NAME                 SALARY

=============== =============== ================

Robert          Nelson                 105900.00

Carol           Nordstrom               42742.50


Sue Anne        O'Brien                 31275.00

Pierre          Osborne                110000.00

Значения, определяющие нижний и верхний диапазоны, могут не являться реальными величинами из базы данных. И это очень удобно - ведь мы не всегда можем указать точные значения диапазонов!

SELECT first_name, last_name, salary

FROM employee

WHERE last_name BETWEEN "Nel" AND "Osb"
                           получить список сотрудников,
                                                                         фамилии которых находятся
                                                                         между  “Nel” и “Osb”

FIRST_NAME      LAST_NAME                 SALARY

=============== =============== ================

Robert          Nelson                 105900.00

Carol           Nordstrom               42742.50

Sue Anne        O'Brien                 31275.00

В данном примере значений “Nel” и “Osb” в базе данных нет. Однако, все сотрудники, входящие в диапазон, в нижней части которого начало фамилий совпадает с “Nel” (т.е. выполняется условие “больше или равно”), а в верхней части фамилия не более “Osb” (т.е. выполняется условие “меньше или равно” - а именно “O”, “Os”, “Osb”), попадут в выборку. Отметим, что при выборке с использованием предиката BETWEEN поле, на которое накладывается диапазон, считается упорядоченным по возрастанию.

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

SELECT first_name, last_name, hire_date

FROM employee

WHERE hire_date NOT BETWEEN "1-JAN-1989" AND "31-DEC-1993"                          получить список самых “старых”
                                                                       и самых “молодых” (по времени
                                                                       поступления на работу)
                                                                       сотрудников

FIRST_NAME      LAST_NAME          HIRE_DATE

=============== ================ ===========

Robert          Nelson           28-DEC-1988

Bruce           Young            28-DEC-1988

Pierre          Osborne           3-JAN-1994

John            Montgomery       30-MAR-1994

Mark            Guckenheimer      2-MAY-1994


 Блобы


Получение информации о BLOb выглядит совершенно аналогично обычным полям. Полученные значения можно отображать с использованием data-aware компонент Delphi, например,  TDBMemo или TDBGrid. Однако, в последнем случае придется самому прорисовывать содержимое блоба (например, через OnDrawDataCell). Подробнее об этом см. на уроке,  посвященном работе с полями.

SELECT job_requirement
FROM job                  получить список
                                                                       должностных требований
                                                                       к кандидатам на работу

JOB_REQUIREMENT: 

No specific requirements.

JOB_REQUIREMENT: 

15+ years in finance or 5+ years as a CFO

with a proven track record.

MBA or J.D. degree.

...



 CONTAINING


Предикат CONTAINING аналогичен предикату LIKE, за исключением того, что он не чувствителен к регистру букв. Разрешено также использовать конструкцию  NOT  CONTAINING.

SELECT first_name, last_name

FROM employee

WHERE last_name CONTAINING "ne"
                        получить список сотрудников,
                                                                  фамилии которых содержат буквы
                                                                  “ne”, “Ne”, “NE”, “nE”

FIRST_NAME      LAST_NAME           

=============== ====================

Robert          Nelson              

Ann             Bennet              

Pierre          Osborne



 IN


Предикат IN проверяет, входит ли заданное значение, предшествующее ключевому слову “IN” (например, значение столбца или функция от него) в указанный в скобках список. Если заданное проверяемое значение равно какому-либо элементу в списке, то предикат принимает значение true. Разрешено также использовать конструкцию  NOT  IN.

SELECT first_name, last_name, job_code

FROM employee

WHERE job_code IN ("VP", "Admin", "Finan")
                        получить список сотрудников,
                                                                  занимающих должности
                                                                  “вице-президент”, “администратор”,
                                                                  “финансовый директор”

FIRST_NAME      LAST_NAME        JOB_CODE

=============== ================ ========

Robert          Nelson           VP      

Terri           Lee              Admin   

Stewart         Hall             Finan   

Ann             Bennet           Admin   

Sue Anne        O'Brien          Admin   

Mary S.         MacDonald        VP      

Kelly           Brown            Admin   

А вот пример запроса, использующего предикат  NOT  IN:

SELECT first_name, last_name, job_country

FROM employee

WHERE job_country NOT IN

      ("USA", "Japan", "England")
                       получить список сотрудников,
                                                                работающих не в США, не в Японии
                                                                и не в Великобритании

FIRST_NAME      LAST_NAME        JOB_COUNTRY    

=============== ================ ===============

Claudia         Sutherland       Canada         

Roberto         Ferrari          Italy          

Jacques         Glon             France         

Pierre          Osborne          Switzerland



 IS NULL


В SQL-запросах NULL означает, что значение столбца неизвестно. Поисковые условия, в которых значение столбца сравнивается с NULL, всегда принимают значение unknown (и, соответственно, приводят к ошибке), в противоположность true или false, т.е.

WHERE dept_no = NULL

или даже

WHERE NULL = NULL.

Предикат  IS NULL  принимает значение true только тогда, когда выражение слева от ключевых слов “IS NULL” имеет значение null (пусто, не определено). Разрешено также использовать конструкцию  IS NOT NULL, которая означает “не пусто”, “имеет какое-либо значение”.

SELECT department, mngr_no

FROM department

WHERE mngr_no IS NULL      получить список отделов,
                                                                         в которых еще не назначены
                                                                         начальники

DEPARTMENT                MNGR_NO

========================= =======

Marketing                  <null>

Software Products Div.     <null>

Software Development       <null>

Field Office: Singapore    <null>

Предикаты EXIST, ANY, ALL, SOME, SINGULAR мы рассмотрим в разделе, рассказывающем о подзапросах.



 Использование фильтров для ограничения числа записей в DataSet


          Процедура ApplyRange позволяет Вам установить фильтр, который ограничивает диапазон просматриваемых записей. Например, в БД Customers, поле CustNo имеет диапазон от 1,000 до 10,000. Если Вы хотите видеть только те записи, которые имеют номер заказчика между 2000 и 3000, то Вы должны использовать метод ApplyRange, и еще два связанных с ним метода. Данные методы работают только с индексированным полем.

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

procedure SetRangeStart;

procedure SetRangeEnd;

procedure ApplyRange;

procedure CancelRange;

Кроме того, у TTable есть дополнительные методы для управления фильтрами:

procedure EditRangeStart;

procedure EditRangeEnd;

procedure SetRange;

Для использования этих процедур необходимо:

1.  Сначала вызвать SetRangeStart и использовать свойство Fields для определения начала диапазона.

2.  Затем вызвать SetRangeEnd и вновь использовать свойство Fields для определения конца диапазона.

3.  Первые два шага подготавливают фильтр, и теперь все что Вам необходимо, это вызвать ApplyRange, и новый фильтр вступит в силу.

4.  Когда нужно прекратить действие фильтра - вызовите CancelRange.

          Программа RANGE, которая есть среди примеров Delphi, показывает, как использовать эти процедуры. Чтобы создать программу, поместите TTable, TDataSource и TdbGrid на форму. Соедините их так, чтобы Вы видеть таблицу CUSTOMERS из подкаталога DEMOS. Затем поместите два объекта TLabel на форму и назовите их ‘Start Range’ и ‘End Range’. Затем положите на форму два объекта TEdit. Наконец,  добавьте кнопки ‘ApplyRange’ и ‘CancelRange’. Когда Вы все выполните, форма имеет вид, как на рис.7

Рис.7: Программа RANGE показывает как ограничивать число записей таблицы для просмотра.

          Процедуры SetRangeStart и SetRangeEnd позволяют Вам указать первое и последнее значения в диапазоне записей, которые Вы хотите видеть. Чтобы начать использовать эти процедуры, сначала выполните double-click на кнопке ApplyRange, и создайте процедуру, которая выглядит так:


procedure TForm1.ApplyRangeBtnClick(Sender: TObject);

begin

  Table1.SetRangeStart;

  if RangeStart.Text <> '' then

    Table1. Fields[0].AsString := RangeStart.Text;

  Table1.SetRangeEnd;

  if RangeEnd.Text <> '' then

    Table1.Fields[0].AsString := RangeEnd.Text;

 Table1.ApplyRange;

end;

Сначала вызывается процедура SetRangeStart, которая переводит таблицу в режим диапазона (range mode). Затем Вы должны определить начало и конец диапазона. Обратите внимание, что Вы используете свойство Fields для определения диапазона:

Table1.Fields[0].AsString := RangeStart.Text;

          Такое использование свойства Fields - это специальный случай, так как синтаксис, показанный здесь, обычно используется для установки значения поля. Этот специальный случай имеет место только после того, как Вы перевели таблицу в режим диапазона, вызвав SetRangeStart.

          Заключительный шаг в процедуре показанной выше - вызов ApplyRange. Этот вызов фактически приводит ваш запрос в действие. После вызова ApplyRange, TTable больше не в находится в режиме диапазона, и свойства Fields функционирует как обычно.

          Обработчик события нажатия кнопки ‘CancelRange’:

procedure TForm1.CancelRangeBtnClick(Sender: TObject);

begin

  Table1.CancelRange;

end;


 Использование квалификатора AS


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

SELECT count(*) AS number

FROM employee            подсчитать количество служащих

     NUMBER

===========

         42

SELECT "сотрудник " || first_name || " " ||        last_name AS employee_list

FROM employee           получить список всех сотрудников

EMPLOYEE_LIST                                 

==============================================

сотрудник Robert Nelson                        

сотрудник Bruce Young                         

сотрудник Kim Lambert                         

сотрудник Leslie Johnson                      

сотрудник Phil Forest                         

сотрудник K. J. Weston                        

сотрудник Terri Lee                           

сотрудник Stewart Hall

...



 Использование SetKey для поиска в таблице


Для того, чтобы найти некоторую величину в таблице, программист на Delphi может использовать две процедуры SetKey и GotoKey. Обе эти процедуры предполагают, что поле по которому Вы ищете индексировано. Delphi поставляется с демонстрационной программой SEARCH, которая показывает, как использовать эти запросы.

          Чтобы создать программу SEARCH, поместите TTable, TDataSource, TDBGrid, TButton, TLabel и TEdit на форму, и расположите их как показано на рис.6. Назовите кнопку Search, и затем соедините компоненты БД так, чтобы Вы видели в DBGrid1 таблицу Customer.

Рис.6: Программа SEARCH позволяет Вам ввести номер заказчика и затем найти его по нажатию кнопки.

Вся функциональность программы SEARCH скрыта в единственном методе, который присоединен к кнопке Search. Эта функция считывает строку, введенную в окно редактора, и ищет ее в колонке CustNo, и наконец помещает фокус на найденной записи. В простейшем варианте,  код присоединенный к кнопке Search выглядит так:

procedure TSearchDemo.SearchClick(Sender: TObject);

begin

  Table1.SetKey;

  Table1.FieldByName(’CustNo’).AsString := Edit1.Text;

  Table1.GotoKey;

end;

          Первый вызов в этой процедуре установит Table1 в режим поиска. Delphi должен знать, что Вы переключились в режим поиска просто потому, что свойство Fields используется по другому в этом режиме. Далее, нужно присвоить свойству Fields значение, которое Вы хотите найти. Для фактического выполнения поиска нужно просто вызывать Table1.GotoKey.

          Если Вы ищете не по первичному индексу файла, тогда Вы должны определить имя индекса, который Вы используете в свойстве IndexName. Например, если таблица Customer имеет вторичный индекс по полю City, тогда Вы должны установить свойство IndexName равным имени индекса. Когда Вы будете искать по этому полю, Вы должны написать:

Table1.IndexName := ’CityIndex’;

Table1.Active := True;

Table1.SetKey;

Table1.FieldByName(’City’).AsString := Edit1.Text;


Table1.GotoKey;

Запомните: поиск не будет выполняться, если Вы не назначите правильно индекс (св-во IndexName). Кроме того, Вы должны обратить внимание, что IndexName - это свойство TTable, и не присутствует в других прямых потомках TDataSet или TDBDataSet.

          Когда Вы ищете некоторое значение в БД,  всегда существует вероятность того, что поиск окажется неудачным. В таком случае Delphi будет автоматически вызывать exception, но если Вы хотите обработать  ошибку сами, то могли бы написать примерно такой код:

procedure TSearchDemo.SearchClick(Sender: TObject);

begin

  Cust.SetKey;

  Cust.FieldByName('CustNo').AsString:= CustNoEdit.Text; 

  if not Cust.GotoKey then

    raise Exception.CreateFmt('Cannot find CustNo %g',

[CustNo]);

end;

          В коде, показанном выше, либо неверное присвоение номера, либо неудача поиска автоматически приведут к сообщению об ошибке ‘Cannot find CustNo %g’.

          Иногда требуется найти не точно совпадающее значение, а близкое к нему, для этого следует вместо GotoKey пользоваться методом GotoNearest.


 Использование TDataSource для проверки состояния БД:


          TDataSource имеет три ключевых события, связанных с состоянием БД

OnDataChange

OnStateChange

OnUpdateData

OnDataChange происходит всякий раз, когда Вы  переходите на новую запись, или состояние DataSet сменилось с dsInactive на другое, или начато редактирование. Другими словами, если Вы вызываете Next, Previous, Insert, или любой другой запрос, который должен привести к изменению данных, связанных с текущей записью, то произойдет событие OnDataChange. Если в программе нужно определить момент, когда происходит переход на другую запись, то это можно сделать в обработчике события OnDataChange:

procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);

begin

   if DataSource1.DataSet.State = dsBrowse then begin

     DoSomething; 

   end;

end;

          Событие OnStateChange событие происходит всякий раз, когда изменяется текущее состояние DataSet. DataSet всегда знает, в каком состоянии он находится. Если Вы вызываете Edit, Append или Insert, то TTable знает, что он теперь находится в режиме редактирования (dsEdit или dsInsert). Аналогично, после того, как Вы делаете Post, то TTable знает что данные больше не редактируется, и переключается обратно в режим просмотра (dsBrowse).

          Dataset имеет шесть различных возможных состояний, каждое из которых включено в следующем перечисляемом типе:

TDataSetState = (dsInactive, dsBrowse, dsEdit, dsInsert,

                 dsSetKey, dsCalcFields);

В течение обычного сеанса работы, БД часто меняет свое состояние между Browse, Edit, Insert и другими режимами. Если Вы хотите отслеживать эти изменения, то Вы можете реагировать на них написав примерно такой код:

procedure TForm1.DataSource1StateChange(Sender: TObject);

var

  S: String;

begin

  case Table1.State of

    dsInactive: S := 'Inactive';

    dsBrowse: S := 'Browse';

    dsEdit: S := 'Edit';

    dsInsert: S := 'Insert';

    dsSetKey: S := 'SetKey';

    dsCalcFields: S := 'CalcFields';


  end;

  Label1.Caption := S;

end;

          OnUpdateData событие происходит перед тем, как данные в текущей записи будут обновлены. Например, OnUpdateEvent будет происходить между вызовом Post и фактическим обновлением информации на диске.

          События, генерируемые TDataSource могут быть очень полезны. Иллюстрацией этого служит следующий пример. Эта программа работает с таблицей COUNTRY, и включает TTable, TDataSource, пять TEdit, шесть TLlabel, восемь кнопок и панель. Действительное расположение элементов показано на рис.11.  Обратите внимание, что шестой TLabel расположен на панели внизу главной формы.



Рис.11: Программа STATE показывает, как отслеживать текущее состояние таблицы.

Для всех кнопок напишите обработчики, вроде:

procedure TForm1.FirstClick(Sender: TObject);

begin

  Table1.First;

end;

          В данной программе есть одна маленькая хитрость, которую Вы должны понять, если хотите узнать, как работает программа. Так как есть пять отдельных редакторов TEdit на главной форме, то хотелось бы иметь некоторый способ обращаться к ним быстро и легко. Один простой способ состоит в том, чтобы объявить массив редакторов:

Edits: array[1..5] of TEdit;

          Чтобы  заполнить массив, Вы можете в событии OnCreate главной формы написать:

procedure TForm1.FormCreate(Sender: TObject);

var

  i: Integer;

begin

  for i := 1 to 5 do

    Edits[i] := TEdit(FindComponent('Edit' + IntToStr(i)));

  Table1.Open;

end;

Код показанный здесь предполагает, что первый редактор, который Вы будете использовать назовем Edit1, второй Edit2, и т.д. Существование этого массива позволяет очень просто использовать событие OnDataChange, чтобы синхронизировать содержание объектов TEdit с содержимом текущей записи в DataSet:

procedure TForm1.DataSource1DataChange(Sender: TObject;

                                       Field: TField);

var

  i: Integer;

begin

  for i := 1 to 5 do



    Edits[i].Text := Table1.Fields[i - 1].AsString;

end;

Всякий раз, когда вызывается Table1.Next, или любой другой из навигационных методов, то будет вызвана процедура показанная выше. Это обеспечивает то, что все редакторы всегда содержат данные из текущей записи.

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

procedure TForm1.DataSource1UpdateData(Sender: TObject);

var

  i: Integer;

begin

  for i := 1 to 5 do

    Table1.Fields[i - 1].AsString := Edits[i].Text;

end;

          Программа будет автоматически переключатся в режим редактирования каждый раз, когда Вы вводите что-либо в одном из редакторов. Это делается в обработчике события OnKeyDown (укажите этот обработчик ко всем редакторам):

procedure TForm1.Edit1KeyDown(Sender: TObject;

  var Key: Word; Shift: TShiftState);

begin

  if DataSource1.State <> dsEdit then

    Table1.Edit;

end;

Этот код  показывает, как Вы можете использовать св-во State DataSource, чтобы определить текущий режим DataSet.

Обновление метки в статусной панели происходит при изменении состояния таблицы:

procedure TForm1.DataSource1StateChange(Sender: TObject);

var

  s : String;

begin

  case DataSource1.State of

    dsInactive : s:='Inactive';

    dsBrowse   : s:='Browse';

    dsEdit     : s:='Edit';

    dsInsert   : s:='Insert';

    dsSetKey   : s:='SetKey';

    dsCalcFields : s:='CalcFields';

  end;

  Label6.Caption:=s;

end;

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


 Изменение порядка выводимых строк (ORDER BY)


Порядок выводимых строк может быть изменен с помощью опционального (дополнительного) предложения ORDER BY в конце SQL-запроса. Это предложение имеет вид:

ORDER BY <порядок строк> [ASC | DESC]

Порядок строк может задаваться одним из двух способов:

·     именами столбцов

·     номерами столбцов.

Способ упорядочивания определяется дополнительными зарезервированными словами ASC и DESC. Способом по умолчанию - если ничего не указано - является упорядочивание “по возрастанию” (ASC). Если же указано слово “DESC”, то упорядочивание будет производиться “по убыванию”.

Подчеркнем еще раз, что предложение ORDER BY должно указываться в самом конце запроса.



 Класс TDataBase


Объект типа TDataBase не является обязательным при работе с базами данных, однако он предоставляет ряд дополнительных возможностей по управлению соединением с базой данных. TDataBase служит для:

·     Создания постоянного соединения с базой данных

·     Определения собственного диалога при соединении с базой данных (опрос пароля)

·     Создания локального псевдонима базы данных

·     Изменения параметров при соединении

·     Управления транзакциями

TDataBase является невидимым во время выполнения объектом. Он находится на странице “Data Access” Палитры Компонент. Для включения в проект TDataBase нужно “положить” его на главное окно вашей программы.

Создание постоянного соединения с базой данных

         

Если вы работаете с базой данных, то перед началом работы выполняется процедура соединения с этой базой. В процедуру соединения, кроме прочего, входит опрос имени и пароля пользователя (кроме случая работы с локальными таблицами Paradox и dBase через IDAPI). Если в программе не используется TDataBase, то процедура соединения выполняется при открытии первой таблицы из базы данных. Соединение с базой данных обрывается, когда в программе закрывается последняя таблицы из этой базы (это происходит в том случае, если свойство KeepConnections объекта Session установлено в False, но об этом чуть позже). Теперь, если снова открыть таблицу, то процедура установки соединения повторится и это может быть достаточно неудобно для пользователя. Чтобы соединение не обрывалось даже в том случае, когда нет открытых таблиц данной базы, можно использовать компонент типа TDataBase. В свойстве AliasName укажите псевдоним базы данных, с которой работает программа; в свойстве DatabaseName - любое имя (псевдоним БД), на которое будут ссылаться таблицы вместо старого псевдонима базы. Свойство Connected установите в True - процедура соединения с базой будет выполняться при запуске программы. И, наконец, свойство KeepConnection нужно установить в True (см. рис.1).




Рис.A: Свойства TDataBase в Инспекторе объектов

В нашем примере, после задания свойств DataBase1 нужно у всех таблиц, работающих с IBLOCAL в свойстве DatabaseName поставить Loc_IBLOCAL.

Определение собственного диалога при соединении с базой данных

         

          По умолчанию при соединении с базой данных используется диалог опроса имени и пароля пользователя, показанный на рис.2



Рис.B: Диалог авторизации пользователя

При желании можно изменить внешний вид диалога или вообще его отменить. Для этого используются свойства и события класса TDataBase - LoginPrompt, Params и OnLogin.

          Чтобы отключить опрос имени и пароля установите свойство LoginPrompt в False. При этом в свойстве Params требуется в явном виде (во время дизайна либо во время выполнения) указать имя и пароль пользователя. Например, в программе можно написать (до момента соединения с базой, например в событии для Form1 OnCreate):

DataBase1.LoginPrompt:=False;

DataBase1.Params.Clear;

DataBase1.Params.Add(‘USER NAME=SYSDBA’);

DataBase1.Params.Add(‘PASSWORD=masterkey’);

DataBase1.Connected:=True;

          Чтобы использовать свой собственный диалог, в котором можно опрашивать не только имя и пароль пользователя, но и, например, сетевой протокол - создайте обработчик события OnLogin для DataBase1:

procedure TForm1.Database1Login(Database: TDatabase;

  LoginParams: TStrings);

begin

  Form2.ShowModal;

  if Form2.ModalResult = mrOK then

    with LoginParams do begin

      Values['USER NAME'] := User_Name;

      Values['PASSWORD'] := User_Pass;

    end;

end;

Здесь Form2 - новое окно-диалог для ввода имени и пароля, User_Name и User_Pass - строки, куда сохраняются введенные имя и пароль.

Создание локального псевдонима базы данных

          Обычно, псевдоним базы данных(Alias) определяется в утилите конфигурации BDE и информация о нем сохраняется в файле конфигурации IDAPI.CFG. Однако, в программе можно использовать не только ранее определенный в утилите конфигурации BDE псевдоним базы данных, но и так называемый локальный (т.е. видимый только внутри данной программы) псевдоним. Это иногда бывает нужно, например, для того, чтобы обезопасить программу в случае удаления используемого псевдонима из файла конфигурации BDE.



          Для того, чтобы создать локальный псевдоним БД, положите на главное окно проекта компонент DataBase1. Дальнейшие действия можно выполнить с помощью Инспектора Объектов, но удобнее это сделать через редактор компонент. Щелкните дважды мышкой на DataBase1 - появится диалог, показанный на рис.3



Рис.C: Редактор компоненты класса TDataBase

В этом диалоге требуется указать имя базы данных - это будет ее локальный псевдоним, на который ссылаются таблицы (свойство DatabaseName);  тип драйвера (в нашем примере это INTRBASE); а также параметры, используемые при соединении с базой данных. Получить список параметров в поле “Parameter Overrides” можно по нажатию кнопки “Defaults”. Набор параметров зависит от типа БД, с которой вы работаете. Этим параметрам нужно присвоить требуемые значения - указать путь к серверу, имя пользователя и т.д. После выхода из редактора компонент имя, указанное в поле “Name” появится в списке имен баз данных для компонент типа TDataSet (TTable, TQuery etc.).

Изменение параметров при соединении

          Иногда требуется изменить определенные в утилите конфигурации BDE параметры, используемые при установлении соединения с БД. Это можно сделать во время дизайна с помощью диалога, показанного на  рис.3, в  поле “Parameter Overrides”. Либо во время выполнения программы (до попытки соединения) прямым присвоением свойству Params объекта DataBase1:

DataBase1.Params.Add(‘LANGDRIVER=ancyrr’);

Управление транзакциями

          TDataBase  позволяет начать в БД транзакцию (метод StartTransaction), закончить (Commit) или откатить ее (RollBack).  Кроме того,  можно изменять уровень изоляции транзакций (свойство TransIsoltion).



TransIsolation                         Oracle                      Sybase and              Informix                  InterBase

                                                                                Microsoft SQL

 

Dirty read                                Read committed       Read committed       Dirty Read               Read committed

Read committed(Default)        Read committed       Read committed       Read committed       Read committed

Repeatable read                       Repeatable read       Read committed       Repeatable Read      Repeatable Read

                            

“Dirty Read” - внутри вашей текущей транзакции видны все изменения,  сделанные другими транзакциями, даже если они еще не завершились по Commit. “Read Committed” -  видны только “закоммитченные” изменения, внесенные в базу. “Repeatable Read” - внутри транзакции видны те данные, что были в базе на момент начала транзакции, даже если там на самом деле уже имеются изменения.


 Класс TDataSet


          TDataSet класс - один из наиболее важных объектов БД. Чтобы         начать работать с ним, Вы должны взглянуть на следующую иерархию:

TDataSet

       |

   TDBDataSet

       |

       |-- TTable

       |-- TQuery  

       |-- TStoredProc

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

Как Вы увидите в далее, TQuery имеет определенные методы для обработки SQL запросов.

          TDataSet - инструмент, который Вы будете использовать чтобы  открыть таблицу, и перемещаться по ней. Конечно, Вы никогда не будете непосредственно создавать объект типа TDataSet. Вместо этого, Вы будете использовать TTable, TQuery или других потомков TDataSet (например, TQBE). Полное понимание работы системы, и точное значение TDataSet, будут становиться все более ясными по мере прочтения этой главы.

На наиболее фундаментальном уровне, Dataset это просто набор записей, как изображено на рис.1

Рис.1: Любой dataset состоит из ряда записей (каждая содержит N полей) и указатель на текущую запись.

          В большинстве случаев dataset будет иметь a прямое, один к одному, соответствие с физической таблицей, которая существует на диске. Однако, в других случаях Вы можете исполнять запрос или другое действие, возвращающие dataset, который содержит либо любое подмножество записей одной таблицы, либо объединение (join) между несколькими таблицами. В тексте будут иногда использоваться термины DataSet и TTable как синонимы.

          Обычно в программе используются объекты типа TTable или TQuery, поэтому в следующих нескольких главах будет предполагаться существование объекта типа TTable называемого Table1.

          Итак, самое время начать исследование TDataSet. Как только Вы познакомитесь с его возможностями, Вы начнете понимать, какие методы использует Delphi для доступа к данным, хранящимся на диске в виде БД. Ключевой момент здесь - не забывать, что почти всякий раз, когда программист на Delphi открывает таблицу, он будет использовать TTable или TQuery, которые являются просто некоторой надстройкой над TDataSet.



 Конкатенация


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

SELECT "сотрудник " || first_name || " " ||        last_name

FROM employee           получить список всех сотрудников

                                              

==============================================

сотрудник Robert Nelson                       

сотрудник Bruce Young                         

сотрудник Kim Lambert                          

сотрудник Leslie Johnson                      

сотрудник Phil Forest                         

сотрудник K. J. Weston                        

сотрудник Terri Lee                           

сотрудник Stewart Hall

...



 Краткий Обзор


        В этой главе Вы узнаете некоторые основные понятия о запросах (queries) и транзакциях. Это достаточно широкие понятия, поэтому обсуждение разбито на следующие основные части:

·     Объект TQuery.

·     Использование SQL с локальным и удаленным серверами (Select, Update, Delete и Insert).

·     Использование SQL для создания объединения (joins), связанных курсоров (linked cursors) и программ, которые ведут поиск заданных записей.

          Сокращение SQL означает Structured Query Language - Язык Структурированных Запросов, и обычно произноситься либо как "Sequel" либо  " Ess Qu El”. Однако, как бы Вы его ни произносили, SQL - это мощный язык БД, который легко доступен из Delphi, но который отличается от родного языка Delphi. Delphi может использовать утверждения SQL для просмотра  таблиц, выполнять объединение таблиц, создавать отношения один-ко-многим, или исполнить почти любое действие, которое могут сделать ваши основные инструменты БД. Delphi поставляется с Local SQL, так что Вы можете выполнять запросы SQL при работе с локальными таблицами, без доступа к SQL серверу.

          Delphi обеспечивает поддержку “pass through SQL”, это означает то, что Вы можете составлять предложения SQL  и посылать их непосредственно серверам Oracle, Sybase, Inrterbase и другим. “Pass through SQL” - это мощный механизм по двум причинам:

1.     Большинство серверов могут обрабатывать SQL запросы очень быстро, а это означает, что используя SQL для удаленных данных, Вы получите ответ очень быстро.

2.     Есть возможность составлять SQL запросы, которые заставят сервер исполнить специализированные задачи, недоступные через родной язык Delphi.

          Перед чтением этой статьи Вы должны иметь, по крайней мере, элементарное понятие о серверах и различиях между локальными и удаленными (remote) данными.



 LIKE


Предикат LIKE используется только с символьными данными. Он проверяет, соответствует ли данное символьное значение строке с указанной маской. В качестве маски используются все разрешенные символы (с учетом верхнего и нижнего регистров), а также специальные символы:

% - замещает любое количество символов (в том числе и 0),

_  - замещает только один символ.

Разрешено также использовать конструкцию  NOT  LIKE.

SELECT first_name, last_name

FROM employee

WHERE last_name LIKE "F%"    получить список сотрудников,
                                                                         фамилии которых начинаются       с буквы “F”

FIRST_NAME      LAST_NAME           

=============== ====================

Phil            Forest              

Pete            Fisher              

Roberto         Ferrari

SELECT first_name, last_name

FROM employee

WHERE first_name LIKE "%er" получить список сотрудников,
                                                                            имена которых       заканчиваются буквами “er”                          

FIRST_NAME      LAST_NAME           

=============== ====================

Roger           De Souza             

Roger           Reeves              

Walter          Steadman

А такой запрос позволяет решить проблему произношения (и написания) имени:

SELECT first_name, last_name

FROM employee

WHERE first_name LIKE "Jacq_es"
                           найти сотрудника(ов),
                                                                            в имени  которого
                                                                            неизвестно произношение
                                                                            буквы перед окончанием “es”

FIRST_NAME      LAST_NAME           

=============== ====================

Jacques         Glon

Что делать, если требуется найти строку, которая содержит указанные выше специальные символы (“%”, “_”) в качестве информационных символов? Есть выход! Для этого с помощью ключевого слова ESCAPE нужно определить так называемый escape?символ, который, будучи поставленным перед символом “%” или “_”, укажет, что этот символ является информационным. Escape?символ не может быть символом “\” (обратная косая черта) и, вообще говоря, должен представлять собой символ, никогда не появляющийся в упоминаемом столбце как информационный символ. Часто для этих целей используются символы “@” и “~”.

SELECT first_name, last_name

FROM employee

WHERE first_name LIKE "%@_%" ESCAPE "@"
                          получить список сотрудников,
                                                                       в имени которых содержится “_”
                                                                       (знак подчеркивания)



 Литералы


Для придания большей наглядности получаемому результату можно использовать литералы. Литералы - это строковые константы, которые применяются наряду с наименованиями столбцов и, таким образом, выступают в роли “псевдостолбцов”. Строка символов, представляющая собой литерал, должна быть заключена в одинарные или двойные скобки.

SELECT first_name, "получает", salary,         "долларов в год"

FROM employee           получить список сотрудников
                                                                  и их зарплату

FIRST_NAME               SALARY               

=========== ======== ========== ==============

Robert      получает  105900.00 долларов в год

Bruce       получает   97500.00 долларов в год

Kim         получает  102750.00 долларов в год

Leslie      получает   64635.00 долларов в год

Phil        получает   75060.00 долларов в год

K. J.       получает   86292.94 долларов в год

Terri       получает   53793.00 долларов в год

...



 Логические операторы


К логическим операторам относятся известные операторы AND, OR, NOT, позволяющие выполнять различные логические действия: логическое умножение (AND, “пересечение условий”), логическое сложение (OR, “объединение условий”), логическое отрицание (NOT, “отрицание условий”). В наших примерах мы уже применяли оператор AND. Использование этих операторов позволяет гибко “настроить” условия отбора записей.

Оператор AND означает, что общий предикат будет истинным только тогда, когда условия, связанные по “AND”, будут истинны.

Оператор OR означает, что общий предикат будет истинным, когда хотя бы одно из условий, связанных по “OR”, будет истинным.

Оператор NOT означает, что общий предикат будет истинным, когда условие, перед которым стоит этот оператор, будет ложным.

В одном предикате логические операторы выполняются в следующем порядке: сначала выполняется оператор NOT, затем - AND и только после этого - оператор OR. Для изменения порядка выполнения операторов разрешается использовать скобки.

SELECT first_name, last_name, dept_no,

       job_code, salary

FROM employee

WHERE dept_no = 622

   OR job_code = "Eng"

  AND salary <= 40000

ORDER BY last_name     получить список служащих,
                                                                занятых в отделе 622
                                                                          или
                                                                на должности “инженер” с зарплатой
                                                                не выше 40000

FIRST_NAME   LAST_NAME     DEPT_NO JOB_CODE      SALARY

============ ============= ======= ======== ===========

Jennifer M.  Burbank       622     Eng         53167.50

Phil         Forest        622     Mngr        75060.00

T.J.         Green         621     Eng         36000.00

Mark         Guckenheimer  622     Eng         32000.00

John         Montgomery    672     Eng         35000.00

Bill         Parker        623     Eng         35000.00


Willie       Stansbury     120     Eng         39224.06

SELECT first_name, last_name, dept_no,

       job_code, salary

FROM employee

WHERE (dept_no = 622

   OR job_code = "Eng")

  AND salary <= 40000

ORDER BY last_name     получить список служащих,
                                                                занятых в отделе 622
                                                                или на должности “инженер”,
                                                                зарплата которых не выше 40000

FIRST_NAME   LAST_NAME     DEPT_NO JOB_CODE      SALARY

============ ============= ======= ======== ===========

T.J.         Green         621     Eng         36000.00

Mark         Guckenheimer  622     Eng         32000.00

John         Montgomery    672     Eng         35000.00

Bill         Parker        623     Eng         35000.00

Willie       Stansbury     120     Eng         39224.06


 Методы


Метод

Назначение

procedure NewColumn;

Реализует вывод информации в следующей колонке отчета, а если определена единственная колонка, – в его следующей странице.

procedure NewPage;

Реализует вывод информации в следующей странице отчета.

procedure Prepare;

Готовит отчет для вывода в файл (см. ниже примечание 1).

procedure Preview;

Выводит стандартное окно предварительного просмотра (см. ниже примечание 2).

procedure Print;

Печатает отчет на принтере.

procedure PrintBackGround;

Инициирует печать отчета в фоновом режиме (в отдельном потоке команд). После завершения печати вызывается обработчик события OnAfterPrint.

procedure PrinterSetup;

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

Примечание 1.

Для вывода отчета в файл нужно сначала подготовить его с помощью обращения к методу Prepare, затем сохранить в файле методом Save объекта TQuickRep.QRPrinter, после чего уничтожить этот объект и поместить NIL в свойстве TQuickRep.QRPrinter:

MyReport.Prepare;

MyReport.QRPrinter.Save(‘REport.QRP’);

MyReport.QRPrinter.Free;

MyReport.QRPrinter := NIL;

Примечание 2.

Стандартное окно предварительного просмотра показано на рис. 4.

Рис. 4. Окно предварительного просмотра отчета.

Чтобы на этапе конструирования просмотреть в окне предварительного просмотра содержимое отчета в том виде, как он будет выводиться на печать, нужно выбрать  опцию Preview во вспомогательном меню компонента QuickRep. Следует заметить, что при этом не будут видны некоторые данные, например значения вычисляемых полей. Они будут выводиться только во время выполнения.

Назначение инструментальных кнопок окна:

Масштабирует отчет так, чтобы его страница полностью показывалась в окне.

Отображает отчет в масштабе 1:1.

Масштабирует отчет так, чтобы ширина страницы отчета соответствовала ширине окна.

Показывает первую (последнюю) страницу отчета.

Показывает предыдущую (следующую) страницу отчета.

Вызывает стандартное окно настройки принтера (печатает отчет).

Сохраняет отчет в файле (загружает отчет из файла).



 Объект Session


Объект Session, имеющий тип TSession создается автоматически в программе, работающей с базами данных (в этом случае Delphi подключает в программу модуль DB). Вам не нужно заботиться о создании и уничтожении данного объекта, но его методы и свойства могут быть полезны в некоторых случаях. В этом компоненте содержится информация обо всех базах данных, с которыми работает программа. Ее можно найти в свойстве DataBases. Со свойством KeepConnections данного объекта мы уже знакомы. Это свойство определяет, нужно ли сохранять соединение с базой, если в программе нет ни одной открытой таблицы из этой базы. NetDir - директория, в которой лежит общий сетевой файл PDOXUSRS.NET, необходимый BDE. PrivateDir - директория для хранения временных файлов.

С помощью методов объекта Session можно получить информацию о настройках BDE, например, список всех псевдонимов, драйверов баз данных или список всех таблиц в базе.

Еще одно важное назначение объекта Session - доступ с его помощью к таблицам Paradox, защищенным паролем. Прежде, чем открыть такую таблицу, требуется выполнить метод AddPassword :

Session.AddPassword(‘my_pass’);

Удалить пароль можно с помощью метода RemovePassword или RemoveAllPasswords.



 Обновление (Refresh)


          Как Вы уже знаете, любая таблица, которую Вы открываете всегда “подвержена изменению”. Короче говоря, Вы должны расценить таблицу скорее как меняющуюся, чем как статическую сущность. Даже если Вы - единственное лицо, использующее данную TTable, и даже если Вы не работаете в сети, всегда существует возможность того, что программа с которой Вы работаете, может иметь два различных пути изменения данных в таблице. В результате, Вы должны всегда знать, необходимо ли Вам обновить вид таблицы на экране.

          Функция Refresh связана с функцией Open, в том смысле что она считывает данные, или некоторую часть данных, связанных с данной таблицей. Например, когда Вы открываете таблицу, Delphi считывает данные непосредственно из файла БД. Аналогично, когда Вы Регенерируете таблицу, Delphi считывает данные напрямую из таблицы. Поэтому Вы можете использовать эту функцию, чтобы перепрочитать таблицу, если Вы думаете что она могла измениться. Быстрее и эффективнее, вызывать Refresh, чем вызывать Close и затем Open.

          Имейте ввиду, однако, что обновление TTable может иногда привести к неожиданным результатам. Например, если a пользователь рассматривает запись, которая уже была удалена, то она исчезнет с экрана в тот момент, когда будет вызван Refresh. Аналогично, если некий другой пользователь редактировал данные, то вызов Refresh приведет к динамическому изменению данных. Конечно маловероятно, что один пользователь будет изменять или удалять запись в то время, как другой просматривает ее, но это возможно.



 Обзор


На этом уроке мы познакомимся с ядром баз данных компании Борланд - Borland Database Engine (BDE), а также научимся создавать и редактировать алиасы - механизм, облегчающий связь с базами данных. Кроме того, мы изучим, как конфигурировать ODBC драйверы.


На данном уроке мы изучим, как создавать таблицы базы данных с помощью утилиты Database Desktop, входящей в поставку Delphi. Хотя для создания таблиц можно использовать различные средства (SQL - компонент TQuery и компонент TTable), применение этой утилиты позволяет создавать таблицы в интерактивном режиме и сразу же просмотреть их содержимое - и все это для большого числа форматов. Это особенно удобно для локальных баз данных, в частности Paradox и dBase.




На данном уроке мы познакомимся еще с одной возможностью создания таблиц - через посылку SQL-запросов. Как Вы, наверное, могли заметить на предыдущем уроке, Database Desktop не обладает всеми возможностями по управлению SQL-серверными базами данных. Поэтому с помощью Database Desktop удобно создавать или локальные базы данных или только простейшие SQL-серверные базы данных, состоящие из небольшого числа таблиц, не очень сильно связанных друг с другом. Если же Вам необходимо создать базу данных, состоящую из большого числа таблиц, имеющих сложные взаимосвязи, можно воспользоваться языком SQL. При этом можно воспользоваться компонентом Query в Delphi, каждый раз посылая по одному SQL-запросу, а можно записать всю последовательность SQL-предложений в один так называемый скрипт и послать его на выполнение. Конечно, для этого нужно хорошо знать язык SQL, но, уверяю Вас, сложного в этом ничего нет! Конкретные реализации языка SQL незначительно отличаются в различных SQL-серверах, однако базовые предложения остаются одинаковыми для всех реализаций.




          В этой статье вы узнаете о Редакторе DataSet и о способах управления компонентом TDBGrid во время выполнения программы. Здесь же будут рассмотрены вычисляемые поля - весьма ценная особенность Редактора DataSet.

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




В данной статье рассказывается об управлении соединением с базой данных при помощи компоненты TDataBase  и объекта TSession, который создается в программе автоматически. Описываются процедуры создания локального псевдонима базы данных и доступа к таблицам Paradox по паролю.




          Все операции, выполняемые с данными на SQL сервере, происходят в контексте транзакций. Транзакция - это групповая операция, т.е. набор действий с базой данных; самым существенным для этих действий является правило либо все, либо ни чего. Если во время выполнения данного набора действий, на каком-то этапе невозможно произвести очередное действие, то нужно выполнить возврат базы данных к начальному состоянию (произвести откат транзакции). Таким образом (при правильном планировании транзакций), обеспечивается целостность базы данных. В данном уроке объясняется, как начинать, управлять и завершать транзакции с помощью SQL выражений. А так же рассматривается вопрос об использовании транзакций в приложениях, созданных в Delphi. Вся приведенная информация касается InterBase.




SQL (обычно  произносимый как "СИКВЭЛ" или “ЭСКЮЭЛЬ”) символизирует собой Структурированный Язык Запросов. Это - язык, который дает Вам возможность создавать и работать в реляционных базах данных, являющихся наборами связанной информации, сохраняемой в таблицах.

Информационное пространство становится более унифицированным. Это привело к необходимости создания стандартного языка, который мог бы использоваться в большом количестве различных видов компьютерных сред. Стандартный язык позволит пользователям, знающим один набор команд, использовать их для создания, нахождения, изменения и передачи информации - независимо от того, работают ли они на персональном компьютере, сетевой рабочей станции, или на универсальной ЭВМ.

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

Элегантность и независимость от специфики компьютерных технологий, а также его поддержка лидерами промышленности в области технологии реляционных баз данных, сделало SQL (и, вероятно, в течение обозримого будущего оставит его) основным стандартным языком. По этой причине, любой, кто хочет работать с базами данных 90-х годов, должен знать SQL.

Стандарт SQL определяется ANSI (Американским Национальным Институтом Стандартов) и в данное время также принимается ISO (Международной Организацией по Стандартизации). Однако, большинство коммерческих программ баз данных расширяют SQL без уведомления ANSI, добавляя различные особенности в этот язык, которые, как они считают, будут весьма полезны. Иногда они несколько нарушают стандарт языка, хотя хорошие идеи имеют тенденцию развиваться и вскоре становиться стандартами "рынка" сами по себе в силу полезности своих качеств.

На данном уроке мы будем, в основном, следовать стандарту ANSI, но одновременно иногда будет показывать и некоторые наиболее общие отклонения от его стандарта.

Точное описание особенностей языка приводится в документации на СУБД, которую Вы используете. SQL системы InterBase 4.0 соответствует стандарту ANSI-92 и частично стандарту ANSI-III.



 Ограничения на число выводимых строк


Число возвращаемых в результате запроса строк может быть ограничено путем использования предложения WHERE, содержащего условия отбора (предикат, рис.2). Условие отбора для отдельных строк может принимать значения true, false или unnown. При этом запрос возвращает в качестве результата только те строки (записи), для которых предикат имеет значение true.

Типы предикатов, используемых в предложении WHERE:

· сравнение с использованием реляционных операторов

= равно

<>       не равно

!=       не равно

> больше

< меньше

>=       больше или равно

<=       меньше или равно

·      BETWEEN

·      IN

·      LIKE

·      CONTAINING

·      IS NULL

·      EXIST

·      ANY

·      ALL



 Open или ExecSQL?


          После того, как составлен SQL запрос, есть два различных способа выполнить его. Если Вы хотите получить курсор, то нужно вызывать Open. Если выражение SQL не подразумевает возвращение курсора, то нужно вызывать ExecSQL. Например, если происходит вставка, удаление или обновление данных (т.е. SQL запросы INSERT, DELETE, UPDATE), то нужно вызывать ExecSQL. Тоже самое можно сказать по-другому: Open вызывается при запросе типа SELECT, а ExecSQL - во всех остальных случаях.

          Вот типичный SQL запрос, который используется для удаления записи из таблицы:

delete from Country where Name = ‘Argentina’;

Этот запрос удалил бы любую запись из таблицы COUNTRY, которая имеет значение "Argentina" в поле Имя.

          Не трудно заметить, что это тот случай, когда удобно использовать параметризованный запрос. Например, неплохо было бы менять имя страны, которую требуется удалить:

delete from Country where Name = :CountryName

В этом случае переменная :CountryName может быть изменена во время выполнения:

Query2.Prepare;

Query2.Params[0] := ‘Argentina’;

Query2.ExecSQL;

Код сначала вызывает Prepare, чтобы сообщить Delphi что он должен разобрать SQL запрос и подготовить свойство Params. Следующим шагом присваивается значение свойству Params и затем выполняется подготовленный SQL запрос.  Обратите внимание, что он выполняется через ExecSQL, а не Open.

          Программа INSQUERY из примеров Delphi демонстрирует эту технику (проект C:\DELPHI\DEMOS\DB\INSQUERY.DPR)



 Операции сравнения


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

Что же может быть элементом сравнения? Элементом сравнения может выступать:

·     значение поля

·     литерал

·     арифметическое выражение

·     агрегирующая функция

·     другая встроенная функция

·     значение (значения), возвращаемые подзапросом.

При сравнении литералов конечные пробелы игнорируются. Так, предложение WHERE first_name = ‘Ïåòð    ‘ будет иметь тот же результат, что и предложение WHERE first_name = ‘Ïåòð’.

SELECT first_name, last_name, dept_no

FROM employee

WHERE job_code = "Admin"   получить список  сотрудников
                                                                         (и номера их отделов),
                                                                         занимающих должность
                                                                         администраторов

FIRST_NAME      LAST_NAME            DEPT_NO

=============== ==================== =======

Terri           Lee                  000     

Ann             Bennet               120    

Sue Anne        O'Brien              670    

Kelly           Brown                600

SELECT first_name, last_name, dept_no,

       job_country

FROM employee

WHERE job_country <> "USA" получить список  сотрудников
                                                                            (а также номера их отделов
                                                                            и страну),
                                                                            работающих вне США

FIRST_NAME      LAST_NAME        DEPT_NO JOB_COUNTRY

=============== ================ ======= ==============

Ann             Bennet           120     England

Roger           Reeves           120     England

Willie          Stansbury        120     England

Claudia         Sutherland       140     Canada

Yuki            Ichida           115     Japan

Takashi         Yamamoto         115     Japan

Roberto         Ferrari          125     Italy

Jacques         Glon             123     France

Pierre          Osborne          121     Switzerland



 Основные понятия о TDataSource


          Класс TDataSource используется в качестве проводника между TTable или TQuery и компонентами, визуализирующими данные, типа TDBGrid, TDBEdit и TDBComboBox (data-aware components). В большинстве случаев, все, что нужно сделать с DataSource - это указать в свойстве DataSet  соответствующий TTable или TQuery. Затем, у data-aware компонента в свойстве DataSource указывается TDataSource, который используется в настоящее время.

          TDataSource также имеет свойство Enabled, и оно может быть полезно всякий раз, когда Вы хотите временно отсоединить, например, DBGrid от таблицы или запроса. Эти требуется, например, если нужно программно пройти через все записи в таблице. Ведь, если таблица связана с визуальными компонентами (DBGrid, DBEdit и т.п.), то каждый раз, когда Вы вызываете метод TTable.Next, визуальные компоненты будут перерисовываться. Даже если само сканирование в таблице двух или трех тысяч записей не займет много времени, то может потребоваться значительно больше  времени, чтобы столько же раз перерисовать визуальные компоненты. В случаях подобных этому, лучше всего установить поле DataSource.Eabled в False. Это позволит Вам просканировать записи без перерисовки визуальных компонент. Это единственная операция может увеличить скорость в некоторых случаях на несколько тысяч процентов.

          Свойство TDataSource.AutoEdit указывает, переходит ли DataSet автоматически в режим редактирования при вводе текста в data-aware объекте.



 Основные понятия о TQuery


          Предыдущий Урок был, в основном, посвящен объекту TTable, который служит для доступа к данным. При использовании TTable, возможен доступ ко всему набору записей из одной таблицы. В отличие от TTable, TQuery позволяет произвольным образом (в рамках SQL) выбрать набор данных для работы с ним. Во многом, методика работы с объектом TQuery похожа на методику работы с TTable, однако есть свои особенности.

          Вы может создать SQL запрос используя компонент TQuery следующим способом:

1.  Назначите Псевдоним (Alias) DatabaseName.

2.  Используйте свойство SQL чтобы ввести SQL запрос типа
“Select * from Country”.

3.  Установите свойство Active в True

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

          Две основных вещи, которые Вы должны понять прежде, чем перейти дальше:

·     Этот урок не является учебником для начинающих по SQL, а, скорее, описанием объекта TQuery и основных задач, которые Вы можете решить с его помощью. Если Вы не знаете ничто об SQL, Вы все же сможете воспользоваться этой статьей, и, в конце концов, приобретете некоторое понимание основ SQL. Однако, для полного изучения языка, Вы должны обратиться к любой из большого количества книг и документов, доступных по этому предмету.

·     Delphi использует pass through SQL, поэтому для разных SQL серверов синтаксис может быть несколько разным. Версия SQL для локальных таблиц (Local SQL) очень сильно урезан, по сравнению со стандартом. Чтобы узнать о его возможностях, Вы должны прочитать не только эту статью, но также файл LOCALSQL.HLP.

          Вы увидите, что объект TQuery один из наиболее полезных и гибких компонентов, доступных в Delphi. С ним Вы сможете воспользоваться всей мощью, предоставляемой лидерами среди промышленных SQL серверов, вроде InrterBase, Oracle или Sybase.



 Открытие и закрытие DataSet


В этой главе Вы узнаете некоторые факты об открытии и закрытии DataSet.

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

          Прежде всего, Вы должны поместить во время дизайна на форму объект TTable и указать, с какой таблицей хотите работать. Для этого нужно заполнить в Инспекторе объектов свойства DatabaseName и TableName. В DatabaseName можно либо указать директорию, в которой лежат таблицы в формате dBase или Paradox (например, C:\DELPHI\DEMOS\DATA), либо выбрать из списка псевдоним базы данных (DBDEMOS). Теперь, если свойство Active установить в True, то при запуске приложения таблица будет открываться автоматически.

Имеются два различных способа открыть таблицу во время выполнения программы. Вы можете написать следующую строку кода:

Table1.Open;

Или, если Вы предпочитаете, то можете установить свойство Active равное True:

Table1.Active := True;

Нет никакого различия между результатом производимым этими двумя операциями. Метод Open, однако, сам заканчивается установкой свойства Active в True, так что может быть даже чуть более эффективно использовать свойство Active напрямую.

        Также, как имеются два способа открыть a таблицу, так и есть два способа закрыть ее. Самый простой способ просто вызывать Close:

Table1.Close;

Или, если Вы желаете, Вы можете написать:

Table1.Active := False;

Еще раз повторим, что нет никакой существенной разницы между двумя этими способами. Вы должны только помнить, что Open и Close это методы (процедуры), а Active - свойство.

          Навигация (Перемещение по записям)

После открытия таблицы, следующим шагом Вы должны узнать как перемещаться по записям внутри него.

Следующий обширный набор методов и свойства TDataSet обеспечивает все , что Вам нужно для доступа к любой конкретной записи внутри таблицы:


procedure First;

procedure Last;

procedure Next;

procedure Prior;

property BOF: Boolean read FBOF;

property EOF: Boolean read FEOF;

procedure MoveBy(Distance: Integer);

Дадим краткий обзор их функциональных возможностей:

·     Вызов Table1.First перемещает Вас к первой записи в таблице.

·     Table1.Last перемещает Вас к последней записи.

·     Table1.Next перемещает Вас на одну запись вперед.

·     Table1.Prior перемещает Вас на одну запись Назад.

·     Вы можете проверять свойства BOF или EOF, чтобы понять, находитесь ли Вы в начале или в конце таблицы.

·     Процедура MoveBy перемещает Вас на N записей вперед или назад в таблице. Нет никакого функционального различия между запросом Table1.Next и вызовом Table1.MoveBy(1). Аналогично, вызов Table1.Prior имеет тот же самый результат, что и вызов Table1.MoveBy(-1).

          Чтобы начать использовать эти навигационные методы, Вы должны поместить TTable, TDataSource и TDBGrid на форму, также, как Вы делали это в предыдущем уроке. Присоедините DBGrid1 к DataSource1, и DataSource1 к Table1. Затем установите свойства таблицы:

·     в DatabaseName имя подкаталога, где находятся демонстрационные таблицы (или псевдоним DBDEMOS);

·     в TableName установите имя таблицы CUSTOMER.

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

Однако, иногда нужно перемещаться по таблице “программным путем”, без использования возможностей, встроенных в визуальные компоненты. В следующих нескольких абзацах объясняется как можно это сделать.

        Поместите две кнопки на форму и назовите их Next и Prior, как показано на рис.2.



Рис.2 : Next и Prior кнопки позволяют Вам перемещаться по БД.



Дважды щелкните на кнопке Next - появится заготовка обработчика события:

procedure TForm1.NextClick(Sender: TObject);

begin

end;

Теперь добавьте одну строчку кода так, чтобы процедура выглядела так:

procedure TForm1.NextClick(Sender: TObject);

begin

  Table1.Next;

end;

Повторите те же самые действия с кнопкой Prior, так, чтобы функция связанная с ней выглядела так:

procedure TForm1.PriorClick(Sender: TObject);

begin

  Table1.Prior;

end;

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

          Теперь добавьте еще две кнопки и назовите их First и Last, как показано на рис.3



Рис.3: Программа со всеми четырьмя кнопками.

Сделайте то же самое для новых кнопок.

procedure TForm1.FirstClick(Sender: TObject);

begin

  Table1.First;

end;

procedure TForm1.LastClick(Sender: TObject);

begin

  Table1.Last;

end;

          Нет ничего более простого чем эти навигационные функции. First перемещает Вас в начало таблицы, Last перемещает Вас в конец таблицы, а Next и Prior перемещают Вас на одну запись вперед или назад.

          TDataSet.BOF - read-only Boolean свойство, используется для проверки, находитесь ли Вы в начале таблицы. Свойства BOF возвращает true в трех случаях:

·        После того, как Вы открыли файл;

·        После того, как Вы вызывали TDataSet.First;

·        После того, как вызов TDataSet.Prior не выполняется.

Первые два пункта - очевидны. Когда Вы открываете таблицу, Delphi помещает Вас на первую запись; когда Вы вызываете метод First, Delphi также перемещает Вас в начало таблицы. Третий пункт, однако, требует небольшого пояснения: после того, как Вы вызывали метод Prior много раз, Вы могли  добраться до начала таблицы, и следующий вызов Prior будет неудачным - после  этого BOF и будет возвращать True.



        Следующий код показывает самый общий пример использования Prior, когда Вы попадаете к началу a файла:

while not Table.Bof do begin

  DoSomething;

  Table1.Prior;

end;

В коде, показанном здесь, гипотетическая функция DoSomething будет вызвана сперва на текущей записи и затем на каждой следующей записи (от текущей и до начала таблицы). Цикл будет продолжаться до тех пор, пока вызов Table1.Prior не сможет больше переместить Вас на предыдущую запись в таблице. В этот момент BOF вернет True и программа выйдет из цикла. (Чтобы оптимизировать вышеприведенный код, установите DataSource1.Enabled в False перед началом цикла, и верните его в True после окончания цикла.)

          Все сказанное относительно BOF также  применимо и к EOF. Другими словами, код, приведенный ниже показывает простой способ пробежать по всем записям в a dataset:

Table1.First;

while not Table1.EOF do begin

  DoSomething;

  Table1.Next;

end;

Классическая ошибка в случаях, подобных этому: Вы входите в цикл while или repeat, но забываете вызывать Table1.Next:

Table1.First;

repeat

  DoSomething;

until Table1.EOF;

Если Вы случайно написали такой код, то ваша машина зависнет. Также, этот код мог бы вызвать проблемы, если Вы открыли пустую таблицу. Так как здесь используется цикл repeat, DoSomething был бы вызван один раз, даже если бы нечего было обрабатывать. Поэтому, лучше использовать цикл while вместо repeat в ситуациях подобных этой.

EOF возвращает True в следующих трех случаях:

·        После того, как Вы открыли пустой файл;

·        После того, как Вы вызывали TDataSet.Last;

·        После того, как вызов TDataSet.Next не выполняется.

Единственная навигационная процедура, которая еще не упоминалась - MoveBy, которая позволяет Вам переместиться на N записей  вперед или назад в таблице. Если Вы хотите переместиться на две записи вперед, то напишите:

MoveBy(2);

И если Вы хотите  переместиться на две записи назад, то:

MoveBy(-2);

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

Prior и Next - это простые функции, которые вызывают MoveBy.


 Отслеживание состояния DataSet


          В предыдущей части Вы узнали, как использовать TDataSource, чтобы узнать текущее состоянии TDataSet. Использование DataSource - это простой путь выполнения данной задачи. Однако, если Вы хотите отслеживать эти события без использования DataSource, то можете написать свои обработчики событий TTable и TQuery:

property OnOpen

property OnClose

property BeforeInsert

property AfterInsert

property BeforeEdit

property AfterEdit

property BeforePost

property AfterPost

property OnCancel

property OnDelete

property OnNewRecord

Большинство этих свойств очевидны. Событие BeforePost функционально подобно событию TDataSource.OnUpdateData, которое объяснено выше. Другими словами, программа STATE работала бы точно также,  если бы Вы отвечали не на DataSource1.OnUpdateData а на Table1.BeforePost. Конечно, в первом случае Вы должен иметь  TDataSource на форме, в то время, как во втором этого не требуется.

Урок 5: Компонент TTable. Создание таблиц
с помощью компонента TTable

 

Содержание урока 5:

Обзор................................................................................................................ 2

Создание таблиц с помощью компонента TTable......................................... 2

Заключение........................................................................................................ 7

 


Обзор

 

На этом небольшом уроке мы завершим изучение возможностей создания таблиц. Как Вы помните, мы уже освоили два способа создания таблиц - с помощью утилиты Database Desktop, входящей в поставку Delphi  и с помощью SQL-запросов, которые можно использовать как в WISQL (Windows Interactive SQL - клиентская часть Local InterBase), так и в компоненте TQuery. Теперь мы рассмотрим, как можно создавать локальные таблицы в режиме выполнения с помощью компонента TTable.



 Передача параметров через TDataSource


          В предыдущем Уроке Вы видели способ создания отношения однин-ко-многим между двумя таблицами. Теперь речь пойдет о  выполнении того же самого действия с использованием объекта TQuery. Этот способ более гибок в том отношении, что он не требует индексации по полям связи.

          Объект TQuery имеет свойство DataSource, которое может использоваться для того, чтобы создать связь с другим DataSet. Не имеет значения, является ли другой DataSet объектом TTable, TQuery, или некоторый другим потомком TDataSet. Все что нужно для установления соединения - это удостовериться, что у того DataSet есть связанный с ним DataSource.

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

          Рассмотрите следующий параметризованный запрос:

select * from Orders where CustNo = :CustNo

В этом запросе :CustNo - связывающая переменная, которой должно быть присвоено значение из некоторого источника. Delphi позволяет использовать поле TQuery.DataSource чтобы  указать другой DataSet, который предоставит эту информацию автоматически. Другими  словами, вместо того, чтобы использовать свойство Params и “вручную” присваивать значения переменной, эти значения переменной могут быть просто взяты автоматически из другой таблицы.  Кроме того, Delphi всегда сначала пытается выполнить параметризованный запрос используя свойство DataSource, и только потом (если не было найдено какое-то значение параметра) будет пытаться получить значение переменной из свойства Params. При получении данных из DataSource считается, что после двоеточия стоит имя поля из DataSource. При изменении текущей записи в главном DataSet запрос будет автоматически пересчитываться.

Давайте переделаем пример из прошлого урока (LINKTBL - связывание двух таблиц). Создайте новый проект, положите на форму один набор TTable, TDataSource и TDBGrid. Привяжите его к таблице CUSTOMER. Положите на форму второй набор - TQuery, TDataSource и TDBGrid и свяжите объекты между собой. (см рис.4).

В свойстве SQL наберите текст запроса:

select * from Orders where CustNo = :CustNo

В свойстве DatabaseName для Query1 укажите DBDEMOS.

В свойстве DataSource для Query1 укажите DataSource1.

Поставьте Active = True и запустите программу.

Рис.4: Программа LINKQRY - связанные курсоры с помощью SQL