Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Базы данных и репортинг > Метод FindKey и TQuery


Автор: Serggggg 11.2.2004, 14:25
Всем привет!
Прошу совета. Проблема не очень сущетвенная, но желательно разобраться.
Как мы все знаем, у компонента TTable есть такой чудный метод, как FindKey, который после Refresh'а позволяет вернуться к нужной записи. В моем случае используется TQuery. Вышеупомянутого метода для него нет. Запрос "обновляется" посредством операций
Active:=False;
Active:=True;
Курсор (естественно) скачет сразу на первую запись. А как мне перевести его на ту позицию (скажем, Grid'a), с которой перед обновлением запроса производилось, например, удаление записи?

Автор: x77 11.2.2004, 14:40
используя Locate по ключевому полю. в принципе, можно по любому, но Locate использует при поиске индексы, поэтому желательно, чтобы поле было как минимум индексированным, а лучше - ключевым.

Автор: Kesh 11.2.2004, 15:26
Имхо, для квери это не самый лучший способ...
Представь, что у тебя клиент, подсоединенный к табличке в базе, в которой записей эдак около миллиона, да еще плюсом есть какое-нить поле типа BLOB/CLOB/BFile. В Oracle при реализации такой квери ты получишь где-то N первых записей, остальные будут подгружаться по мере необходимости. А теперь представь, что ты своим FindKey обращаешься к одной из последних записей в квере... Придется пролистывать всю кверю, плюс тянуть LOB-поля, и все это по сети, а если в BLOB'e еще и картинка, еще и BMP'шная... Представь себе загрузку сети...
А если у тебя при этом еще и несколько клиентов...

P.S. Все это конечно же ИМХО...

Автор: Maverick 11.2.2004, 16:21
А не лучше перед удалением запомнить номер строки, на которой стоишь... а после обновления - направиться к этому номеру... Разве порядок-нумерация строк изменится? По-моему просто сдвинется на одну запись... Значит, попадешь достаточно близко... Или сам запрос в Query меняется?

Автор: x77 11.2.2004, 16:22
kesh, при описанной тобой ситуации, нужная запись получается отдельным запросом. ну а для всех прочих случаев... есть же, в конце концов, стандартные решения, зачем сразу в крайности?

Автор: x77 11.2.2004, 16:28
Maverick, о том и речь. делаем DisableControls, запись удаляется, курсор сдвигается на строку, запоминается значения ключевого поля (полей), выключается/включается квери, курсор позиционируется на место методом Locate, делаем EnableControls.

Автор: Maverick 11.2.2004, 16:36
Вы меня не поняли.... x77.... Зачем Locate.... Запомнить надо RecNo... Потом вернуться к нему же.... или RecNo+1....
Зачем искать?.... Заранее запомнить...

Автор: x77 11.2.2004, 16:49
Maverick, насколько я знаю, RecNo имеет смысл только для локальных баз (dBase, Paradox, etc.) для Interbase или Oracle RecNo равен -1 или вообще какой-нибудь чепухе.

Автор: Serggggg 11.2.2004, 18:18
Значение ключевого поля - это замечательно. Я и сам сторонник поски по ключу. Но в инспекторе объектов IndexName или IndexFieldName задаётся только для таблиц, а не для запросов. Для компонента TQuery такого свойства там не описано.
x77, база действительно на Oracle.

Автор: Kesh 11.2.2004, 18:47
И все же для квери запоминать какой-то номер мне кажется не есть гуд...
Ибо для реляционых баз данных, в силу определения, нет понятия первая, предыдущая, последующая, последняя запись... Важно только условие выборки...

Автор: Maverick 11.2.2004, 18:58
x77.... Ты прав.... Пробую под Informix - =-1.... Буду знать....

Автор: Петрович 12.2.2004, 10:26
Цитата
А не лучше перед удалением запомнить номер строки, на которой стоишь... а после обновления - направиться к этому номеру... Разве порядок-нумерация строк изменится? По-моему просто сдвинется на одну запись... Значит, попадешь достаточно близко...


Для реляционных БД понятия "номер строки" - несуществует. Даже в локальных БД (dBase, Paradox, и п.р.) в которых действителен RecNo, такой метод не годится! Поскольку во первых, помимо тебя, в таблице могут копошиться и другие юзеры которые могут вставлять/удалять записи. И во вторых, возможны запросы манипулирующие неопределенным числом записей, например DELETE FROM TABLE Vasya WHERE Price > 100

Правильным способом будет использовать Locate. Если есть primary key, то использовать его, если нет, то можно использовать все поля.

Например можно описать такой объект:
Код
type
 tSaveContext = class
   private
     fds          :TDataSet;
     FieldsList   :String;           // Список полей DataSet'а
     FieldsValues :array of Variant; // Список значений текущей записи
   public
     constructor Create (ads :TDataSet);
     destructor Destroy; override;
   end;

constructor tSaveContext.Create (ads :TDataSet);
var i :Integer;
begin
 inherited Create;

 if  not Assigned(ads)  then  Exit;
 if  not ads.Active     then  Exit;

 fds := ads;

 // сохранить список имен и значений полей текущей записи
 FieldsList := '';
 with  fds  do begin
   SetLength(FieldsValues,Fields.Count);
   for i:=0  to  Fields.Count-1  do with  Fields[i]  do begin
     FieldsList      := FieldsList + FieldName + ';';
     FieldsValues[i] := Value;
   end;
 end;
 Delete(FieldsList,Length(FieldsList),1);
end;

destructor tSaveContext.Destroy;
begin
 if  Assigned(fds)  and  fds.Active  and  (Length(FieldsList) = 0)  then begin
   try
     fds.Locate(FieldsList,FieldsValues,[]);
     fds := nil;
     FieldsList   := '';
     SetLength(FieldsValues,0);
   except
     // Ошибки в данном случае не повод для беспокойства, просто не восстановится
     // позиция в датасете
   end;
 end;

 inherited;
end;


А потом его использховать, например так:
Код
procedure Refresh(ads :tDataSet);
begin
 with  tSaveContext.Create(ads)  do try
   ads.Close; // или ads.Active := False;
   ads.Open;  // или ads.Active := True;
 finally
   Free;
 end;
end;

Автор: Serggggg 12.2.2004, 10:52
Благодарю за совет. Стоит попробовать!

Автор: Maverick 12.2.2004, 17:14
[QUOTE]Для реляционных БД понятия "номер строки" - несуществует. Даже в локальных БД (dBase, Paradox, и п.р.) в которых действителен RecNo, такой метод не годится! Поскольку во первых, помимо тебя, в таблице могут копошиться и другие юзеры которые могут вставлять/удалять записи. И во вторых, возможны запросы манипулирующие неопределенным числом записей, например DELETE FROM TABLE Vasya WHERE Price > 100[/QUOT]

Последнее время были задания для DBF для работы одного пользователя.... в стиле с навигатором ДОБАВИТЬ/УДАЛИТЬ.... Вот и дошел до ручки....

Автор: cvi 13.2.2004, 19:05
Вижу моего решения еще нет.
Пишу так

var
Save_Place : TBookMark;

Save_Place := Query.(Что то типа)SaveBookMark;(точно не помню как называется)
Query.Actice := False;
Query.Active := True;
Query.GotoBookMark(Save_Place);
Query.FreeBookMark(Save_Place);

точно работает по Interbase, paradox

Спасибо за внимание. smile.gif)

Автор: Medved 13.2.2004, 20:44
Цитата
Save_Place := Query.(Что то типа)SaveBookMark;(точно не помню как называется)

Этот метод называется GetBookmark.

Но учтите, вот из F1:
GetBookmark relies on a protected method to obtain the bookmark value. TDataSet descendants implement this method to provide their own type of bookmark support. Unidirectional datasets do not support bookmarks, and so do not return a meaningful value.

Иначе говоря, GetBookmark не будет работать на однонаправленных наборах данных (т.е. не подлежащих редактированию, кэшированию, фильтрации и индексированию, а также навигации. Такой набор данных напрмер возвращает dbExpress).

Автор: Петрович 13.2.2004, 21:17
Цитата
Save_Place := Query.(Что то типа)SaveBookMark;(точно не помню как называется)
Query.Actice := False;
Query.Active := True;
Query.GotoBookMark(Save_Place);


Тогда-уж проще:

Код
...
var
 SavedBookmark :String;
...
 SavedBookmark := Query.Bookmark;
 Query.Actice := False;
 Query.Active := True;
 Query.Bookmark := SavedBookmark;
...


Однако не стал рекомендовать, поскольку где-то читал что закладка (Bookmark) сохраненная до закрытия датасета может стать недействительной после его открытия. Причем сам когда-то "обжегся" (Interbase через IBX), теперь использую закладки только когда в промежутке не надо закрывать датасет.

Автор: Medved 13.2.2004, 21:30
Цитата

Однако не стал рекомендовать, поскольку где-то читал что закладка (Bookmark) сохраненная до закрытия датасета может стать недействительной после его открытия. Причем сам когда-то "обжегся" (Interbase через IBX), теперь использую закладки только когда в промежутке не надо закрывать датасет.


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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)