Delphi 3 и создание приложений баз данных

         

Объединение файлов конфигурации


Файлы конфигурации могут быть объединены. Это важно в том случае, когда необходимо установить на компьютер приложение или группу приложений. При этом нужно добавить в файл конфигурации новые псевдонимы БД. Можно завести их вручную. Однако, если вместе с дистрибутивом приложений поставляется и конфигурационный файл, псевдонимы из него могут быть добавлены в основную (или любую другую) конфигурацию. Для этого нужно выбрать элемент меню Object | Merge Configuration и выбрать конфигурационный файл, информация из которого будет добавляться, и затем ответить Yes на грозное предупреждение о том, что результаты объединения не могут быть отменены операцией Undo. На рис. 5.3, 5.4 и 5.5 представлены исходная конфигурация, добавляемая конфигурация и результирующая конфигурация.



Применение TField


Компонент TField позволяет обращаться к полям таблиц баз данных. Каждый набор данных - неважно, ТТаЫе или TQuery - состоит из записей, а те, в свою очередь, состоят из полей. Таким образом, в составе записи имеется минимум одно поле.

В Delphi имеется возможность использовать при работе с НД или все поля, определенные в данной ТБД на текущий момент, или использовать только часть существующих полей.

Использование только части имеющихся полей дает то преимущество, что значения неиспользуемых полей не могут быть изменены ни вследствие алгоритмической ошибки, ни вследствие злонамеренного умысла, поскольку неиспользуемые поля считаются неизвестными.

Существует два способа задания состава полей для НД.

Первый

состоит в том, что после создания НД (компонент ТТаЫе или TQuery) не предпринимается никаких дополнительных действий по уточнению состава полей. Тогда:

для компонента TTable - будет разрешен доступ ко всем полям, определенным в данный момент в ТБД, связанной с компонентом ТТаЫе;

для компонента TQuery - будет разрешен доступ ко всем полям (в том числе и результатам выражений), указанным в списке возвращаемых полей в операторе SELECT. Хотя этот оператор в результате выполнения SQL-запроса может возвращать НД, составленный из нескольких физических ТБД, сами поля будут считаться принадлежащими к единому НД, образовавшемуся в результате выполнения SQL-запроса из свойства TQuery.SQL. К полю в этом случае можно обращаться с помощью метода FieldByName компонентов TTable и TQuery

function FieldByName(const FieldName: string): TField;

или через свойство указанных компонентов Fields [Index], которое возвращает указатель на тип TField. Подробнее об этом свойстве будет сказано ниже.

Определить во время выполнения приложения, используются для набора данных все поля по умолчанию или только их часть, определенная посредством редактора полей, можно при помощи свойства набора данных

го, rt property DefaultFields: Boolean;

ЗАМЕЧАНИЕ.

Аббревиатура го (read only) означает, что свойство доступно только для чтения; rt (run time) означает, что свойство доступно только во время выполнения приложения. Полный список принятых сокращений см. в начале книги. Значение True указывает, что используются поля по умолчанию; False -

что используются поля, определенные при помощи редактора полей.

Второй

способ определения состава полей заключается в том, что для данного НД поля как компоненты TField добавляются в форму с помощью редактора полей Delphi. О нем будет сказано чуть ниже.

К таким полям можно обращаться через его имя, определяемое в свойстве Name компонента TField, соответствующего данному полю.

По умолчанию, при добавлении в форму компонента TField, его имя генерируется так: берется имя НД (т.е. значение свойства Name компонента TTable или TQuery, к которому принадлежит поле) и к нему добавляется имя поля, взятое из структуры ТБД (TTable) или из запроса (TQuery).



Так, поле с именем FIO, используемый в НД Students, при добавлении в форму средствами редактора полей, получит имя StudentsFIO.

ЗАМЕЧАНИЕ.

Это имя будет относиться целиком к компоненту TField, а не только к значению (например, "Иванов И.И."), которое поле FIO содержит в текущей записи ТБД. Компонент TField, как будет показано ниже, обладает рядом свойств, методов и событий, обращаться к которым следует через указание имени компонента TField и имени свойства, метода или обработчика события, например:

StudentsFIO. Index := 5; // изменить порядковый номер поля StudentsFIO. Readonly := True; //запретить изменение значений поля

Таким образом, экземпляр StudentsFIO компонента TField трактуется не как конкретное значение, которое принимает поле FIO в конкретной строке ТБД, а как весь столбец набора данных, обладающий единым поведением, т.е. едиными свойствами, методами и событиями для всех записей набора данных. Значение поля для текущей записи доступно с помощью свойств Value, AsBoolean, AsString и т.д. Например:

IF StudentsFIO.AsString = 'Иванов' then ....

Итак, когда поле НД определено в форме в качестве экземпляра компонента TField, к нему можно обращаться по имени (содержащемуся в свойстве Name), а также через метод НД FieldByName и свойство Fields [Index].

Если хотя бы для одного поля НД создан компонент TField, первый принцип, т.е. принцип использования всех полей ТБД или результата SQL-запроса, отвергается. В НД будут считаться определенными только те поля, для которых созданы компоненты TField, а иные поля - отвергаться как несуществующие для данного НД (TTable или TQuery).

К "несуществующим" полям обратиться изданного НД нельзя никак. Такие попытки будут возбуждать исключительные ситуации с сообщением 'Field <имя> not found'.

Вновь вернуться к принципу использования всех полей ТБД или результата SQL-запроса можно, удалив в редакторе полей все определенные ранее TField или добавив с его помощью недостающие поля.

То, какие поля определять в данном НД, зависит от назначения разрабатываемого приложения и диктуется целесообразностью.

ЗАМЕЧАНИЕ.

Если нужно иметь доступ к полю, но не показывать его значений в компонентах, визуализирующих данные (например, в компоненте TDBGrid), свойство Visible этого поля следует установить в состояние False.

Использование редактора полей


Для того, чтобы определить один или несколько компонентов TField, нужно:

1. Выбрать необходимый НД (компонент TTable или TQuery);

2. Нажать правую кнопку мыши;

3. Во всплывающем меню выбрать режим Field Editor (запуская тем самым редактор полей};

4. Вновь нажать правую кнопку мыши и во всплывающем меню выбрать Add Fields',

5. В появившемся списке полей ТБД (TTable) или полей, участвующих в запросе (TQuery), выбрать необходимые (рис. 6.1)

и нажать кнопку Ok. Для каждого из указанных полей будет создан компонент TField:

6. Если необходимо изменить свойства конкретного поля или написать обработчик для какого-либо события, необходимо в редакторе полей выбрать нужное поле и, используя инспектор объектов, установить значение в свойство или определить обработчик события.



Типы полей


Поля в таблицах баз данных различаются по типу - символьные, целочисленные, логические, blob-поля и т.д.

Соответственно, по типу различаются и компоненты TField, и собственно, TField есть родительский тип, определяющий базовые свойства и методы для своих потомков, типизированных полей. Иерархия компонентов- полей такова:

TField

TBIobField большой двоичный объект

TGraphicField графическое поле(работает с содержимым blob-поля как с графическим изображением)

TMemoField мемо-поле (интерпретирует BLOB-поле как большой текст)

TBooleanField логическое поле

TBinaryField нетипизированное двоичное поле

TBytesFieid поле для хранения байтовых значений фиксированной длины

TVarBytesField поле для хранения байтовых значений переменной длины

TDateTimeField поле для хранения даты и времени

TDateField поле для хранения только даты

TTimeField поле для хранения только времзни

TNumericField поле для хранения числовых значений

TBCDField BCD-значений

TFloaTField значений с плавающей точкой

TCurrencyField в том числе в денежном формате

TIntegerField целочисленных значений

TAutoIncField в том числе автоинкрементных

TSmallIntField в том числе коротких целых

TWordField в том числе в формате беззнакового длинного целого

TStringField поле для хранения строковых значений

Особенности каждого типа будут рассмотрены ниже, равно как и процедуры и функции Delphi, применяемые для работы с полями определенных типов. Покажем общие для всех этих типов свойства, методы и события.



Обращение к полям и их значениям


Как было сказано в п. 6.1, следует различать обращение к полю и обращение к его значению. Рассмотрим способы обращения к полям.

К полю можно обратиться, указав имя поля несколькими способами :

1. Если полю соответствует компонент TField - через имя данного компонента, которое определяется свойством Name. Повторим, что по умолчанию имя компонента TField устанавливается как результат сцепления имени TTable или TQuery и собственно имени поля в ТБД. Например, для поля FIO, определенного в ТБД, работа с которой происходит через Table 1, по умолчанию будет выбрано имя Table I FIO. Тогда использование поля происходит подобно приведенному ниже:

Table1FIO.AsString := 'Иванов'; // или

Table1FIO.Value := 'Иванов';

2. Используя метод FieldByName ('ИмяПоля ') набора данных, function FieldByName(const FieldName: string): TField;

Например,

Table1.FieldByName('FIO).AsString := 'Иванов'; //или

Table1.FieldByName('FIO).Value:= 'Иванов';

3. Используя свойство Fields[индекс] набора данных,

property Fields[Index: Integer]: TField;

Индекс

является порядковым номером поля в определении ТБД. Отсчет идет от 0. Пусть поле Name определено в ТБД третьим по счету. Тогда его индекс равен 2 и использование поля может происходить так:

Table1.Fields[2].AsString := 'Иванов'; // или

Tablel.Fields[2].Value:= 'Иванов' ;

Если поле входит в индекс данного НД, свойство IsIndexField: Boolean;

возвращает во время выполнения значение True.

4. Используя свойство набора данных

property FieldValues|const FieldName: string]: Variant;

Это свойство позволяет обращаться к полю через его имя, указываемое как содержимое параметра FieldName, например: Tablel.FieldValues['FIO'] := 'Иванов';

Поскольку свойство FieldValues принимается для набора данных по умолчанию, его имя при обращении к полю можно опускать: Tablel['Name'] := 'Иванов' ;

ЗАМЕЧАНИЕ 1.

Более предпочтительным считается обращение к полю через его имя или через метод FieldByName, поскольку в этом случае мы обращаемся к конкретному полю по его имени. Следовательно, к несуществующему полю обратиться нельзя.

Пусть поле FIO удалено из структуры ТБД. Тогда обращение к нему по имени из ассоциированного с данной ТБД НД приведет к ошибке.

Менее предпочтительным является обращение к полю через свойство набора данных Fields [индекс]. Если поле FIO было объявлено в ТБД вторым по счету (обращение Fields[l]), а затем удалено из структуры ТБД, обращение Fields[l] в программном коде будет воспринято как обращение к физически второму полю в ТБД. Этим полем будет третье поле, следовавшее за FIO перед тем, как FIO было удалено из структуры ТБД. После удаления FIO третье поле станет по счету вторым.

Налицо алгоритмическая ошибка, которая может быть не распознана, если FIO и поле, ставшее после удаления FIO вторым, имеют одинаковые или совместимые типы.

Такие ошибки очень трудно локализовать. Поэтому, если это возможно, следует воздержаться от обращения к полю через Fields [индекс].

ЗАМЕЧАНИЕ 2

. Свойство Index компонента TField содержит порядковый номер поля:

• в ТБД, если для компонента TTable, ассоциированного с данной ТБД, не создано ни одного компонента TField;

• в списке компонентов TField, относящихся к данному НД - для компонента TTable, если для него определен хотя бы один TField, и всегда - для компонента TQuery.

Порядок следования полей важен, когда содержимое НД визуализируется посредством компонента TDBGrid. В этом случае номер столбца в компоненте TDBGrid соответствует номеру поля в списке TField. И наоборот, если изменить порядок столбца в компоненте TDBGrid (например, "перетащив" столбец на другое место), это приведет к изменению индекса и Fields[l] до перетаскивания будет относиться к другому полю, нежели Fields[l] после перетаскивания столбцов, поскольку свойство Index перетаскиваемого и некоторых других полей изменится.

Это относится как к случаю, когда TField для НД определены, так и к случаю, когда используются все поля ТБД. В последнем случае "перетаскивание" столбца в компоненте TDBGrid на новое место, конечно, не изменит физического порядка следования полей в структуре ТБД; однако с логической точки зрения его индекс (порядковый номер) для данного НД изменится.

Последствия, которые могут принести в структуру НД изменения свойства Index, делают обращения к полю через свойство Fields [индекс] набора данных нежелательными.



Обращение к значению поля. Свойства Value и AsNNN


К значению поля можно обратиться при помощи свойств Value и AsNNN. Свойство

property Value: Variant

возвращает значения следующих типов:

property Value Variant; // Все компоненты

property Value string; //TStringField, TBIobField

property Value Longint; //TAutoIncField, TIntegerField, TSmallintField,TWordField

property Value Double; //TBCDField, TCurrencyField, TFloatField

property Value Boolean; //TBooleanField

property Value TDateTime; //TDateField, TDateTimeField, TTimeField

Это дает возможность пользоваться свойствами приведения типов полей (AsString, Aslnteger и т.д.) в гораздо меньших масштабах. Тем не менее, обойтись без них удается далеко не всегда.

Аналогичные значения возвращает рассмотренное в предыдущем разделе свойство набора данных FieldValues.

Например,

var N : Integer;

N := TablelNumber.Value; // TablelNumber типа TIngereField

Обращение к значению поля через свойство

AsNNN.

Существуют следующие свойства приведения типов полей:

property AsBoolean: Boolean;

property AsCurrency: Currency;

property AsDateTime: TDateTime;

property AsFloat: Double;

property Aslnteger: Integer;

property AsString: String;

property As Variant: Variant;

Каждое из этих свойств приводит значение поля к соответствующему типу данных, означенному в названии свойства. Например, если TablelNumber -компонент TIntegerField (поле, хранящее целочисленные значения), для приведения его к типу String можно воспользоваться свойством

Editl.Text := TablelNumber.AsString;

Несомненно, тип поля должен быть совместимым с типом данных, к которому приводится значение поля. Например, если TablelSumma - компонент TFloatField (поле, хранящее вещественные значения), попытка привести его к несовместимому типу Boolean, IF TablelSumma.AsBoolean THEN ...

приведет к ошибке компиляции.

В приводимой ниже таблице показана совместимость значений полей разных типов.

Обозначение:

= типы равнозначны;

+ преобразование возможно;

+ RI преобразование возможно, округление до ближайшего целого;

? преобразование происходит, если возможно; часто зависит от формата показа (свойство DisplayFormat);

х преобразование не разрешено;

Рассмотрим свойства семейства AsNNN более подробно:

property AsBoolean: Boolean; -

числовые значения приводятся к типу Boolean, если содержат 0 (False) или 1 (True). Символьные значения - если содержат в качестве первого символа "Y", "у", "Т" или "t" (или "Yes" или "True"), и False во всех иных случаях.

property AsDateTime: TDateTime; -

для приведения к типу TDateTime значений TDateField, TDateTimeField и TTimeField, хотя вместо этого лучше использовать свойство Value, а также для приведения к типу TDateTime строковых значений, находящихся в соответствующем формате.

property AsFloat: Double; -

служит для приведения к типу Double значений полей TFloatField, TBCDField и TCurrencyField, AsFloat, хотя вместо этого лучше использовать свойство Value.

property Aslnteger: Longint; -

служит для приведения к типу Longint полей типа TIntegerField, TSmallintField и TWordField, хотя вместо этого лучше использовать свойство Value.

Для полей типа TStringField преобразование к Longint выполняется, если оно возможно.

property AsCurrency: Currency; -

служит для приведения к типу Currency. property AsString: string; - служит для приведения к типу String. property As Variant: Variant; - служит для приведения к типу Variant.

Событие OnGetText


Пусть поле ТБД хранится не в том виде, в котором должно показываться. Тогда отформатировать его перед тем, как оно будет показано в визуальных компонентах, работающих с данными - TDBGrid, TDBEdit и т.д., можно, определив алгоритм форматирования в обработчике события OnGetText для данного компонента TField.

В процедуре-обработчике присутствуют такие параметры:

Text -

отформатированное значение, показываемое в столбце компонента TDBGrid, в TDBEdit или других визуальных компонентах, связанных с БД;

Display Text -

позволяет определить, произошло событие OnGetText при показе значения (значений) поля (True) или при модификации пользователем значения поля (False).

Пример.

Пусть поле Company входит в состав ТБД, содержимое которой показывается в TDBGrid с использованием Table 1. Необходимо при показе содержимое данного поля заключать в кавычки (хотя оно хранится без кавычек). Если пользователь захочет изменить значение поля в какой-либо записи, нужно показывать содержимое поля, представленное заглавными буквами.

procedure TForm1.Table1CompanyGetText(Sender: TField; var

Text: OpenString;

DisplayText: Boolean);

begin

IF DisplayText THEN

Text := '"' + TablelCompany.AsString + '"'

ELSE

Text := AnsiUpperCase(TablelCompany.AsString);

end;

Когда происходит показ некоторой записи из Table 1 в TDBGrid, для нее возникает событие OnGetText и вызывается приведенный выше обработчик с DisplayText = True. В результате в компоненте TDBGrid весь столбец, соответствующий полю TablelCompany, будет содержать значения в кавычках.

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

ОГРАНИЧЕНИЕ.

Если для поля определен обработчик события OnGetText, игнорируются режимы форматирования, определенные в свойствах DisplayFormat и EditMask данного поля.

Свойство DisplayFormat


Свойство

property DisplayFormat: string;

применяется для форматирования при показе полей типа TDateField, TDateTimeField, TIntegerField, TSmallintField, TTimeField, TWordField.

Для форматирования полей типа TIntegerField, TSmallintField и TWordField может также применяться стандартная процедура procedure Str(X [: Width [: Decimals ]]; var S);

Для форматирования полей типа TDateField, TDateTimeField и TTimeField применяется функция function Date TimeToStr (Date Time: TDateTime): String;

Для форматирования полей типа TBCDField, TCurrencyField и TFloatField применяется функция function FloatToTextFmt(Buffer: PChar; Value: Extended:

Format: PChar): Integer;

Поддерживаются следующие спецификаторы форматов:

Спец-р Что влечет
0 Число. Если незначащий разряд равен 0, показывать его.
# Число. Если незначащий разряд равен 0, не показывать его.
Десятичная точка. Разделяет целую и дробную часть числа Принимается во внимание только первая точка, остальные игнорируются.
' Разделитель тысяч. Каждая группа чисел из 3 разрядов в целой части отделяется от иных разрядов запятой.
' Разделяет положительное, отрицательное и нулевое значение.
Е+ Научный формат действительных чисел
"XX' или 'XX' Символы внутри двойных или одинарных парных кавычек не форматируются и выводятся как есть. Например, число 123.45 с форматом '#.# "рублей"' выведется как '123.5 рублей'

Примеры использования форматов.

Форматируемое число 987654.321 -987654.321 0.27 -0.27 0
не форматировано 987654.321 -987654.321 0.27 -0.27 0
0 987654 -987654 0 0 0
0.00 987654.32 -987654.32 0.27 0 0.00
# 987654 -987654      
#.## 987654.32 -987654.32 .27 -.27  
#,##0.00 987,654.32 -987,654.32 0.27 -0.27 0.00
#,##0.00; (#,##0.0) 987,654.32 (987,654.32) 0.27 (0.3) 0.00
#,##0.00;; Нолик 987,654.32 -987,654.32 0.27 -0.27 Нолик
О.ОООЕ+00 9.877Е+05 -9.877Е+0 2.700Е-01 -2.700E-01 0.000Е+00
#.###Е-0 9.877Е5 -9.877Е5 2.7Е-1 -2.7Е-1 0Е0

Пусть значение поля 3456.777. Тогда, если DisplayFormat := '#.##', то будет показано 3456.78.

ЗАМЕЧАНИЕ.

Формат показа поля может быть динамически переназначен во время выполнения. Например, для заполнения данной таблицы мы воспользовались компонентами Edit1 и Button1 и производили динамическую замену формата поля Table I Salary так:

procedure TForm1.Button1Click(Sender: TObject);

begin

Table1Salary.DisplayFormat := Edit1.Text;

end;

DisplayFormat

игнорируется, если определен обработчик для события OnGetText.

ЗАМЕЧАНИЕ.

В случае, если поле, требующее обязательного ввода в него значения (свойство Required = True), на момент запоминания в таблице БД (т.е. на момент начала выполнения метода Post) содержит пустое или нулевое значение, возбуждается исключение EDBEditError.

Форматирование полей во время их редактирования


Свойство property EditMask: string;

служит для контроля правильности вводимых в поле значений. Ограничения накладываются при помощи формата. Если некоторый введенный символ не удовлетворяет маске, он не воспринимается.

Для строковых полей значение данного свойства может использоваться для форматирования не только входных, но и выходных значений вместе со свойством Display Text.

Маска представляет собой символьную строку. Она состоит из 3-х частей:

1. Собственно маска;

2. Символ, определяющий, будут ли литералы (символ после указателя '\') включаться в форматируемое значение как его часть (значение 1) или не будут(значение 0);

3. Символ, используемый в маске для представления пробела.

Символы, которые могут входить в маску:

! Подавляет ведущие пробелы. В отсутствие этого символа в данных подавляются хвостовые пробелы
> Все следующие символы будут на верхнем регистре, пока не встретится символ <
< Все следующие символы будут на нижнем регистре, пока не встретится символ >
<> Регистр не проверяется. Все остается на том регистре, как ввел пользователь
\ Следующий за ним символ является литералом, т.е. включается в форматируемое значение
L В данной позиции должен появиться только символ алфавита
| Аналогично L, но символ в данной позиции может и отсутствовать
А В данной позиции должен появиться только символ алфавита или цифра
а Аналогично А, но символ в данной позиции может и отсутствовать
С В позиции обязателен любой символ
с Аналогично С, но символ в данной позиции может отсутствовать
0 В данной позиции обязателен цифровой символ
9 В данной позиции должен появляться только цифровой символ или не появляться никакой
# В данной позиции должен появляться только цифровой символ плюс или минус или не появляться никакой
: Разделитель часов, минут и секунд для значения типа времени Если в данной национальной кодировке для указанных целей используется иной символ, он используется вместо символа ':'
/ Разделитель месяца, дня и года в датах. Если в данной национальной кодировке для указанных целей используется иной символ, он используется вместо символа ':'
; Разделитель частей маски
_ Заменитель пробела в маске

В модуле Mask имеются следующие константы, которые определяют некоторые компоненты маски по умолчанию

Имя константы Нач.значение Смысл
DetaultBlank _ Обозначение пробела в маске
MaskFieldSeparator , Разделитель частей маски
MaskNoSave 0 Если 0, символы маски не будут включаться в значение; если 1,-будут.

Пример.

Маска '!\(999\)000\-00\-00;1;_'. Введено '0952223344'. В период ввода представлялось на экране как '(095)222-33-44', запомнилось как '(095)222-33-44'. Если бы была использована маска '!\(999\)000\-00\-00;0;_', в период ввода представлялось на экране как '(095)222-33-44', запомнилось как '0952223344'.

Значение свойства Edit Mask игнорируется, если определен обработчик для события OnGelText.

Свойство го property EditMaskPtr: string;

возвращает значение маски редактирования. Поскольку свойство доступно только на чтение, его следует использовать вместо EditMask в тех случаях, когда маска должна быть только прочитана. В итоге мы защищаемся от случайных изменений маски.

Свойство property EditFormat: string;

применяется для форматирования значений полей типа TIntegerField, TSmallintField, TWordField перед их редактированием. Форматирование выполняется функцией Float To TextFmt.

Пример.

Пусть значение поля 3456.777. Тогда если EditFormat := '#.#', то при редактировании значение поля первоначально будет показано как 3456.8.

Свойство property Text: string;

содержит строковое изображение значения поля в том виде, в котором оно показывается в визуальном компоненте, когда НД находится в режиме редактирования (dsEdit) . Свойство Display Text содержит строковое изображение значения поля, когда НД находится не в режиме редактирования.



Свойство IsNull и обработчики событий OnSetText, On Validate, OnChange


Свойство IsNull: Boolean; во время выполнения возвращает True, если поле содержит пустое значение.

Проверить введенное в поле значение на его соответствие некоторым ограничениям или условиям можно в обработчике события OnValidate. Это событие наступает при изменении значения поля либо вручную, либо программно. Событие наступает до выполнения метода Post, который запоминает изменения БД. Поэтому, если полю присвоено неверное значение, выполнение метода Post можно предотвратить, выполнив метод Abort или возбудив исключительную ситуацию (raise Exception.Create}. Заметим, что для новых записей данное событие выставляется также, поскольку при занесении значений в поля пустые значения заменяются на непустые (т.е. модифи-цируются). Данный подход контроля правильности значений называется ориентированным на поля.

Существует и другой подход, ориентированный на записи. Он состоит в том, что в структуре ТБД при ее определении описываются ограничения на значения, которые может принимать данное поле. В этом случае контроль правильности ведется автоматически.

Пример.

Пусть поле Table I Company не должно содержать символа '@'.

procedure TForm1.Table1CompanyValidate(Sender: TField) ;

begin

IF POS('@',TablelCompany.AsString) > 0 THEN

begin

ShowMessage('Обнаружен символ @!');

Abort;

end;

end;

или

procedure TForm1.Table1CompanyValidate(Sender: TField);

begin

IF POS('@',TablelCompany.AsString) > 0 THEN

raise Exception.Create('Неверное значение');

end;

Если символ '@' содержится в значении, присвоенном полю, метод Abort или принудительно возбужденная исключительная ситуация не позволят выполниться методу Post и запись с неверным полем не будет физически записана в БД НД останется в том состоянии, в котором он находился (режиме редактирования dsEdit или добавления новой записи dslnsert}.

Для проверки введенного значения может быть использовано и другое событие, OnSetText Подобно событию OnVahdate, оно возникает при изменении значения поля Однако на момент события новое значение полю не присвоено и, если этого не сделать программно в обработчике события, сделано никогда не будет.

Например,

пусть в поле Table 1PUR_PRICE типа TFloatField было изменено значение с 10 на 200 Пусть новое значение не должно превышать 100 Тогда обработчик события будет выглядеть так

procedure TFormI.TablelPUR_PRICESetText(Sender: TField; const Text: String);

var Tmp : Real;

begin

Tmp := StrToFloat(Text);

IF Tmp > 100 THEN

ShowMessage('Ошибочное значение')

ELSE

TablelPUR_PRICE.Value := Tmp;

end;

что аналогично такому обработчику события OnVahdate.

procedure TFormI.TablelPUR_PRICEValidate(Sender: TField);

begin

IF TablelPUR_PRICE.Value > 100 THEN begin

ShowMessage('Ошибочное значение');

Abort;

END;//if

end;

Как видно, особенность события OnSetText состоит в том, что в обработчик передается константа-параметр Text, содержащая в текстовом виде новое значение, назначенное полю, в то время как действительное значение поля остается без изменения

Третье событие, OnChange, может быть использовано для тех же целей, что и On Validate

procedure TFormI.TablelPUR_PRICEChange(Sender: TField);

begin

IF TablelPUR_PRICE.Value > 100 THEN

raise Exception.Create('Ошибочное значение');

end;

Порядок вызова обработчиков событий OnSetText, On Validate, OnChange

При изменении значения поля события, обработчики которых позволяют контролировать правильность занесенного в поле значения, вызываются в следующей последовательности

1 OnSetText;

2 On Validate,

3 OnChange

Это важно, когда действия по проверке правильности нового значения поля сосредоточены не в одном обработчике, а распределены в обработчиках разных событий

Относительно события OnSetText известно, что если полю присвоено ошибочное значение, то нет нужды выполнять метод Abort или возбуждать исключительную ситуацию для предотвращения занесения этой записи в ТБД (поскольку новое значение в поле в этом случае еще не занесено) Наоборот, если поле удовлетворяет критериям правильности, в него программно нужно записать введенное пользователем новое значение (передаваемое в обработчик как параметр const Text String)

В обработчиках событий OnVahdate и OnChange, наоборот, в этом случае необходимо выполнять метол Abort или возбуждать исключительную ситуацию для предотвращения занесения этой записи в ТБД (поскольку новое значение в поле в этом случае уже занесено)

Однако следует помнить, что событие OnChange возникает только после события On Validate Поэтому, обработчик события OnChange может быть и не вызван, если обработчик On Validate выполняет метод Abort или возбуждает исключительную ситуацию для того, чтобы измененная запись с некорректным значением поля не была записана в ТБД



Значение поля по умолчанию и ограничения на значения поля


Значение поля по умолчанию можно установить при помощи свойства property DefaultExpression: string;

В случае указания значений, отличных от целочисленного, они должны заключаться в кавычки

В компоненте TField могут быть определены ограничения на значения этого или иных полей. Ограничение указывается при помощи SQL-подобного синтаксиса в свойстве

property CustomConstraint: string;

например,

TablelOklad.CustomConstraint :=' Oklad >= 300 and Oklad <= 2000';

Свойство property ConstraintErrorMessage: string;

позволяет указать сообщение об ошибке, выдаваемое пользователю в случае, если введенное значение поля не удовлетворяет ограничению, указанному в свойстве CustomConstraint, например

TablelOklad. ConstraintErrorMessage := ' Оклад должен быть в диапазоне 300...2000 ';

Свойство property ImportedConstraint: string;

содержит ограничения значения поля, "навязанные" сервером. Их не нужно переопределять; дополнительные ограничения можно наложить при помощи свойства CustomConstraint.

Свойство ro property HasConstraints: Boolean;

возвращает True, если для поля определены ограничения в свойствах CustomConstraint, ImportedConstraint или DefaultExpression. В противном случае свойство возвращает False.



Создание вычисляемых полей


Если необходимо создать вычисляемое поле, значение которого вычисляется по значениям других полей, поступают так:

1. В редакторе полей необходимо создать новое поле, пометив его как Calculated. Для этого нужно сделать текущим (при помощи мыши) необходимый НД, нажать правую кнопку мыши, выбрать в меню Field Editor и снова нажать правую кнопку мыши и выбрать в меню New Field. Затем в окне диалога необходимо указать имя поля, его тип и для строковых полей - длину (рис. 6.3).

Для нового поля будет создан компонент TField, доступ к которому отныне можно осуществлять редакторе полей. 2. Для компонента НД, к которому принадлежит вычисляемое поле,

необходимо определить обработчик события OnCalcFields. Например, для НД Table 1, ассоциированному с ТБД "Сотрудники", будем заносить в вычисляемое поле TablelVychis! значение 'Да', если в поле Table1inYaz (знание иностранных языков) этой записи содержится значение True. В противном случае в поле TablelVychis! будем заносить пустое значение (форма приложения показана на рис. 6.4.):

procedure TGridForm.TablelCalcFields(DataSet: TDataSet) ;

begin

IF Table1InYaz.Value THEN TablelVychos!.AsString := 'Да' ELSE

TablelVychosl.AsString := '';

end;

Событие OnCalcFields возникает всякий раз, когда курсор (указатель записи) перемещается в НД от записи к записи (например, после выполнения методов Next, Last и т.д., или при движении по записям в TDBGrid вручную). Это событие возникает и при инициализации НД (после открытия), а также после фильтрации записей в НД, что, впрочем также связано с изменением местоположения указателя записи.

Кроме того, если свойство набора данных AutoCalcFields установлено в True, событие OnCalcFields наступает также и при модификации значений невычисляемых полей в режимах dslnsert и dsEdit данного НД или НД, реляционно с ним связанного (когда установлены ограничения целостности в самой ТБД, а не тогда, когда они подразумеваются).

Процедура-обработчик события OnCalcFields содержит реализацию алгоритма вычисления значения вычисляемого поля или группы полей. Необходимо помнить, что в этом обработчике значение может быть присвоено только вычисляемому полю и не может - полю, определенному в структуре таблицы БД.

ЗАМЕЧАНИЕ.

Иногда бывает необходимым присваивать вычисляемым полям значения, не содержащиеся в полях других таблиц. Иными словами, иногда бывает полезным записывать в вычисляемое поле значение некоторых переменных формы. Например, пусть мы добавляем записи в НД ТЫ, по некоторому алгоритму рассчитывая значения поля Summa. Пусть для расчета значения поля Summa используется переменная TekOstatok. И мы хотим значение TekOstatok для каждой записи занести в вычисляемое поле TbITO.

И здесь встречается одна сложность: занесение значения в вычисляемое поле производится в обработчике события OnCalcFields и это происходит в то время, когда значения локальной переменной TekOstatok уже утрачены.

В этой ситуации в процессе выполнения алгоритма, добавляющего записи в ТЬ1, приходится запоминать значения локальной переменной в каком-либо динамическом списке, а затем извлекать из него соответствующие элементы в обработчике события OnCalcFields, присваивая значения этих элементов полю TbITO.



Поля выбора данных


Кроме обычных полей, связанных с полями ТБД, и вычисляемых полей, в Delphi имеется возможность создавать поля выбора данных (lookup).

Поля выбора данных

одного набора данных содержат значения их другого набора данных, связанного по ключу с НД, к которому принадлежит поле выбора данных. Поле выбора данных всегда доступно только для чтения и не может быть одновременно полем выбора данных и вычисляемым полем. Реляционное отношение НД, служащего источником значений для поля выбора данных и НД, к которому оно принадлежит, есть "один-ко-многим" и реже "один-к-одному". Это означает, что на один вариант значения в наборе данных-источнике должно приходиться одно или несколько связанных значений в НД, к которому принадлежит поле выбора.

Для определения поля набора данных необходимо создать новое поле в редакторе полей, сразу же установив радио-группу Field Type в значение Lookup (рис. 6.5.).

Затем устанавливаем значения свойств:

DataSet -

Имя НД-источника значений для поля выбора данных;

Key Fields -

Индексные поля набора данных-владельца поля выбора данных. По этим полям НД-владелец соединяется с НД-источником значений поля выбора данных. Если в индексе имеется несколько полей, они перечисляются через точку с запятой;

Lookup Fields - Индексные поля НД-источника значений для поля выбора. По значениям этих индексных полей устанавливается связь набора-источника со значениями индексных полей НД-владельца поля выбора (они указаны в параметре Key Fields). Если в индексе имеется несколько полей, они перечисляются через точку с запятой;

Result Field -

Поле набора данных-источника, возвращаемое в качестве результата. Необходимо следить, чтобы тип вновь создаваемого поля и поля результата совпадали.

Указанным выше параметрам редактора полей соответствуют свойства компонента TField

property LookupDataSet: TDataSet;

property KeyFields: string;

property LookupKeyFields: string;

property LookupResultField: string;

Аналогичным по последствиям будет установка соответствующих свойств в инспекторе объектов для вновь добавляемого поля. Заметим, что свойство поля

property Lookup: Boolean;

должно быть установлено в True.

Разберем пример отношения "один-к-одному" Пусть существует ТБД 'Сотрудники", которая включает в себя поля "Табельный номер" (TabNum), "ФИО" (FIO), "Должность" (Doljnost) и "Ученая степень" (UchStepen) С ней ассоциирован НД Table 1 (рис 6 6)

и ТБД "Информация о сотрудниках", которая включает в себя ФИО сотрудника (поле FIO), год рождения (GodRojd) и семейное положение (SemPoloj). С ней ассоциирован НД DataModule1 Table1 (рис 6 7). ТБД "Сотрудники" и "Информация о сотрудниках" связаны по индексу, образованному полями FIO

Требуется при просмотре в ТБД "Сотрудники" выводить год рождения данного сотрудника ниже компонента TDBGrid, в DBTextl

Для этой цели для Tablel, связанной с НД "Сотрудники", входим в редактор почеи и создаем новое поле, сразу же установив радио-группу Field Type в значение Lookup (рис 6 8)

Затем устанавливаем значения свойств

DataSet

НД DataModule Table1, который связан с ТБД "Информация о сотрудниках"),

Key Fields

поле FIO НД Tablel (ТБД "Сотрудники"), которое отображается в TDBGrid),

Lookup Fields

поле FIO НД DataModulel Table (ТБД "Информация о сотрудниках"),

Result Field

поле GodRojd НД DataModulel Tablel (ТБД "Информация о сотрудниках")

Таким образом, нами определен новый компонент Table 1GR типа TField (а точнее, TStringField), источником данных для которого служит поле GodRojd ТБД "Информация о сотрудниках", из той ее записи, у которой значение поля FIO совпадает со значением поля FIO соответствующей записи ТБД "Сотрудники"

Далее размещаем в форме компоненты Label 1 и DBTextl (связанный с полем Table1GR) Тогда для текущей записи сотрудника в DBGndl в DBText1 отображается год рождения для данного сотрудника из ТБД "Информация о сотрудниках"(рис 6.9)



Поля выбора данных, использующиеся


Поля выбора данных могут использоваться для автоматического занесения информации в данную ТБД из другой ТБД. Часто это очень удобно. Рассмотрим пример, для чего несколько модифицируем показанные выше ТБД.

Допустим имеется ТБД "Сотрудники" состоящая из полей: уникальное TabNum (табельный номер), FIO (ФИО), KodDol (код должности), и ТБД "Оклады", состоящая из полей уникальное KodDol (код должности), ОоЦпо$1(должность), Oklad (оклад). ТБД "Сотрудники" и "Оклады" состоят в связи "многие-к-одному" или, если смотреть со стороны ТБД "Оклады", "один-ко-многим". Полем связи является Doljnost.

Как видно, в ТБД "Сотрудники" может быть несколько записей с одним и тем же значением поля KodDol (например, 144), в то время как в ТБД "Оклады" конкретный код должности может встречаться только единожды.

Присвоение кодов каким-либо атрибутам таблиц БД является весьма распространенным приемом. Если по этим атрибутам для нескольких ТБД имеют место реляционные отношения, для хранения индексов по кодам (цифровым полям) и их использования для доступа к данным требуется много меньше дискового пространства и времени, нежели для хранения индексов по оригинальным значениям. В нашем примере это обосновано: лучше строить индекс по коду (слово), нежели по символьному значению поля Doljnost (рис. 6.10). Это особенно актуально, если символьное поле имеет большую длину (например, 200 символов). Индексы по таким полям получаются очень большими.

Другой выгодой от использования кодов является то, что в справочнике (lookup-ТБД) "Оклады" коды должности KodDol представляют собой автоинкрементное поле, т.е. поле, уникальное значение которого BDE устанавливает автоматически. В дальнейшем его менять нельзя. Это снимает необходимость каскадного изменения в дочерней ТБД "Сотрудники" при изменении значения поля связи (KodDol) в родительской ТБД "Оклады". ЗАМЕЧАНИЕ. Автоинкрементные поля более свойственны локальным СУБД. Для удаленных (серверных) СУБД автоинкрементные поля заменяются другими механизмами, например, генераторами (InterBase). Если бы связь между данными ТБД была установлена по полю Doljnost, a не KodDol, то при изменении значения должности с "приват-доцент" на "доцент" в ТБД "Оклады" на значение "доцент" должны были бы измениться значения всех записей в ТБД "Сотрудники", у которых поле Doljnost содержит значение "приват-доцент" (рис. 6.11, 6.12).

Однако поскольку мы используем для связи между названными таблицами код, в ТБД "Оклады" значение поля Doljnost можно менять сколь угодно много раз - на связь между ТБД это не окажет никакого влияния. Заметим, что по полю Doljnost в ТБД "Оклады" должен быть построен уникальный индекс, чтобы предотвратить возможность ввода двух одинаковых должностей с разными окладами. В данном примере кодом будет являться поле KodDol, a семантически значимым полем поле Doljnost. Более подробно с данным вопросом можно ознакомиться в разделе "Обеспечение ссылочной целостности"

Поставим задачу. Пусть при вводе данных в ТБД "Сотрудники" нам известна должность конкретного сотрудника, но кода ее, мы, разумеется, не помним. Поэтому нужно:

• обеспечить просмотр и выбор интересующей должности из справочника (lookup-ТБД) "Оклады";

• обеспечить занесение кода этой должности из поля KodDol ТБД "Оклады" в поле KodDol ТБД "Сотрудники".

В форме НД SotrTable соответствует ТБД "Сотрудники", а НД OkladyTable соответствует ТБД "Oklady".

Добавим в набор данных SotrTable поле выбора данных Lookupchik (рис. 6.13)

Значения параметров поля выбора, показанные на рис. 6.13, можно читать так "Исходя из реляционной связи по полю KodDol со стороны НД SotrTable (свойство Key Fields) и по полю KodDol (свойство LookupKeys) со стороны набора данных-источника значений поля выбора (набора данных OkladyTable, свойство DalaSel), заполнять поле реляционной связи в SotrTable значением, взятым из записи, которая будет выбрана в ниспадающем меню В ниспадающем меню показывать только поле Doljnost (параметр ResultField) НД OkladyTable "



Буфер значений полей выбора данных


Свойство

property LookupCache: Boolean;

определяет, будут ли значения полей выбора данных храниться в кэше (буфере) - значение True -, или нет (значение False).

Свойство

ro property LookupList: TLookupList;

содержит список значений из набора данных-источника для полей выбора данных, индексированных набором значений полей, список которых содержится в свойстве KeyFields. Метод ValueOfKey компонента TLookupList возвращает результирующее поле (значение поля выбора данных)

Value := LookupList.ValueOfKey(DataSet.FieldValues[KeyFields]) ;

Список полей выбора данных формируется после открытия НД или после обновления списка методом компонента Tfield procedure RefreshLookupList;



Обзор полей TxxxField


• TStringField - хранит строковое значение длиной до 255 символов. Строки большей длины нужно хранить в blob-полях (TMemoField).

Свойство property Transliterate: Boolean; указывает, следует ли производить преобразование символов в ANSI в том случае, если символьные поля в ТБД-источнике находятся не в ANSI-кодировке или содержат расширенные ASCII-символы. Когда свойство установлено в True, для

преобразования ASCII символов в иную кодировку используется функция AnsiToNative и функция NativeToAnsi для перевода в ANSI.

• целочисленные поля -

применяются для хранения целых чисел различной длины:

TIntegerField-

от -2,147,483,648 до 2,147,483,647

TSmallintField -

от -32,768 до 32,767

TWordField

- от 0 до 65,535

Свойства property MaxValue: Longint; и property MinValue: Longint; могут определять максимальное и минимальное значение поля.

• числовые поля с плавающей точкой -

применяются для хранения целых чисел различной длины:

TFloatField -

числа, чьи абсолютные значения - 5.0*10-324 to 1.7*10+308 до 15-16

TCurrencyField -

аналогично TFloatField, но в денежном формате

TBCDField -

вещественные десятичные числа с фиксированным числом разрядов после точки. До 18 символов. Диапазон представляемых чисел зависит от числа знаков. Применяется только для Paradox.

Свойство property Precision: Integer; позволяет указать число знаков после десятичной точки (по умолчанию 15).

Свойства property MaxValue: Longint; и property MinValue: Longint; могут определять максимальное и минимальное значение поля.

• TBooleanField -

содержит значения True или False.

• поля даты и времени:

TDateTimeField -

содержит значения даты и времени в формате TDateTime.

TDateField -

значения даты в формате TDate

TTimeField

- значения времени в формате TTime.

• поля для хранения значений произвольных форматов:

TBIobField -

произвольное байтовое поле без ограничения длины.

Метод procedure LoadFromFile(const FileName: string); загружает содержимое поля из файла, метод procedure LoadFromStream(Stream: TStream); - из потока.

Метод procedure SaveToFile(const FileName: string); сохраняет содержимое поля в файле, метод procedure SaveToStream(Stream: TStream); - в потоке.

Свойство property BlobSize: Integer; содержит размер в байтах blob-поля данной записи.

Свойство property Transliterate: Boolean; указывает, следует ли производить преобразование символов в ANSI в том случае, если blob-поля в ТБД-источнике находятся не в ANSI-кодировке или содержат расширенные ASCII-символы. Когда свойство установлено в True, для преобразования ASCII символов в иную кодировку используется функция AnsiToNative и функция NativeToAnsi для перевода в ANSI.

Свойство property BlobType: TBIobType; возвращает тип blob-поля. Возможные значения: ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle, ftTypedBinary.

TBytesField -

произвольное байтовое поле без ограничения длины. Не имеет методов LoadFromFile, LoadFromStream, SaveToFile, SaveToStream. Свойство

property DataSize: Word

позволяет определить во время выполнения, сколько байт нужно для хранения поля в памяти.

TVarBytesFieId -

произвольное байтовое поле длиной до 65,535 байт. Текущая длина может быть получена из первых двух байт поля.

TMemoField -

строковое значение неопределенной длины (мемо-поле).

Метод procedure Clear; очищает мемо-поле.

Метод procedure LoadFromFile(const FileName: string); загружает содержимое поля из файла, метод procedure LoadFromStream(Stream: TStream); - из потока.

Метод procedure SaveToFile(const FileName: string); сохраняет содержимое поля в файле, метод procedure SaveToStream(Stream: TStream); - в потоке.

Свойство property BlobSize: Integer; содержит размер в байтах blob-поля данной записи.

Свойство property Transliterate: Boolean; указывает, следует ли производить преобразование символов в ANSI в том случае, если blob-поля в ТБД-источнике находятся не в ANSI-кодировке или содержат расширенные ASCII-символы. Когда свойство установлено в True, для преобразования ASCII символов в иную кодировку используется функция AnsiToNative и функция NativeToAnsi для перевода в ANSI.

Для работы с мемо-полями в БД Delphi предоставляет компонент TDBMemo. Его описание приводится в разделе "Компоненты для работы с текущей записью набора данных ".

TGraphicField -

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

Информация о типе поля


Свойство го property DataType: TFieldType;

возвращает информацию о типе данных поля.

Возможные значения:

ftUnknown неизвестный или неопределенный тип

ftString строковое или символьное поле

ftSmallint 16-битное целое

ftlnteger 32-битное целое

ftWord 16-битное беззнаковое

ftBoolean логическое поле

ftFloat числовое с плавающей точкой

ftCurrency поле в денежном формате

ftBCD двоично-десятичное поле

ftDate поле типа даты

ftTime поле типа времени

ftDateTime поле типа даты и времени

ftBytes фиксированное число байт в двоичном представлении

fiVarBytes переменное число байт в двоичном представлении

ftAutoInc автоинкрементное поле (автоматически увеличивающийся счетчик, 32-битное целое)

FtBlob большой двоичный объект

FtMemo текстовое мемо-поле

FtGraphic графическое поле

FtFmtMemo форматированное текстовое мемо-поле

FtParadoxOle поле Paradox OLE

FtDBaseOle поле dBASE OLE

FtTypedBinary типизированное двоичное поле



Информация о виде поля


Вид поля определяется свойством

property FieldKind: TFieldKind;

Возможны следующие значения:

fkData физическое поле в базе данных

fkCalculated вычисляемое поле

fkLookup поле, возвращающее значение (lookup-поле)

fkInternalCalc вычисляемое поле, значение которого можно хранить в наборе данных

ЗАМЕЧАНИЕ.

Поле awssiflcInternalCalc есть вычисляемое поле, которое может записываться в набор данных. Однако не следует путать его с просто вычисляемым (fkCalculated) полем. Последнее определяется как вычисляемое в редакторе полей и алгоритм его расчета задается в обработчике события набора данных OnCalcFields. Просто вычисляемые поля не могут храниться в качестве полей НД.

Поле вида fkInternalCalc может храниться в НД, но оно не определяется как вычисляемое в редакторе полей, а вычисляется SQL-сервером или BDE и содержится в "живом" (то есть доступном для изменения) наборе данных, возвращаемом как результат выполнения SQL-запроса.



Имя поля в SQL-запросах


Пусть после выполнения запроса

SELECT TOVAR AS Т, ZENA AS Z FROM TOVARY WHERE ZENA > 100

получим набор данных, состоящий из полей Т и Z. Чтобы определить для такого поля, какое физическое поле в таблице послужило источником его формирования, используют свойство property Origin: string;

Однако, значение свойства Origin доступно в среде редактора полей и только для тех полей, для которых явно создан компонент TField в редакторе полей.



Проверка применимости символа в поле


Свойство function IsValidChar(InputChar: Char): Boolean; virtual;

возвращает True, если символ InputChar может быть записан в поле того или иного вида:

Тип поля Верные символы
FtBoolean все
FtSmallInt числа 0 ..9, плюс (+), минус (-).
FtWord числа 0 ..9, плюс (+), минус (-)
FtAutoInc числа 0 ..9, плюс (+), минус (-)
FtDate все
Ftlnteger числа 0 ..9, плюс (+), минус (-)
FtTime все
FtCurrency числа 0 ..9, плюс (+), минус (-), буква Е или е и разделитель дробной и целой части (определяется текущими установками Windows на конкретном компьютере)
FtDateTime все
ftFloat числа 0 ..9, плюс (+), минус (-), буква Е или е и разделитель дробной и целой части (определяется текущими установками Windows на конкретном компьютере)
ftBCD числа 0 ..9, плюс (+), минус (-), разделитель дробной и целой части (определяется текущими установками Windows на конкретном компьютере)
ftString все
ftVarBytes все
ftBytes все
ftBlob все
ftDBaseOle все
ftFmtMemo все
ftGraphic все
ftMemo все
ftParadoxOle все
ftTypedBinary все
ftUnknown все


Набор данных TTable


TTable содержит записи, источником которых может быть только одна ТБД. Состав записей зависит от того, производится ли в TTable фильтрация по какому-либо условию. Если нет, то в TTable будут представлены все записи ТБД, ассоциированной с данной TTable; если да, то в TTable попадут только записи, удовлетворяющие условию фильтрации.

По той причине, что одна и та же TTable может в один момент времени содержать неотфильтрованные записи, в другой - отфильтрованные по одному условию и в третий момент времени - записи, отфильтрованные по третьему условию, считают, что НД TTable содержит подмножество множества записей ТБД, которая ассоциирована с данным TTable.

Псевдоним БД указывается в свойстве DatabaseName, имя ассоциированной ТБД - в свойстве TableName.



Набор данных TQuery


TQuery содержит записи, источником которых могут являться несколько ТБД, а также агрегированные значения (такие как сумма, минимум, максимум, среднее), просчитанные по полям одной или нескольких таблиц.

НД формируется так: выполняется SQL-запрос, представленный оператором SELECT (свойство SQL), и в качестве НД возвращаются записи из таблиц-источников, удовлетворяющие определенным условиям (если они имеются).

ТБД-источники перечисляются в разделе FROM оператора SELECT. Условия выборки записей указываются в разделе WHERE. Записи результирующего НД состоят из полей, перечисленных после ключевого слова SELECT (или, если указан символ * - всех полей). Например:

SELECT T.NumTeacher, S.NumStudent, T.ExamDate

FROM teachers T, students S WHERE (T.Kurs = S.Kurs)

В данном случае будет возвращен НД, состоящий из записей, источником которых служат ТБД teachers и students. Эти записи должны удовлетворять условию равенства поля Kurs обеих ТБД. В результирующем НД будет всего 3 поля - NumTeacher, ExamDate из teachers и NumStudent из students.

Более подробно об операторе SELECT и иных SQL-операторах см. соответствующие разделы во второй части книги.

Псевдоним БД указывается в свойстве DatabaseName. В случае, если псевдоним указан, все ТБД в разделе FROM оператора SELECT считаются принадлежащими данной БД; в противном случае для каждой ТБД необходимо указывать маршрут поиска, а если он не указан, ТБД должны располагаться в текущем каталоге.

Как и в случае использования TTable, НД TQuery может содержать полное множество записей какой-либо ТБД. В этом случае, являющемся, вообще говоря, частным, в качестве списка полей следует указать символ "*", выборка должна вестись из одной ТБД и должно отсутствовать условие фильтрации в операторе SELECT.



Понятие наборов данных


Под набором данных в Delphi понимается группа записей из одной или нескольких ТБД, доступная для использования через компоненты TTable или TQuery.

Как набор данных, рассматривается также компонент TStoredProc (хранимая процедура в серверной базе данных, возвращающая набор данных). Однако применение компонента TStoredProc ограничено. Во-первых, он может применяться только для серверных (удаленных) БД; во-вторых, только для тех из них, которые поддерживают механизмы хранимых процедур, возвращающих набор данных. Поэтому в дальнейшем при рассмотрении наборов данных, будем ориентироваться в основном на компоненты TTable и TQuery.

Если рассматривать иерархию компонентов Delphi, компоненты TTable и TQuery являются наследниками компонента TDBDataSet, потомка компонента TBDEDataSet, который, в свою очередь, является наследником компонента TDataSet.

Компоненты TTable и TQuery имеют общие свойства, методы и события;

это обусловливается тем, что они имеют общих "родителей". Именно эти свойства, методы и события рассматриваются в настоящем разделе. При этом TTable и TQuery называются общим термином "набор данных" (НД). В литературе по Delphi TTable и TQuery при рассмотрении их общих свойств часто называют типом DataSet, по имени типа их предка.

TTable и TQuery имеют также свойства, методы и события, присущие только TTable или только TQuery. Такие свойства, методы и события для каждого из данных компонентов рассматриваются далее в отдельных главах, посвященных каждому из указанных компонентов.

Перед рассмотрением общих возможностей для работы с НД разберем общие черты и различия TTable и TQuery.



Состояния наборов данных


НД могут находиться в одном из 6 состояний:

dslnactive НД закрыт.
dsBrowse Состояние по умолчанию для открытого НД. Показывает, что записи просматриваются, но в данный момент не изменяются.
dsEdit НД находится в состоянии редактирования текущей записи (после явно или неявно вызванного метода Edit).
dslnsert НД находится в состоянии добавления новой записи (после явно или неявно вызванного метода Insert или Append).
dsSetKey НД находится в состоянии поиска записи по критерию, заданному методами FindKey, GotoKey, FindNearest или GotoNearest. По окончании поиска НД переходит в состояние dsBrowse.
dsCalcFields Выполняется установление значений вычисляемых полей (по алгоритму, заданному в обработчике события OnCalcFields). В данном режиме изменения в НД вноситься не могут. После выхода из режима НД переходит в предыдущее состояние.
dsFilter Обрабатывается фильтрация записей в НД при свойстве Filtered, установленном в True. Имеет место текущий вызов события OnFilterRecord для определения того, удовлетворяет ли текущая запись условию фильтрации, описанному в обработчике данного события. После выполнения события OnFilterRecord НД переводится в состояние dsBrowse.

Рассмотрим методы, которые могут переводить БД из одного состояния в другое.

• Inactive—>dsBrowse

НД во время выполнения программы можно открыть методами Table. Open, Query. Open. Во время разработки и во время выполнения НД можно открыть, установив в True свойства Table. Active и Query. Active.

dsBrowse—>Inactive НД во время выполнения программы можно закрыть методами Table. Close, Query. Close. Во время разработки и во время выполнения НД можно закрыть, установив в False свойства Table.Active и Query.Active.

Заметим, что если какая-либо запись на момент закрытия НД находится в режиме редактирования (dsEdit) или добавления новой записи (dslnsert), применение метода Close не приводит к автоматической выдаче метода Post. Таким образом, НД закрывается, находясь в режимах dslnsert или dsEdit, а не dsBrowse. В этом случае изменения, сделанные в записи, не запоминаются. Для перевода НД из указанных режимов в режим dsBrowse, перед тем как НД закрывается, используйте обработчик события BeforeClose. Заметим, что описанная ситуация будет встречаться в первую очередь для внезапно или принудительно закрываемых НД.

• dsBrowse—>dsEdit

Перевести НД в режим редактирования можно методом Edit. После этого значения полей текущей записи можно изменять.

• dsEdit—> dsBrowse

Метод Post приводит к запоминанию измененной записи в НД. Метод Cancel отменяет изменения, сделанные в полях записи. Запись не запоминается в НД.

• dsBrowse—>dslnsert

Перевести НД в режим вставки можно методами Insert или Append, После этого программе становится доступна пустая запись, полям которой нужно присвоить какие-либо значения. Чтобы полям новой записи присвоить умалчиваемые значения , следует воспользоваться обработчиком события OnNewRecord.

dslnsert—> dsBrowse Метод Post добавляет новую запись в НД. Если НД не находится в режиме dslnsert, возбуждается исключительная ситуация. Метод Cancel отменяет добавление новой записи в НД. Содержимое полей, назначенных новой записи, теряется.

• dsBrowse—>dsSetKey

НД находится в данном состоянии, когда осуществляется поиск записи, удовлетворяющей условию, установленному методом SetKey (и затем, возможно, измененному методом EditKey). Именно эти методы и переводят НД в режим dsBrowse. Поиск записи производится одним из следующих методов: GoToKey, GoToNearest, FindKey, FindNearest. В случае успешного или неуспешного завершения метода поиска, НД переводится в состояние dsBrowse.

• dsBrowse—>dsFilter

НД находится в данном состоянии всякий раз, когда приложение обрабатывает событие OnFilterRecord при фильтрации записей (при свойстве Filtered = True). При этом НД переводится из состояния dsBrowse в состояние dsFilter. Это предотвращает модификацию НД во время фильтрации. После завершения вызова обработчика события OnFilterRecord НД переводится в состояние dsBrowse. Вызов события OnFilterRecord производится для каждой записи НД при установке свойства Filtered в состояние True.

Получить текущее состояние НД

можно, используя метод State. Он возвращает следующие константы: dslnactive, dsBrowse, dsEdit, dslnsert, dsSetKey, dsCalcFields, dsFilter.

Пример

IF Tablel.State = dslnactive THEN Table1.Active := True;

Реакция на изменение состояния набора данных.

Событие OnStateChange (компонент DataSource) наступает всякий раз при изменении состояния НД. Следующий пример показывает, как отобразить на экране (в компоненте Label 1) сообщение о текущем состоянии НД:

procedure TForm1.DataSourcelStateChange(Sender: TObject);

var S : String;

begin

CASE Table1.State OF

dslnactive S = 'He активна' ;

dsBrowse S = 'Просмотр' ;

dsEdit S = 'Редактирование';

dslnsert S = 'Вставка';

dsSetKey S = 'Установка ключа' ;

dsCalcFields S = 'Вычисляемое поле' ;

END; {case}

Label1.Caption := S;

end;

Свойства некоторых компонентов, в первую очередь Enabled, могут зависеть от состояния НД. Например, если кнопка Button 1 должна быть доступной для нажатия только в режиме dslnsert, допустим такой код:

procedure TFormI.DataSourcelStateChange(Sender: Tobject) ;

begin

Button1.Enabled := (Tablel.State = dslnsert);

end;



Общие положения


Существует два способа работы с записями в НД.

Способ, основанный на использовании операторов SQL, предполагает оперирование группами записей. Именно так работают SQL-операторы группового обновления НД UPDATE, INSERT, DELETE и выборки групп записей SELECT. Записи, удовлетворяющие некоторому условию, выдаются группами; даже если условию удовлетворяет только одна запись, считается, что в данном случае группа состоит из одной записи.

Второй способ состоит в оперировании единичными записями. Если необходимо изменить, добавить или удалить группу записей, необходимая операция выполняется для каждой из таких записей. Для этого такие записи в НД нужно отыскать, для чего применяются навигационные методы. Они всегда работают с единичной записью и связанны с понятием курсора НД.

Понятие курсора набора данных.

Под курсором набора данных понимается указатель текущей записи в конкретном наборе данных. Текущая запись - та запись, над которой в данный момент времени можно выполнять какие-либо операции (удаление, изменение, чтение значений, содержащихся в записи полей).

Существует 5 методов для изменения курсора НД:

Procedure First; Устанавливает курсор на первую запись в наборе данных.
procedure Last; Устанавливает курсор на последнюю запись в наборе данных.
procedure Next; Перемещает курсор на следующую запись в наборе данных.
procedure Prior; Перемещает курсор на предыдущую запись в наборе данных.
function MoveBy(n:Integer): Integer; Перемещает курсор на n записей к'концу набора данных (n > 0) или к началу набора (n < 0.)


Определение начала и конца набора данных


Свойство property BOF: Boolean;

Возвращает True, если курсор установлен на первую запись в наборе данных.

Свойство property EOF: Boolean;

извращает True, если курсор установлен на последнюю запись в наборе данных.



Порядок следования и порядок сортировки записей


Может сложиться впечатление, что первая и последняя записи набора Данных всегда фиксированы, что это физически первая и последняя записи в наборе данных. Это неверно. Во-первых, как уже отмечалось выше, набор данных может содержать часть записей из ТБД. Поэтому набор данных -понятие логическое, а не физическое. Для TTable последовательность расположения записей в наборе данных определяется используемьш индексом, обуславливающим сортировку. Для TQuery порядок следования записей либо случаен, особенно при использовании в качестве источника более одной ТБД, либо упорядочен в порядке перечисления полей, в разделе ORDER BY.

Начиная работать с НД, имеющим одну сортировку записей, мы можем затем переопределить сортировку, и записи "перестроятся" в соответствии с новой сортировкой, т.е. логический порядок их следования изменится.

При изучении вопросов навигации по НД следует говорить прежде всего о Логическом характере следования записей, поскольку физический характер их расположения в конкретном случае неизвестен.

Пусть, например, имеется ТБД, состоящая из 2 полей: Name (Фамилия сотрудника) и Oklad (Оклад). Пусть в ТБД имеется 10 записей, которые вводились следующим образом и. следовательно, в таком порядке и хранятся:

Пусть данная ТБД проиндексирована по 2 индексам. Первый - по возрастанию поля Name, второй - по возрастанию поля Oklad.

Пусть в наборе данных, связанном с этой ТБД, установлен фильтр на поле Oklad (см. метод SetRange). Значение в этом поле должно лежать в диапазоне 500..1500. Если порядок прохождения записей определяется индексом по полю Name (Tablel.IndexfieldNames := 'Name'), получим следующий НД (рис. 7.1):

Если порядок прохождения записей определяется индексом по полю Oklad. получим следующий НД (рис. 7.2):

Как можно заметить, при сортировке по полю Name метод Last установит курсор на запись со значением поля Name, равным 'Якунин'; при сортировке по полю Oklad - на запись со значением поля Name, равным Name=' Юрьев'. Аналогично, если текущей является 3-я по счету запись от начала НД, для первого случая это будет 'Юрьев', для второго - 'Якунин'. Метод Next переместит курсор для первого случая на запись 'Иванов', для второго - на запись 'Якунин'.



Навигация по набору данных вниз


Для выполнения действий, начиная от некоторой стартовой записи и до конца набора данных, используют цикл WHILE not EOF. Стартовая запись может устанавливаться методом FindKey (см. ниже). Приведем пример для случая, когда стартовая запись - первая в наборе:

WITH Tablel do begin First;

WHILE not EOF do begin

{Какие-либо действия}

Next;

END; {while]

END; {with}



Навигация по набору данных вверх


Для выполнения каких-либо действий, начиная от некоторой стартовой записи и до начала набора данных, используют цикл WHILE not BOF. Стартовая запись может устанавливаться методом FindKey (см. ниже). Приведем пример для случая, когда стартовая запись - последняя в наборе:

WITH Tablel do begin Last;

WHILE not BOF do begin

{Какие-либо действия}

Prior;

END; {while}

END; {with}



Спонтанные перемещения по набору данных


Вообще говоря, часто необходимо в зависимости от каких-либо условий "прыгать" по НД взад-вперед. Поэтому распространен вариант одновременного использования Next, Prior и MoveBy в одном программном блоке. При этом важно помнить о том обстоятельстве, что применение метода Edit, когда изменяется значение индексного поля, по которому в настоящий момент ведется сортировка в НД, может переместить запись вниз или вверх. Например, для приводимой выше таблицы, состоящей из полей Name и Oklad, выполним следующий код, осуществляющий увеличение окладов сотрудников на 1000:

Tablel.IndexFieldnames := 'Oklad';

WITH Tablel do begin

First;

WHILE not EOF do begin

Edit;

TablelOklad.Value := TablelOklad.Value + 1000;

Post;

Next;

END;//while

END; //with

Нетрудно убедиться, что указанный фрагмент не будет правильно работать: оклад господина Яковлева (500) после увеличения на 1000 составит 1500, поэтому запись переместится в НД после записи 'Юрьев', в то же время оставаясь текущей. После запоминания измененной записи (Post) будет предпринята попытка перейти к следующей записи (Next). Однако наша запись, прежде логически первая, после изменения поля Oklad стала логически последней. Поскольку курсор НД находится на последней записи, свойство Tablel.EOF будет автоматически установлено в True. Поэтому метод Next выполнен не будет. На новом шаге цикла WHILE (2-м по счету) произойдет остановка цикла из-за выполнения условия прекращения цикла (рис. 7.3):

Поэтому следует всегда придерживаться правила: не изменять значения индексного поля при прохождении набора данных в цикле.

Можно предложить способ решения данной проблемы, который состоит в простом переборе записей при отключенной сортировке по исходному индексу или при сортировке по другому индексу. Например, для описанного выше НД следующий код будет правильным (результат работы - на рис. 7.5.):

var OldIndexFieldnames : String;

begin

// запомним старое индексное поле

OldIndexFieldnames := Tablel.IndexFieldnames;

// назначим индекс по неизменяемому полю

Tablel.IndexFieldnames := 'Name';

WITH Tablel do begin

First;

WHILE not EOF do begin

Edit;

TablelOklad.Value := TablelOklad.Value + 1000;

Post;

Next;

END;//while

END; //with

// восстановим старое индексное поле

Tablel.IndexFieldnames := OldIndexFieldnames;

IF OldIndexFieldnames = 'Oklad' THEN

TablelOklad.Index := 0;

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

Рассмотрим другой способ. Он состоит в применении к набору данных временной фильтрации.

ЗАМЕЧАНИЕ.

Данный способ приводится лишь для того, чтобы показать, что могут иметь место и другие подходы к разрешению указанной проблемы. Однако лучше не привыкать к подобному "хитроумию", поскольку оно оправдано лишь когда проблему невозможно разрешить другими способами. Способ изменения текущего индекса, рассмотренный выше, проще и безопаснее. Второй способ имеет более узкое применение, поскольку в этом случае индексное поле должно иметь у всех записей одно и то же значение. Рекомендую повторно вернуться к его рассмотрению после того, как вы ознакомитесь с фильтрацией записей в НД при помощи свойства Filtered, которое описано ниже Пусть в НД для рассмотренного выше примера имеется поле Otdel. Пусть текущий индекс в НД - также построен по полю Otdel. Имеются 2 отдела: Х и Z. Пусть необходимо сменить название отдела Х на Y.

Разместим в форме 2 компонента TEdit - OldOtdelEdit, для указания имени отдела, которое требуется заменить, и NewOtdelEdit, для указания нового имени отдела (рис. 7.6.):

Для набора данных Tablel определим обработчик события OnFilterRecord

procedure TFormI.Table1FilterRecord(DataSet: TDataSet;

var Accept: Boolean);

begin

Accept := TablelOtdel.AsString = OldOtdelEdit.Text;

end;

Этот обработчик будет фильтровать в НД Tablel только те записи, у которых поле Otdel содержит старое значение отдела.

Поясним, что в примере используется свойство набора данных

property RecordCount: Integer;

Оно возвращает текущее число записей в НД.

Определим обработчик события нажатия кнопки "Все записи - изменить код отдела":

procedure TFormI.Button4Click(Sender: TObject);

begin

// включим фильтрацию по старому имени отдела

Tablel.Filtered := True;

//последовательно перебираем записи

WITH Tablel do begin

First;

WHILE not (RecordCount = 0) do begin

Edit;

//изменяем название отдела

TablelOtdel.Value := NewOtdelEdit.Text;

//после запоминания изменений запись перестает

//удовлетворять условиям фильтрации и "исчезает"

//из набора данных. После выполнения каждого метода Post

//отфильтрованный набор данных уменьшается на одну запись

Post;

END;//while

END; //with

// отменим фильтрацию

Tablel.Filtered := False;

end;

Заметим, что в цикле нет перехода к новой записи с помощью метода Next. Дело в том, что перед началом изменения значения поля Otdel включается фильтрация. После того, как значение поля изменилось, запись перестает удовлетворять условию фильтрации, поскольку фильтрация ведется по старому значению поля Otdel. Такая запись, не удовлетворяющая условию фильтрации, в НД не входит. После того, как все записи изменены, фильтрация отменяется. Отметим также, что условием окончания цикла WHILE сделан факт отсутствия в НД записей (при включенной фильтрации это записи, удовлетворяющие условию фильтрации).

Результаты работы показаны на рис. 7.7. и 7.8.

Такой способ часто применим при работе со связанными НД, родительским и дочерним. Заметим, что фильтрация в данном примере обеспечивается свойством Filtered, общим для TTable и TQuery. Однако можно применять и иные средства фильтрации, например, метод SetRange компонента TTable.



Реакция на изменение курсора набора данных


Событие OnDataChange (компонент DataSource) возникает всякий раз при изменении курсора НД, т.е. при переходе к новой текущей записи. Это событие возникает, когда курсор БД уже находится на новой записи.

Событие происходит и в режимах dslnsert и dsEdit:

- при изменении какого-либо поля;

- при первом перемещении с измененного поля на другое поле. Два события компонента типа "набор данных" также происходят при переходе к новой записи:

property BeforeScroll: TDataSetNotifyEventI;

Событие наступает перед переходом на другую запись в наборе данных. property AfterScroll: TDataSetNotifyEventI;

Событие наступает после перехода на другую запись в наборе данных.



Временное отключение визуализации при работе с НД


При выполнении действий с НД, влекущих за собой частое изменение местоположения курсора БД, в визуальном компоненте, показывающем записи (например, TDBGrid) или текущую запись (TDBEdit и др.), будет возникать эффект "прокрутки" записей. Он не всем нравится. Кроме этого, при смене местоположения курсора БД (т.е. при смене текущей записи НД) необходимо время для отражения произошедших изменений в визуальном компоненте.

Для устранения данной проблемы имеются методы procedure DisableControls; procedure EnableControls;

Первый отключает связь с визуальным компонентом, а второй -восстанавливает ее. Например, при последовательном переборе записей НД, произведенном таким образом:

WITH Tablel do begin

DisableControls;

First;

WHILE not EOF do begin

{Какие-либо действия}

Next;

END; {while}

EnableControls;

END; {with}

в таблице компонента DBGridI не будет видно эффекта прокрутки записей. Наоборот, у пользователя возникнет иллюзия, что курсор БД сразу переустановился с текущей записи набора данных на его последнюю запись.



Свойства, запрещающие или разрешающие изменять записи в НД


Свойство property CanModify: Boolean;

набора данных определяет, может ли НД переводиться в состояние dslnsert и dsEdit (CanModify= True) или не может (CanModify =False). Это свойство зависит от значения свойства Readonly набора данных. Если Read0nly= True, CanModify автоматически переводится в False. Когда Read0nly= False, CanModify может принимать значения как True, так и False, устанавливая таким образом возможность изменения НД в зависимости от каких-либо условий.

Свойство AutoEdit компонента TDataSourse, связанного с данным НД, определяет, возможен ли (True) автоматический перевод НД в состояние dsEdit, или невозможен (False). В последнем случае для изменения НД программа должна вызвать метод Edit . Свойство AutoEdit не влияет на возможность перевода в состояние dslnsert. Для того, чтобы запретить НД переход в режим dslnsert, достаточно либо сделать НД открытым только для чтения (свойство НД ReadOnly = True), либо установить режим НД Readonly = True для полей, входящих в состав первичного ключа. В этом случае корректировка записи будет возможна, за исключением указанных полей.

НД автоматически может переводиться в состояние dslnsert или dsEdit, если пользователь в визуальном компоненте, связанном с НД, выполняет определенные действия. Вид этих действий зависит от визуального компонента, связанного с НД.

Например, для перехода в режим dsEdit в компоненте TDBGrid, связанном с набором данных, достаточно изменить значение любого поля; в компонентах TDBEdit или TDBMemo, связанных с отдельными полями НД, следует изменить значение поля, с которым связаны TDBEdit или TDBMemo; для компонента DBNavigator, связанного с данным НД, нужно нажать соответствующую кнопку и т.д.



Изменение текущей записи


Чтобы изменить запись в НД, этот НД нужно перевести методом Edit из состояния dsBrowse в состояние dsEdit, затем произвести изменение значения одного или нескольких полей записи и использовать метод Post для запоминания измененной записи в НД. Post в данном случае при благополучном исходе переводит НД из состояния dsEdit в состояние dsBrowse.

Для отказа от запоминания измененной записи в НД используется метод Cancel. Он также переводит НД из состояния dsEdit в состояние dsBrowse.

Метод Edit Редактирование записи должно быть разрешено (свойство property ReadOnly: Boolean; должно быть установлено в False). Помимо этого, могут быть запрещены для корректировки отдельные поля записи (когда свойство Readonly соответствующих компонентов TField установлено в True). Метод Edit может вызываться: программно, автоматически, когда пользователь в визуальном компоненте, связанном с НД, выполняет определенные действия. Вид этих действий зависит от визуального компонента.

Автоматический перевод набора данных в режим редактирования должен быть разрешен свойством AutoEdit соответствующего компонента DataSource (значение True).

Пример.

Изменение значения поля Oklad в текущей записи набора данных, запоминание изменений. Новое значение оклада вводится пользователем в поле ввода в форме (компонент TEdit с именем Editi). Введенное пользователем значение доступно через свойство Editl.Text. Это свойство строкового типа, поэтому оно должно быть преобразовано в целочисленный вид перед присваиванием его числовому полю Oklad.

WITH Tablel. do begin

Edit;

FieldByName('Oklad').Value := IntToStr(Editl.Text);

Post;

END;//with

Метод SetFields

Метод

procedure SetFields(const Values: array ofconst);

объединяет функциональность методов Edit, Post и действий по присваиванию значений полям изменяемой записи. В ходе выполнения метода сначала НД переводится в режим dsEdit. Затем полям записи присваиваются значения, перечисленные в открытом массиве Values При этом первое значение в списке присваивается первому полю, второе - второму и т.д Естественно, что значения в списке должны быть совместимы с теми полями, которым они присваиваются. Например, попытка присвоить полю типа Real символьное значение приведет к возбуждению исключительной ситуации

Если в списке число значений меньше числа полей записи, те поля, которым "не хватило" значений, сохраняют свое первоначальное значение. Наоборот, если значений больше, чем нужно для заполнения полей, "лишние" значения теряются.

После успешного присваивания значения полям записи автоматически выполняется метод Post.

Метод SetFields обычно выполняется после того, как пользователь введет значения в переменные и будет произведен контроль правильности их значений

Пример.

Table1.SetFields(['Петров', 'Бухгалтер']) ;

Добавление новой записи


Чтобы добавить новую запись в НД, нужно вызвать метод Insert для перевода НД из состояния dsBrowse в состояние dsEdit Затем производится присваивание значения одному или нескольким полям записи, после чего выполняется метод Post для запоминания новой записи в НД Post при благополучном исходе переводит НД из состояния dsEdit в состояние dsBrowse

Для отказа от запоминания новой записи в НД используется метод Cancel Он также переводит НД из состояния dsEdit в состояние dsBrowse.

Метод Insert При добавлении записи изменение НД должно быть разрешено (свойство Readonly должно быть установлено в False). Помимо этого, могут быть запрещены для изменения отдельные поля записи (когда свойство Readonly соответствующих компонентов TField установлено в True). В этом случае в них нельзя ввести новые значения.

Метод Insert может вызываться: программно, автоматически, когда пользователь в визуальном компоненте, связанном с НД, предпринимает соответствующие действия. Для перехода в режим dslnsert в компоненте TDBGrid достаточно нажать на клавиатуре клавишу Insert или, находясь на последней записи НД, попытаться перейти на нижнюю, несуществующую запись. То же происходит при нажатии соответствующей кнопки связанного с данным НД компонента TDBNavigator,.

Пример.

Добавление записи

Table1.Insert;

// установка значений полей добавляемой записи

Tablel.Post;

Метод Append

Метод procedure Append; аналогичен методу Insert, но он добавляет запись в конец набора данных, в то время как Insert добавляет ее после текущей записи.

Для индексированных НД применение метода Append приводит к тем же последствиям, что и применение метода Insert.

Метод InsertRecord

Метод procedure InsertRecord(const Values: array ofconst); объединяет функциональность методов Insert, Post и действий по присваиванию значений полям новой записи. В функциональном отношении он полностью аналогичен методу SetFields (см. выше).

Метод AppendRecord

Метод procedure AppendRecord(const Values: array of const); отличен от метода InsertRecord только тем, что помещает новую запись не после текущей записи, а вслед за последней записью НД.



Запоминание изменений - метод Post


Выполнение метода Post приводит к запоминанию изменений, сделанных в режиме добавления или изменения записи.

Если НД не находится в режиме dslnsert или dsEdit, применение Post приводит к возбуждению исключительной ситуации. Вызов Post зависит от способа, которым ранее был вызван метод Insert или Edit: программно; автоматически.

Post

обычно вызывается автоматически, если пользователь предпринимает соответствующие действия, направленные на запоминание измененной записи в НД. Вид этих действий зависит от визуального компонента, связанного с НД. Например, для компонента TDBGrid, связанного с набором данных, это -переход к другой записи. Для НД, управляемого компонентом TDBNavigator, это - нажатие соответствующей экранной клавиши компонента TDBNavigator. Реже изменения в наборе данных, автоматически переведенном в режим редактирования, запоминаются путем программного вызова метода Post.

Метод Post, независимо от того, вызывается он программно или автоматически, может завершиться неудачно. Причиной этого могут послужить неверные значения в соответствующих полях записи. Например:

• поле обязательного заполнения (свойство Required = True у соответствующего компонента TField) содержит пустое значение;

• для ТБД, для которой определен уникальный ключ, возникла ситуация дублирования ключа (Key Violation), то есть ключевое поле (группа полей) данной записи содержит значение, которое уже хранится в этом поле (группе полей) в другой записи;

обработчики событий типа OnValidate (компонент TField) или BeforePostRecord обнаружили, что какое-либо поле содержит неверное значение, не удовлетворяющее некоторым условиям. В этом случае программно возбуждается исключительная ситуация, которая подавляет выполнение Post.

В лучшем случае при возникновении препятствий для выполнения Post запись переводится в состояние, в котором НД находился до выполнения метода (dslnsert или dsEdit).



Отмена сделанных изменений - метод Cancel


Метод Cancel отменяет все изменения, сделанные в записи. Если НД находился в режиме добавления новой записи, запись в НД не добавляется. Если НД находился в режиме изменения записи, изменявшаяся запись в НД не записывается и данные в ней остаются в том состоянии, в котором они находились до перехода в режим dsEdit. Сам НД переводится в режим dsBrowse. Вызов Cancel зависит от способа, которым ранее был вызван метод Insert или Edit: программно; автоматически.

Cancel

вызывается автоматически, если пользователь предпримет соответствующие действия, направленные на запоминание измененной записи в НД. Вид этих действий зависит от визуального компонента, связанного с НД. Например, для компонента TDBGrid, связанного с набором данных, это нажатие клавиши Esc. Для НД, управляемого компонентом TDBNavigator, это нажатие соответствующей экранной клавиши компонента TDBNavigator.

Оценка изменения записи


Часто бывает необходимо знать, вносились ли в запись изменения в режимах dslnsert или dsEdit. Это актуально в тех случаях, когда внесение изменений в поля записи зависит от каких-либо условий, которые могут наступать или не наступать в разные моменты работы приложения. Свойство НД property Modified: Boolean; автоматически устанавливается в True, если значение какого-либо поля записи НД было изменено в режимах dslnsert или dsEdit. Методы Post и Cancel переводят свойство в состояние False.

Пример.

В следующем фрагменте запись будет запомнена в НД, только если в нее вносились изменения:

Table1.Edit;

IP Tablel.Modified THEN Post ELSE Cancel;



Реакция на изменение данных


Событие On UpdateData (компонент DataSource) возникает для измененной (или вновь добавляемой) записи, когда выполнен метод Post, но физическое перезаписывание измененной записи в ТБД еще не произошло.

Событие On Validate (компонент TField) возникает после любого изменения значения поля, произведенного вручную или программно (это относится и к вводу значения в поле при создании новой записи). Это событие служит для контроля правильности значений поля, если на него накладываются какие-либо ограничения. Событие возникает перед выполнением метода Post, физически записывающего измененную запись в ТБД. В случае несоответствия значения поля накладываемым ограничениям выполнение Post (и, следовательно, физическое запоминание в БД записи с неверным полем) можно предотвратить, используя метод Abort или принудительно возбудив исключительную ситуацию (raise Exception. Create). Например,

procedure TForm1.TableKodIzdeliaValidate(Sender: TField);

begin

IF TableKodIzdelia.AsInteger > 1000 THEN raise Exception.Create('Неверное значение кода изделия');

end;



Удаление записи


Удаление текущей записи в наборе данных реализуется методом Delete. Например: Table1.Delete;

Удаление записи может производиться: программно; автоматически, если это предусмотрено в том или ином компоненте. Так, в компоненте TDBGrid нажатие комбинации клавиш Ctrl + Del влечет за собой удаление записи, которое, в соответствии с опциями настройки TDBGrid, может выполняться как с запросом подтверждения, так и без него.

Необходимо помнить об одной важной особенности. Записи в различных СУБД могут удаляться 2 способами:

• пометка записи в ТБД как удаленной. Сама запись физически не удаляется из НД. В зависимости от СУБД новые записи могут записываться на место помеченных как "удаленные" или в конец ТБД. В последнем случае такие ТБД могут "разбухать" до больших размеров, поэтому время от времени для них проводят операцию сжатия, при которой помеченные как удаленные записи физически уничтожаются, а остальные записи "сдвигаются" вверх, заполняя образовавшиеся пустоты в ТБД. немедленное удаление записей из ТБД, вследствие чего последующие записи "сдвигаются" вверх, заполняя образовавшиеся в ТБД пустоты. В Delphi при работе с НД реализован второй метод. После удаления записи все оставшиеся записи "сдвигаются" наверх. При удалении одной записи это может быть несущественным, однако, если нужно удалить несколько записей, это способно внести осложнения. Например, пусть требуется удалить все записи в Tablel. Можно было бы предположить, что данную потребность можно реализовать следующим программным кодом:

WITH Tablel do begin

First;

WHILE not EOF do begin

Delete;

Next; // Ошибка!

END;//while

END;//with

Однако в действительности этот код приведет к удалению примерно половины записей в Tablel. Причина этого лежит в том, что когда мы удаляем запись (например, № 3), последующие записи автоматически перемещаются вверх, и поэтому запись, бывшая до удаления следующей (№ 4), становится текущей (№ 3). После выполнения метода Next осуществляется переход к записи № 4. Таким образом, записи удаляются через одну (см. рис. 7.9 и 7.10).

Удалив ненужный вызов Next, мы сотрем все записи:

WITH Tablel do begin

First;

WHILE not (RecordCount = 0) do

Delete;

END;//with

Чаще всего нужно удалять не все записи НД а часть записей, удовлетворяющих некоторому условию Рассмотрим два способа

ЗАМЕЧАНИЕ Я рекомендую вернуться к повторному рассмотрению этих способов после того как вы ознакомитесь

- с фильтрацией записей в НД при помощи свойства Filtered

- с поиском записей в НД при помощи метода Locate

Материал о них приведен в данномраздече ниже

Во-первых, можно воспользоваться временной фильтрацией удаляемых записей в момент группового удаления (См п 7 3 6)

Второй способ состоит в использовании метода Locate, реализующего поиск необходимой записи по точному соответствию значений некоторых полей

Метод Locate указывает поля, по которым ведется поиск, и значения этих полей, по которым нужно найти записи В нашем случае это поле Otdel и свойство OtdelEdit Text (компонент TEdit), где содержится наименование отдела, по которому нужно удалить все записи в НД Если запись с таким значением поля Otdel найдена, Locate возвращает True, в противном случае

False Как только запись найдена она удаляется, когда Locate возвратит False это значит что все записи с указанным наименованием одела удалены (см рис 7 13 и 7 14)

// Обработчик нажатия кнопки 'Удалить все записи с указанным отделом"

procedure TFormI Button4Click(Sender TObject);

begin

WITH Tablel do begin

First;

WHILE Locate('Otdel', OtdeiEdit Text,[]) do Delete;

END; //with

end;

ЗАМЕЧАНИЕ.

Для больших НД, а также если в процессе удаления в НД меняются индексы, определяющие текущую сортировку, рекомендуется перед реализацией удаления отключать связь НД с управляющими компонентами, а по окончании удаления - включать-

WITH Tablel do begin

DisableControls;

EnableControls;

END; {with}



Сценарий обновления записей на одной форме с компонентом TDBGrid


Записи набора данных периодически добавляются, изменяются и удаляются Если выполнение указанных действий ложится на пользователя, в приложении должны быть реализованы удобный интерфейс добавления, изменения и удаления записей

Несомненно, действия над набором данных в этом случае тесно связаны с вопросами реализации пользовательского интерфейса в приложении Материал именно такого рода содержится в данном разделе Он будет полезен начинающим разработчикам или тем, кто только начинает изучать Delphi, поскольку показывает, что соединение пользовательского интерфейса и действий над набором данных не является чем-то исключительно сложным

Если Вас не интересует, как можно реализовать в форме интерфейс пользователя для внесения изменений в набор данных, Вы можете пропустить данный раздел

Существует несколько вариантов внесения изменений в набор данных в приложении-

• изменения реализуются при выполнении программного кода Как правило, такой способ внесения изменений в НД используется для транзакционных таблиц, т е таблиц, данные в которых формируются приложением автоматически Например, в транзакционную таблицу "Суммарный отпуск товаров" приложением автоматически вносятся изменения при вводе записей в другую, операционную таблицу БД, "Отпуск товаров"

• изменения в НД вносит пользователь. Реализовать пользовательский интерфейс в этом случае можно одним из следующих способов

• пользователь вносит изменения в НД, пользуясь компонентом TDBGrid. Например, для добавления новой записи в НД он нажимает на клавиатуре кнопку Insert, после чего в TDBGrid появляется новая, пустая строка; вводит данные в поля новой строки, запоминает новую запись (перемещая курсор на другую запись) или отказывается от запоминания, нажимая на клавиатуре кнопку Esc;

• пользователь нажимает одну из трех кнопок "Добавить", "Изменить", "Удалить", которые размещены в той же форме, что и компонент TDBGrid, в котором показываются записи набора данных По нажатию кнопки набор данных переводится в соответствующее состояние и вызывается другая форма, в которую пользователь вводит новые значения полей записи и нажимает кнопку "Запомнить изменения " , "Отменить" или "Подтвердить удаление записи". После этого реализуются необходимые действия, то есть выполняются методы Post, Cancel или Delete;

• пользователь нажимает одну из трех кнопок "Добавить", "Изменить", "Удалить", которые размещены в той же форме, что и компонент TDBGrid, в котором показываются записи набора данных. По нажатию кнопки набор данных переводится в соответствующее состояние и в этой же форме активизируется панель, на которой расположены поля текущей записи набора данных. Пользователь вводит новые значения полей записи, после чего подтверждает или отменяет добавление или изменение записи, при удалении записи ему, разумеется, ничего вводить не нужно, а нужно лишь удаление подтвердить или отменить. Рассмотрим реализацию последнего сценария. Пусть имеется ТБД Sklady, записи которой содержат наименования складов Предположим, что эта ТБД будет использоваться в приложении как справочник Запись состоит из единственного поля Sklad. По этому полю построен первичный индекс, т е. данное поле может содержать только уникальное значение.

Поместим в форму компонент DBGndl для показа записей из ТБД Sklady Первоначальную высоту TDBGrid определим равной 257 Ниже DBGndl поместим в форме панель PanelToInput Values и определим ее как "невидимую" (свойство Visible = False) В'данной панели расположим компонент DBEdit1, который ссылается на поле Sklad Соответственно, когда панель невидима, невидимы и компоненты, в ней расположенные

Поместим в форму панель InsertEditDeletePanel и установим ее свойство Visible = True. В панели разместим экранные кнопки для работы с отдельной записью "BcTaBHTb"(InsertButton), "Изменить" (EditButton), "Удалить" (DeleteButton), и кнопку для выхода "Выйти" (ExitButton) с модальным свойством ModalResult = mrOk

Поместим в форме панель PostCancelPanel и сделаем ее невидимой (свойство Visible = False) Разместим в панели экранные кнопки "Запомнить" (PostButton) и "Отменить"(Сапсе1Вийоп) для запоминания сделанных изменений в БД или отказа от запоминания (рис 7.15)

Совместим местоположение панелей PostCancelPanel и InsertEditDeletePanel, сделав их одинаковыми по размеру (рис. 7.16):

Напишем следующие обработчики нажатия экранных кнопок:

// константы для обозначения высоты TDBGrid

const

NORMAL_HEIGHT = 257; // в режиме просмотра записей

UPDATE_HEIGHT = 193; // в режимах добавления и изменения

// Обработчик нажатия кнопки "Включить"

procedure TFormI.InsertButtonClick(Sender: TObject);

begin

// визуализируем панель с кнопками "Запомнить", "Отменить'

PostCancelPanel.Visible := True;

// делаем невидимой панель с кнопками "Включить",

// "Изменить" и т.д.

InsertEditDeletePanel.Visible := False;

// уменьшаем высоту DBGrid

DBGridl.Height := UPDATE_HEIGHT;

// делаем видимой панель для ввода значения в запись

PanelToInputValues.Visible := True;

// переводим НД в режим добавления записи

Tablel.Insert;

// передаем фокус управления на ввод значения в запись

DBEditI.SetFocus;

end;

// Обработчик нажатия кнопки "Изменить"

procedure TFormI.EditButtonClick (Sender: TObject);

begin

// делаем невидимой панель с кнопками "Включить",

// "Изменить" и т.д.

InsertEditDeletePanel.Visible := False;

// уменьшаем высоту DBGrid

DBGridl.Height := UPDATE_HEIGHT;

// делаем видимой панель для ввода значения в запись

PanelToInputValues.Visible := True;

// переводим НД в режим редактирования записи

Tablel.Edit;

// передаем фокус управления на ввод значения в запись

DBEditI.SetFocus;

end;

// Обработчик нажатия кнопки "Удалить"

procedure TFormI.DeleteButtonClick(Sender: TObject);

begin

// удаляем запись, в случае подтверждения пользователем

IF MessageDIg('Подтвердите удаление записи',

mtConfirmation,[mbYes,mbNo],0) = mrYes THEN

Tablel.Delete;

// передаем управление на DBGrid

DBGridI.SetFocus ;

end;

// Обработчик нажатия кнопки "Запомнить"

procedure TFormI.PostButtonClick(Sender: TObject);

begin

// пытаемся запомнить изменения в ТБД; при возникновении //исключения полагаем, что его причиной является дублирова

//ние ключа. В этом случае сообщаем пользователю и отменяем

//внесение изменений в ТБД

TRY

Tablel.Post;

EXCEPT

on EDBEngineError do begin

MessageDIg('Такая запись уже есть',mtlnformation, [mbOk],0) ;

Tablel.Cancel;

end; {on}

END; {try}

// увеличиваем высоту DBGrid

DBGridI.Height := NORMAL_HEIGHT;

// делаем невидимой панель для ввода значения в запись

PanelToInputValues.Visible := False;

// делаем невидимой панель для ввода значения в запись

PostCancelPanel.Visible := False;

// делаем видимой панель с кнопками "Включить", "Изменить" и т.д.

InsertEditDeletePanel.Visible := True;

// передаем управление на DBGrid

DBGridI.SetFocus ;

end;

// Обработчик нажатия кнопки "Отменить"

procedure TFormI.CancelButtonClick(Sender: TObject);

begin

Tablel.Cancel;

// увеличиваем высоту DBGrid

DBGridI.Height := NORMAL_HEIGHT;

// делаем невидимой панель для ввода значения в запись

PostCancelPanel.Visible := False;

// делаем видимой панель с кнопками "Включить", "Изменить" и т.д.

InsertEditDeletePanel.Visible := True;

// делаем невидимой панель для ввода значения в запись

PanelToInputValues.Visible := False;

// передаем управление на DBGrid

DBGridI.SetFocus;

end;

// Обработчик события выхода из формы.

// Если в этот момент НД находится в состоянии,

// отличном от dsBrowse, т.е. в dslnsert, dsEdit,

// он принудительно возвращается в состояние dsBrowse.

procedure TFormI.FormDeactivate(Sender: TObject);

begin

IF Tablel.State 0 dsBrowse THEN Tablel.Cancel;

end;

В режиме просмотра записей (dsBrowse) видим окно, показанное на рис. 7.17

А в режимах добавления новой записи (dslnsert) или редактирования (dsEdit) видим окно, (рис. 7.18)

При нажатии кнопки "Удалить" выдается модальная форма подтверждения удаления (рис. 7.19). Эта форма реализуется функцией Delphi MessageDlg.



Закладки на записях НД


Подобно тому, как в книге нужную страницу можно заложить закладкой и впоследствии быстро найти эту страницу, в НД аналогичные действия можно осуществить для записи. Для этой цели НД обладает следующими методами:

function GetBookmark: Tbookmark -

создает для текущей записи объект-закладку и возвращает ссылку на него;

procedure GotoBookmark(Bookmark : Tbookmark) -

перемещает курсор БД на запись, определяемую закладкой-параметром;

procedure FreeBookmark(Bookmark : Tbookmark)

- освобождает системные ресурсы закладки Bookmark;

function Bookmark Valid(Bookmark: TBookmark): Boolean; -

возвращает True, если закладке Bookmark назначено значение, и False - если не назначено;

function CompareBookmarks(Bookmarkl, Bookmark!: TBookmark): Integer; -

сравнивает две закладки, Bookmark 1 и Bookmark!, и возвращает: 0 - если закладки идентичны; 1 - если различаются;

тип TBookmark - указатель на экземпляр типа "закладка".

ЗАМЕЧАНИЕ

Метод GotoBookmark возбуждает исключительную ситуацию, если передаваемый параметр не указывает на закладку (т.е. если закладка не создана, а мы пытаемся на нее перейти). Пример

var MyBookmark : TBookMark;

{ заложить закладку }

MyBookmark := Tablel.GetBookmark;

{ перейти на запись, на которой заложена закладка}

IF Tablel. BookmarkValid. (MyBookmark) THEN

Tablel.GotoBookmark(MyBookmark);

{ освободить ресурсы, выделенные для закладки}

IF Tablel. BookmarkValid(MyBookmark) THEN

Tablel.FreeBookmark(MyBookmark);

Закладки используются в первую очередь тогда, когда :

1. Нужно проделать какие-либо действия над НД, изменяющие местоположение курсора БД;

2. Вернуться к той записи, которая была текущей перед выполнением п. 1.



Метод Locate


function Locate(const KeyFields: string; const Key Values: Variant;

Options: TLocateOptions): Boolean;

Метод Locate ищет первую запись, удовлетворяющую критерию поиска, и если такая запись найдена, делает ее текущей. В этом случае в качестве результата возвращается True. Если поиск был неуспешен, возвращается False.

Параметры:

Список KeyFields указывает поле или несколько полей, по которым ведется поиск, в виде строкового выражения. В случае нескольких поисковых полей их названия разделяются точкой с запятой.

Критерии поиска задаются в вариантном массиве Key Values так, что i-e значение в Key Values ставится в соответствие i-му полю в KeyFields. В случае поиска по одному полю в Key Values указывается одно значение.

Options

позволяет указать необязательные значения режимов поиска:

loCaselnsensilive -

поиск ведется без учета высоты букв, т.е. если в Key Values указано 'принтер', а в некоторой записи в данном поле встретилось 'Принтер' или 'ПРИНТЕР', запись считается удовлетворяющей условию поиска;

loPartialKey -

запись считается удовлетворяющей условию поиска, если она содержит часть поискового контекста; например, удовлетворяющими контексту 'Ма' будут признаны записи с значениями в искомом поле "Машин ', 'Макаров' и т.д.

Locate

отличается от методов FindKey, FindNearest, GoToKey, Go ToNearest (компонент TTable) следующим:

• FindKey, FindNearest, GoToKey, Go ToNearest производят поиск только по полям, входящим в состав текущего индекса TTable; в случае, когда условию поиска удовлетворяет несколько записей, текущей станет логически самая первая из них (в порядке сортировки записей в НД, определяемом текущим индексом);

• Locate производит поиск по любому полю; поле или поля, по которым производится поиск, могут не только не входить в текущий индекс, но и не быть индексными вообще.

В случае, если поля поиска входят в какой-либо индекс, Locate использует этот индекс при поиске. Если искомые поля входят в несколько индексов, трудно сказать, какой из них будет использован. Соответственно, трудно предсказать, какая запись из множества записей, удовлетворяющих критерию поиска, будет сделана текущей - особенно в случае, если поиск ведется не по текущему индексу.

При поиске по полям, не входящим ни в один индекс, применяются фильтры BDE.

Пример.

Пусть имеется ТБД "Сотрудники кафедры" с целочисленным TabNum (табельный номер) и строковыми полями FIO (ФИО), Doljnos) (Должность), UchStepen (Ученая степень).

Пусть ТБД имеет индексы по полям: 'TabNum' , 'FIO', 'Doljnost;FIO'.

Пример

А. Осуществим поиск по полям 'Doljnost; UchStepen' (индексное и неиндексное) при различных текущих индексах в НД. Поисковый контекст -['доцент', 'кхн'] при режиме частичного совпадения значений:

procedure TFormX.LocateButtonClick(Sender: TObject);

begin

Tablel.Locate ( 'Doljnost;UchStepen',

VarArrayOf(['доцент','кхн']),[loPartialKey]) ;

end;

Результаты поиска показаны на рис.7.20 - 7.22. Как видно из рисунков, при различных текущих индексах в момент выполнения поиска, результаты поиска также могут быть различными.

Пример Б Осуществим поиск по полю 'FIO' ( входит в два индекса) при различных текущих индексах в НД Поисковый контекст - ['Ма'] при режиме частичного совпадения значении

procedure TFormX.LocateButtonClick(Sender: TObject);

begin

Tablel.Locate('FIO','Ma', [ioPartialKey]) ;

end;

Этому критерию соответствуют записи с FIO = 'Манишкина А А ', 'Мануйлова В А','Маслаченко В Ф ', ' Массалитин В Ф ' (см рис 7 23 - 7 25) Как видим, результат поиска при различных текущих индексах одинаков

Пример В

Пусть заранее неизвестно, по какому полю необходимо производить поиск Тогда поместим в форму компонент RadioGroup1, в котором перечислим поля поиска, и компонент Edit1 для ввода условий поиска (см рис 7.26)

Напишем такой обработчик

procedure TFormX.ButtonlClick(Sender: TObject) ;

var Pole : Shortstring;

begin

CASE RadioGroupl.Itemlndex OF

0 : Pole := 'TabNum' ;

1 : Pole := 'FIO';

2 : Pole := 'Doljnost;

3 : Pole := 'UchStepen';

END;

IF not Tablel.Locate(Pole,Editl.Text,[loCaselnsensitive, loPartialKey]) THEN ShowMessage('Запись не найдена');

end;

Преимущество показанного способа в том, что мы вместо выполнения нескольких Locate (для поиска по каждому полю) выполняем один метод Locate независимо от поля, по которому производится поиск



Использование методов FindFirst, FindLast, FindNext, FindPrior


Известно, что набор данных может быть отфильтрован с использованием свойства Filtered Условие фильтрации задается свойством Filter или описывается в обработчике события OnFliter Re cord Свойство Filtered указывает, выполнять ли фильтрацию (значение True) или нет (значение False) В этом случае в НД показываются все записи, а не только удовлетворяющие условию фильтрации

Для НД, в котором определены условия фильтрации, но сама фильтрация в текущий момент не включена, Delphi предоставляет интересную возможность Она заключается в том, что в неотфильтрованном в данный момент НД можно обеспечить навигацию только между теми записями, которые удовлетворяют условию фильтрации (оно в текущий момент, когда свойство Filtered = False, не действует)

Для этой цели используются методы FindFirst, FindLast. FindNext, FindPrior

Условие фильтрации можно сделать совпадающим с условием поиска, указанным в параметре./^ Values метода Locate При этом поиск с помощью указанных методов имеет довольно большое преимущество перед поиском с помощью Locate если в Locate можно указывать только значения, то в условии фильтрации можно указывать логические условия, например

Accept := (DataSet['Doljnosf] = 'доцент') AND (DataSet['TabNum'] > 150000);

т е. включать в НД только записи о сотрудниках-доцентах, и притом не всех, а только тех из них, у которых табельный номер больше 150000

В случае, если искомая запись найдена, данные методы возвращают True, в противном случае - False. Аналогичный результат возвращает свойство Found

function FindFirst: Boolean; -

переходит на первую запись, удовлетворяющую фильтру;

function FindLast: Boolean; -

переходит на последнюю запись, удовлетворяющую фильтру;

function FindNext: Boolean; -

переходит на следующую запись, удовлетворяющую фильтру;

function FindPrior: Boolean; -

переходит на предыдущую запись, удовлетворяющую фильтру;

property Found: Boolean; -

возвращает True, если последнее обращение к одному из методов FindFirst, FindLast, FindNext, FindPrior привело к нахождению нужной записи.

Пример.

Предоставить пользователю возможность перемещаться на первую, последнюю, следующую, предыдущую запись, удовлетворяющую условию "Содержимое Edit1 входит как часть ФИО сотрудника". Заметим, что свойство Table1.Filtered = False, т.е. хотя в обработчике события Table1.OnFilterRecord и указано условие фильтрации, в НД показываются все записи, и он остается в неотфильтрованном состоянии (см. рис. 7.27 и 7.28)

условие

фильтрации

procedure TFormX.TablelFilterRecord(DataSet: TDataSet;

var Accept: Boolean);

begin

Accept := POS(Editl.Text,DataSet['FIO']) > 0;

end;

// нажата кнопка "Первая"

procedure TFormX.FindFirstButtonClick(Sender: T0b;ect) ;

begin

Labell .Caption := ";

IF not Tablel.FindFirst THEN

Labell.Caption := 'Нет такой записи';

end;

// нажата кнопка "Последняя"

procedure TFormX . FmdLastButtonClick (Sender : TObject) ;

begin

Labell.Caption := ";

IF not Tablel.FindLast THEN

Labell.Caption := 'Нет такой записи';

end;

// нажата кнопка "Следующая"

procedure TFormX.FindNextButtonClick(Sender: TObject);

begin

Labell.Caption := ";

IF not Tablel.FindNext THEN

Labell.Caption := 'Нет такой записи';

end;

// нажата кнопка "Предыдущая"

procedure TFormX.FindPriorButtonClick(Sender: TOb^ect) ;

begin

Labell.Caption := ";

IF not Tabiel.FindPrior THEN

Labell.Caption := 'Нет такой записи';

end;

Заметим, что поскольку фильтрация записей с использованием события OnFilter Record или (и) свойства Filter может применяться только на небольших объемах записей (из-за того, что при этом используется последовательный метод доступа к записям в ТБД), аналогичные ограничения накладываются и на поиск записей с использованием методов FindFirst, FindLast, FindNext, FindPrior.



Метод Lookup


function Lookup(const KeyFields: string; const Key Values: Variant;

const ResultFields: string): Variant;

Метод Lookup находит запись, удовлетворяющую условию, но не делает ее текущей, а возвращает значения некоторых полей этой записи. Тип результата - Variant или вариантный массив. Независимо от успеха поиска записи, указатель текущей записи в НД не изменяется.

Lookup осуществляет поиск только на точное соответствие критерия поиска и значения полей записи. Такой режим, как loPartialKey метода Locate (поиск по частичному соответствию значений), отсутствует.

Параметры:

В KeyFields указывается список полей, по которым необходимо осуществить поиск. При наличии в этом списке более чем одного поля, соседние поля разделяются точкой с запятой.

Key Values

указывает поисковые значения полей, список которых содержится в KeyFields. Если имеется несколько поисковых полей каждому i-му полю в списке KeyFields ставится в соответствие i-oe значение в списке Key Values. При наличии одного поля, его поисковое значение можно указывать в качестве Key Values непосредственно; в случае нескольких полей - их необходимо приводить к типу вариантного массива при помощи Var-Array'Of.

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

Если запись в результате поиска не найдена, метод Lookup возвращает Null, что выявляется при помощи предложения

IF VarType(LookupResults) = varNull THEN ...

В противном случае Lookup возвращает из этой записи значения полей, список которых указан в ResultFields. При этом размерность результата зависит от того, сколько результирующих полей указано в ResultFields'.

• указано одно поле - результатом будет значение соответствующего типа или Null, если поле в найденной записи содержит пустое значение;

указано несколько полей - результатом будет вариантный массив, число элементов в котором меньше или равно числу результирующих полей;

меньше потому, что некоторые поля найденной записи могут содержать пустые значения. Рассмотрим несколько вариантов.

Пример

А. Одно результирующее поле (результат - значение типа Variant) Будем осуществлять поиск в ТБД "Сотрудники" по полю 'FIO'. Поисковое значение будем вводить в Edit I. В качестве результата будем выдавать значение поля "UchStepen' (ученая степень) найденной записи.

procedure TFormX.LookuplButtonClick(Sender: TObject);

var

LookupResults : Variant; // результат

begin

// осуществить поиск

LookupResults := Tablel.Lookup('FIO', Editl.Text, 'UchStepen') ;

Label 1.Caption := ";

// содержит ли результат пустое значение или Null?

CASE VarType(LookupResults) OF

varEmpt : Labell.Caption := 'Пустой результат';

varNull : Labell.Caption := 'Запись не найдена';

ELSE

// нет, результат содержит какое-то значение

Labell.Caption := LookupResults;

END; //case

end;

Заметим,что в присваивании

Labell.Caption := LookupResults;

имеет место приведение вариантного типа к строковому. Более подробно о приведении вариантных типов см. описание вариантного типа в документации и встроенной системе помощи Delphi.

Пример Б.

Несколько результирующих полей (результат - вариантный массив)

Некоторые сведения по использованию вариантного массива:

Если переменная типа Variant является вариантным массивом, функция VarIsArray(LookupResults) возвращает True.

При работе с переменным числом возвращаемых полей, в конкретном случае верхнюю и нижнюю границы массива LookupResults можно определить при помощи функций VarArrayLowBound(LookupResults, 1) и VarArrayHigh-Bound( LookupResults, 1).

Тип i-го элемента вариантного массива можно определить как VarType (LookupResults [ i]).

Будем осуществлять поиск в ТБД "Сотрудники" по полю 'FIO\ Поисковое значение будем вводить в Editi. В качестве результата будем выдавать значения полей "TabNum,DolJnost,UchStepen' найденной записи (табельный номер, должность, ученая степень).

procedure TFormX.LookupButtonClick(Sender: TObject) ;

var

LookupResults : Variant; // результат

begin

// осуществить поиск

LookupResults := Tablel.Lookup('FIO', Editl.Text, '' TabNum;Doljnost;UchStepen') ;

// будем показывать значения результирующих полей в TLabel

Labell.Caption := ";

Label2.Caption := ";

Label3.Caption := ";

// результат - вариантный массив ?

IF VarIsArray(LookupResults) THEN

begin

Labell.Caption := LookupResults[0];

IF LookupResults[1] 0 Null THEN

Label2.Caption := LookupResults [1];

IF LookupResults[2] о Null THEN

Label3.Caption := LookupResults [2];

end // then

ELSE

// результат - не вариантный массив, а единичное значение

CASE VarType(LookupResults) OF

varEmpty : Labell.Caption := 'Пустой результат';

varNull : Labell.Caption := 'Запись не найдена';

END; //case

end;

Если запись не найдена, VarType (LookupResults) возвращает значение varNull; если поиск по какой-либо причине не был произведен, VarType (LookupResults) возвращает значение varEmpty. Если какое-либо из полей, чьи значения возвращаются в результате поиска в вариантном массиве, содержит пустое значение, соответствующий элемент вариантного массива также будет содержать пустое значение (Null). В этом случае обращение к нему возбудит исключительную ситуацию, поэтому нужна предварительная проверка.



Поиск записей в наборах данных


Помимо приводимых ниже средств поиска записей в НД, можно воспользоваться методами Find, Go ToKey, FindNearest, Go ToNearest компонента TTable.



Свойство Filtered


property Filtered: Boolean;

Свойство Filtered, установленное в True, инициирует фильтрацию, условие которой записано или в обработчике события OnFilterRecord, или содержится как строковое значение в свойстве Filter. Если установлены разные условия фильтрации и в событии OnFilterRecord, и в свойстве Filter, выполняются оба.

Например, если в НД одновременно установлены фильтры

Tablel.Filter = ' [Doljnost] = 'профессор''';

procedure TFormX.TablelFilterRecord(DataSet: TDataSet;

var Accept: Boolean);

begin

Accept := DataSet['UchStepen'] = 'дтн';

end;

то установка Tablel Filtered в True приведет к двум фильтрациям; в результирующем наборе данных будут показаны только записи, у которых поле Doljnost содержит значение 'профессор' и поле UchStepen содержит значение 'дтн'.

Установка Filtered^ False приведет к отмене фильтрации, условия которой указаны в событии OnFilterRecord или (и) свойстве Filter. При этом фильтрация,

наложенная на НД методом SetRange или ApplyRange и ему сопутствующими методами, не нарушается

Пример

Пусть ТБД "Сотрудники" подвергается фильтрации

procedure TFormI.Table1FilterRecord(DataSet: TDataSet;

var Accept: Boolean);

begin

Accept := DataSet['UchStepen'] = 'доцент';

end;

и по нажатию кнопки "SetRange" показываются только записи, у которых табельный номер больше 150000

procedure TFormI.SetRangeClick(Sender: TObject) ;

begin

Tablel.SetRange ( [150000],[900000]) ;

end;

Нажатие клавиши "CancelRange" снимает фильтрацию по табельному номеру (рис 7 29 - 7 31)

procedure TFormI.CancelRangeClick(Sender: TOb^ect);

begin

Tablel.CancelRange;

end;

Последовательность установки фильтров произвольна - SetRange может применяться после Filtered = True, и наоборот

Отмена одного из этих условий фильтрации не приводит к отмене другого способа фильтрации (рис 7 32, 7 33)

или



Событие OnFilterRecord


property OnFilterRecord: TFilterRecordEvent;

Событие OnFilterRecord возникает, когда свойство Filtered устанавливается в Tine Обработчик события OnFilterRecord имеет два параметра имя фильтруемого набора данных и var Accept, указывающий условия фильтрации записей в НД В отфильтрованный ИД включаются только те записи, для которых параметр Accept имеет значение True

В условие фильтрации могут входить любые поля НД, в том числе не входящие в текущий индекс, а также не входящие ни в один индекс Возможность фильтрации НД по неиндексным полям, а также полям, не входящим в текущий индекс, выгодно отличает способ фильтрации с использованием события OnFilterRecord и свойства Filteredот способов фильтрации с использованием методов Set Range, Apply Range и им сопутствующих методов (компонент TTable) Последние, как будет показано в разделе, посвященном компоненту TTable, позволяют производить фильтрацию НД только по индексным полям, входящим к тому же в состав индекса, текущего на момент фильтрации Кроме этого, второй способ часто не позволяет реализовывать сложные логические конструкции при указании условий фильтрации

Однако следует помнить о том, что при указании условий фильтрации НД в обработчике OnFilterRecord, в нем последовательно перебираются все записи ТБД при анализе их на предмет соответствия условию фильтрации, в то время как методы SetRange, ApplyRange и им сопутствующие методы используют индексно-последовательный метод доступа, т е работают с частью записей в физической ТБД Это делает использование OnFilterRecord предпочтительным для небольших объемов записей и сильно ограничивает применение данного способа фильтрации при больших объемах данных

Всякий раз, когда приложение обрабатывает событие OnFilterRecord, НД переводится из состояния dsBrowse в состояние dsFilter Это предотвращает модификацию НД во время фильтрации После завершения текущего вызова обработчика события OnFilterRecord, НД переводится в состояние dsBro\\se

Пример

Отфильтровать ТБД "Сотрудники" согласно условию "Показать всех доцентов"

procedure TFormI.TablelFilterRecord(DataSet: TDataSet;

var Accept: Boolean);

begin

Accept := DataSet['Doljnost] = 'доцент';

end;

Прцмер

Отфильтровать ТБД "Сотрудники' по условию 'Показать всех сотрудников с табельным номером, вводимым в Editi, и с вхождением в ФИО символов, вводимых пользователем в Edit2"

procedure TFormI.TablelFilterRecord(DataSet: TDataSet;

var Accept: Boolean);

begin

Accept :- (DataSet['TabNum'] > Editi.Text))

AND (Pos(Edit2.Text,DataSet['FIO']) > 0);

end;

Методы FindFirst, FindLast, FindNext, FindPrior (см. выше подраздел "Поиск записей в наборах данных") также используют свойство OnFilterRecord, когда выполняют навигацию по НД.



Свойство Filter


property Filter: string;

Свойство Filter позволяет указать условия фильтрации. В этом случае НД будет отфильтрован, как только его свойство Filtered станет равным True. Синтаксис похож на синтаксис предложения WHERE SQL-оператора SELECT с тем исключением, что: имена переменных программы указывать нельзя, можно указывать имена полей и литералы (явно заданные значения).

Можно применять операторы отношения:

< Меньше чем; > Больше чем; >= Больше или равно; <= Меньше или равно; = Равно; <> Не равно

а также использовать логические операторы AND, NOT и OR:

([Doljnost] = 'доцент') AND ([TabNum] > 300000)

Строку фильтрации можно ввести во время выполнения (рис. 7.34, 7.35):

//когда проставляется галка в поле компонента CheckBoxl (то есть

//когда CheckBoxl.Checked =True), пользователь включает

//фильтрацию; когда пользователь снимает отметку (то есть когда

// CheckBoxl.Checked =True), пользователь выключает фильтрацию

procedure TFormI.CheckBoxIClick(Sender: TObject);

begin

Table1.Filter := Editl.Text;

Table1.Filtered := CheckBoxl.Checked;

end;

Однако при этом нужно следить, чтобы введенная строка соответствовала требованиям, предъявляемым к синтаксису строки Filter.

Другим способом мог бы быть обработчик, считывающий значения фильтрации и преобразующий их к формату строки Filter. На рис. 7.36 и 7.37 показана форма, где значения, по которым осуществляется фильтрация, вводятся в поля компонентов TEdit1. После этого приложение автоматически формирует строку условия фильтрации и заносит ее в свойство Filter. Сформированная строка условия фильтрации для наглядности показывается в форме приложения в компоненте Label3.



Свойство FilterOptions


property FilterOptions: TFilterOptions;

TFilterOption = (foCaseInsensitive, foNoPartialCompare);

Свойство FilterOptions позволяет установить режимы фильтрации с использованием свойства Filter. По умолчанию FilterOptions = [ ];

foCaseInsensitive -

Фильтрация производится без учета разницы в высоте букв;

foNoPartialCompare -

поиск производится на точное соответствие. В противном случае, при фильтре FIO = 'Ма' в отфильтрованный НД будут включены записи, у которых в поле FIO частично входит "Ма". например (если не используется опция foCaseInsensitive), 'Мануйлова' и 'Комарова'.

Навигация в неотфильтрованном НД между записями, удовлетворяющими фильтру


Методы FindFirst, FindLast, FindNext, FindPrior позволяют перемещаться в неотфильтрованном НД (у которого Filtered = False) между записями, удовлетворяющими условию фильтрации. Условие фильтрации задается событием OnFilterRecord или (и) свойством Filter. Действие данных методов таково: они кратковременно переводят НД в отфильтрованное состояние (Filtered = True) без визуализации этой фильтрации в TDBGrid или другом подобном компоненте, находят соответствующую запись и переводят НД в неотфильтрованное состояние (Filtered = False).

Если искомая запись найдена, данные методы возвращают True, в противном случае - False. Аналогичный результат возвращает свойство Found.

Более подробно указанные методы рассмотрены в подразделе "Поиск записей в наборах данных" (см. п.7.7).



Фильтрация записей в наборах данных


Помимо описываемых ниже средств, для фильтрации данных могут использоваться: методы SetRange или ApplyRange (и сопутствующие им методы) - в компоненте Table; секция WHERE оператора SELECT языка SQL - в компоненте TQuery.



Использование компонента TFieldDefs


Компонент TFieldDefs содержит информацию о полях, объявленных в составе ТБД, ассоциированной с данным НД.

Для НД типа TTable компонент TFieldDefs содержит информацию обо всех полях, объявленных в структуре ТБД, ассоциированной с данным TTable. He следует путать это свойство с свойством Fields, которое содержит информацию о всех компонентах типа TField, объявленных для данного набора данных с использованием редактора полей.

Для НД типа TQuery компонент TFieldDefs содержит информацию обо всех полях, объявленных в качестве возвращаемых полей в операторе SELECT. Подробнее см. замечание, помещенное в конце данного подраздела. Свойство набора данных

property FieldDefs: TFieldDefs;

в качестве результата возвращает указатель на объект типа TFieldDefs данного НД и, таким образом, с его помощью можно использовать свойства и методы

компонента TFieldDefs для определения числа и характеристик полей, объявленных в таблице базы данных, ассоциированной с данным НД. Рассмотрим свойства и методы компонента TFieldDefs.

Свойства компонента TFieldDefs

• property Count: Integer;

Возвращает количество компонентов типа TIndexDef, каждый из которых содержит информацию о конкретном поле в составе ТБД.

• property Items[Index: Integer]: TFieldDef;

Данное свойство является набором объектов типа TFieldDef, каждый из которых содержит информацию о конкретном поле, объявленном в составе ТБД. Доступ к конкретному объекту осуществляется через указание Items[Index], где Index лежит в диапазоне Q..Count-\.

Объект типа TFiezldDef обладает следующими свойствами:

• property DataType: TFieldType;

Свойство DataType возвращает тип поля как значение из перечислимого типа TFieldType:

TFieldType = (ftString, ftSmallint, ftlnteger, ftWord, ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime, ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle, ftTypedBinary);

• property FieldNo: Integer;

Свойство FieldNo возвращает физический номер поля, использующийся Borland Database Engine (BDE) для доступа к полю.

• property Name: string;

Свойство Name возвращает физическое имя поля в ТБД.

• property Required: Boolean;

Данное свойство возвращает True, если поле требует обязательного заполнения каким-либо значением, и False в противном случае.

• property Size: Integer;

Данное свойство возвращает размер поля. Он важен для полей типов: ftString, ftBCD, ftBytes, ftVarBytes, ftBlob, ftMemo or ftGraphic. Для полей всех остальных типов размер поля определяется его типом. Для полей BCD возвращается число знаков после десятичной точки.

Пример.

Считать сведения о полях ТБД, ассоциированной с Table 1, и поместить в ListBox1 информацию об имени каждого поля, его типе, размере и значении его свойства Required (результат работы приводимого кода на рис.7.38):

var i : Integer;

TipPolja : String; // тип поля

Tmp : String; // результат форматирования

TmpReq : String[3];//рабочая переменная

begin

ListBoxl.Clear;

Tablel.FieldDefs.Update;

// считываем поля и заносим в ListBoxl сведения об

//их имени, типе, размере и свойстве Required

FOR i := О ТО Tablel.FieldDefs.Count - 1 do begin

CASE Tablel.FieldDefs.Items[i].DataType OF

ftString TipPolja = 'String';

ftSmallint TipPolja = 'Smallint' ;

ftlnteger TipPolja == 'Integer';

ftWord TipPolja = 'Word';

ftBoolean TipPolja = 'Boolean';

ftFloat TipPolja = 'Float';

ftCurrency TipPolja = 'Currency' ;

ftBCD TipPol;a = 'BCD';

ftDate TipPolja = 'Date';

ftTime TipPolja = 'Time';

ftDateTime TipPolja = 'DateTime';

ftBytes TipPolja = 'Bytes' ;

ftVarBytes TipPolja = 'VarBytes' ;

ftAutoInc TipPolja = 'AutoInc';

ftBlob TipPolja = 'Blob';

ftMemo TipPolja = 'Memo';

ftGraphic TipPolja = 'Graphic';

ftFmtMemo TipPolja = 'FmtMemo' ;

ftParadoxOle TipPolja = 'ParadoxOle';

ftDBaseOle TipPolja = 'DBaseOle';

ftTypedBinary TipPolja = 'TypedBinary';

END;

IF Tablel.FieldDefs.Items[i].Required THEN

TmpReq := 'Req'

ELSE

TmpReq := ' - ';

Tmp := Format('%-16s %-10s %-5s %4d', [Tablel.FieldDefs.Items[i].Name, TipPol]a, TmpReq, Tablel.FieldDefs.Items[i].Size]) ;

ListBoxl.Items.Add(Tmp);

END;

Методы компонента TFieldDefs

procedure Add(const Name: string; DataType: TFieldType; Size: Word; Required: Boolean);

Метод Add добавляет в список Items новый элемент- поле.

Параметр Name определяет имя нового поля.

Параметр DataType определяет тип поля как одно из значений перечислимого типа TFieldType (см. выше описание свойства TTable. TFieldDefs.Items [Index].DataType).

Параметр Si:e указывает размер поля или 0 в случае, когда размер поля явно определяется его типом (например, ftlnteger определяет целочисленное поле длиной в слово).

Параметр Required определяет, должно ли поле в обязательном порядке содержать какое-либо значение, или не должно.

procedure Clear;

Метод Clear очищает FieldDefs. Это необходимо, например, при создании новой ТБД (см. метод TTable. CreateTable.

function Find(const Name: string): TFieldDef;

Метод Find ищет поле по его имени, определяемому параметром Name. В случае успеха метод возвращает указатель на объект TIndexDefs. Items.

function Index0f(const Name: string): Integer; Метод Index Of ищет поле по его имени и возвращает индекс объекта TIndexDefs списке Items. У найденного элемента значение свойства Name совпадает с параметром Name метода.

procedure Update;

Метод Update обновляет содержимое свойства TFieldDef текущей информацией о полях в составе ТБД. Он также позволяет заносить в TFieldDef информацию о полях неоткрытого НД.

ЗАМЕЧАНИЕ.

Для НД типа TQuery в свойстве FieldDefs будет содержаться информация только о тех полях, которые представлены в списке после оператора SELECT:

SELECT P.NN, P.DatePrih, P.Tovar

FROM prihod P

WHERE ...

Тогда свойство FieldDefs будет содержать информацию только о полях NN, DatePrih, Tovar таблицы Prihod.

Это правило действует и для случая двух и более таблиц БД, участвующих в запросе:

SELECT P.NN, P.DatePrih, P.Tovar, P.Kolvo, K.KursUSD

FROM prihod P, KursUSD К

WHERE K.Datel= P.DatePrih

В случае использования в качестве полей результирующего запроса агрегированных функций типа SUM, а также результатов выражений, имя вычисляемого или агрегированного поля берется по его псевдониму, следующему после ключевого слова as:

SELECT P.NN, P.DatePrih, P.Tovar, P.Kolvo, K.KursUSD, P.KolVo * K.KursUSD as Zena

FROM prihod P, KursUSD К

WHERE K.Datel= P.DatePrih

В данном случае вычисляемое поле P.KolVo * K.KursUSD имеет в результирующем НД имя ' Zena '.

Если псеводним вычисляемому или агрегированному полю не присвоен, его имя берется как копия арифметического выражения, по которому значение этого поля вычисляется. Например:

SELECT P.NN, P.DatePrih, P.Tovar, P.Kolvo, K.KursUSD,

P.KolVo * K.KursUSD

FROM prihod P, KursUSD К

WHERE K.Datel= P.DatePrih

Имя вычисляемого по выражению P.Kolvo * K.KursUSD поля будет P.KolVo * K.KursUSD '.



Использование свойств FieldCount и Fields


property FieldCount: Integer;

Свойство FieldCount возвращает число компонентов TField, определенных в редакторе полей для данного НД. В общем случае число TField не равно числу полей, физически объявленных в ТБД, ассоциированной с данным TTable, поскольку из объявленных полей в качестве TField могут быть добавлены не все, плюс к тому могут быть объявлены, например, вычисляемые поля.

В том случае, если с использованием редактора полей ни одно поле из физически объявленных в структуре ТБД не было добавлено в форму в качестве TField, FieldCount будет возвращать значение, совпадающее с числом физически объявленных в ТБД полей.

Для компонента TQuery всегда будет возвращаться число полей, объявленных в качестве полей возвращаемого результирующего НД в операторе SELECT:

SELECT F.polel, F.pole2, S.pole99, ...

FROM first F, second S, ...

При этом не важно, сколько имеется ТБД-источников для SQL-запроса и к какому из них принадлежат возвращаемые поля, поскольку они считаются принадлежащими набору данных, возвращаемому в результате выполнения оператора SELECT, и ни с чем иным в данном контексте не ассоциируются.

property Fields[Index: Integer]: TField;

Свойство Fields есть набор компонентов TField, определенных для НД. К отдельному полю можно обратиться, указав Fields /Index J, где Index лежит в диапазоне O..Count-1.

Пример.

Пусть в редакторе полей для Table 1 определена группа полей (компонентов TField). Необходимо записать в ListBox2 имена физических полей и для каждого из них - имя компонента TField, ассоциированного с полем:

ListBox2.Clear;

FOR i := 0 ТО Table1.FieldCount - 1 do

ListBox2.Items.Add(Format('%-16s %-10s', [Tablel.Fields[i].FieldName, Tablel.Fields[i].Name])) ;

Как можно видеть из рис.7.39, использование свойств FieldDefs и Fields имеет разную природу: если первое выдает информацию о физически объявленных полях в ТБД, второе выдает сведения о логических полях НД, т.е. полях, добавленных в коллекцию объектов типа TField для этого НД:

Рис 7.39 Информация о компонентах TField

Заметим, что поле VychislPole является вычисляемым и физически в структуре ТБД отсутствует.



Свойства DefaultFields, CacheBlobs, метод ClearFields


Свойство property DefaultFields Boolean; содержит True, если для НД используются динамически создаваемые поля (из структуры записи таблицы БД), и False, если для НД определены постоянные поля (компоненты TField) в редакторе полей.

procedure ClearFields;

Метод очищает содержимое полей текущей записи набора данных. Если НД не находится в режиме вставки новой записи или редактирования, возбуждается исключение. В случае успешного выполнения вызывается обработчик события OnDataChange для компонента TDataSource, связанного с НД.

property CacheBlobs: Boolean;

Определяет, выделяется ли в памяти буфер для хранения содержимого blob-поля текущей записи НД. Если свойство установлено в True (значение по умолчанию), буфер выделяется, если False - нет. Буфер необходим, если содержимое blob-поля (например, мемо-поля) показывается в форме для текущей записи НД и должно быстро обновляться при переходе на новую запись.