ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 23.03.2025
Просмотров: 413
Скачиваний: 1
Программируемый доступ к базе данных
Иногда возникает необходимость решения алгоритмически сложных задач, используя в качестве исходных данные из базы данных. В Delphi предусмотрена возможность обрабатывать данные из баз данных программами на языке Паскаль – выполнять программируемый доступ к базе данных. Так придется поступить, если средствами языка SQL задачу решить невозможно. Кроме того, этот подход полезен при реализации кнопок и пунктов меню для изменения данных. Рассмотрим реализацию программируемого доступа к базе данных. При этом предстоит решить следующие задачи:
организовать доступ к файлам баз данных,
обеспечить передвижение по записям файлов,
поддерживать соответствие между полями в базах данных и переменными программы.
Доступ к файлам баз данных
Предположим, что с помощью Database Desktop файлы базы данных созданы и туда загружены данные. Необходимо знать структуру файлов (имена полей, их типы), а также вторичные индексы. Доступ к файлам базы данных осуществляется с помощью компонент Delphi Table и Query. При использовании Table мы можем получить доступ ко всем полям и записям, при использовании Query – только к тем записям и полям в них, которые выделены в результате выполнения сформулированного на SQL запроса. Использование названных компонент ничем не отличается от описанного выше. Компоненты DataSource, DBGrid и DBEdit не нужны и их мы на форму своего приложения переносить не будем.
Допустим, что на форму перенесена компонента Table и ее свойствам даны описанные выше значения (обеспечен доступ к файлу). Поставим перед собой задачу показать на экране по очереди все записи файла (рис. 7.1). Эту задачу, естественно, можно было решить и без программы на Паскале, но покажем на ее базе некоторые характерные приемы. В качестве файлов используем созданные в гл. 5.
При запуске приложения запускается процедура
procedure TForm1.FormCreate(Sender: TObject);
begin
with Table1 do {Все действия выполняются с Table1}
begin
Active:=True; {Откроем доступ к файлу }
first;{ Установим файл в начало}
{Доступ к полям файла по имени}
Edit1.Text:=IntToStr(FieldByName('SPEC').AsInteger);
{Доступ к полям файла по порядковому номеру}
Edit2.Text:=Fields[1].AsString;
end;
end;
Рис. 7.1. Просмотр файла
В нашем примере путь и имя файла задаются в инспекторе свойств объекта Table. Их можно задавать и программно (как мы дали значение свойству Active). Вспомните стандартное диалоговое окно открытия файлов, рассмотренное нами в п. 2.2.2. Его и можно использовать, чтобы узнать имя открываемого файла.
Для обеспечения передвижения по файлу имеются стандартные процедуры (других средств для этого в нашем случае нет). С одной из них – First – мы уже ознакомились: она обеспечит установку файла на начало. После открытия файл находится в начале и по умолчанию. Остальные процедуры:
Last – текущей становится последняя запись файла;
Neхt – переход к следующей записи;
Prior – переход к предыдущей записи;
MoveBy(N) – обеспечит передвижение на заданное число записей; если N > 0, то вперед, если N < 0, то назад.
Кроме того, имеются две переменных типа Boolean, которые показывают, достигнут ли конец (начало) файла или нет:
EOF=True, если достигнут конец файла, False в остальных случаях;
BOF=True, если достигнуто начало файла, False в остальных случаях.
Вопрос: что означает ситуация EOF=BOF=True?
Реализация остальных кнопок.
procedure TForm1.Button1Click(Sender: TObject);
begin
with Table1 do
begin
{Проверим, достигнут ли конец файла}
if eof then
ShowMessage('Нет следующей записи')
else
begin
next; {Переход к следующей записи}
{Чтение значений из файла}
Edit1.Text:=IntToStr(FieldByName('SPEC').AsInteger);
Edit2.Text:=Fields[1].AsString;
end;
end;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
with Table1 do
begin
{Проверим, достигнуто ли начало файла}
if bof then
ShowMessage('Нет предыдущей записи')
else
begin
prior; {Переход к предыдущей записи}
Edit1.Text:=IntToStr(FieldByName('SPEC').AsInteger);
Edit2.Text:=Fields[1].AsString;
end;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Table1.Active:=False; {Закроем файл}
Close;
end;
В принципе отсутствие проверок достижения конца (начала) файла не является ошибкой, просто записи не будут меняться. Легко заметить, что вместо Edit1.Text значение можно присвоить и обычным переменным программы (глобальным, локальным формальным) для дальнейшего их использования в вычислениях. При этом необходимо строго соблюдать тип переменных и все необходимые преобразования запрограммировать самому. В Delphi свойства AsInteger, AsFloat, AsString, AsDate, AsDateTime, AsBoolean должны соответствовать типу данных в файле. Преобразования между данными разных типов выполняются процедурами val и str и функциями DateToStr (дата в символьную строку), StrToDate (символьная строка в дату), IntToStr (целое в строку), StrToInt (строка в целое) и т.д. Описание этих процедур и функций можно узнать через Help. При их использовании не забывайте, что некоторые преобразования возможны всегда (число в символьную строку), а многие (строка в число) не всегда.
Поиск данных в файле
Задачи на поиск данных в файле обычно имеют следующую формулировку: По какой специальности учится Петров А.Б.? Предположим, что существует файл, среди полей которого имеются ФАМИЛИЯ и СПЕЦИАЛЬНОСТЬ. Поиск выполняем по полю ФАМИЛИЯ, при нахождении требуемой записи считываем значение из поля СПЕЦИАЛЬНОСТЬ. Для организации поиска очень важно, проводится он по индексированным (по ключу или по вторичному индексу – это несущественно) или по неиндексированным полям. Поиск по неиндексированному полю предполагает полный перебор всех записей файла до нахождения нужной записи или до конца файла. Поиск может быть прерван до достижения конца файла, если нас интересует лишь одна запись с требуемым значением в поле и такая запись была найдена или заранее известно, что существует только одна такая запись. В противном случае придется просматривать весь файл. В общем случае недопустим перенос всего файла в массив (запись) и поиск там. Файлы предназначены для хранения большого количества информации, которое обычно не помещается во внутренней памяти ЭВМ. Такой способ обработки допустим, если имеем дело с небольшими файлами и требуется их многократный просмотр.
Проиллюстрируем сказанное на примерах: пример 1 содержит поиск по неиндексированному полю до нахождения первой записи с требуемым значением; пример 2 содержит поиск по индексированному полю. Для примера 1 используем файл студентов. Вид экрана после успешного поиска представлен на рис. 7.2. Если поиск кончился неудачно (студента нет в файле), то выдается соответствующее сообщение.
Запомните правило! Не должно быть программ, которые после завершения не выдают ответа. В любом случае должно быть выдано сообщение о результате работы программы, каким бы ни был этот результат.
Приведем реализацию кнопки НАЧИНАЙ:
procedure TForm1.Button1Click(Sender: TObject);
Var
s1,s2 :string;
b1 :Boolean;
begin
b1:=true;
s1:=Edit1.Text; {Заданная фамилия студента}
with Table1 do
begin
First; {Поиск начинается с первой записи}
while b1 AND NOT EOF do
{ Цикл выполняется до конца файла или до нахождения требуемого студента}
begin
s2:=FieldByName('FAM').AsString;
if s1=s2 then
begin{Студент найден}
Edit2. Text:=IntToStr(FieldByName('SPEC').AsInteger);
b1:=false;
end;
next; {Переход к следующей записи файла}
end;
end;
if b1 then
ShowMessage('Нет такого студента'); {Если студент не найден}
end;
Предупреждение:операция сравнения символьных строк (if s1=s2 ...) опасная: при малейшем несовпадении строк (лишний пробел в начале, использование больших/маленьких букв) равенства не будет!
Рис. 7.2. Поиск по неиндексированному полю
Рассмотрим поиск по индексированному полю, несущественно по ключевому или по вторичному. Вспомним, что файл студентов был индексирован по полю специальность, имя индекса SPEC_IND. Для индексации необходимо войти в Database Desktop, как поступить дальше – см. выше! Вид главной формы с ответом приведен на рис. 7.3. Поиск по индексированному полю заканчивается при нахождении первой записи с заданным значением. Вспомните, что в ключевом поле повторение значений недопустимо, в поле вторичного индекса это, в общем случае, разрешено.
Рис. 7.3. Поиск по индексу
Реализация кнопки НАЧИНАЙ:
procedure TForm1.Button1Click(Sender: TObject);
Var
s1 :integer;
begin
with Table1 do
begin
IndexName:='SPEC_IND'; {Задаем имя индекса}
s1:=StrToInt(Edit1.Text);{Требуемое значение индексного поля}
if Findkey([s1]){Поиск по индексу}
then Edit2.text:=FieldByName('FAM').AsString
else ShowMessage('Не найден');
end;
end;
Дадим некоторые разъяснения к этой программе:
если вторичный индекс определен по одному полю, то вместо имени индекса, как в примере, можно задать имя индексного поля, в данном случае IndexFieldNames:='SPEC';
имя индекса (но не индексного поля) можно задать в свойстве IndexName объекта Table, в таком случае в процедуре соответствующий оператор не требуется;
можно проводить одновременно поиск по нескольким индексным полям, в таком случае IndexFieldNames присваивается несколько имен полей, разделенных точками с запятой, а в FindKey в квадратных скобках должны быть значения для каждого поля.
Имеется процедура поиска FindNearest, которая дает положительный результат поиска всегда: будет найдена наиболее близкая к заданному значению запись. Фактические параметры для него задаются точно так, как для FindKey.