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

       

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


Вообще говоря, часто необходимо в зависимости от каких-либо условий "прыгать" по НД взад-вперед. Поэтому распространен вариант одновременного использования 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.



Содержание раздела