Агрегатные поля
Агрегатные поля предназначены для выполнения вычислительных операций со значениями полей набора данных с использованием агрегатных функций SQL. К таким функциям относятся:
AVG — вычисляет среднее значение; COUNT — возвращает число записей; MIN — вычисляет минимальное значение; МАХ — вычисляет максимальное значение; SUM — вычисляет сумму.Агрегатные поля не входят в структуру полей набора данных, т. к. агрегатные функции подразумевают объединение записей таблицы для получения результата. Следовательно, значение агрегатного поля нельзя связать с какой-то одной записью, оно относится ко всем или группе записей.
Использование агрегатных полей возможно только в компоненте TClientDataSet и его аналогах, т. к. он обеспечивает кэширование данных, необходимое для проведения вычислений (см. гл. 22).
Агрегатные поля не отображаются вместе со всеми полями в компонентах TDBGrid, в Редакторе полей они расположены в отдельном списке, а их свойство index (см. выше) всегда имеет значение —1. Для представления значения агрегатного поля можно воспользоваться одним из компонентов отображения данных, который визуализирует значение одного поля (например, TDBText или TDBEdit), или свойствами самого поля:
Labell.Caption := MyDataSetAGGRFIELDl.AsString;
Для создания агрегатного поля необходимо использовать команду New field из всплывающего меню Редактора полей.
Для представления агрегатных полей имеется специальный класс
TAggregateField.
Его свойство
property Expression: string;
задает вычисляемое выражение.
В его состав могут входить агрегатные функции, имена полей набора данных и простейшие арифметические операции:
SUM(Pirce*ItemCount) - SUM(Balance)
Вычисление значения проводится только для тех агрегатных полей, свойство
property Active: Boolean;
которых имеет значение True.
Вычисление включенных свойством Active агрегатных полей выполняется только в том случае, если булевское свойство AggregatesActive клиентского компонента набора данных имеет значение True.
По умолчанию экземпляр класса TAggregateFieid создается со свойством
Visible = False.
Свойство
property GroupingLevel: Integer;
задает уровень группировки полей набора данных при вычислении. При значении 0 расчет проводится для всех записей набора данных. При значении 1 записи группируются по первому полю набора данных и расчет осуществляется для каждой группы. При значении 2 записи разбиваются на группы по первому и второму полям и т. д.
Однако группировка по уровням выше нулевого возможна, только если в наборе данных используется индекс по группирующим полям. Например, если свойство GroupingLevel = 2 и набор данных начинаются с полей СustNo и orderNo, в свойстве indexName компонента набора данных и свойстве
property IndexName: String;
объекта агрегатного поля должно быть имя индекса, включающего оба эти поля.
По причине необходимости подключения индексов, уровень группировки выше нулевого возможен только в табличных компонентах.
Класс TField
Как уже говорилось выше, в большой иерархии классов для полей различных типов данных класс TField является базовым (см. рис. 13.1), он инкапсулирует свойства и методы абстрактного поля данных. Именно от него происходят все классы типизированных полей. В реальной работе класс TField не используется, но его значение трудно переоценить. Практически все основные свойства классов типизированных полей унаследованы от класса TField без каких-либо изменений, а дополнительные свойства и методы обеспечивают работу конкретного типа данных.
Что касается методов-обработчиков событий, то четыре метода, определенные в классе TField, наследуются всеми потомками без изменения и дополнения.
Ниже приведены свойства и методы класса TField. Имя объекта содержит свойство
property Name: TComponentName;
При создании объекта поля на этапе разработки имя объекта складывается из имени соответствующего компонента набора данных и имени поля.
Свойство
property FieldName: String;
возвращает имя поля таблицы базы данных. Свойство
property FullName: string;
используется, если текущее поле является дочерним для другого поля. В этом случае свойство содержит имена всех родительских полей.
Название поля в таблице базы данных содержится в свойстве
property Origin: String;
Свойство
property FieldNo: Integer;
возвращает исходный порядковый номер поля в наборе данных. Если объекты полей являются статическими, их фактический порядок может быть изменен в Редакторе полей.
Свойство
property Index: Integer;
содержит индекс объекта поля в списке Fields.
Функциональное назначение поля определяется свойством
type TFieldKind = (fkData, fkCalculated, fkLookup, fklnternalCalc,
fkAggregate);
property FieldKind: TFieldKind;
В большинстве случаев его значение определяется автоматически в момент создания объекта поля. Да и впоследствии вряд ли возникнет необходимость сделать реальное поле данных вычисляемым. Обычно попытка изменить значение свойства FieldKind вызывает ошибку. Рассмотрим возможные значения этого свойства:
fkData — поле данных; fkCalculated — вычисляемое поле; fkLookup — поле синхронного просмотра; fklnternalCalc — внутреннее вычисляемое поле; fkAggregate — агрегатное поле. Если поле является вычисляемым, свойство
property Calculated: Boolean;
принимает значение True.
На связанный набор данных указывает свойство
property DataSet: TDataSet;
которое при создании объекта средствами среды разработки заполняется автоматически.
Свойство
property DataType: TFieldType;
возвращает тип данных поля, а свойство
property DataSize: Integer;
содержит объем памяти, необходимый для хранения значения поля.
Одной из важнейших задач класса TFieid является обеспечение доступа к текущему значению поля. В этом случае класс взаимодействует с буфером текущей записи набора данных, а значение можно получить при помощи нескольких свойств.
Свойство
property Value: Variant
всегда содержит значение, которое сохранено после последнего выполнения метода Post набора данных:
with Tablel do begin Open;
while Not EOF do begin
if Fields[0].Value > 10
then Fields[1].Value := Fields[1].Value*2;
Next;
end;
Close;
end;
В этом примере при помощи метода Next осуществляется перебор всех записей набора данных. Если значение первого поля больше 10, то значение второго поля удваивается. Для этого применяется свойство value объектов полей набора данных.
Однако из-за использования вариантов свойство value является относительно медленным. И для преобразования текущего значения поля к необходимому виду можно применять целую группу быстрых свойств AS ..., которые содержат значение в определенном типе данных. Чаще всего используется свойство Asstring, например, оно может применяться для представления числовых значений полей в элементах управления:
Editl.Text := Tablel.Fields[0].AsString;
Примечание
При работе со статическими объектами полей при передаче значений желательно использовать свойства из группы AS ..., т. к. неявное задание типа свойством Value может привести к ошибке преобразования данных типа Variant.
Если свойство
property CanModify: Boolean;
имеет значение False, значение поля нельзя редактировать. Однако это свойство является только средством для определения возможности редактирования.
Свойство
property Readonly: Boolean;
позволяет запретить редактирование (Readonly := True) или разрешить его (Readonly := False).
Большая группа свойств отвечает за представление и форматирование значения поля.
Свойство
property DisplayText: String;
содержит значение поля в строковом формате до начала редактирования. Свойство
property Text: String;
предназначено для использования компонентами отображения данных при редактировании. Поэтому эти два свойства могут иметь разные значения в случае, если значение поля в строковом формате при редактировании и просмотре различно. У классов-наследников TField для этого достаточно задать шаблон отображения данных для поля (свойство Display/Format) и шаблон редактирования данных (свойство EditFormat). Например, вещественное число при просмотре может иметь разделители тысяч, а при редактировании нет. В этом случае рассматриваемые свойства будут иметь следующий вид:
DisplayText = ' 1 452,32'
Text = 4452,32'
Свойства Text и DisplayText влияют на использование метода-обработчика onGetText. Если параметр DisplayText имеет значение True, то параметр Text содержит значение свойства DisplayText, в противном случае в метод передается значение поля в строковом формате.
Если поле не имеет значения, то при помощи свойства DefaultExpression можно задать некоторое постоянное значение, которое будет появляться в компоненте отображения данных при пустом поле. Если постоянное значение содержит какие-либо символы кроме цифр, то все выражение нужно обязательно брать в кавычки.
В случае возникновения исключительных ситуаций во время использования поля генерируется соответствующее сообщение, в котором в качестве имени поля применяется значение свойства DisplayName. Если задано свойство DispiayLabel, то DisplayName приравнивается к нему, в противном случае для задания свойства DisplayName используется свойство FieldName. Другим способом задать значение свойства DisplayName невозможно.
Свойство
property DisplayWidth: Integer;
определяет число символов для отображения значений поля в визуальных компонентах отображения данных.
Свойство
property Visible: Boolean;
отвечает за видимость поля в визуальных компонентах отображения данных. При этом компоненты, отображающие одно поле, перестают показывать его значения, а компоненты типа TDBGrid не отображают колонки, связанные с полем.
Примечание
Еще несколько групп свойств класса TField, а также его методы-обработчики рассматриваются ниже в этой главе.
Объектные поля
Наряду с обычными типами данных (строковым, целочисленным и т. д.), при работе с полями набора данных можно использовать более сложные типы, представляющие собой совокупность более простых типов.
В Delphi существуют- четыре класса объектных полей. Это — TADTField, TArrayField, TDataSetField, TReferenceField. Их общим Предком является класс TObjectField. Классы TADTFieid, TArrayField обеспечивают доступ к набору дочерних полей одного типа из родительского. Эти типы полей можно использовать, если сервер БД поддерживает объектные типы и соответствующие поля имеются в наборе данных. Поэтому объектные поля можно создавать статически и динамически, так же, как и простые поля.
Для доступа к дочерним полям в этих классах имеются свойства:
property Fields: TFields;которое представляет собой индексированный список объектов дочерних полей (см. гл. 12);
property FieldValues[Index: Integer]: Variant;которое содержит значения дочерних полей;
property FieldCount: Integer;которое возвращает количество дочерних полей.
Классы TDataSetField и TReferenceField предоставляют доступ к данным из связанных наборов данных.
Ссылка на используемый набор данных задается свойством
property DataSet: TDataSet;
Более подробно классы TDataSetField и TReferenceField рассматриваются в части V.
Объекты полей
Объекты полей инкапсулируют свойства и методы полей различных типов данных. Они функционируют совместно с набором данных и очень тесно связаны с ним. Например, для того чтобы получить значения полей из текущей записи набора данных, разработчик должен создать примерно такой код:
Editl.Text := Tablel.Fields[0].AsString;
Свойство Fields представляет собой индексированный список объектов полей набора данных (см. гл. 12). Если разработчик не изменяет порядок следования полей в наборе данных, то расположение объектов полей в списке Fields соответствует структуре таблицы базы данных.
Каждый объект полей хранит ряд параметров, определяющих поле. Например, в наборе данных к объекту поля можно обратиться, зная только название поля:
Editl.Text := Tablel.FieldByName('SomeField1).AsString;
Для того чтобы присвоить значение полю в текущей записи, можно воспользоваться приведенными выше способами или, если тип данных поля неизвестен, свойством Fieidvalues:
Tablel.FieldValues['SomeField'] := Editl.Text;
Знание имени поля дает самый простой способ обращения к текущему значению поля:
Tablel['SomeField'] := Editl.Text;
Editl.Text := Tablel['SomeField'];
Примечание
При присваивании значений полям набора данных необходимо контролировать состояние, в котором находится набор данных (см. гл. 12).
В основе классов, описывающих иерархию типизированных полей, лежит класс TField. От него порождены другие классы, обеспечивающие работу целых групп полей, объединенных по типам данных.
Что же такое объект поля и какие возможности он предоставляет разработчику?
Во-первых, назначение класса TField, как базового класса поля, заключается в умении взаимодействовать с компонентом отображения данных для обеспечения правильной визуализации данных. Например, объект поля хранит способ выравнивания, параметры шрифта, текст заголовка и т. д.
Во-вторых, с точки зрения набора данных объект поля является хранилищем текущего значения этого поля (а не всего столбца данных, как это можно себе представить по названию).
![](image/gl-13-1.gif)
Рис. 13.1. Иерархия классов полей
Компоненты отображения данных при работе с набором данных взаимодействуют именно с полями. Например, колонки компонента TDBCrid при отсутствии дополнительных настроек соответствуют расположению объектов полей в связанном наборе данных.
Ограничения
Контроль вводимых в поля набора данных в Delphi возложен на объекты полей, а не на компоненты отображения данных. Именно в рамках этого вопроса мы рассмотрим работу методов-обработчиков событий базового класса TField.
Перед сохранением значения поля в БД всегда вызывается метод-обработчик onvalidate, при этом автоматически проводится проверка на выполнение задаваемых ограничений и ограничений типа данных. Кроме этого, здесь можно предусмотреть свою дополнительную обработку:
procedure TForml.TablelSomeFieldValidate(Sender: TFieid);
begin
if (Sender as TFieid).Value < 0 then
begin
ShowMessage('Значение поля не может быть отрицательным');
(Sender as TFieid).Value := Null;
end;
end;
Различают контроль значения в целом и посимвольный контроль. Метод OnValidate проверяет значение поля целиком. Если при проверке обнаружена ошибка, то выдается сообщение и фокус формы устанавливается на соответствующем компоненте отображения данных.
Если метод OnValidate не вызвал исключительной ситуации, то при сохранении значения поля в БД вызывается обработчик события onchange. В принципе, можно предусмотреть операции по контролю данных и в этом методе, но тогда в случае ошибки возникает нежелательная исключительная ситуация, которая может привести к серьезным сбоям в работе приложения.
Проверить текущее значение поля перед его появлением в компоненте отображения данных можно в методе-обработчике OnGetText. Если параметр DisplayText принимает истинное значение, то в параметре Text передается значение свойства DisplayText (значение в строковом формате в таком виде, как оно будет показано в компоненте отображения данных — с символами форматирования). В противном случае в параметре Text передается текущее значение в строковом формате.
Примечание
При использовании метода-обработчика OnGetText на разработчика ложится обязанность самостоятельно предусмотреть передачу значения в компонент отображения данных, в противном случае компонент останется незаполненным.
В методе- обработчике onSetText можно осуществлять текущий контроль значения в строковом формате в том виде, как оно представлено в компоненте отображения данных. Напомним, что этот обработчик вызывается при каждом изменении свойства Text класса TField.
Рассмотренные методы-обработчики удобнее всего использовать для проверки текущего значения с точки зрения программной логики. Например, чтобы отпускная цена не была ниже закупочной или чтобы остаток не был больше первоначального количества товара в партии. Для проверки правильности самого значения класс TField имеет несколько полезных свойств.
Если на сервере БД задано ограничение на некоторое поле, его можно использовать в приложении Delphi при помощи свойства importedconstraint
Для создания собственного ограничения можно использовать свойство Customconstraint, в котором применяется синтаксис SQL:
Value>10
ИЛИ
OutputPrice>InputPricexl.25
При возникновении ошибки совсем не лишним будет, если программа выдаст некое осмысленное сообщение, которое поможет пользователю исправить оплошность. При работе с методами-обработчиками это можно предусмотреть в программном коде.
Для встроенного контроля предусмотрено специальное свойство — GonstraintErrorMessage, которое выводится в виде сообщения при возникновении ошибки. Согласитесь, что это гораздо проще, чем исправлять и перекомпилировать соответствующие файлы ресурсов. Если приложение работает с сервером БД и возникла ошибка ограничения поля, то выводится сообщение, определяемое сервером, а не этим свойством.
Если для поля заданы ограничения, то свойство HasConstraints принимает истинное значение.
Посимвольный контроль данных осуществляется свойством validchars, в котором можно определить допустимые в строковом представлении значения поля символы, и методом isvalidchar, который определяет допустимость использования переданного в параметре символа.
Еще один мощный инструмент контроля данных предоставляет свойство EditMask, которое позволяет создавать шаблоны ввода данных, облегчая тем самым работу пользователя и уменьшая возможность ошибки. Рассмотрим правила создания шаблонов.
Шаблон состоит из трех частей.
Первая часть содержит управляющие символы собственно шаблона. Доступные для создания шаблона символы приведены в табл. 13.2.
Таблица 13.2. Управляющие символы шаблона
Символ
|
Описание
|
!
|
|
>
|
Все символы после этого преобразуются в заглавные |
<
|
Все символы после этого преобразуются в строчные |
<>
|
Все символы после этого остаются в том регистре, как это было задано пользователем |
\
|
Символ, следующий за этим, считается алфавитным, а не управляющим |
L
|
В позиции этого символа обязательно должен находиться только алфавитный символ |
I
|
В позиции этого символа может находиться алфавитный символ |
А
|
В позиции этого символа обязательно должен находиться алфавитный символ или цифра |
а
|
В позиции этого символа может находиться алфавитный символ или цифра |
C
|
В позиции этого символа обязательно должен находиться знак препинания |
с
|
В позиции этого символа может находиться знак препинания |
0
|
В позиции этого символа обязательно должна находиться цифра |
9
|
В позиции этого символа может находиться цифра |
#
|
В позиции этого символа может находиться цифра, плюс или минус |
:
|
Символ разделения часов, минут и секунд (зависит от системных установок) |
/
|
Символ разделения дней, месяцев, годов (зависит от системных установок) |
;
|
Символ разделения частей шаблона |
-
|
Символ автоматического ввода в текст пробела |
Вторая часть состоит из одного символа и определяет, могут ли не арифметические символы быть частью вводимого текста. Если здесь расположен ноль, то можно вводить только цифры, если любой другой символ — можно использовать и алфавитные символы.
В третьей части содержится символ, используемый для обозначения мест, запрещенных для ввода.
Части шаблона разделяются точкой с запятой.
Например, шаблон для ввода телефонного номера выглядит следующим образом:
!\{999\)000-0000;1;_
Поля и типы данных
Каждая таблица БД и, следовательно, каждый набор данных приложения имеет собственную структуру, которая определяется совокупностью полей. Каждое поле набора данных представляет собой объект, содержащий описание типа данных, которому должно соответствовать значение, находящееся в записи на определенном месте. Иначе, полем можно назвать совокупность ячеек с данными конкретного типа, расположенных в одном и том же месте каждой записи набора данных, или попросту — это столбец в таблице.
В наборе данных приложения баз данных Delphi каждому полю соответствует собственный объект. Основой объектов полей является класс TFieid, который инкапсулирует основные свойства абстрактного поля, не зависящего от типа данных. От этого базового класса порождены другие класса, обеспечивающие функционирование реальных объектов полей, зависящих от типа данных.
Программист, грамотно использующий возможности полей, может решать существенно более сложные задачи и создавать эффективные и гибкие приложения баз данных.
Эта глава посвящена изучению объектов полей набора данных и приемов работы с ними. В ней рассматриваются следующие вопросы:
объект поля в наборе данных; динамические и статические объекты полей; способы использования объектов полей в наборе данных; класс TField — основа использования полей в наборах данных; типы объектов полей и типы данных; ограничения.
Поля синхронного просмотра
При создании для исходного набора данных нового поля синхронного просмотра необходимо использовать перечисленные ниже свойства.
Свойствоproperty LookupDataSet: TDataSet;
задает набор данных синхронного просмотра.
Свойствоproperty LookupResultField: String;
представляет поле синхронного просмотра из набора данных LookupDataSet, данные из которого будут появляться в созданном поле.
Свойствоproperty LookupKeyFields: String;
содержит поле (или поля) из набора данных синхронного просмотра, по значению которого выбирается значение из поля LookupResultField.
Свойствоproperty KeyFields: String;
определяет поле (или поля) из исходного набора данных, для которого создается поле синхронного просмотра.
Для просмотра данных из поля синхронного просмотра можно использовать любые компоненты отображения данных, но естественно будет применить специальные компоненты синхронного просмотра, о которых рассказывается в гл. 15.
Кроме этого, очень удобно использовать поля синхронного просмотра в компоненте TDBGrid. Если такое поле связать с одной из колонок компонента, то для него автоматически заполняется список синхронного просмотра. Его элементы хранятся в свойстве PickList, которое имеется в любой колонке. Теперь пользователю достаточно выбрать нужную колонку в сетке и, щелкнув на появившейся в текущей ячейке кнопке, получить возможные значения для замены. Одновременно с изменением поля синхронного просмотра изменяется и ключевое поле (свойство KeyFields) исходного набора данных.
Примечание
При использовании полей синхронного просмотра в компоненте TDBGrid открывать набор данных синхронного просмотра необязательно. При этом свойство LookupCache, о котором речь пойдет ниже, обязательно должно иметь значение False.
Для идентификации полей синхронного просмотра можно использовать булевское свойство Lookup базового класса TField, которое принимает истинное значение для таких полей.
Свойство
property LookupCache: Boolean;
определяет режим использования специального буфера значений синхронного просмотра. Если это свойство истинно, то буфер работает.
Буфер основан на свойстве LookupList. При открытии исходного набора данных каждое поле синхронного просмотра получает свое значение, одновременно с этим, в соответствии со всеми имеющимися в исходном наборе данных значениями ключевого поля, заполняется и буфер синхронного просмотра. Впоследствии, при перемещении на другую запись, значение синхронного просмотра берется не из набора данных синхронного просмотра, а из буфера. Этот механизм при небольших объемах значений синхронного просмотра позволяет увеличить скорость работы с исходным набором данных, особенно в режиме удаленного доступа при низкоскоростных сетях.
При изменениях в наборе данных синхронного просмотра можно использовать метод
procedure RefreshLookupList;
который обновляет текущее значение поля и список значений в буфере.
Специально для разработчиков в базовый класс TField включено свойство
property Offset: Integer;
которое возвращает размер буфера в байтах.
Для создания поля синхронного просмотра удобнее всего воспользоваться Редактором полей компонента доступа к данным. После выбора команды New field из всплывающего меню в одноименном диалоге (см. рис. 13.3), помимо обычных действий, соответствующих созданию поля данных, необходимо задать значения свойств в группе Lookup definition. Элементы управления группы становятся доступны после выбора типа поля (радиокнопка Lookup в группе Field type). Группа Lookup definition включает следующие элементы:
в списке Dataset представлены все доступные в модуле наборы данных, из которых нужно выбрать набор данных синхронного просмотра (свойство Lookup DataSet); список Result Field позволяет выбрать поле синхронного просмотра (свойство LookupResultField); список Lookup Keys задает ключевое поле в наборе данных синхронного Просмотра (свойство LookupKeyFields); список Key Fields определяет ключевое поле исходного набора данных (свойство KeyFields).
с полями является важным этапом
Работа с полями является важным этапом в процессе разработки приложения баз данных. Для этого используются специальные объекты, которые инкапсулируют возможности полей таблицы БД. В Delphi имеется целая иерархия классов, обеспечивающая применение полей самых различных типов. В основе этой иерархии лежит класс TField.
По способу создания объекты полей делятся на статические и динамические.
По функциональным возможностям объекты полей бывают полями данных, вычисляемыми, синхронного просмотра, агрегатными.
Объекты полей играют важную роль в работе наборов данных. С их помощью можно получить доступ к текущим значениям, задать ограничения на вводимые величины и проверить их правильность.
Статические и динамические поля
В Delphi предусмотрено два способа создания объектов полей.
Динамические поля используются программой в случае, если разработчик не создал для них объекты явным образом на этапе разработки. Каждый не заданный явно объект поля автоматически создается при открытии набора данных в соответствии со структурой связанной таблицы БД. Любой объект поля является прямым наследником класса TField, а его конкретный тип зависит от типа данных поля таблицы. При этом свойства динамического поля устанавливаются в соответствии с параметрами поля таблицы базы данных.
Компонент набора данных после подключения к таблице БД без дополнительных настроек использует только динамические поля. К свойствам и методам динамических полей можно обратиться программно, для этого следует использовать индексированное свойство Fields компонента доступа к данным, которое объединяет все поля набора данных (см. выше) или метод FieldByName.
Динамические поля используются в случаях, когда заданные характеристики полей в таблице базы данных полностью удовлетворяют разработчика и нет необходимости рассматривать какое-либо поле вне набора данных.
Статические поля создаются программистом на этапе разработки, их свойства доступны в Инспекторе объектов, а их названия можно выбрать из списка объектов активной формы в верхней части Инспектора объектов. Название статического объекта поля обычно складывается из названий таблицы и поля, например ordersCUSTNO.
Создаются статические объекты полей при помощи специализированного Редактора полей, который вызывается двойным щелчком на компоненте набора данных на форме или командой Fields Editor из всплывающего меню этого компонента.
Редактор полей представляет собой простой список уже созданных статических полей. Все управление осуществляется командами из всплывающего меню. В верхней части окна Редактора расположены кнопки навигатора для перемещения по набору данных, которые активны только при открытом наборе данных. Если набор данных имеет агрегатные поля данных (см. ниже), то они размещаются в отдельном списке в нижней части окна Редактора полей (рис. 13.2).
![](image/gl-13-2.gif)
Рис. 13.2. Редактор полей с отдельным списком агрегатных полей
![](image/gl-13-3.gif)
Рис. 13.3. Диалог создания нового статического поля Редактора полей набора данных
Добавить к списку статических полей новое поле, существующее в таблице БД, можно при помощи команды Add fields из всплывающего меню Редактора. Удаление элемента из списка осуществляется клавишей <Delete>. Перетаскиванием элементов списка при помощи мыши можно изменить их расположение. Таким образом можно создавать произвольные комбинации статических полей.
Примечание
Как только для набора данных создан хотя бы один статический объект поля, считается, что набор данных содержит только те поля, которые имеются в списке статических полей. Эту особенность можно использовать для искусственного ограничения структуры данных таблиц. Все поля набора данных расположены в том порядке, как это указано в списке Редактора полей, независимо от их реального положения в таблице базы данных.
Команда New field из всплывающего меню Редактора полей позволяет создать статическое поле, которое реально не существует в структуре данных таблицы (рис. 13.3). Для выбора типа поля используется группа радиокнопок Field Type:
Data — поле данных; Calculated — вычисляемое поле; Lookup — поле синхронного просмотра. Реже создаются поля данных, которые обязательно должны базироваться на реальных полях таблицы. Если для такого объекта соответствующее поле в таблице будет удалено или его тип данных будет изменен, то при открытии набора данных генерируется исключительная ситуация.
Примечание
Для клиентских наборов данных многоуровневых приложений диалог создания нового поля позволяет выбрать два дополнительных типа поля — это агрегатные поля (радиокнопка Aggregate) и внутренние вычисляемые поля (радиокнопка InternalCalc).
Типы данных
В среде разработки Delphi можно создавать приложения для работы с самыми разными базами данных. Такая универсальность подразумевает необходимость применения средств, которые бы обеспечили возможность работы со многими типами данных, используемыми в этих базах данных.
Естественно, что существует большая группа типов данных, конкретная реализация которых практически не отличается от платформы к платформе. Это, например, строки, символы, целые и вещественные числа и т. д.
Есть типы данных, которые реализованы далеко не на каждой платформе. Есть, наконец, просто уникальные типы данных.
Для удовлетворения потребностей разработчиков в Delphi применен следующий способ работы с типами данных.
Тип данных однозначно связан с конкретным полем таблицы базы данных. Без этого поля само понятие типа данных не имеет практического смысла. В Delphi свойства абстрактного поля инкапсулирует класс TField, который не имеет заранее определенного типа данных. Уже от этого класса порождено целое семейство классов для типизированных полей (см. рис. 13.1), каждый из которых умеет обращаться со своим типом данных.
Примечание
В классе TField имеется свойство DataType, которое отвечает за тип данных, но оно не может быть изменено.
Весь список доступных типов данных содержится в типе TFiieidType:
type TFieldType = (ftUnknown, ftString, ftSmallint, ftlnteger, ftWord, ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime, ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle, ftTypedBinary, ftCursor, ftFixedChar, ftWideString, ftLargeint, ftADT, ftArray, ftReference, ftDataSet, ftOraBlob, ftOraClob, ftVariant, ftlnterface, ftlDispatch, ftGuid, ftTimeStamp, ftFMTBcd);
В табл. 13.1 рассматриваются все типы данных, которые можно использовать при разработке приложений для работы с базами данных.
Таблица 13.1. Типы данных
Тип |
Класс |
Описание |
Неизвестный (ftUnknown) |
Неопределенный тип данных |
|
Строковый (ftString) |
TStringField |
Строка длиной до 8192 символов |
Целый короткий (ftSmallint) |
TSmalllntField |
16-битное целое в диапазоне от -32 768 до 32 767 |
Целый (ftlnteger) |
TIntegerField |
32-битное целое в диапазоне от -2 147 483 648 до 2 147 483 647 |
Целый положительный (ftWord) |
TWordField |
1 6-битное целое в диапазоне от 0 до 65535 |
Логический (ftBoolean) |
TBooleanField |
Значения True и False |
Вещественный (ftFloat) |
TFloatField |
Вещественные положительные и отрицательные числа с точностью 15 цифр после запятой в диапазоне от 5,0x1 0"324 до 1,7x1 0308 |
Денежный (ftCurrency) |
TCurrencyField |
Вещественные положительные и отрицательные числа с точностью 15 цифр после запятой в диапазоне от 5,0x1 0"324 до 1,7x1 0308. Дополнительно вставляется символ валюты |
Десятичный с двоичным кодированием (ftBCD) |
TBCDField |
Вещественные числа с повышенной точностью (до 4 знаков перед запятой и до 20 знаков после запятой). Могут храниться в двоичном и десятичном форматах |
Дата (ftDate) |
TDateField |
Дата |
Время (ftTime) |
TDateTimeField |
Время |
Календарный (ftDateTime) |
TDateTimeField |
Комбинированный формат с одновременным хранением даты и времени |
Фиксированный буфер (ftBytes) |
TBytesField |
Набор байтов фиксированного размера. Для работы с этим типом требуется выделять и освобождать память (методы GetMem И FreeMem) |
Переменный буфер (ftVarBytes) |
TVarBytes Field |
Набор байтов переменного размера. Текущий размер буфера хранится в первых двух байтах. Для работы с этим типом требуется выделять и освобождать память (методы GetMem И FreeMem) |
Автоинкрементный (ftAutoInc) |
TAutoIncField |
Значение поля в каждой новой записи автоматически увеличивается на 1 . Целое число в диапазоне от -2 147 483 648 до 2 147 483 647. Применяется для обеспечения уникальности значений ключей |
BLOB (ftBlob) |
TBLOBField |
Большой двоичный массив. Используется для хранения любых данных, которые можно преобразовать в цифровой массив (Memo, Graphic). В базах данных такие данные хранятся в отдельных файлах, а поле содержит лишь ссылки на них |
Memo (ftMemo) |
TMemoField |
Набор строк произвольной длины |
Графический (ftGraphic) |
TGraphicField |
Формат для хранения изображений |
Форматированный Memo (ftFmtMemo) |
Форматированный набор строк произвольной длины |
OLE Paradox (ftParadoxOle) |
Поле OLE для таблиц Paradox |
OLE dBASE (ftDBaseOle) |
Поле OLE для таблиц dBASE |
|
Типизированный двоичный (ftTypedBinary) |
Типизированный двоичный |
|
Курсор Oracle (ftCursor) |
Курсор для хранимых процедур сервера Oracle |
|
Фиксированный символьный (ftFixedChar) |
TStringField |
Строка символов с нулевым символом в конце |
Расширенный строковый (ftWideString) |
Динамически выделяемая строка 16-битных символов в кодировке Unicode |
|
Целый большой (ftLargeint) |
TLargelntField |
64-битное целое число |
Абстрактный (ftADT) |
TADTField |
Произвольный тип данных, создаваемый пользователем на сервере БД и используемый в приложении |
Массив (ftArray) |
TArrayField |
Массив полей любого типа, кроме TarrayField |
Ссылочный (ftReference) |
TReferenceField |
Указатель на объект, содержащийся в другой таблице |
Набор данных (ftDataSet) |
TDataSetField |
Содержит набор данных, интегрированный в текущий набор данных |
BLOB Oracle 8 (ftOraBlob) |
Тип BLOB для сервера Oracle 8 |
|
CLOB Oracle 8 (ftOraClob) |
Тип CLOB для сервера Oracle 8 |
|
Вариант (ftVariant) |
TVariantField |
Вариант |
Интерфейс (ftlnterface) |
TInterfaceField |
Ссылка на интерфейс (потомок от lUnknown) |
Ссылка на интерфейс IDispatch (ftlDispatch) |
TIDispatchField |
Ссылка на интерфейс (потомок от IDispatch) |
Глобальный идентификатор (ftGuid) |
TGuidField |
Глобальный идентификатор GUID |
Календарный (ftTimeStamp) |
Календарный тип для наборов данных dbExpress |
|
Десятичный с двоичным кодированием (ftFMTBcd) |
TFMTBCDField |
Тип BCD повышенной точности |
Примечание
В Delphi тип данных BCD напрямую не поддерживается. Его использование обеспечивает денежный тип (ftcurrency). Поэтому точность BCD ограничена 20 цифрами после запятой. Для устранения этого ограничения используется тип FMTBcd, который обладает требуемой точностью.
Как видно из таблицы, наряду с традиционными типами данных, в Delphi имеются и специальные типы, использование которых значительно расширяет функциональность приложений. В частности, типы ftinterface, ftIDispatch, ftGuid позволяют создавать полноценные приложения БД для СОМ и OLE DB.
Практически на всех серверах БД пользователь имеет возможность создавать собственные типы данных. Для их использования в приложениях Delphi имеется абстрактный тип данных и класс TADTField. Абстрактный тип может включать любой скалярный тип данных (числа, даты, ссылки, массивы, наборы данных).
Автоинкрементный тип данных давно используется в СУБД. Поле автоинкрементного типа для каждой новой записи автоматически увеличивает свое значение на единицу. Тем самым, каждая запись имеет собственный уникальный идентификатор, который очень часто используется в качестве первичного ключа.
Данные типа BLOB (Binary Large OBject) представляют собой двоичные массивы произвольной длины. В самом поле содержится лишь ссылка на отдельный файл базы данных, в котором хранится двоичный массив. Таким образом, поля типа BLOB являются универсальным носителем любых данных, которые имеют скалярную и нескалярную структуру и которые можно преобразовать в двоичное представление.
Тип Memo представляет собой набор строк произвольной длины (его разновидность — форматированный Memo), основан на формате BLOB. Используется при необходимости сохранить текст из компонента тмето или из текстового редактора.
Графический тип данных используется для хранения в базе данных изображений, основан на формате BLOB. Поле TGraphicFieid непосредственно взаимодействует с компонентом отображения данных (например TDBimage). Изображения должны храниться в формате BMP.
Типы данных ParadoxOle и dBaseOle разработаны специально для использования возможностей СУБД Paradox и dBASE по работе с данными OLE. В Delphi эти типы данных основаны на формате BLOB.
Специально для работы с сервером Oracle 8 предназначены типы CLOB и BLOB.
Тип ftArray организует массив из данных любой структуры, за исключением таких же массивов. Для каждого элемента массива может создаваться собственный объект TField. Для управления этим механизмом используется свойство SparseArrays в классе TDataSet.
В качестве отдельного поля в набор данных можно включить и любой другой произвольный набор данных. Для этого используется специальный тип данных и класс TDataSetField. Причем каждым полем из интегрированного набора данных тоже можно управлять.
Ссылочный тип данных также использует внешние наборы данных, но в этом случае можно подключать и использовать только отдельные поля.
Виды полей
Теперь рассмотрим классификацию полей набора данных в зависимости от их функционального назначения. Самыми распространенными полями являются поля данных, базирующиеся на реальных полях таблицы БД. Свойства объектов таких полей устанавливаются в соответствии с параметрами полей таблицы БД.
Кроме этого, в практике программирования часто применяются поля синхронного просмотра и вычисляемые поля.
Процесс создания всех типов полей набора данных практически не отличается (см. выше). Тем не менее такое разнообразие позволяет успешно решать самые сложные задачи программирования приложений БД.
Ниже мы рассмотрим только поля синхронного просмотра и вычисляемые поля, т. к. поля данных не содержат каких-либо существенных особенностей в применении.
С точки зрения набора данных большой разницы между этими двумя видами полей нет. Однако значения для всех полей синхронного просмотра рассчитываются раньше, чем для вычисляемых полей. Поэтому вы можете использовать поля синхронного просмотра в выражениях вычисляемых полей и не можете сделать наоборот.
Внутренние вычисляемые поля
Помимо простых вычисляемых полей существуют внутренние вычисляемые поля (FieidKind = fkinternaicaic). Они используются в клиентских наборах данных (компоненты TCiientDataSet) и отличаются тем, что их значения сохраняются в наборе данных.
Внутренние вычисляемые поля могут быть использованы для фильтрации методом - обработчиком OnFilterRecord.
Вычисляемые поля
Вычисляемые поля существенно облегчают разработку приложений баз данных, т. к. позволяют получать новые данные на основе существующих, не изменяя при этом структуру таблиц БД. Выражения для получения значений вычисляемых полей разработчик должен разместить в методе-обработчике OnCalcFields набора данных. Здесь можно использовать любые арифметические, логические операции и функции, любые операторы языка, свойства и методы любых компонентов, в том числе запросы SQL:
procedure TForml.TablelCalcFields(DataSet: TDataSet)
; begin
with Tablel do
TabielCalcFieldl.Value := Fields[0].Value + Fields[1].Value;
with Queryl do
begin
Params[0].AsInteger := Tablel.Fields[0].Aslnteger;
Open;
TabielCalcFieldl.Value := Fields[0].AsString;
Close;
end;
end;
Метод OnCalcFields выполняется при открытии набора данных, при переходе в режим редактирования, при передаче фокуса между компонентами отображения данных или колонок сетки, при удалении записи. Но для этого нужно, чтобы свойство AutoCaicFields набора данных было равно значению True.
Примечание
Необходимо учитывать, что сложные вычисляемые поля могут существенно замедлить работу набора данных (особенно при использовании при этом запросов SQL). Кроме того, в процессе редактирования набора данных (при изменении значения поля, сохранении изменений и переходе на следующую запись) вычисляемые поля рассчитываются несколько раз подряд. Для уменьшения числа автоматических обращений к методу OnCalcFields нужно использовать свойство AutoCaicFieids := False.
Для создания вычисляемого поля достаточно в диалоге создания нового поля Редактора полей в качестве типа поля задать "вычисляемое", в остальном процесс совпадает с созданием поля данных.
В выражениях вычисляемых полей можно использовать другие вычисляемые поля, но они обязательно должны быть определены в методе OnCalcFields до этого.
Вычисляемые поля нельзя использовать при фильтрации набора данных при помощи метода-обработчика onFilterRecord, т. к. он вызывается до метода-обработчика OnCalcFields, а вычисляемые поля не сохраняются.