Спасатели из подземной ловушки вызволяли корову

Поиск и фильтрация данных в Delphi     Михаил Голованов, Мастера Delphi     Введение    Базы данных являются удобным средством хранения структурированной информации. Однако само по себе накопление и хранение информации делает базу данных большой кучей мусора. Удобно организованный поиск и отбор информации способен резко повысить эффективность ее использования. В данной статье мы попытаемся разобраться в методах поиска информации, реализованных в Delphi. Предполагается, что Вы в общих чертах знакомы с теорией реляционных баз данных и имеете некоторый опыт программирования на Delphi.   Общие положения    Для понимания реализации методов поиска Delphi необходимо сделать краткое отступление относительно идеологии построения баз данных. Итак, всю систему работы с БД можно представить в виде трех слоев или модулей (рис.1).  Хранилище данных отвечает за хранение информации и обеспечение ее целостности и непротиворечивости.  Бизнес логика реализует набор правил предметной области приложения, т.е правил изменения и дополнения информации.  Презентационный слой выводит данные в форме доступной пользователю и обеспечивает интерфейс для взаимодействия с бизнес логикой.   Очевидно, что нас будет интересовать взаимодействие двух верхних модулей с хранилищем данных.  В зависимости от специфики реализации базы данных данные слои могут объединяться или реализовываться как отдельные независимые модули.     рис.1. Для нашей задачи системы управления базами данных можно классифицировать по следующим признакам: По типу хранилища данных:  - файловые  - серверные   Файловые хранилища представляют собой набор файлов таблиц БД, сосредоточенных, как правило, в одной директории. Клиентское приложение имеет доступ к таблицам на уровне файловой системы. Серверные - хранилище данных представляет собой программу, извлекающую информацию из файла(ов) БД по запросам клиентских приложений. При этом не на ПК клиента не требуется иметь непосредственный доступ к файловой системе сервера.   По количеству слоев:  - Двухуровневые  - Многослойные   Двухуровневые СУБД совмещают в клиентском приложении бизнес логику и презентационные функции. Примером работы такой схемы является классическое приложение СУБД, где клиентское приложение осуществляет отображение данных пользователю и реализует функции проверки, контроля и отправки информации в хранилище данных.  Многослойные (как правило 3-х) приложения кроме клиентской программы имеют специальную программу - сервер приложений. Сервер приложений реализует функции доступа к хранилищу данных и передает запрошенные данные клиентской программе, проверяя правильность запросов.    Поиск данных    Универсальные методы класса 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;  Разница между ними в том, что функция Lookup при поиске записи позиционирует курсор на найденную запись, а 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 установим равным 50000) AND (Population > 20000000) Active true DataSource1 DataSet Query1 DBNavigator1, DBGrid1 DataSource DataSource1 Button1 Caption Применить Button2 Caption Отменить Вид главной формы на этапе разработки приведен на рис. 4. рис. 4. В обработчике события OnCreate формы реализуем копирование текста запроса из Query1, код приведен ниже. procedure TForm1.FormCreate(Sender: TObject); begin Memo1.Text:=Query1.SQL.Text; end; Кнопка Button1 при нажатии будет переносить текст запроса, исправленный пользователем, из Memo1 обратно в Query1. Обработчик нажатия Button1 будет выглядеть так: procedure TForm1.Button1Click(Sender: TObject); begin Query1.Active:=false; Query1.SQL.Text:=Memo1.Text; Query1.Active:=true; end; Обработчик события нажатия Button2 установим, указывающим на FormCreate. Откомпилируем и запустим нашу программу. Убедимся в том, что при изменении текста запроса (площади и населения) происходит изменение записей в DBGrid1. Данный подход имеет ряд существенных недостатков, затрудняющих его практическое использование. 1. Пользователь приложения обязан разбираться в языке SQL; 2. Отсутствуют средства автоматического контроля синтаксиса запроса; 3. При изменении текста запроса, он отправляется на сервер, где каждый раз анализируется и компилируется, что снижает быстродействие поиска. 4. Для генерации запроса пользователю необходимо знать имена таблиц и полей базы данных. По указанным выше причинам, данный подход применяется редко. Как правило, применение данного подхода оправдана при написании приложений для администраторов БД или разработчиков отчетов. Код данного примера содержится в файле Find2. Более приемлемым является применение параметризированных запросов. В этом случае в тексте запроса содержатся параметры для дальнейшей подстановки конкретных значений на этапе выполнения. Параметры в тексте запроса начинаются с двоеточия. Итак, создадим новое приложение, разместим на главной форме компоненты Query, DataSource, DBGrid, DBNavigator, кноку Button1. Установим их свойства аналогично предыдущему примеру. Далее текст св-ва SQL компонента установим равным: SELECT Name, Capital, Continent, Area, Population FROM "country.db" Country WHERE (Name = :Name_param) В последней строке мы определили параметр запроса Name_param. Теперь открыв в инспекторе объектов редактор св-ва Params компонента Query1, мы увидим элемент, соответствующий определенному параметру. Выбрав данный элемент отредактируем его свойства. DataType - тип значения параметра (по умолчанию ftUnknown) установим в ftString. ParamType - тип параметра (по умолчанию ptUnknown) установим в ptInput. Обработчик события нажатия на кнопку будет выглядеть следующим образом: procedure TForm1.Button1Click(Sender: TObject); begin Query1.Close; if not Query1.Prepared then Query1.Prepare; Query1.ParamByName('Name_param').Value:=Edit1.Text; Query1.Open; end; Для увеличения скорости поиска в строках 4 и 5 осуществляется проверка подготовки запроса. Если запрос не подготовлен (что случается при старте программы) вызывается метод Prepare. При этом сервер анализирует и компилирует запрос, сохраняя его на сервере. Далее серверу необходимо отсылать лишь значения параметров запроса. Скомилируем и запустим на выполнение созданное приложение. В строке поиска введем значение Canada и нажмем кнопку "Применить". Результатом поиска будет вывод в строки в DBGrid1, как это показано на рис. 5. рис. 5. Код данного примера содержится в файле Find3. Для поиска записей частично соответствующих введенному критерию изменим текст запроса компонента Query1 следующим образом: SELECT Name, Capital, Continent, Area, Population FROM "country.db" Country WHERE (Name Like :Name_param) Как видно мы заменили оператор строго равенства (=) на LIKE. Оператор Like позволяет искать текстовые значения нестрого соответствующие заданному критерию. При этом в критерии поиска можно использовать знаки подстановки. % - любые символы, _ - один символ. Например, критерий поиска С% в нашем случае даст нам вывод четырех записей (Canada, Chile, Colombia, Cuba). В одном запросе можно комбинировать несколько условий поиска с помощью операторов OR, AND итд. Конечно, мы осветили лишь небольшую часть возможностей SQL по поиску данных. Если Вы хотите получить более подробную информацию, рекомендую приобрести какую-либо книгу по SQL. Фильтрация В отличие от методов поиска, предполагающих извлечение данных из хранилища данных фильтрация предполагает отбор уже извлеченных данных в клиентском приложении. Для реализации данного подхода в 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". Заключение Конечно, невозможно изложить в статье все приемы и хитрости поиска данных, а тем более в такой гибкой и мощной среде разработки как Delphi. Мы рассмотрели лишь общие положения. В частности, за рамками статьи остались такие темы, как поиск в трехуровневых приложениях баз данных. Если Вы имеете пожелания / дополнения / замечания / исправления можете прислать их автору по адресу mgoblin@mail.ru, буду Вам очень признателен, т.к без Вашей помощи написание данной статьи было бы практически невозможно. Внимание! Запрещается перепечатка данной статьи или ее части без согласования с автором. Если вы хотите разместить эту статью на своем сайте или издать в печатном виде, свяжитесь с автором. Автор статьи: Михаил Голованов " title="Поиск и фильтрация данных в Delphi Михаил Голованов, Мастера Delphi Введение Базы данных являются удобным средством хранения структурированной информации. Однако само по себе накопление и хранение информации делает базу данных большой кучей мусора. Удобно организованный поиск и отбор информации способен резко повысить эффективность ее использования. В данной статье мы попытаемся разобраться в методах поиска информации, реализованных в Delphi. Предполагается, что Вы в общих чертах знакомы с теорией реляционных баз данных и имеете некоторый опыт программирования на Delphi. Общие положения Для понимания реализации методов поиска Delphi необходимо сделать краткое отступление относительно идеологии построения баз данных. Итак, всю систему работы с БД можно представить в виде трех слоев или модулей (рис.1). Хранилище данных отвечает за хранение информации и обеспечение ее целостности и непротиворечивости. Бизнес логика реализует набор правил предметной области приложения, т.е правил изменения и дополнения информации. Презентационный слой выводит данные в форме доступной пользователю и обеспечивает интерфейс для взаимодействия с бизнес логикой. Очевидно, что нас будет интересовать взаимодействие двух верхних модулей с хранилищем данных. В зависимости от специфики реализации базы данных данные слои могут объединяться или реализовываться как отдельные независимые модули. рис.1. Для нашей задачи системы управления базами данных можно классифицировать по следующим признакам: По типу хранилища данных: - файловые - серверные Файловые хранилища представляют собой набор файлов таблиц БД, сосредоточенных, как правило, в одной директории. Клиентское приложение имеет доступ к таблицам на уровне файловой системы. Серверные - хранилище данных представляет собой программу, извлекающую информацию из файла(ов) БД по запросам клиентских приложений. При этом не на ПК клиента не требуется иметь непосредственный доступ к файловой системе сервера. По количеству слоев: - Двухуровневые - Многослойные Двухуровневые СУБД совмещают в клиентском приложении бизнес логику и презентационные функции. Примером работы такой схемы является классическое приложение СУБД, где клиентское приложение осуществляет отображение данных пользователю и реализует функции проверки, контроля и отправки информации в хранилище данных. Многослойные (как правило 3-х) приложения кроме клиентской программы имеют специальную программу - сервер приложений. Сервер приложений реализует функции доступа к хранилищу данных и передает запрошенные данные клиентской программе, проверяя правильность запросов. Поиск данных Универсальные методы класса 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; Разница между ними в том, что функция Lookup при поиске записи позиционирует курсор на найденную запись, а 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. Код обработчика события приведен ниже: Рис.2. Рис.3. 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. Исходный код данного примера расположен в файле Find1. Итак, подведем некоторые итоги. Функции 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 в режим редактирования ключа поиска. Код данного примера содержится в файле Find4. Методы поиска диапазона записей позволяют отобразить записи таблицы, лежащие в указанном диапазоне значений поля. Для таблиц 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; Код данного примера содержится в файле Find5. Использование TQuery Компонент TQuery предназначен для извлечения данных с помощью языка SQL (Structured query language). Компонент используется в двухуровневых приложениях работы с базами данных и в сервере приложений в трехуровневых. Он, как правило, применяется для работы с клиент-серверными базами данных. Сам компонент TQuery не имеет специальных методов поиска записей, однако, возможности поиска заложены в языке SQL.Ограничения на простые данные, получаемые предложением select, реализуются с помощью предложения where. В качестве иллюстрации приведем следующий пример. Создадим новое приложение, разместим на его главной форме компоненты: Query, DataSource, DBGrid, DBNavigator, Memo, две кнопки Button1 и Button2. Установим свойства компонентов, как это указано в таблице 2. Таблица 2 Query1 DatabaseName BCDEMOS SQL SELECT Name, Capital, Continent, Area, PopulationFROM "country.db" CountryWHERE (Area > 50000) AND (Population > 20000000) Active true DataSource1 DataSet Query1 DBNavigator1, DBGrid1 DataSource DataSource1 Button1 Caption Применить Button2 Caption Отменить Вид главной формы на этапе разработки приведен на рис. 4. рис. 4. В обработчике события OnCreate формы реализуем копирование текста запроса из Query1, код приведен ниже. procedure TForm1.FormCreate(Sender: TObject); begin Memo1.Text:=Query1.SQL.Text; end; Кнопка Button1 при нажатии будет переносить текст запроса, исправленный пользователем, из Memo1 обратно в Query1. Обработчик нажатия Button1 будет выглядеть так: procedure TForm1.Button1Click(Sender: TObject); begin Query1.Active:=false; Query1.SQL.Text:=Memo1.Text; Query1.Active:=true; end; Обработчик события нажатия Button2 установим, указывающим на FormCreate. Откомпилируем и запустим нашу программу. Убедимся в том, что при изменении текста запроса (площади и населения) происходит изменение записей в DBGrid1. Данный подход имеет ряд существенных недостатков, затрудняющих его практическое использование. 1. Пользователь приложения обязан разбираться в языке SQL; 2. Отсутствуют средства автоматического контроля синтаксиса запроса; 3. При изменении текста запроса, он отправляется на сервер, где каждый раз анализируется и компилируется, что снижает быстродействие поиска. 4. Для генерации запроса пользователю необходимо знать имена таблиц и полей базы данных. По указанным выше причинам, данный подход применяется редко. Как правило, применение данного подхода оправдана при написании приложений для администраторов БД или разработчиков отчетов. Код данного примера содержится в файле Find2. Более приемлемым является применение параметризированных запросов. В этом случае в тексте запроса содержатся параметры для дальнейшей подстановки конкретных значений на этапе выполнения. Параметры в тексте запроса начинаются с двоеточия. Итак, создадим новое приложение, разместим на главной форме компоненты Query, DataSource, DBGrid, DBNavigator, кноку Button1. Установим их свойства аналогично предыдущему примеру. Далее текст св-ва SQL компонента установим равным: SELECT Name, Capital, Continent, Area, Population FROM "country.db" Country WHERE (Name = :Name_param) В последней строке мы определили параметр запроса Name_param. Теперь открыв в инспекторе объектов редактор св-ва Params компонента Query1, мы увидим элемент, соответствующий определенному параметру. Выбрав данный элемент отредактируем его свойства. DataType - тип значения параметра (по умолчанию ftUnknown) установим в ftString. ParamType - тип параметра (по умолчанию ptUnknown) установим в ptInput. Обработчик события нажатия на кнопку будет выглядеть следующим образом: procedure TForm1.Button1Click(Sender: TObject); begin Query1.Close; if not Query1.Prepared then Query1.Prepare; Query1.ParamByName('Name_param').Value:=Edit1.Text; Query1.Open; end; Для увеличения скорости поиска в строках 4 и 5 осуществляется проверка подготовки запроса. Если запрос не подготовлен (что случается при старте программы) вызывается метод Prepare. При этом сервер анализирует и компилирует запрос, сохраняя его на сервере. Далее серверу необходимо отсылать лишь значения параметров запроса. Скомилируем и запустим на выполнение созданное приложение. В строке поиска введем значение Canada и нажмем кнопку "Применить". Результатом поиска будет вывод в строки в DBGrid1, как это показано на рис. 5. рис. 5. Код данного примера содержится в файле Find3. Для поиска записей частично соответствующих введенному критерию изменим текст запроса компонента Query1 следующим образом: SELECT Name, Capital, Continent, Area, Population FROM "country.db" Country WHERE (Name Like :Name_param) Как видно мы заменили оператор строго равенства (=) на LIKE. Оператор Like позволяет искать текстовые значения нестрого соответствующие заданному критерию. При этом в критерии поиска можно использовать знаки подстановки. % - любые символы, _ - один символ. Например, критерий поиска С% в нашем случае даст нам вывод четырех записей (Canada, Chile, Colombia, Cuba). В одном запросе можно комбинировать несколько условий поиска с помощью операторов OR, AND итд. Конечно, мы осветили лишь небольшую часть возможностей SQL по поиску данных. Если Вы хотите получить более подробную информацию, рекомендую приобрести какую-либо книгу по SQL. Фильтрация В отличие от методов поиска, предполагающих извлечение данных из хранилища данных фильтрация предполагает отбор уже извлеченных данных в клиентском приложении. Для реализации данного подхода в 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". Заключение Конечно, невозможно изложить в статье все приемы и хитрости поиска данных, а тем более в такой гибкой и мощной среде разработки как Delphi. Мы рассмотрели лишь общие положения. В частности, за рамками статьи остались такие темы, как поиск в трехуровневых приложениях баз данных. Если Вы имеете пожелания / дополнения / замечания / исправления можете прислать их автору по адресу mgoblin@mail.ru, буду Вам очень признателен, т.к без Вашей помощи написание данной статьи было бы практически невозможно. Внимание! Запрещается перепечатка данной статьи или ее части без согласования с автором. Если вы хотите разместить эту статью на своем сайте или издать в печатном виде, свяжитесь с автором. Автор статьи: Михаил Голованов " border="0" style="float: left;" />Опасные ловушки. В Черемхово сотрудники МЧС провели необычную операцию. Пострадавшую вызволяли из-под земли с помощью крана, а в чувство приводили с помощью массажа.

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

- Её за рога надо тащить!

- Не надо. Шею мы свернём, если за рога!

- Не свернём!

Тем временем на глубине пяти метров Зорька - так зовут несчастное животное, - свернувшись калачиком, терпеливо ждала подмоги.

Шестилетняя Зорька вместе с остальным черемховским стадом мирно паслась на пустыре. Никто так и не смог вспомнить, в какой момент она исчезла. Случайно её обнаружили прохожие. Оказалось, Зорька провалилась в подполье. Когда-то на этом месте были дома. И таких ловушек по всему пустырю остались десятки.

- Да ноги у неё затекли в одном положении! Отпускайте, она упёрлась, ей больно! Зоренька!

Её пытались тянуть верёвкой. Убедились: ничего не получится. Решили обвязать пожарным рукавом - брезент должен выдержать. Но прежде Зорьке ставят дозу антистресса. И вот на дворе уже вечер, а спасательная операция пока результатов не принесла. Поднять тяжёлое и испуганное животное вручную никак не получается. Сотрудники МЧС посовещались и решили - нужен кран. Эвакуатор прибыл через считанные минуты.

- Ну вроде перелома нету!

Уже на поверхности измученной Зорьке делают массаж. Важно убедиться, что кости целы и корова сможет встать на ноги. Происходит это не сразу, а после долгих уговоров.
 
- Вот когда остаёшься один на один с бедой, знаете, как это страшно! Я не знала, куда звонить, в первую очередь позвонила в МЧС, благо что откликнулись, приехали. И мэру они сами позвонили. Он приехал. И никто не отказал, спасибо всем!

Нина Яковлевна говорит: теперь Зорька будет под особым присмотром и опекой. Ведь после всего пережитого ей необходимо как можно больше заботы. Как-никак, кормилица для всей семьи.
 
Истоник: ВЕСТИ Иркутск