Установка уровня доступа
В случае многопользовательского доступа к БД для таблицы можно задать уровни доступа, которые будут действовать для других приложений. Уровень доступа определяет возможность записи данных в таблицу и их чтения.
Метод LockTable (LockType: TLockType) устанавливает блокировку для таблицы, при этом параметр LockType задает тип блокировки:
□ ltReadLock (запрещены запись и чтение записей таблицы); этот режим является своего рода режимом монопольного доступа;
□ ltWriteLock (запрещена запись в таблицу); другие приложения не могут выполнять модификацию записей таблицы, но чтение данных им разрешено.
Метод UnLockTable (LockType: TLockType) снимает установленную ранее блокировку.
Рассмотрим на конкретном примере, как осуществляется блокировка таблицы.
procedure TForml.ButtonlClick(Sender: TObject);
var n: longint;
begin
try
// Блокировка таблицы от внесения изменений Tablel.LockTable(ltWriteLock); // Пересчет окладов Tablel.First;
for n:= 1 to Tablel.RecordCount do begin Tablel.Edit; Tablel.FieldByName('Salary').AsFloat:=
Tablel.FieldByName('Salary').AsFloat + 500; Tablel.Next,• Tablel.Post; end; finaly
// Отмена блокировки таблицы Tablel.UnLockTable(ltWriteLock); end; end;
Оклады всех сотрудников увеличиваются на 5 00 (рублей). На время пересчета таблица блокируется, что защищает ее от внесения изменений другими прило-
Глава 7. Навигационный доступ к данным с помощью BDE
169
жениями. При операциях с таблицей выполняется локальная обработка исключений, состоящая в отмене блокировки таблицы.
Сортировка набора данных
Порядок расположения записей в наборе данных может быть неопределенным. По умолчанию записи не отсортированы или сортируются, например, для таблиц Paradox по ключевым полям, а для таблиц dBase в порядке их поступления в файл таблицы.
С отсортированными записями набора данных работать более удобно. Сортировка заключается в упорядочивании записей по определенному полю в порядке возрастания или убывания содержащихся в нем значений. Сортировку можно выполнять и по нескольким полям. Например, при сортировке по двум полям сначала записи упорядочиваются по значениям первого поля, а затем группы записей с одинаковым значением первого поля сортируются по второму полю.
|
Сортировка наборов данных Table и Query выполняется различными способами. Здесь мы рассмотрим сортировку набора данных Table, для компонента Query вопросы сортировки рассмотрены в главе 8.
Сортировка наборов данных Table выполняется автоматически по текущему индексу. При смене индекса происходит автоматическое переупорядочивание записей. Таким образом, сортировка возможна по полям, для которых создан индекс. Для сортировки по нескольким полям нужно создать индекс, включающий эти поля.
Направление сортировки определяет параметр ixDescending текущего индекса, по умолчанию он выключен, и упорядочивание выполняется в порядке возрастания значений. Если признак ixDescending индекса включен, то сортировка выполняется в порядке убывания значений.
Напомним, что задать индекс (текущий индекс), по которому выполняется сортировка записей, можно с помощью свойств indexName или indexFieldNames. Эти свойства являются взаимоисключающими, и установка значения одного из них приводит к автоматической очистке значения другого. В качестве значения свойства IndexName указывается имя индекса, установленное при его создании. При использовании свойства IndexFieldNames указываются имена полей, образующих соответствующий индекс.
|
В связи с тем, что главный индекс (ключ) таблиц Paradox не имеет имени, выполнить сортировку по этому индексу можно только с помощью свойства
IndexFieldNames.
Вот небольшой пример, иллюстрирующий сортировку с указанием имен индексов:
procedure TForml.Button4Click(Sender: TObject);
begin
case RadioGroupl.Itemlndex of
0: Tablel.IndexName:= 'indName';
170
Часть II. Технологии доступа к данным
1: Tablel.IndexName:= 'indBirthDay'; end; end;
В качестве набора данных используется компонент Tablel, а сортировка выполняется двумя способами: по индексу indName, созданному для поля Name, и по индексу indBirthDay, созданному для поля BirthDay.
Еще один пример сортировки, на этот раз с указанием имен индексных полей (для связанной с набором данных таблицы поле Code задано автоинкрементным и определено в качестве главного индекса):
procedure TForml.Button5Click(Sender: TObject);
begin
case RadioGroupl.Itemlndex of
end;
end;
Здесь сортировка выполняется по следующим полям: Name (индекс indName), Name И BirthDay (индекс indNameBirthDay), Code (главный индекс).
Рис. 7.1. Вид формы для сортировки набора данных |
А теперь рассмотрим более сложный пример сортировки. В качестве набора данных снова используется компонент Tablel. Пользователь может управлять сортировкой его записей с помощью двух групп переключателей: в первой задается вид, а во второй — направление сортировки. Сортировка выполняется после нажатия кнопки Сортировать (btnsort). Вид формы при проектировании показан на рис. 7.1.
Глава 7. Навигационный доступ к данным с помощью BDE
171
В связи с тем, что компоненты Table и DataSource являются невизуальными и при выполнении приложения не видны, их можно размещать в любом удобном месте формы, где они не мешают другим компонентам. Часто компоненты Table и DataSource помещаются на компоненте DBGrid, как это сделано в рассматриваемом примере.
|
Ниже приводится обработчик события нажатия кнопки btnSort, вызывающей выполнение сортировки.
procedure TForml.btnSortClick(Sender: TObject);
begin
case RadioGroupl.Itemlndex of
0: Tablel.IndexName:= 'indName';
1: Tablel.IndexName:= 'indBirthDay';
2: Tablel.IndexName:=
end;
case RadioGroup2.Itemlndex of
0: Tablel.IndexDefs
[Tablel.IndexDefs.IndexOf(Tablel.IndexName)].Options:=
Tablel.IndexDefs
[Tablel.IndexDefs.IndexOf(Tablel.IndexName)].Options +
[ixDescending];
1: Tablel.IndexDefs
[Tablel.IndexDefs.IndexOf(Tablel.IndexName)].Options:=
Tablel.IndexDefs
[Tablel.IndexDefs.IndexOf(Tablel.IndexName)].Options -
[ixDescending];
end;
end;
Поля, по которым сортируются записи, устанавливаются через свойство IndexName. При отсутствии сортировки этому свойству присваивается пустая строка. Для таблиц Paradox это означает сортировку по первому полю. Для таблиц dBase записи располагаются в порядке их поступления в файл таблицы.
Управление направлением сортировки осуществляется с помощью параметра ixDescending текущего индекса. Для определения номера текущего индекса в списке IndexDefs используется метод IndexOf.
Навигация по набору данных
Навигация по набору данных заключается в управлении указателем текущей записи (курсором). Этот указатель определяет запись, с которой будут выполняться такие операции, как редактирование или удаление.
172
Часть II. Технологии доступа к данным
Перемещение по записям
Перед перемещением указателя текущей записи набор данных автоматически переводится в режим просмотра. Если текущая запись находилась в режимах редактирования или вставки, то перед перемещением указателя сделанные в записи изменения вступают в силу, для чего набор данных автоматически вызывает метод CheckBrowseMode.
Для перемещения указателя текущей записи в наборе данных используются следующие методы:
□ процедура First — установка на первую запись;
□ процедура Next — установка на следующую запись (при вызове метода для последней записи указатель не перемещается);
□ процедура Last — установка на последнюю запись;
□ процедура Prior — установка на предыдущую запись (при вызове метода для первой записи указатель не перемещается);
□ функция MoveBy (Distance: Integer): Integer — перемещение на число записей, определяемое параметром Distance. Если его значение больше нуля, то перемещение осуществляется вперед, если меньше нуля — то назад. При нулевом значении параметра указатель не перемещается. Если заданное параметром Distance число записей выходит за начало или конец набора данных, то указатель устанавливается на первую или на последнюю запись. В качестве результата возвращается число записей, на которое переместился указатель.
При перемещении указателя текущей записи учитываются ограничения и фильтр, определенные для набора данных. Таким образом, перемещение выполняется по записям набора данных, которые он содержит в текущий момент времени. Число записей определяется свойством RecordCount.
Значения указателя текущей записи изменяют также методы, связанные с поиском записей, например, метод FindFirst. Они рассматриваются ниже в этой главе.
(Замечание^
При изменении порядка сортировки набора данных расположение его записей может измениться, что чаще всего и происходит, но указатель по-прежнему указывает на первоначальную запись, даже если она находится на другом месте и имеет новое значение свойства RecNo.
Рассмотрим следующий пример:
procedure TForml.Button3Click(Sender: TObject); var s: real;
n: integer; begin s = 0;
Глава 7. Навигационный доступ к данным с помощью BDE
173
// Установка текущего указателя на первую запись
Tablel.First;
for n:= 1 to Tablel.RecordCount do begin
s:= s + Tablel.FieldByName('Salary').AsFloat;
// Перемещение текущего указателя на следующую запись
Tablel.Next;
end; Label2.Caption:= FloatToStr(s); end;
В приведенной процедуре перебираются все записи набора данных Tablel, при этом в переменной s накапливается сумма значений, содержащихся в поле Salary. Перебор записей осуществляется с помощью метода Next, вызываемого в цикле. Предварительно с помощью метода First указатель устанавливается на первую запись. После выполнения кода указатель будет установлен на последнюю запись.
Отметим, что используемая для перебора записей переменная п имеет тип integer, совпадающий с типом longint свойства RecordCount.
Аналогичным образом можно перебрать все записи набора данных, начиная с последней. Естественно, при этом нужно вызывать методы Last и Prior.
Для контроля положения указателя текущей записи можно использовать свойство RecNo, которое содержит номер записи, считая от начала набора данных (для локальных таблиц dBase и Paradox).
Для таблиц Paradox свойство RecNo можно использовать еще и для перехода к записи с известным номером: такой переход выполняется установкой свойства RecNo в значение, равное номеру нужной записи. Например:
procedure TForml.ButtonlClick(Sender: TObject);
begin
Tablel.RecNo:= StrToInt(Editl.Text);
end;
При нажатии кнопки Buttonl указатель текущей записи набора данных Tablel устанавливается на запись, номер которой содержит редактор Editl.
Для определения начала и конца набора данных при перемещении указателя текущей записи можно использовать свойства bof и eof типа Boolean соответственно. Эти свойства доступны для чтения при выполнении приложения. Свойство bof показывает, находится ли указатель на первой записи набора данных. Этому свойству присваивается значение True при установке указателя на первой записи, например, сразу после вызова метода First. Свойство eof показывает, находится ли указатель на последней записи набора данных. Этому свойству устанавливается значение True при размещении указателя на последней записи.
174
Часть II. Технологии доступа к данным
(_____ ЗамечаниеJ
Для пустого набора данных свойства BOF и EOF имеют значения True.
При изменении порядка сортировки или фильтрации, а также при удалении или добавлении записей значения свойств BOF и EOF могут изменяться. Например, если направление сортировки изменяется на противоположное, то первая запись становится последней.
При работе с таблицами одновременно нескольких приложений, когда постоянно добавляются или удаляются записи, значения свойств bof и eof соответствуют действительному состоянию набора данных в определенные моменты времени. Так, свойство EOF устанавливается в значение True сразу после выполнения метода Last. Если после этого другим приложением в конец набора данных добавлена новая запись, то значение свойства EOF становится неправильным.
Рассмотрим еще один пример, иллюстрирующий работу с указателем текущей записи:
procedure TForml.Button2Click(Sender: TObject);
var s: real;
begin
s:= 0;
Tablel.First;
while not Tablel.EOF do begin
s:= s + Tablel.FieldByName('Salary').AsFloat;
Tablel.Next;
end; s:= s + Tablel.FieldByName('Salary').AsFloat; Label2.Caption:= FloatToStr(s); end;
Как и в предыдущем примере, здесь перебираются все записи набора данных Tablel и подсчитывается сумма значений, содержащихся в поле Salary. Отличие заключается в том, что использован итерационный цикл с верхним окончанием, условием выхода из которого является достижение последней записи набора данных.
Иногда в цикле выполняются сложные действия. При этом на перебор записей набора данных может потребоваться достаточно большое время, в течение которого приложение (и компьютер) не реагирует на команды пользователя. Поэтому в циклы, надолго загружающие работой компьютер, рекомендуется вставлять вызов метода Application. ProcessMessages, обеспечивающий системе Windows возможность обрабатывать сообщения. Кроме того, программист должен предусмотреть досрочный выход из длительного цикла, например, с помощью переменной-признака.
Перед началом длительного цикла обработки записей БД и выполнения расчетов целесообразно выдать предупреждающее сообщение пользователю.
Глава 7. Навигационный доступ к данным с помощью BDE
175
При перемещении по записям набора данных связанные с ним визуальные компоненты отображают изменения данных, причем смена отображения может происходить достаточно быстро, вызывая неприятное мелькание на экране. Чтобы избежать этого эффекта, можно программно до начала цикла перебора записей временно отключить набор данных от всех связанных с ним визуальных компонентов, а по окончании цикла снова подключить. Для этого предназначены методы DisableControls и EnableControls.
Рассмотрим следующий пример. Пусть в цикле перебираются все записи набора данных Table 1, начиная с первой, и подсчитывается сумма значений, содержащихся в поле salary. Для запуска и остановки процесса расчета используются кнопки Начать (btnworkstart) и Прервать (btnWorkBreak). Поле индикатора ProgressBarl служит для показа хода работы, а процент перебранных записей отображается в надписи lblWorkPercent, находящейся рядом с индикатором. На рис. 7.2 показан вид формы во время выполнения приложения.
Код модуля приведенной на рис. 7.2 формы имеет вид:
unit uNavig; interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Db, DBTables, Grids, DBGrids, StdCtrls, ExtCtrls, DBCtrls, ComCtrls;
type
TForml = class(TForm)
btnClose: | TButton; |
DataSourcel: | TDataSource; |
Tablel: | TTable; |
DBGridl: | TDBGrid; |
Panell: | TPanel; ■ |
ProgressBarl: | TProgressBar; |
btnWorkStart: | TButton; |
btnWorkBreak: | TButton; |
Labell: | TLabel; |
lblWorkPercent: | TLabel; |
lblSum: | TLabel; |
procedure btnCloseClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure btnWorkStartClick(Sender: TObject); procedure btnWorkBreakClick(Sender: TObject);
176
Часть II. Технологии доступа к данным
private
{ Private declarations } public
{ Public declarations }
end; var Forml: TForml;
WorkBreak: boolean;
implementation
{$R *.DFM}
procedure TForml.FormCreate(Sender: TObject); begin
btnWorkStart.Enabled:= True; btnWorkBreak.Enabled: = False; WorkBreak:= False; end;
procedure TForml.btnWorkStartClick(Sender: TObject);
label 10;
var s: real;
begin
btnWorkStart.Enabled:= False;
btnWorkBreak.Enabled:= True;
s:= 0;
ProgressBarl.Max:= Tablel.RecordCount;
Tablel.DisableControls;
Tablel.First;
while not Tablel.EOF do begin
s:= s + Tablel.FieldByName('Salary').AsFloat;
// Без этого вызова не будет обрабатываться нажатие кнопки // btnWorkBreak
Application.ProcessMessages;
ProgressBarl.Position:= Tablel.RecNo;
lblWorkPercent.Caption:=
IntToStr(Round(ProgressBarl.Position/ProgressBarl.Max*100)) +
if WorkBreak then goto 10;
Tablel.Next;
end; lblSum.Caption:= 'Итого ' + FloatToStr(s); 10: Tablel.EnableControls;
Глава 7. Навигационный доступ к данным с помощью BDE
177
ProgressBarl.Position:= 0; lblWorkPercent.Caption:= ''; btnWorkStart.Enabled: = True; btnWorkBreak.Enabled: = False; WorkBreak:= False; end;
procedure TForial.btnWorkBreakClick(Sender: TObject) begin
WorkBreak: = True; end;
procedure TForml.btnCloseClick(Sender: TObject); begin Close; end;
end.
Рис. 7.2. Форма приложения, выполняющего перебор записей набора данных
Цикл обработки запускается нажатием кнопки btnWorkStart. Перед началом цикла определяется максимальное значение индикатора ProgressBarl, переменная WorkBreak устанавливается в False, а визуальные компоненты отключаются от набора данных Tablel. В цикле происходит подсчет суммы, а также отображение хода выполнения работ в поле индикатора ProgressBarl и надписи lblWorkPercent. По окончании цикла результат отображается в надписи lblSum, и визуальные компоненты снова подключаются к набору данных Tablel.
178
Часть II. Технологии доступа к данным
Для прерывания обработки служит кнопка btnWorkBreak, при ее нажатии переменная-признак WorkBreak принимает значение True. Значение этой переменной проверяется в цикле обработки, и если оно равно True, то цикл прекращается путем вызова процедуры Break. Чтобы при выполнении цикла обрабатывались событие нажатия кнопки btnWorkBreak и другие события, в цикл включен вызов метода ProcessMessages.
При любом изменении положения указателя текущей записи для набора данных генерируются события BeforeScroll и AfterScroll типа TDataSetNotifyEvent.
Пользователь имеет возможность перемещаться по набору данных с помощью элементов управления, в качестве которых часто используются компоненты DBGrid и DBNavigator. Управление этими элементами с помощью мыши или клавиатуры приводит к автоматическому вызову соответствующих методов, перемещающих указатель текущей записи в заданное место. Например, после нажатия кнопкок First record, Prior record, Next record или Last record компонента DBNavigatorl косвенно вызываются методы First, Prior, Next или Last, перемещающие указатель текущей записи соответственно на первую, предыдущую, следующую или последнюю записи набора данных, с которым связан (через источник данных DataSource) компонент DBNavigatorl.
Другим вариантом является размещение в форме элементов управления, например, кнопки Button, предназначенных для навигации по набору данных. Кроме того, часто в форме также размещаются элементы (обычно кнопки) для управления такими операциями, как редактирование, вставка записей, фильтрация и сортировка набора данных.
Рассмотрим в качестве примера форму приложения, в которой осуществляется перемещение по набору данных с помощью пяти кнопок (рис. 7.3). При нажатии какой-либо из этих кнопок вызывается соответствующий метод перемещения текущего указателя в заданном направлении. Например, при нажатии кнопки Предыдущая запись (btnPrior) вызывается метод Prior. При перемещении указателя на произвольное число записей дополнительно используется обратный счетчик SpinEditl, в поле которого вводится это число.
Код модуля uNavig2 рассматриваемой формы приложения имеет следующий вид:
unit uNavig2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, DBGrids, Db, DBTables, Spin;
type
TForml = class(TForm)
DataSourcel: TDataSource; Queryl: TQuery;
Глава 7. Навигационный доступ к данным с помощью BDE
179
DBGridl: TDBGrid;
btnFirst: TButton;
btnNext: TButton;
btnPrior: TButton;
btnLast: TButton;
lblRecord: TLabel;
btnMoveBy: TButton;
SpinEditl: TSpinEdit;
procedure FormCreate(Sender: TObject); procedure btnFirstClick(Sender: TObject); procedure btnNextClick(Sender: TObject); procedure btnPriorClick(Sender: TObject); procedure btnLastClick(Sender: TObject); procedure QuerylAfterScroll(DataSet: TDataSet) procedure btnMoveByClick(Sender: TObject);
private
{ Private declarations } public
{ Public declarations }
end;
var
Forml: TForml;
implementation
{$R *.DFM}
procedure TForml.FormCreate(Sender: TObject); begin
// Набор данных является редактируемым Queryl.RequestLive:= True; Queryl.Close; Queryl.SQL.Clear;
Queryl.SQL.Add('SELECT * FROM Goods.db'); Que г у 1. Open; SpinEditl.Value:= 0; end;
// Переход на первую запись
procedure TForml.btnFirstClick(Sender: TObject);
180
Часть II. Технологии доступа к данным
begin
Queryl.First; end;
// Переход к следующей записи
procedure TForml.btnNextClick(Sender: TObject); begin
Queгу1.Next; end;
// Переход к предыдущей записи
procedure TForml.btnPriorClick(Sender: TObject); begin
Queryl.Prior; end;
// Переход на последнюю запись
procedure TForml.btnLastClick(Sender: TObject); begin
Queryl.Last; end;
// Перемещение на число записей, заданное в счетчике SpinEditl procedure TForml.btnMoveByClick(Sender: TObject); begin
Queryl.MoveBy(SpinEditl.Value); end;
// Вывод номера записи и пересчет границ счетчика procedure TForml.QuerylAfterScroll(DataSet: TDataSet); begin
lblRecord.Caption:= 'Запись номер ' + IntToStr(Queryl.RecNo); SpinEditl.MinValue:= 1 - Queryl.RecNo;
SpinEditl.MaxValue:= Queryl.RecordCount - Queryl.RecNo; end;
end.
Сразу после того, как положение указателя изменилось, в надписи lblRecord выводится номер текущей записи в наборе данных. Пересчитываются минимальное и максимальное значения обратного счетчика, определяющие диапазон допустимых значений, указываемых при вызове метода MoveBy. Эти действия кодируются в обработчике события OnAfterScroll для того, чтобы информация о текущей записи обновлялась сразу после перемещения текущего указателя. Событие OnAfterScroll происходит также при открытии набора данных, когда указатель текущей записи устанавливается на первую запись.
Глава 7. Навигационный доступ к данным с помощью BDE
181
Рис. 7.3. Форма приложения, в которой осуществляется перемещение по набору данных
В качестве набора данных использован компонент Queryl, задание основных свойств которого выполняется при создании формы. SQL-запрос (select * from Goods. db) указан в свойстве SQL. Он обеспечивает получение всех полей и записей таблицы Goods.db. Свойство RequestLive установлено в значение True, чтобы обеспечить возможность редактирования набора данных. Таким образом, в рассмотренном примере компонент Query практически не отличается от компонента Table в плане работы с редактируемым набором данных, связанным с одной таблицей.
Иногда при подключении к одной таблице нескольких наборов данных возникает необходимость синхронизации их текущих указателей. Для этих целей используется метод GotoCurrent (Table: TTable), устанавливающий указатель на текущую запись в наборе данных Table. Например:
Tablel.GotoCurrent(Table2);
Здесь в наборе данных Tablel текущий указатель устанавливается на запись, которая является текущей в наборе данных ТаЫе2.
Переход по закладкам
Побочным эффектом выполнения ряда операций с наборами данных является изменение положения указателя текущей записи. Часто этот эффект нежелателен, т. к. после выполнения такой операции указатель находится не в том месте, где был до начала операции. При этом приходится снова отыскивать нужную запись и позиционировать на ней указатель, что неудобно даже при небольшом количестве записей.
Примером такой операции является рассмотренный выше расчет суммы значений по полю Salary, при котором выполняется перебор всех записей в прямом или обратном порядке. По окончании цикла указатель находится на первой или последней записи, а не там, где он был до начала суммирования.
182
Часть II. Технологии доступа к данным
(_____ ЗамечаниеJ
Положение указателя текущей записи может измениться также при формировании отчета из записей набора данных.
Для восстановления прежнего положения текущего указателя можно использовать, например, поиск записей: до начала операции данные текущей записи запоминаются, а после ее выполнения осуществляется поиск записи по этим данным. Похожим вариантом восстановления прежнего положения указателя является использование номера записи в наборе данных, который доступен через свойство RecNo.
Переход на определенную запись может понадобиться также для позиционирования указателя на запись, информация о которой была сохранена. Осуществляемые при этом действия не отличаются от указанных выше. Информация, необходимая для выполнения последующего перехода к требуемой записи, может запоминаться предварительно в удобный момент, например, при просмотре или редактировании записи.
Вот пример, иллюстрирующий, как восстановить положение текущего указателя:
var RecordNumber: integer;
// Запоминание номера текущей записи RecordNumber:= Queryl.RecNo;
// Операция, изменяющая положение указателя
// Переход к записи с запомненным номером
Queryl.First;
Queryl.MoveBy(RecordNumber - 1);
Здесь для перемещения текущего указателя используется номер записи, запоминаемый в переменной RecordNumber.
В таблицах Paradox для возврата к записи можно установить свойство RecNo набора данных в предварительно сохраненное значение. Например:
var RecordNumber: integer;
// Запоминание номера текущей записи RecordNumber:= Tablel.RecNo;
// Операция, изменяющая положение указателя
// Переход к записи с запомненным номером Tablel.RecNo:= RecordNumber;
Вопросы, связанные с поиском записей, рассматриваются в данной главе ниже.
Глава 7. Навигационный доступ к данным с помощью BDE
183
Кроме описанных способов, для перехода на определенную запись можно использовать закладки — специальные пометки каких-либо записей. Закладка имеет тип TBookmark, представляющий собой обычный указатель Pointer, с помощью которого можно ссылаться на различные объекты, в данном случае — на записи набора данных.
Перед использованием закладку нужно создать, т. е. определить запись, с которой эта закладка связана. Для этого предназначена функция GetBookmark: TBookmark, которая создает закладку на текущей записи набора данных. Возвращаемую закладку обычно запоминают в переменной типа TBookmark и впоследствии используют для перехода к помеченной ею записи.
Иногда закладка может неверно указывать позицию связанной с ней записи, при этом говорят о нестабильности закладки. Стабильность закладки в значительной степени зависит от форматов таблиц. Так, для таблиц dBase закладка всегда стабильна; для таблиц Paradox закладка стабильна, если у таблицы определен главный ключ. Таким образом, закладка устойчиво указывает на связанную с ней запись в тех случаях, когда существует признак, позволяющий однозначно различать записи. В противном случае закладка может быть нестабильной и ошибочно указывать на другую запись набора данных. Для проверки стабильности закладки используется функция DBlGetCursorProps (модуль DBE).