
- •Вопрос 13
- •Вопрос 14
- •Вопрос 15
- •Вопрос 16 Способы поиска записей в таблице. Обзор.
- •Навигация (Перемещение по записям)
- •Вопрос 17 Поиск записей в наборе данных: фильтрация данных. Примеры использования данного вида поиска в приложении Delphi.
- •Вопрос 18 Поиск записей в нборе данных: последовательный поиск. Примеры использования данного вида поиска в приложениях Delphi для работы с бд .
Вопрос 16 Способы поиска записей в таблице. Обзор.
В большинстве случаев, когда Вы хотите получить доступ из программы к индивидуальные полям записи, Вы можете использовать одно из следующих свойств или методов, каждый из которых принадлежат TDataSet:
property Fields[Index: Integer];
function FieldByName(const FieldName: string): TField;
property FieldCount;
Навигация (Перемещение по записям)
После открытия a таблицы, следующим шагом Вы должны узнать как перемещаться по записям внутри него.
Следующий обширный набор методов и свойства 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.
Вопрос 17 Поиск записей в наборе данных: фильтрация данных. Примеры использования данного вида поиска в приложении Delphi.
Поиск данных
Универсальные методы класса TDataSet
Класс TDataSet является базовым классом компонента, реализующим функции доступа к БД. Многие из его методов являются абстрактными и реализуются в потомках. Он имеет два метода для поиска данных: Locate и Lookup. Данные методы ищут запись, удовлетворяющую заданным условиям.
function Locate(const KeyFields: string; const KeyValues: Variant;
Options: TLocateOptions): Boolean; virtual;
function Lookup(const KeyFields: string; const KeyValues: Variant;
const ResultFields: string): Variant; virtual;
Разница между ними в том, что функция Locate при поиске записи позиционирует курсор на найденную запись, а Lookup не делает этого. Если поля указанные для поиска индексированы, то поиск производится с использованием индекса, что значительно ускоряет поиск. В качестве примера рассмотрим использование данных методов на примере TTable в двухуровневом приложении. Итак, начнем с генерации нового приложения File/New Application. Мы получим проект приложения с главной формой. На данную форму поместим компонент Table с закладки Data Access, по умолчанию он будет иметь имя Table1. Теперь настроим компонент Table1 на взаимодействие с таблицей country из базы данных DBDEMOS. Для этого необходимо установить следующие свойства Table1 в следующей последовательности:
1. DatabaseName ==> DBDEMOS 2. TableName ==> country.db
Теперь можно активизировать компонент Table1, установив его свойство Active в значение true. Далее разместим на форме компонент DataSource, позволяющий сделать данные Table1 доступными для визуальных компонентов отображения данных. Свяжем DataSource1 c Table1, установив св-во DataSet компонента DataSource1 равным Table1. Разместим визуальные компоненты отображения данных со страницы Data Controls: DBGrid и DBNavigator. Установим св-во DataSource обоих равным DataSource1. В результате мы получим форму, изображенную на рис. 2. Откомпилировав и запустив данный проект, мы сможем просматривать и редактировать данные таблицы country. Остается добавить элементы для организации поиска в таблице. Разместим на форме компонент Edit1 и две кнопки. Св-во Caption кнопки Button1 установим равным "Locate", а Button2 "Lookup". Далее в обработчике события OnClick кнопки Button1 организуем вызов метода Locate. Код обработчика события приведен ниже:
Рис.
1.
Рис.
2.
TForm1.Button1Click(Sender: TObject);
begin
if not Table1.Locate('Name', Edit1.Text, []) then
ShowMessage('Запись не найдена');
end;
Разберем код более подробно. Строка Table1.Locate организует поиск записи в таблице Country. Первый параметр этой функции - поля, значения которых нужно проверять. В данном случае мы ищем запись по одному полю Name. Второй параметр, что шаблон поиска и третий опции поиска. Функция возвращает значение типа boolean, указывающее на успешность поиска.
Теперь пришло время протестировать наш пример. Запустим программу на выполнение, в строке ввода пишем Cuba и нажимаем кнопку Locate. Курсор в DBGrid1 должен переместиться на запись, имеющую в поле Name введенное значение (рис.3).
Однако наш пример имеет пока один недостаток, в строку редактирования необходимо вводить полное имя c учетом регистра, т.е если мы вместо Cuba введем, например Cu или cuba, то наш поиск будет безрезультатным. Естественно это не может нас не устраивать. Поэтому пришло время рассмотреть более подробно опции поиска. Данный параметр имеет тип TlocateOptions и позволяет задавать набор из двух параметров поиска: loCaseInsensitive и loPartialKey. Установка первого из них отменяет чувствительность к регистру в текстовых полях, а второй позволяет искать запись частично соответствующие заданному условию. С учетом вышесказанного код обработчика событий будет выглядеть следующим образом:
procedure TForm1.Button1Click(Sender: TObject);
begin
if not Table1.Locate('Name', Edit1.Text, [loCaseInsensitive,
loPartialKey]) then
ShowMessage('Запись не найдена');
end;
Следующей проблемой является поиск записи по нескольким полям. Для организации поиска по имени страны и континенту добавим на форму еще один компонент Edit2. Код обработчика события нажатия на кнопку Locate изменим следующим образом:
procedure TForm1.Button1Click(Sender: TObject);
begin
if not Table1.Locate(
'Continent;Name',
VarArrayOf([Edit2.Text, Edit1.Text]),
[loCaseInsensitive, loPartialKey]) then
ShowMessage('Запись не найдена');
end;
Как видно, при поиске по нескольким полям, все они перечисляются в параметре функции Locate
Запустив приложение, в строке поиска континента пишем South america, а в строке "страна" - C. Нажимаем кнопку Lookup - результат поиска - установка курсора в DBGrid на запись Chile.
В ходе написания этой статьи выяснилась одна особенность. Частичный поиск при поиске по нескольким полям работает лишь для последнего поля, указанного в списке.
Модифицируем наш пример для использования функции Lookup. Ниже приведен код обработчика события нажатия на кнопку Lookup:
procedure TForm1.Button2Click(Sender: TObject);
var
Res: Variant;
begin
Res := Table1.Lookup('Continent;Name', VarArrayOf([Edit2.Text,
Edit1.Text]), 'Area');
if Res <> Null then
ShowMessage('Area ' + string(Res));
end;
Как видно из кода, мы проводим поиск по полям Continent, Name. При нахождении записи мы выдаем сообщение о площади страны, при это курсор на найденную запись не перемещается. К сожалению в параметрах функции отсутствует LocateOptions.
Исходный код данного примера расположен в файле.
Итак, подведем некоторые итоги. Функции Locate и Lookup предназначены для поиска в базе данных одной записи, удовлетворяющей заданным условиям. Данные методы определены в классе TDataSet как виртуальные и могут быть переопределены в классах потомках. Метод Locate устанавливает курсор на обнаруженную запись, Lookup этого не делает. Метод Locate, хотя и с некоторыми оговорками, может искать записи по частично заданному ключу.
Необходимо отметить, что очень часто необходимо найти сразу несколько записей. Для решения такого рода задач нужно применять методы, предоставляемые классом Ttable, Tquery …, либо проводить фильтрацию. Как это сделать описано ниже.
Методы класса TTable
Компонент TTable предназначен для работы с таблицей база данных. Данный компонент используется в двухуровневых приложениях баз данных, либо в сервере приложений в трехуровневой БД. Как правило, использование TTable для работы с клиент-серверной БД оказывается менее эффективным, чем TQuery, поскольку TTable извлекает сразу ВСЕ записи из таблицы, а в TQuery лишь удовлетворяющие условиям запроса.
Компонент имеет несколько специфических методов для поиска данных. Данные методы используются для поиска только по индексированным полям (для dBase и Paradox как минимум). Все они делятся на две группы:
- Методы поиска одной записи - Методы поиска диапазона записей.
Рассмотрим сначала первую группу методов. К ним относятся GotoKey, FindKey, GotoNearest, FindNearest. Первые два метода используются для поиска строго соответствия, другие ищут частичное соответствие.
В качестве примера создадим новое приложение, на главной форме разместим компоненты Table, DataSource, DBGrid, DBNavigator. Установим св-ва данных компонентов в соответствии с таблицей 1. Отметим, что таблица country.db имеет индексированное поле Name, по которому мы далее организуем поиск.
Таблица 1
Table1 |
|
DatabaseName |
BCDEMOS |
TableName |
Country.db |
Active |
true |
DataSource1 |
|
DataSet |
Table1 |
DBNavigator1, DBGrid1 |
|
DataSource |
DataSource1 |
Далее разместим на форме компонент Edit и четыре кнопки, установив их св-во Caption в GotoKey, GotoNearest, FindKey, FindNearest. Ниже приведен код обработчиков событий нажатия на эти кнопки.
//Использование GotoKey
procedure TForm1.Button1Click(Sender: TObject);
begin
with Table1 do
begin
EditKey;
FieldByName('Name').AsString := Edit1.Text;
if not GotoKey then
ShowMessage('Not found');
end;
end;
// Использование GotoNearest
procedure TForm1.Button2Click(Sender: TObject);
begin
with Table1 do
begin
EditKey;
FieldByName('Name').AsString := Edit1.Text;
GotoNearest;
end;
end;
// Использование FindKey
procedure TForm1.Button3Click(Sender: TObject);
begin
with Table1 do
begin
if not FindKey([Edit1.Text]) then
ShowMessage('Not found');
end;
end;
// Использование FindNearest
procedure TForm1.Button4Click(Sender: TObject);
begin
with Table1 do
begin
FindNearest([Edit1.Text]);
end;
end;
Прокомментируем вышеприведенный код. Методы GotoKey, FindKey проводят поиск на точное соответствие заданному ключу. Они возвращают значение типа boolean, говорящее об успехе поиска.
Методы GotoNearest, FindNearest проводят поиск первой записи хотя бы частично соответствующей ключу. Они не возвращают значений, т.к такой вид поиска всегда будет успешным.
Перед вызовом методов GotoKey, GotoNearest необходимо вызывать метод EditKey или SetKey, чтобы перевести компонент Table в режим редактирования ключа поиска.
Код данного примера содержится в файле.
Методы поиска диапазона записей позволяют отобразить записи таблицы, лежащие в указанном диапазоне значений поля. Для таблиц Paradox и dBase данные методы работают только для индексированных полей. К данным методам относятся SetRangeStart, SetRangeEnd, EditRangeStart, EditRangeEnd, ApplyRange, CancelRange. Техника использования данных функций очень проста.
Сначала необходимо установить начало и конец диапазона вызовом функций SetRangeStart, SetRangeEnd, EditRangeStart, EditRangeEnd, указывая при этом значения полей формирования диапазона. Затем вызовом ApplyRange применить указанный диапазон. Сброс установленного диапазона позволяет вновь отобразить все записи таблицы. Он выполняется вызовом функции CancelRange.
Для иллюстрации вышесказанного создадим новый проект, на главную форму поместим компоненты Table, DataSource, DBGrid, DBNavigator. Установим их свойства как указано в таблице 1. Далее разместим две строки ввода и две кнопки SetRange и CancelRange. Обработчики событий нажатия этих кнопок приведены ниже.
procedure TForm1.SetRangeClick(Sender: TObject);
begin
with Table1 do
begin
SetRangeStart; {Входим в режим установки начала диапазона}
FieldByName('Name').AsString := Edit1.Text; {значение начала диапазона}
SetRangeEnd; { Входим в режим установки конца диапазона }
FieldByName('Name').AsString := Edit2.Text; {значение конца диапазона}
ApplyRange; { Применяем указанный диапазон}
end;
end;
procedure TForm1.CancelRangeClick(Sender: TObject);
begin
Table1.CancelRange; {Очистка диапазона}
end;
Код данного примера содержится в файле.
Фильтрация
В отличие от методов поиска, предполагающих извлечение данных из хранилища данных фильтрация предполагает отбор уже извлеченных данных в клиентском приложении. Для реализации данного подхода в Delphi в компонентах доступа к данным введены два свойства Filter и Filtered. Установка свойства Filtered типа boolean в true переводит компонент в режим фильтрации. В свойстве Filter при этом можно определить значение фильтра для отбора записей. Построение фильтра во многом похоже на построение условия where в SQL запросе. Основное отличие в том, что слово where не пишется, используются другие знаки подстановки, в тексте фильтра нельзя после знаков сравнения вставлять имена полей для локальных таблиц. Если имя поля содержит пробелы, то оно заключается в квадратные скобки, например [Home directory] Свойство FilterOptions позволяет установить дополнительные параметры фильтрации, а именно
foCaseInsensitive - нечувствительность к регистру в текстовых полях; foNoPartialCompare - отсутствие поиска по частичному условию, при установке данной опции знак * воспринимается как литера, а не как знак подстановки любые символы.
Пример фильтрации можно найти в проекте Demos\DB\Filter из примеров, поставляемых с Delphi.
Все вышесказанное позволяет реализовывать фильтрацию данных по достаточно простым условиям. В случае, если необходимо реализовать более сложный нестандартный фильтр можно написать обработчик события OnFilterRecord. Тип события определен как
type TFilterRecordEvent = procedure(DataSet: TDataSet;
var Accept: Boolean) of object;
Другими словами в обработчике события можно изменять переменную Accept, указывая будет ли отображаться каждая конкретная запись. Например обработчик может выглядеть так:
Accept := DataSet['DateOfPayment'] > DataSet['DateOfPurchase'] + 30;
Не забывайте, что обработчик события дополняет, а не замещает свойство Filter, т.е если включена фильтрация (Filtered := true) и св-во фильтр содержит значение фильтра, то в обработчике события и фильтр связаны логическим отношением "AND".