Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.

Предоплата всего

Подписываем
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Предоплата всего
Подписываем
Обзор компонентов, используемых для связи с базами данных.
Перенос полей на форму из редактора полей.
Вычисляемые поля.
Некоторые компоненты визуализации данных.
Компонент Session.
Компонент BatchMove.
Обзор компонентов, используемых для связи с базами данных.
Компоненты, используемые для связи с базами данных, расположены в библиотеки компонентов на странице Data Access (доступ к данным) и Data Controls (управление данными).
Каждое приложение, использующее базы данных, обычно имеет по крайней мере по одному компоненту следующих трех типов:
- Компоненты наборы данных (data set), непосредственно связывающиеся с базой данных. Это такие компоненты, как Table, Query, Stored proc.
- Компонент источник данных (data source), осуществляющий обмен информацией между компонентами первого типа и компонентами визуализации и управления данными. Таким компонентом является DataSource.
Компоненты визуализации и управления данными, такие как DBGrid, DBText, DBEdit и др.
Схема взаимодействия компонентов Delphi с базой данных:
Основные свойства компонента Table и простейшие приложения
на его основе.
Установка связей между компонентами и базой данных, навигация по таблице.
Простейшее приложение для отображения в форме таблицы Pers из базы данных Paradox dbP:
В качестве набора данных используем компонент Table (со страницы Data Access). Источником данных будет компонент DataSource. Это невизуальные компоненты.
В качестве компонента визуализации: DBGrid со страницы Data Control. Это визуальный компонент, в котором будут отображаться данные. Align=alClient.
Установим цепочку связей между этими компонентами в соответствии со схемой взаимодействия компонентов Delphi с базой данных:
Связь между визуальным компонентом и источником данных: свойству DataSource у компонента DBGrid1 в инспекторе объектов присвоить значение из выпадающего списка DataSource1.
Связь между источником данных и набором данных: свойству Dataset у компонента DataSource1 в инспекторе объектов присвоить значение из выпадающего списка Table1.
Связь между набором данных и необходимой таблицей базы данных: для этого предназначены два свойства компонента Table DatabaseName и TableName. В выпадающем списке свойства DatabaseName показываются все доступные BDE псевдонимы баз данных. Выбрать ранее созданный псевдоним dbP. После этого в выпадающем списке свойства TableName появятся таблицы, доступные в данной базе данных. Выберите таблицу Pers.
Можно связаться с таблицей в процессе проектирования, если установить свойство таблицы Table1 Active в true.
Запустив приложение можно просматривать данные, редактировать их (отредактирован-ные данные будут помещаться в базу данных в момент перехода от редактируемой записи к любой другой). Поле Num невозможно изменить, поскольку оно автоматически изменяется и доступно только для чтения. Нельзя задать произвольное имя подразделения Dep, так как имеется таблица Dep, задана целостность на уровне ссылок и допустимы только те значения Dep, которые имеются в головной таблице Dep.
Заранее выставлять для таблиц Active = true допустимо только в процессе настройки и отладки приложения, работающего с локальными базами данных.
В законченном приложении во всех таблицах сначала должно быть установлено Active = false, затем при событии формы эти свойства могут быть установлены в true, а при событии формы onDestroy эти свойства опять должны быть установлены в false. Это исключит неоправданное поддержание связи с базой данных, которое занимает ресурсы, а при работе в сети мешает доступу к базе данных других пользователей. Некоторые свойства компонента DBGrid: ReadOnly запрещение редактирования данных, тот же эффект дает для свойства Options dgEditing=false. У компонента Table есть булево свойство Exclusive определяет доступ к используемой таблице при одновременном обращении к ней нескольких приложений (например, при работе в сети или в многозадачном режиме). Exclusive=true таблица будет закрыта для других приложений, это свойство можно менять только при Active=false. Рассмотрим еще один компонент, управляющий работой с таблицей навигатор DBNavigator (со страницы Data Control). DBNavigator имеет кнопки для управления данными:
nbFirst |
перемещение к первой записи |
nbPrior |
перемещение к предыдущей записи |
nbNext |
перемещение к следующей записи |
nbLast |
перемещение к последней записи |
nbInsert |
вставить новую запись перед текущей |
nbDelete |
удалить текущую запись |
nbEdit |
редактировать текущую запись |
nbPost |
послать отредактированную информацию в базу данных |
nbCancel |
отменить результаты редактирования или добавления новой записи |
bnRefresh |
очистить буфер, связанный с набором данных |
DBNavigator имеет свойство VisibleButtons, пользуясь которым можно убрать любые ненужные в данном приложении кнопки. Если нужно запретить пользователю вводить новые записи nbInsert=false. Если нужно запретить редактирование оставить только кнопки nbFirst, nbPrior, nbNext, nbLast. DBNavigator связывается с источником данных также через свойство DataSource. При работе с DBNavigator внесенные изменения зафиксируются в таблице после нажатия кнопки nbPost. Зададим соединение с базой данных в момент начала работы приложения в событии onCreate:
procedure TForm1.FormCreate(Sender: TObject);
begin
Table1.Active:=true;
end;
И разрыв соединения в момент закрытия формы:
procedure TForm1.FormDestroy(Sender: TObject);
begin
Table1.Active:=false;
end;
Свойства полей
У компонента Table есть свойства IndexName и IndexFieldName.
Свойство IndexName содержит выпадающий список индексов, созданных для таблицы. Если выбрать индекс FIO, записи будут представленными упорядоченными по алфавиту. Индекс depfio упорядочивает по подразделениям, а внутри подразделения по алфавиту. В свойстве IndexFieldName просто перечислены предусмотренные в индексах комбинации полей, если вы забыли. что обозначают имена индексов.
Для редактирования отдельных полей имеется Редактор Полей, вызывается двойным щелчком на компоненте Table.
Каждое поле это объект, свойства которого отображаются в Инспекторе объектов. Класс поля зависит от типа поле: TStringField, TSmallIntField, TBooleanField и т.п. Эти классы являются производными от класса TField базового класса полей.
Некоторые основные свойства полей:
Alignment |
Выравнивание отображаемого текста внутри колонки: влево, вправо, по центру |
DisplayLabel |
Заголовок столбца данного поля |
DisplayWidth |
Ширина колонки число символов |
EditMask, EditFormat |
Форматы отображения данных |
DisplayValue |
Для логических полей: какие значения должны отображаться, если поле имеет значение true или false. |
ReadOnly |
При значании true запрещает вводить данные в поле |
Visible |
При значении false делает поле невидимым |
Вид приложения после установки всех необходимых свойств:
Перенос полей на форму из редактора полей.
Из Редактора Полей можно перетаскивать поля на форму с помощью мыши. Такой упрощенный вариант можно использовать, чтобы просмотреть, что находится в неизвестных таблицах. Например, создадим приложение для просмотра содержимого таблицы biolife.db демонстрационной базы данных DBDEMOS:
- Поместите на форму компонент Table и свяжите его с таблицей biolife.db демонстрационной базы данных DBDEMOS (DatabaseName, TableName).
- Сделайте двойной щелчок на Table1, затем щелкните правой мышкой в окне Редактора полей и из всплявающего меню выберите раздел Add all fields добавить все поля. В окне появяться имена всех полей таблицы. Установите их свойства DisplayLabel, чтобы было понятно то,что отображается (класс, семейство, вид, длина, описание, изображение)
- Щелкните правой кнопкой мыши и из всплывающего меню выберите раздел Select All выделить все поля. Все поля окажутся выделенными. Перетащите их мышью на форму. На форме будут автоматически созданы компоненты, отображающие данные каждого поля и снабженные метками, указанными в свойствах DisplayLabel полей.
- Разместите компоненты должным образом на площади формы, установите в компоненте Table1 свойство Active=true, добавьте навигатор и приложение готово.
На форме автоматически появились:
- источник данных DataSource1, связанный своим свойством DataSet с набором данных Table1;
- компоненты поля редактирования - DBEdit, связанные с данными через свойство Datasource и отображающие данные полей (свойство DataField ) Category(класс), Common_Name(семейство) и т.д. У DBEdit нет свойства Text (оно не объявлено как published для отображения в Инспекторе Объектов). Можно программно во время выполнения устанавливать и читать свойство Text, но нельзя устанавливать его во время проектирования.
- Для отображения поля Notes (Описание) на форме разместился компонент DBMemo1. Это аналог компонента Memo. Поле Graphic (изображение) отображается в компоненте DBImage1 (аналог компонента Image), который используется для просмотра и редактирования изображений. Изображение в DBImage можно вставить во время работы приложения из буфера обмена. В компонентах DBMemo1 и DBImage1 в Инспекторе объеков отсутствуют поля Lines и Picture. Эти свойства можно читать или устанавливать только во время выполнения приложения. Зафиксировать изменения в базе данных можно кнопкой nbPost на навигаторе.
- Чтобы случайно не испортить содержимое демонстрационной базы данных свойству ReadOnly компонента Table1 нужно присвоить значение false.
Ограничения вводимых значений.
Несколько возможноcтей ограничений вводимых значений предоставляют свойства полей:
- Для числовых полей имеются свойства MinValue и Maxvalue. При нарушении этих пределов будет генерироваться исключение EDatabaseError, которое лучше перехватывать в приложении, чтобы выдавать пользователю сообщение на русском языке. например, если требуется принимать на работу сотрудников от 20 до 30 лет, можно установить для поля Year_b ограничения MinValue=1970 и Maxvalue=1980.
- Можно использовать свойства CustomConstraint, которое позволяет написать ограничение на значение поля в виде строки SQL, например: (Year_b>1970) and (Year_b<1980). Свойство ConstraintErrorMessage содержит строку текста, который будет показан пользователю в случае, если он вводит данные, не удовлетворяющие поставленным ограничениям, например: «Возраст претендента на должность не подходит». Это свойство подходит не только к числовым полям.
- Можно использовать обработку события поля onValidate. Это событие возникает перед записью введенного значения поля в буфер записи. Тут можно предусмотреть любые проверки, при появлении недопустимых значений выдать пользователю сообщение.
procedure TForm1.Table1Year_bValidate(Sender: TField);
begin
if (Table1Year_b.Value<1970)and(Table1Year_b.Value>1930) then exit
else begin
ShowMessage('Событие onValidate');
Abort;
end;
end;
Если все нормально, то после события onValidate возникает еще событие onСhange, в обработчике которого тоже еще не поздно сгенерировать исключение.
Есть возможность осуществлять проверку на уровне записи, анализируя различные ее поля. Для этого используется свойство Constraints компонента Table. При выборе этого свойства в Инспекторе объектов открывается окно, в котором, щелкая на кнопке Add можно занести набор ограничений, каждое из которых является самостоятельным объектом со свойствами CustomConstraint (строка SQL, определяющая допустимые ограничения) и ErorMessage (строка текста, которая будет представлена пользователю в случае нарушения ограничений). Например: «Принимаем только мужчин > 1955 г.р. и женщин > 1965 г.р.».
Вычисляемые поля.
Создадим вычисляемое поле, значение которого вычисляется на основании значений других полей записи. Например, поле, вычисляющее возраст сотрудника по году его рождения:
- Вызвать Редактор полей двойным щелчком на Table1. Из контекстного меню Редактора полей выбрать раздел New. Появится окно добавления нового поля в разделе Field Properties (свойства поля) нужно указать:
_ имя поля (Name) назовем его Age
_ тип данных (Type) Smallint;
_ и для некоторых типов (например, для строк) размер (Size).
- В группе радиокнопок Field Type выбрать Calculated.
- Нажать на кнопку OK и вернуться в окно Редактора полей, там появится новое поле Age. Изменить для этого поля в Инспекторе объектов значение DisplayLabel на «Возраст».
- Чтобы указать процедуру вычислений надо выйти из Редактора полей, выделить Table1, перейти в Инспектор объектов и задать обработчик события OnCalcFields, которое наступает каждый раз, когда нада обновить значение вычисляемых полей таблицы. В этом обработчике можно использовать процедуру DecodeDate для преобразования ее первого аргумента, имеющего тип TDateTime (этот тип используется в Delphi для хранения дат и времени), в целые значения года, месяца и дня.
Заголовок процедуры:
procedure DecodeDate(Date: TDateTime; var Year, Month, Day: Word);
Следующий программный код можно использовать, чтобы отобразить текущие значения года, месяца, дня, часа, минуты, секунды, миллисекунды из функции Now, которая возвращает значение текущих даты и времени: Date + Time:
function Now: TDateTime;
procedure TForm1.Button1Click(Sender: TObject);
var
Present: TDateTime;
Year, Month, Day, Hour, Min, Sec, MSec: Word;
begin
Present:= Now;
DecodeDate(Present, Year, Month, Day);
Label1.Caption := 'Сегодня число ' + IntToStr(Day) + ' месяца ' + IntToStr(Month) + ' года ' + IntToStr(Year);
DecodeTime(Present, Hour, Min, Sec, MSec);
Label2.Caption := IntToStr(Hour)+ ' часов '+ IntToStr(Min) + ' минуты ' ;
end;
Результат: Обработчик события OnCalcFields может выглядеть
следующим образом:
procedure TForm1.Table1CalcFields (DataSet: TDataSet);
Var
Year, Month, Day:Word;
begin
DecodeDate(Date,Year,Month,Day);
Table1Age.Value:=Year- Table1Year_b.Value;
end;
Фильтрация данных.
Компонент Table позволяет также отфильтровывать данные по определенным критериям с использоваием свойств: Filter, Filtered, FilterOptions.
Свойство Filtered включает или выключает использование фильтра.
В свойстве Filter записывается сам фильтр в виде строки, содержащей ограничения на значения полей. Например, если свойство Filtered=true и в свойстве Filter записано:
- Dep=Цех 1, то в таблице отобразятся только те записи, в которых поле Dep имеет значение Цех 1
- Dep=Цех * отображение всех записей, в которых значение поля Dep начинается с Цех (При этом опция foNoPartialCompare свойства FilterOptions должно быть false). Свойство FilterOptions содержит опции foNoPartialCompare(=true запрещает частичное совпадение при сравнении) и foCaseInsentitive делает сравнение строк нечувствительным к региситру (если включить эту опцию, то «Цех 1» и «цех 1» будут считаться идентичными).
- При записи условий можно использовать операции отношения =,>,>=,<,<=,<>, а также логические операции and, or, not. Фильтр:
(Dep=Цех 1) and (Year_b<=1970) and (Year_b>=1940)
отобразит сотрудников цеха 1, чей год рождения лежит в заданных пределах. Но использовать в фильтре имена вычисляемых полей (например, поля Age) не разрешается.
Добавим в приложение возможность фильтрации отображаемых записей по отделам, по возрасту и по обоим критериям (см. Рис):
- Перенесем на форму группу радиокнопок RadioGroup (Name=RGF). Зададим в редакторе свойству RGF.Items строки: Нет, Все, Отдел, Возраст. RGF.ItemIndex=1, RGF.Columns=2, RGF.Caption=Фильтрация
- Перенесем на форму выпадающий список ComboBox (Name=CBDep) для выбора подразделения, по которому производится фильтрация. В свойство CBDep.Items занести список имен подразделений.
- Два элемента SpinEdit со страницы Samples (Name=SEmin и SEmax) для задания диапазона возраста при фильтрации. Задать в этих компонентах соотвествующие значения MaxValue, MinValue, Value.
- Кнопку с заголовком «Обновить».
- Фильтрация в приложении должна проводиться при следующих событиях:
- щелчок в группе радиокнопок RGFClick (его текст приведен ниже, для остальных событий проводится делегирование),
- при событии OnChange выпадающего списка отделов
- щелчок на кнопке «Обновить» (при изменении диапазона возраста).
procedure TForm1.RGFClick(Sender: TObject);
begin
Table1.IndexName:='depfio';
if (RGF.ItemIndex=0) then Table1.Filtered:=false //отсутствие фильтрации
else begin if (RGF.ItemIndex=2) //фильтрация по отделу 'Dep=…
then Table1.Filter:='Dep='''+CBDep.Text+''''
else if (RGF.ItemIndex=3) //фильтрация по возрасту Year_b<=… и Year_b>=…
then begin
Table1.Filter:='(Year_b<='+IntToStr(Year-SEMin.Value) +')and(Year_b>='+IntToStr(Year-SEMax.Value)+')';
Table1.IndexName:='Year';
end
else begin //фильтрация по отделу и по возрасту
Table1.Filter:='(Dep='''+CBDep.Text+''')and(Year_b<='+IntToStr(Year-SEMin.Value)
+')and(Year_b>='+IntToStr(Year-SEMax.Value)+')';
end;
Table1.Filtered:=true;
end;
end;
(!!!Второй способ обеспечить фильтрацию использовать обработку события OnFilterRecord!!!).
!!!Использование словарей атрибутов полей.!!!
Создать новый словарь можно с помощью программы SQL Explorer, вызываемой командой главного меню Database|Explore. Для создания нового словаря нада перейти на страницу Dictionary и выполнить команду Dictionary|New. Откроется диалоговое окно создания нового словаря:
-
в первой сторочке нужно указать имя, по которому в дальнейшем можно выбрать этот словарь из других;
- во второй строчке база данных, в которой сохраняется словарь;
-
в третьей строчке имя таблицы, в которой будет сохраняться словать (словарь по умолчанию сохраняется в виде таблицы Paradox);
- в четвертой строчке произвольный комментарий.
Зарегистрировать имеющийся словарь можно командой Dictionary|Registry, выбрав базу данных и имя таблицы:
Некоторые компоненты визуализации данных.
Компонент DBGrid имеет свойство Columns, представляющее собой набор объектов, каждый из которых отражает один солбец таблицы. При выделении этого свойства появляется кнопка, при нажатии на которую открывается окно Редактора Столбцов. Добавлять столбцы в Редактора Столбцов можно по одному, щелкая на первой слева кнопке Add и указывая для них в Инспекторе Объектов соответствующие поля в свойстве FieldName (или выбирая в контекстном меню раздел Add). А можно выбрать все столбцы сразу, выбрав в контекстном меню раздел Add All Fields или щелкнув на второй кнопке справа.
Для выделенного столбца в Инспекторе Объектов отметим следующие свойства:
- ButtonStyle определяет стиль ввода данных в поле текущей записи, может принимать следующие значения:
cbsAuto |
Появление при редактировании кнопки, связанной с выпадающим списком допустимых значений |
cbsEllipsis |
Появление при редактировании кнопки с многоточием …, при щелчке на которой возникает событие OnEditButtonClick компонента DBGrid, в котором можно предусмотреть выбор соответствующего значения. Какое именно поле редактируется в обработчике OnEditButtonClick можно узнать по свойству SelectedField компонента DBGrid. Через это же свойство в поле заносится установленное пользователем значение. |
cbsNone |
Обычное редактирование без каких-либо кнопок |
-
PickList список допустимых значений поля (для столбца Dep можно занести «Бухгалтерия», «Цех 1», «Цех 2»; а для столбца Sex «м», «ж».). Если список PickList не заполнен, то никакой кнопки при редактировании не появляется.
- DropDownRows (по умолчанию 7) определяет допустимое число строк в списке, отображаемое без появления полос прокрутки. Если реальное число строк меньше, размер списка устанавливается автоматически.
Обзор компонентов отображения данных со страницы Data Control:
DBText |
аналог обычной метки Label, но связанный сданными. Позволяет отображать данные некоторого поля, но не дает возможности его редактировать. Тип отображаемого поля может быть различным: строка, число, булева величина. Компонент автоматически переводит соответствующие типы в отображаеме символы. |
DBEdit |
связанный с данными аналог обычного окна редактирования Edit. Позволяет отображать и редактировать данные полей различных типов: строка, число, булева величина. Если задать в компоненте свойство ReadOnly=true, то он превратиться в элемент отображения, как и DBText |
DBMemo |
связанный с данными аналог обычного многострочного редактора Memo. Позволяет отображать и редактировать данные поля типа Memo, а также данные любых типов, указанных выше для предыдущих компонентов. |
DBRichEdit |
связанный с данными аналог обычного многострочного окна редактирования текста в обогащенном формате RTF, область применения та же, что и для компонента DBMemo. |
DBImage |
связанный с данными аналог обычного компонента Image. Компонент позволяет отображать графические поля, например фоторгафии сотрудников. |
DBCheckBox |
связанный с данными аналог обычного индикатора CheckBox. Позволяет отображать и редактировать данные поля булевского типа. Если при выводе ланных поле имеет значение True, то индикатор включсется. И наоборот, при редактировании поля присваиваемое ему значение определяется состоянием индикатора. |
DBRadioGroup |
связанный с данными аналог группы радиокнопок RadioGroup. Позволяет отображать и редактировать поля с ограниченным множеством возможных значений. В нашеи примере это может относится к полю Dep.!!! |
|
|
Все перечисленные компоненты имеют свойства DataSource источник данных (компонент типа TDataSource) и DataField поле, с которым связан компонент.
Для всех этих компонентов в Инспекторе Объектов отсутствуют основные свойства, отображающие содержание: Caption, Text, Image и т.п. Все эти свойства доступны в компонентах толь ко во время выполнения. Эти свойства это значения соответствующих полей таблицы базы данных.
Компонент Session.
Компоненты Session осуществляют общее управление связыванием приложения с базами данных. Delphi автоматически генерирует объект Session в каждом приложении, работающем с базами данных. На этот объект можно ссылаться через глобальную переменную Session.
Для демонстрации методов компонента Session построим приложение, позволяющее просматривать любую заданную пользователем таблицу в любой заданной им доступной базе данных. Это приложение содержит два выпадающих списка типа TComboBox, названных cbAlias для выбора пользователем базы данных и cbTable для выбора таблицы. кнопка просмотр предназначена для просмотра выбранной таблицы в компоненте TDBGrid, связанной цепочкой ссылок с компонентами DataSource1 и Table1.
При создании формы, в обработчике события onCreate нужно загрузить список cbAlias доступными BDE псевдонимами. Это делает метод GetAliasNames объекта Session, который передает в свой параметр типа Tstrings перечень псевдонимов баз данных, зарегистрированных в BDE.
procedure GetAliasNames(List: TStrings);
Обработчик события onCreate должен выглядеть следующим образом:
procedure TForm1.FormCreate(Sender: TObject);
begin
Session.GetAliasNames(cbAlias.Items);//список псевдонимов передается в список cbAlias
end;
При выборе пользователем базы данных в списке cbAlias надо загрузить список cbTable перечнем таблиц выбранной базы данных. Это можно сделать включением в обработчик события onChange компонента cbAlias вызов метода GetTableNames объекта Session:
procedure GetTableNames(const DatabaseName, Pattern: String; Extensions, SystemTables: Boolean; List: TStrings);
Метод GetTableNames загружает в свой последний параметр перечень таблиц базы данных, заданной своим первым аргументом. Второй параметр метода позволяет задать шаблон, отбирающий имена таблиц («р*» - отберет имена таблиц, начинающихся с символа «р»). Пустой шаблон означает выбор всех таблиц. Третий параметр, установленный в true, означает, что в имена таблиц будет включаться расширение файла (это необходимо для таблиц Paradox и dBase). Четвертый параметр задается равным false для баз данных Paradox и dBase, а для баз данных, основанных на SQL, этот параметр устанавливается в true, чтобы возвращать и таблицы двнных и системные таблицы, определяющие структуру данных.
Обработчик события onChange компонента cbAlias:
procedure TForm1.cbAliasChange(Sender: TObject);
begin
Session.GetTableNames(cbAlias.Text,'',true,false,cbTable.Items);
cbTable.ItemIndex:=0;
end;
Обработчик нажатия кнопки «Просмотр»:
procedure TForm1.Button1Click(Sender: TObject);
begin
if cbTable.Text=''
then begin ShowMessage(''); exit; end;
Table1.Active:=false;
Table1.DatabaseName:=cbAlias.Text;//задание свойства DatabaseName
Table1.TableName:=cbTable.Text;// задание свойства TableName
Table1.Active:=true;
end;
Модуль приложения:
unit USession;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Db, DBTables, Grids, DBGrids;
type
TForm1 = class(TForm)
cbAlias: TComboBox;
cbTable: TComboBox;
StaticText1: TStaticText;
StaticText2: TStaticText;
Button1: TButton;
DBGrid1: TDBGrid;
DataSource1: TDataSource;
Table1: TTable;
procedure FormCreate(Sender: TObject);
procedure cbAliasChange(Sender: TObject);
procedure Button1Click(Sender: TObject);
private { Private declarations }
public { Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
Session.GetAliasNames(cbAlias.Items);
end;
procedure TForm1.cbAliasChange(Sender: TObject);
begin
Session.GetTableNames(cbAlias.Text,'',true,false,cbTable.Items);
cbTable.ItemIndex:=0;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if cbTable.Text=''
then begin
ShowMessage(''); exit;
end;
Table1.Active:=false;
Table1.DatabaseName:=cbAlias.Text;
Table1.TableName:=cbTable.Text;
Table1.Active:=true;
end;
end.
Компонент BatchMove.
Предназначен для групповых операций переноса данных из одного набора в другой. Основные свойства:
- Source источник данных;
- Destination приемник данных типа TTable;
- Mode определяет режим переноса данных:
batAppend |
Записи из источника добавляются в приемник, не изменяя существующих там записей. Таблица приемник должна существовать до начала переноса данных |
batUpdate |
Записи в таблице приемнике с ключевыми полями, соответствующими полям в таблице источнике, изменяются на записи из источника. Новые записи в приемник не добавляются. Таблица приемник должна существовать до начала переноса данных и должна иметь индекс. |
batAppendUpdate |
Записи в таблице приемнике с ключевыми полями, соответствующими полям в таблице источнике, изменяются на записи из источника. Записи из таблицы источника, которые не имеют соответствия в приемнике, добавляются туда. Таблица приемник должна существовать до начала переноса данных и должна иметь индекс. |
batDelete |
Записи в таблице приемнике, которым находится соответствие в источнике, удаляются из приемника. Таблица приемник должна существовать до начала переноса данных и должна иметь индекс. |
batCopy |
Таблица приемник создантся и заполняется записями источника. Если таблица приемник уже существовала, то ее содеожимое заменяется на содержимое источника. |
Основной метод компонента Execute выполняет операцию переноса данных. Свойство MovedCount указывает число записей, успешно перенесенных в таблицу-приемник.
При переносе записей из одной таблицы в другую могут возникать проблемы, связанные с несоответствием типов полей в таблице-источнике и таблице-приемнике. В этих случаях свойство AbortOnProblem указывает, должна ли немедленно прекращаться операция, вызвавшая несоответствие типов полей в таблице-источнике и таблице-приемнике.
При задании AbortOnProblem=false желательно одновременно задать свойство ProblemTableName. Это свойство указывает имя таблицы Paradox, в которую будут помещаться записи, в которых обнаружено несоответствие типов полей и которые поэтому не помещены в приемник. Свойство ProblemCount определяет число записей, вкоторых возникли такие проблемы. При задании AbortOnProblem=false записи с полями несоответствующих типов все-таки могут помещаться в таблицу-приемник компонент BatchMove пытается в этом случае преобразоватьтип поля таблицы-источника в тип поля таблицы-приемника. Если такое преобразование возможно, то никаких проблем при переносе записей не возникает.
В некоторых случаях перенос отдельных записей может оказаться невозможным из=за того, что они нарушают целостность данных в таблице-приемнике, например, дублируют значение ключевого поля, которые должны быть уникальнымиВ этих случаях анализируется свойство AbortOnKeyViol и соответствующие ему KeyViolTableName и KeyViolCount.
- !!!ChangedTableName, ChangeCount
- Свойство Mappings типа Tstrings позволяет задать таблицу соответствия полей источника и приемника. По умолчанию поля таблиц переносятся в поля приемника с теми же именами и в той же последовательности, как в источнике (Свойство Mappings задавать не надо). В свойстве Mappings можно указать только часть полей, значения которых надо переносить и в какое поле приемника надо переносить значение поля источника. Если в свойстве Mappings задать строку
DepNew=Dep
то значение поля Dep из источника будет переносится в поле DepNew приемника.
Таким образом, компонент BatchMove является удобным средством копирования, объединения, упорядочивания таблиц.
Построим пример для просмотра режимов работы BatchMove. Будем создавать копию таблицы Pers базы данных dbP и дальше использовать эту копию как приемник. Копия будет располагаться в той же базе данных dbP и иметь имя Pers2:
- перенести на форму по 2 экземпляра компонентов Table, DataSource, DBGrid.
- Table1, DataSource1, DBGrid1 свяжите с источником таблицей Pers базы данных dbP. В компоненте DBGrid1 можно будет просматривать и менять содержащуюся в ней информацию.
- Table2, DataSource2, DBGrid2 свяжите друг с другом, но не связывайте Table2 ни с какой базой данных. Этот компонент будет приемником, в который будут переносится даные их первой таблицы.
- Добавьте на форму компонент BatchMove. Source=Table1, Destination=Table2. Все режимы переноса будут задаваться программно.
- Добавьте на форму метку Label для отображения числа скопированных записей.
- Добавьте на форму кнопки Button, соответствующих различным режимам переноса данных («копировать» для режима batCopy, «добавить» для режима batAppend и т.п.)
- Добавьте на форму кнопку «удалить таблицу».
- В раздел private описания класса добавить процедуру Exec, которая будут выполнять перенос данных из источника в приемник в заданном режиме:
procedure TForm1.Exec;
begin
Table2.Active:=false; //таблица-приемник закрывается
Table2.DatabaseName:=Table1.DatabaseName; //задается база данных
Table2.TableName:='Pers2.db'; //задается таблица-приемник
if (BatchMove1.Mode=batCopy)or(Table2.Exists) then begin //проверяется режим копирования и существование таблицы 2; для всех режимов кроме batCopy таблица должна существовать
BatchMove1.Execute; //осуществляется перенос данных
Table2.Active:=true; //таблица-приемник открывается и в DBGrid2 можно видеть ее содержимое
Label1.Caption:='Обработано '+IntToStr(BatchMove1.MovedCount)+' записей';
end else ShowMessage('Для выполнения этой операции'+#13+'таблица должна существовать');
end;
- Обработчики щелчков на кнопках Button1,2 и т.д. задают режим переноса данных и вызывают процедуру Exec:
procedure TForm1.Button1Click(Sender: TObject); //копировать таблицу
begin
BatchMove1.Mode:=batCopy;
Exec;
end;
- Обработчик щелчка на кнопке «Удалить таблицу»:
procedure TForm1.Button3Click(Sender: TObject); //удалить таблицу
begin
if (Table2.Exists) then begin //если таблица существует
Table2.Close;
Table2.DeleteTable; //таблица удаляется методом DeleteTable
end;
end;
Добавить обработчики для формы OnCreate ( Table1.Active:=true;) и OnDestroy
( Table1.Active:=false;
Table2.Active:=false;)
- Проверить работоспособность приложения и различные способы объединения данных двух таблиц.
- Добавить в приложение возможности из примера, использующего методы компонента Session: обеспечить выбор базы данных и копируемой таблицы. Добавить возможность сохранять таблицу приемник в указанном каталоге под указанным именем (добавив компонент SaveDialog).
Для перекачки данных из таблиц, созданных в одной СУБД, в таблицы другой СУБД требуется только совместимость типов полей этих таблиц.
Модуль приложения:
unit UBatchmove;
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Grids, DBGrids, Db, DBTables, StdCtrls;
type
TForm1 = class(TForm)
Table1: TTable;
Table2: TTable;
DataSource1: TDataSource;
DataSource2: TDataSource;
DBGrid1: TDBGrid;
DBGrid2: TDBGrid;
BatchMove1: TBatchMove;
Label1: TLabel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button2Click(Sender: TObject);
private { Private declarations }
procedure Exec;
public { Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Exec;
begin
Table2.Active:=false; //таблица-приемник закрывается
Table2.DatabaseName:=Table1.DatabaseName; //задается база данных
Table2.TableName:='Pers2.db'; //задается таблица-приемник
if (BatchMove1.Mode=batCopy)or(Table2.Exists) then begin //для всех режимов кроме batCopy таблица должна существовать
BatchMove1.Execute; //осуществляется перенос данных
Table2.Active:=true; //таблица-приемник открывается и в DBGrid2 можновидеть ее содержимое
Label1.Caption:='Обработано '+IntToStr(BatchMove1.MovedCount)+' записей';
end else ShowMessage('Для выполнения этой операции'+#13+'таблица должна существовать');
end;
procedure TForm1.Button1Click(Sender: TObject); //копировать таблицу
begin
BatchMove1.Mode:=batCopy;
Exec;
end;
procedure TForm1.Button3Click(Sender: TObject); //удалить таблицу
begin
if (Table2.Exists) then begin
Table2.Close;
Table2.DeleteTable;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Table1.Active:=true;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Table1.Active:=false;
Table2.Active:=false;
end;
procedure TForm1.Button2Click(Sender: TObject); //добавить записи
begin
BatchMove1.Mode:=batAppend;
Exec;
end;
end.
Пример.
unit Udb2;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DBCtrls, DB, DBTables, Grids, DBGrids, ExtCtrls, Mask, Spin, ComCtrls;
type
TForm1 = class(TForm)
PLeft: TPanel;
DataSource1: TDataSource;
Table1: TTable;
Table1Dep: TStringField;
Table1Proisv: TBooleanField;
Table3: TTable;
Table3Dep: TStringField;
Table3Proisv: TBooleanField;
DataSource3: TDataSource;
DBEdit1: TDBEdit;
DataSource2: TDataSource;
Table2: TTable;
Table2Dep: TStringField;
Table2Fam: TStringField;
Table2Nam: TStringField;
Table2Par: TStringField;
Table2Year_b: TSmallintField;
Table2Sex: TBooleanField;
CheckBox1: TCheckBox;
PanelTop: TPanel;
LabelMain: TLabel;
PanelDep: TPanel;
PanelTyp: TPanel;
PanelPers: TPanel;
TPers: TDBGrid;
Table2Age: TSmallintField;
Table2Photo: TGraphicField;
Button2: TButton;
PageControl1: TPageControl;
TabSearch: TTabSheet;
TabEdit: TTabSheet;
RGF: TRadioGroup;
Button1: TButton;
LabelAge: TLabel;
LabelMin: TLabel;
SEmin: TSpinEdit;
LabelMax: TLabel;
SEmax: TSpinEdit;
RSex: TRadioGroup;
LabelSearch: TLabel;
PanelFam: TPanel;
Edit1: TEdit;
LabelDep: TLabel;
CBEDep: TComboBox;
LabelFam: TLabel;
EFam: TEdit;
LabelNam: TLabel;
ENam: TEdit;
EPar: TEdit;
LabelYear: TLabel;
SEYear: TSpinEdit;
RGSex: TRadioGroup;
BPost: TButton;
LabelPar: TLabel;
DBNavigator1: TDBNavigator;
Table2Charact: TMemoField;
BInsert: TButton;
BDelete: TButton;
CBDep: TComboBox;
procedure CBDepChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Table2CalcFields(DataSet: TDataset);
procedure Edit1Change(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
procedure BPostClick(Sender: TObject);
procedure RGFClick(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure PageControl1Change(Sender: TObject);
procedure Table2AfterScroll(DataSet: TDataSet);
procedure Table2BeforePost(DataSet: TDataSet);
procedure BInsertClick(Sender: TObject);
procedure BDeleteClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private{ Private declarations }
public { Public declarations }
end;
var
Form1: TForm1;
const ChangePhoto:boolean=false;
implementation
{$R *.DFM}
uses udba2;
var Year, Month, Day: Word;
const CanPost:boolean=false;
procedure TForm1.CBDepChange(Sender: TObject);
begin
with Table2 do
if CBDep.ItemIndex = CBDep.Items.Count-1 then
begin
MasterFields := '';
IndexFieldNames := 'Fam;Nam;Par';
DBEdit1.DataSource := nil;
end
else
begin
MasterFields := 'Dep';
IndexFieldNames := 'Dep;Fam;Nam;Par';
DBEdit1.DataSource := DataSource1;
Table1.FindNearest([CBDep.Text]);
end;
{Передача фокуса таблице Tpers, иначе в ней
не отразятся изменения}
Tpers.SetFocus;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
{Заполнение ComboBox CBdep и CBEDep именами отделов}
DecodeDate(Date,Year,Month,Day);
Table1.Active := true;
Table1.First;
CBDep.Clear;
CBEDep.Clear;
while not Table1.eof do
begin
CBDep.Items.Add(Table1Dep.AsString);
CBEDep.Items.Add(Table1Dep.AsString);
Table1.Next;
end;
CBdep.Items.Add('все отделы');
CBDep.ItemIndex:=0;
CBEDep.ItemIndex:=0;
Table1.First;
Table2.Active := true;
Table3.Active := true;
PageControl1.ActivePage := TabSearch;
end;
procedure TForm1.Table2CalcFields(DataSet: TDataset);
begin
table2Age.value:=Year-table2year_b.Value;
end;
procedure TForm1.Edit1Change(Sender: TObject);
begin
if CBdep.Text = 'все отделы'
then Table2.FindNearest([Edit1.Text])
else Table2.FindNearest([CBdep.Text,Edit1.Text]);
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
Table2Dep.Visible := CheckBox1.Checked;
end;
procedure TForm1.BPostClick(Sender: TObject);
var s:string;
const s1=',';
begin
s:='';
if Table2Dep.AsString <> CBEDep.Text
then s := 'отдел';
if Table2Fam.AsString <> EFam.Text
then begin
if s <> '' then s := s + s1;
s := s + ' фамилию';
end;
if Table2Nam.AsString <> ENam.Text
then begin
if s <> '' then s := s + s1;
s := s + ' имя';
end;
if Table2Par.AsString <> EPar.Text
then begin
if s <> '' then s := s + s1;
s := s + ' отчество';
end;
if Table2Year_b.AsInteger <> SEYear.Value
then begin
if s <> '' then s := s + s1;
s := s + ' год рождения';
end;
if Table2Sex.AsBoolean <> (RGSex.ItemIndex = 0)
then begin
if s <> '' then s := s + s1;
s := s + ' пол';
end;
if Form2.DBMemo1.Modified
then begin
if s <> '' then s := s + s1;
s := s + ' характеристику';
end;
if ChangePhoto
then begin
if s <> '' then s := s + s1;
s := s + ' фотографию';
end;
if s <> '' then if Application.MessageBox(PChar('Действительно хотите изменить '+s+'?'),
'Подтвердите сохранение изменений', MB_YESNOCANCEL+MB_ICONQUESTION) = IDYES
then begin
Table2.Edit;
Table2Dep.AsString := CBEDep.Text;
Table2Fam.AsString := EFam.Text;
Table2Nam.AsString := ENam.Text;
Table2Par.AsString := EPar.Text;
Table2Year_b.AsInteger := SEYear.Value;
Table2Sex.AsBoolean := (RGSex.ItemIndex = 0);
CanPost := true;
Table2.Post;
CanPost := false;
Form2.DBMemo1.Modified := false;
ChangePhoto:=false;
end;
end;
procedure TForm1.RGFClick(Sender: TObject);
var s:string[50];
begin
s:='';
Case RGF.ItemIndex of
1,2: s:='(Year_b<='+IntToStr(Year-SEmin.Value) +')and(Year_b>='+IntToStr(Year-SEmax.Value)+')';
end;
Case RGF.ItemIndex of
1,3: begin
if s<>'' then s:=s+'and';
if RSex.ItemIndex=0
then s:=s+'(Sex=true)'
else s:=s+'(Sex=false)';
end;
end;
Table2.Filter := s;
Table2.Filtered := (RGF.ItemIndex > 0);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
If not Form2.Visible then Form2.Show;
end;
procedure TForm1.PageControl1Change(Sender: TObject);
begin
if PageControl1.ActivePage = TabEdit
then begin
Form2.DBMemo1.ReadOnly := false;
Table2AfterScroll(Table2);
end
else Form2.DBMemo1.ReadOnly := true;
end;
procedure TForm1.Table2AfterScroll(DataSet: TDataSet);
begin
if PageControl1.ActivePage = TabEdit
then begin
RGF.ItemIndex := 0;
CBEDep.ItemIndex:=CBEDep.Items.IndexOf(Table2Dep.AsString);
EFam.Text := Table2Fam.AsString;
ENam.Text := Table2Nam.AsString;
EPar.Text := Table2Par.AsString;
SEYear.Value := Table2Year_b.AsInteger;
if Table2Sex.AsBoolean
then RGSex.ItemIndex := 0
else RGSex.ItemIndex := 1;
end
end;
procedure TForm1.Table2BeforePost(DataSet: TDataSet);
begin
if not CanPost
then begin
DataSet.Cancel;
Abort;
end;
end;
procedure TForm1.BInsertClick(Sender: TObject);
begin
Table2.Insert;
PageControl1Change(Self);
end;
procedure TForm1.BDeleteClick(Sender: TObject);
begin
if MessageDlg('Действительно хотите удалить запись?', mtConfirmation,[mbYes,mbNo],0) = mrYes
then Table2.Delete;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Table1.Active := false;
Table2.Active := false;
Table3.Active := false;
end;
end.
unit Udba2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, DBCtrls, StdCtrls, ExtCtrls;
type
TForm2 = class(TForm)
DBMemo1: TDBMemo;
PPhoto: TPanel;
DBImage1: TDBImage;
procedure DBImage1Click(Sender: TObject);
private { Private declarations }
public { Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.DFM}
uses Udb2;
procedure TForm2.DBImage1Click(Sender: TObject);
begin
ChangePhoto := true;
end;
end.