Оптимальное обновление данных

 
0
 
Delphi, Kylix and Pascal
ava
DDX | 29.07.2005, 10:29
используется MS SQL Server, Delphi 7, ADO

все манипуляции с данными (insert, update,delete) происходят путем вызова хранимых процедур сервера.

здесь и хочу спросить вашего совета: каким образом после обновления данных лучше обновлять даные в maste/detail гридах на форме? источником данных для master грида служит запроc (ADOQuery), для подчиненного - view-таблица с сервера (TADOTable)

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

Ответы (25)
ava
Bes | 29.07.2005, 09:59 #
я делаю так

Var
b:TBookmark;
Begin
b:=adoquery.getbookmark;
adoquery.active:=false;
adoquery.active:=true;
adoquery.gotobookmark(b);
b.free;//вот про эту строчку не помню есть она или нет
end;

а что такое с лагом ?
ava
DDX | 29.07.2005, 10:13 #
спасибо за ответ!

с лагом = с небольшой задержкой, т.е. нажимаешь кнопочку "добавить" или изменить, и наблюдаешь изменения в гриде не сразу, а через 1-2 секунды. немного напрягает, тем более, что юзер обычно туп и ненаблюдение изменений мгновенно вводит его в ступор
ava
Bes | 29.07.2005, 11:42 #
сколько записей в таблице?
ava
Петрович | 29.07.2005, 17:53 #
Bes, твой код хорошь, но иногда может вызвать Exception!
Увы, сам на это наткнулся.
Например, стоишь на последней записи, отдельным запросом удаляешь ее, и потом выполняешь твой код.
Вынужден был городить огород с locate. Причем, даже DBGridEh в EhLib'е для этого доработал. (см. здесь)
Но, и там тоже есть пробдлемы - Ошибка TADODataSet.Locate!, проверено в Delphi 6,7,9.

Так что, на сегодня, универсального решения нет. Лично у меня, все таблицы имеют ключевое поле, причем, всегда первое. Этому полу назначено Visible=False, поэтому его не видно. Перед закрытием датасета я сохраняю его значение, и после открытия делаю locate на него.
Если интересует, могу выложить новую доработку DBGridEh в EhLib'е, которая позволяет указывать как искать текущую запись:
добавлено:
type
TDBGridEhSavePosition = ( // какие поля однозначно идентифицируют запись для
// сохранания/восстановления позиции при обновлении, пересортировке и перефильтрации
gspNone // не сохранять позицию (по умолчанию)
,gspByFirstField // первое поле в наборе данных (datasource.dataset.fields[0])
,gspByAllFields // все поля
,gspByKeyFields // поля для которых в ProviderFlags установлен pfInKey
,gspByWhereFields // поля для которых в ProviderFlags установлен pfInWhere
);
...
property SavePosition :TDBGridEhSavePosition read FSavePosition write FSavePosition default gspNone;
ava
Bes | 01.08.2005, 06:40 #
2Петрович:
да-да забыл про это - я решил проблему так
вместо

adoquery.gotobookmark(b);

надо писать

try
   adoquery.gotobookmark(b);
except
end;

и все
P.S. глянул приведенный код из ЭхЛиб и вспомнил что поначалу я тоже проверял где стоит указатель и если на первой или последней записи то при удалении их туда и ставил без букмарка.
ava
DDX | 02.08.2005, 12:25 #
спасибо за советы, букмарки - хорошее решение

единственно, что интересно: если данные отсортированы по алфавиту и добавляется новая запись, то возможно ли установить выделение на неё :qstn

Петрович, если можешь, выложи, или пришли на почту: ddx{песик}.mail.ru,спасибо.
или, может, тоже самое можно скачать из указанной тобой темы?
ava
Петрович | 02.08.2005, 13:43 #
Цитата (DDX @ 2.8.2005, 13:25)
Петрович, если можешь, выложи, или пришли на почту: ddx{песик}.mail.ru,спасибо.

или, может, тоже самое можно скачать из указанной тобой темы?

Вот объект:
/////////////////////////////////////////////////////////////////// tSaveContext
//
// Объект используемый для сохранения/восстановления состояния грида.
//

type
tSaveContext = class
private
Grid :TCustomDBGridEh;
FieldsList :String; // Список полей DataSet'а
FieldsValues :{array of }Variant; // Список значений текущей записи
SaveIsStored :array of Boolean; // Список значений IsStored всех колонок
ActiveRow :Integer; // Номер текущей строки грида
public
constructor SaveGridContext (aGrid :TCustomDBGridEh);
procedure RestoreGridContext;
end;

constructor tSaveContext.SaveGridContext (aGrid :TCustomDBGridEh);
var i,o :Integer;
begin
inherited Create;
Grid := aGrid;

// Если набор данных закрыт, сохранять нечего
if not Assigned(Grid.DataSource)
or not Assigned(Grid.DataSource.DataSet)
or not Grid.DataSource.DataSet.Active then
Exit;

// сохранить список имен и значений ключевых полей текущей записи для восстановления позиции
FieldsList := '';
with Grid.DataSource.DataSet do begin
case aGrid.SavePosition of
gspNone : ;
gspByFirstField : with Fields[0] do begin
FieldsList := FieldName;
FieldsValues := Value;
end;
else begin
FieldsValues := VarArrayCreate([0, Fields.Count-1], varVariant);
o := -1;
for i:=0 to Fields.Count-1 do with Fields[i] do begin
if (aGrid.SavePosition = gspByAllFields)
or ((aGrid.SavePosition = gspByKeyFields ) and (pfInKey in ProviderFlags))
or ((aGrid.SavePosition = gspByWhereFields) and (pfInWhere in ProviderFlags))
then begin
Inc(o);
FieldsList := FieldsList + FieldName + ';';
FieldsValues[o] := Value;
end;
end;
System.Delete(FieldsList,Length(FieldsList),1);
if o = 0 then
FieldsValues := FieldsValues[0];
end;
end;
// SetLength(FieldsValues,Fields.Count);
// for i:=0 to Fields.Count-1 do with Fields[i] do begin
// FieldsList := FieldsList + FieldName + ';';
// FieldsValues[i] := Value;
// end;
// Delete(FieldsList,Length(FieldsList),1);
end;

// Сохранить признак динамического создания для всех колонок,
with Grid do begin
SetLength(SaveIsStored,Columns.Count);
for i:=0 to Columns.Count-1 do with Columns[i] do begin
SaveIsStored[i] := IsStored;
IsStored := True;
end;
end;

// Сохранить номер текущей строки грида
ActiveRow := Grid.DataLink.ActiveRecord;
end;

procedure tSaveContext.RestoreGridContext;
var i :Integer;
begin
try
// Востановить признак динамического создания для всех колонок,
for i:=0 to Length(SaveIsStored)-1 do
Grid.Columns[i].IsStored := SaveIsStored[i];

// Если набор данных закрыт, например не смог открыться после изменения
// запроса, то надо уничтожить все динамически создаваемые колонки.
// Обычно это делает сам набор данных при закрытии. Но, в данном случае,
// мы сами не дали ему это сделать. Придется делать это здесь
if not Assigned(Grid.DataSource)
or not Assigned(Grid.DataSource.DataSet)
or not Grid.DataSource.DataSet.Active then begin
for i := Grid.Columns.Count-1 downto 0 do
with Grid.Columns[i] do if not IsStored then Free;
end
// Если-же набор данных открыт, и есть список полей записи текущей на
// момент сохранения, то надо попытаться переместиться на нее.
// Если это получится (есть запись), то попытаться передвинуть ее строку
// которая была текущей на момент сохранения.
else if Length(FieldsList) > 0 then
if Grid.DataSource.DataSet.Locate(FieldsList,FieldsValues,[]) then
Grid.FDatalink.Dataset.MoveBy(-Grid.FDatalink.Dataset.MoveBy(Grid.DataRowCount-1-ActiveRow));
except
// Ошибки в данном случае не повод для беспокойства, просто не сохранится
// позиция в датасете и/или маркеры сортировки.
end;
Free;
end;
////////////////////////////////////////////////////////////////////////////////

А вот как им пользоваться:

with tSaveContext.SaveGridContext(AGrid) do try
AGrid.DataSource.DataSet.Close;
AGrid.DataSource.DataSet.Open;
finally
RestoreGridContext;
end;
ava
DDX | 02.08.2005, 16:24 #
Петрович
сенкс
ava
Akella | 09.08.2005, 13:53 #

Var
b:TBookmark;
Begin
b:=adoquery.getbookmark;
adoquery.active:=false;
adoquery.active:=true;
//не забывай про BookMarkValid и исключений не будет
!!! if adoquery.BookMarkValid(b) then
adoquery.gotobookmark(b);

b.free;//вот про эту строчку не помню есть она или нет
end;

ava
Bes | 09.08.2005, 14:25 #
Ух ты! Спасибо. Век живи - век учись. smile
ava
AntonSaburov | 09.08.2005, 14:28 #
Что-то опасно как-то сделано - с букмарками это плохое решение. Это же указатели. И гарантированно работать они не будут. Лучше тогда использовать поле ID какое-нибудь (ну чтобы в базе было) и уже по нему делать Locate.

А букмарки - это же указатели. Их к тому же освобождать надо. Не советую.
ava
Akella | 09.08.2005, 14:30 #
Цитата (AntonSaburov @ 9.8.2005, 14:28)
Что-то опасно как-то сделано - с букмарками это плохое решение.

а нафига ж их придумали? smile
Добавлено позднее:
Цитата (AntonSaburov @ 9.8.2005, 14:28)
А букмарки - это же указатели. Их к тому же освобождать надо. Не советую.

что, освобождать не советуешь?

Программеры там, у дяди Бормана, сидят и головы ломают, как сделать то или иное, а мы тут умничать начинаем :(

smile
Как в КВН было сказано про футбол: "Вся страна специалисты, одни мы, 11 человек играть не умеем".
ava
AntonSaburov | 09.08.2005, 14:34 #
Цитата (dsergey @ 9.8.2005, 14:30)
а нафига ж их придумали?

Их удобно использовать, если например надо пробежать по всему DataSet и сосчитать сумму. Или еще чего. Но на это время сам DataSet не закрывается. В этом случае букмарки - штука очень удобная и наверно самая быстрая.
ava
Akella | 09.08.2005, 14:35 #
Программист сам в праве выбирать, что ему использовать: закладки или locate :exclamation
Добавлено позднее:
Цитата (AntonSaburov @ 9.8.2005, 14:34)
Их удобно использовать, если например надо пробежать по всему DataSet и сосчитать сумму. Или еще чего. Но на это время сам DataSet не закрывается.

я и с закрытием DataSet`a использую закладки, пока проблем небыло.


Var
   b:TBookmark;
Begin
try
     b:=adoquery.getbookmark;
     adoquery.active:=false;
     adoquery.active:=true;
     if adoquery.BookMarkValid(b) then
       adoquery.gotobookmark(b)
    else
       adoquery.First;//или adoquery.last;
  finally
adoquery.FreeBookMark(b);
end;   //try
end;

ava
AntonSaburov | 09.08.2005, 14:37 #
Цитата (dsergey @ 9.8.2005, 14:30)
что, освобождать не советуешь?

Не советую использовать в том виде, в котором предлагается. Букмарка - это по сути указатель на строку в DataSet. И если его закрыли, то гарантировать, что все вернется на свои круги - нельзя.

В хелпе прямо сказано - "Allocates a bookmark for the active record in the dataset.". А когда ты закрываешь DataSet, то букмарка указывает непонятно куда. В простейшем случае открыл/закрыл это срабатывает. Но копинуть глубже и можно поиметь проблемы.
ava
Akella | 09.08.2005, 14:43 #
а если в таблице нет ключевого поля?
ava
AntonSaburov | 09.08.2005, 14:47 #
Цитата (dsergey @ 9.8.2005, 14:43)
а если в таблице нет ключевого поля?

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

Представь сколько может быть Сидоровых Николаев Алексеевичей ? Этих полей маловато. Значит придется вводить еще и адреса, телефон. Вообщем нелегкое дело. Поэтому часто вводят (я всегда так делаю) специальное поле ID (на тут как хочешь называй), которое часто делается автоинкрементным и оно для каждой записи уникально. И искать проще, и апдейтить и удалять.
ava
Akella | 09.08.2005, 15:11 #
ADO+MSSQL. Хранимка мне возвращает Поля: Нименование, цена, кол-во, цвет.
После изменения кол-ва, нужно переоткрыть НД, не потеряв тек. запись.
Как быть?
Вот я и пользуюсь закладками.

Хотя и Locate тоже пользуюсь. Я же говорю - это моё дело, чем и как пользоваться. пока не глючит, а Вот если начнёт...
ava
AntonSaburov | 09.08.2005, 15:13 #
Цитата (dsergey @ 9.8.2005, 15:11)
Я же говорю - это моё дело, чем и как пользоваться. пока не глючит, а Вот если начнёт...

Дык я не настаиваю. Просто высказываю свое мнение. А из хранимки ID тоже никто не мешает возвращать smile.

Ладно, скоро уже офтоп начнется и придет страшный модератор нам грозить. ЛУчше не будем его будить. Тс-с-с-с-с smile
ava
Петрович | 09.08.2005, 22:10 #
Я уже писал. Закладки использовать можно.
Спасибо что напомнил что есть метод BookMarkValid. Без него, я иногда получал Exception. С ним, надеюсь, Exception быть не должно.
Однако. В свое время, я вынужден был посмотреть как устроены закладки. По крайней мере реализация заклодок в ADO-компонентах такова, что на самом деле, закладка там это номер записи в наборе данных. Поэтому, если например имеется набор из шести записей:

Вася
Петя
Маша
Витя
Коля
Дима

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

Вася
Маша
Витя
Коля
Дима

Но, текущей записью станет запись Коля вместо ожидаемой Витя!
Можешь проверить.

Добавлено позднее:
Цитата (dsergey @ 9.8.2005, 16:11)
После изменения кол-ва, нужно переоткрыть НД, не потеряв тек. запись.

Вот когда речь идет именно иб изменении какой-либо записи, без изменения их количества, тогда закладки вполне подходят. Правда, если ты уверен что другой пользователь в это время не выполнил INSERT/DELETE из этой таблицы.
ava
Akella | 11.08.2005, 09:51 #
Цитата
Правда, если ты уверен что другой пользователь в это время не выполнил INSERT/DELETE из этой таблицы.

абсолютно согласен, хотя сам проверю это и сообщу.
ava
Akella | 11.08.2005, 10:07 #
Проверил. Ошибок не выдает, но активная запись становиться, та которая стояла после удаляемой, т.е. Петрович прав. :p
Добавлено позднее:
А вот если удалять последнюю запись, то обязательно "вылетит птичка" smile (Exception :exclamation ).

Вывод, можно пользоваться не закладками, а DataSet.RecNo, а рекомендательно Locate. smile=49 smile=idea
ava
Петрович | 11.08.2005, 11:29 #
Цитата (dsergey @ 11.8.2005, 11:07)
Вывод, можно пользоваться не закладками, а DataSet.RecNo, а рекомендательно Locate

smile. Про DataSet.RecNo ты случаем не описался? Как я писал, закладки на нем и построены. Соответственно, с ним те же проблемы что и с закладками.
Добавлено позднее:
Цитата (dsergey @ 11.8.2005, 11:07)
А вот если удалять последнюю запись, то обязательно "вылетит птичка"  (Exception ).

А если сначала проверять BookMarkValid ? А то, самому лень тестить smile
ava
Akella | 11.08.2005, 14:13 #
Цитата
Про DataSet.RecNo ты случаем не описался?

нет, не описАлся. Я имел ввиду, что использование закладок и RecNo - одно и тоже smile=iwashere
Добавлено позднее:
Цитата
А если сначала проверять BookMarkValid ?

а если сначала проверять, то всё будет в ажуре, иначе нафига ж нужна проверка smile
Добавлено позднее:
вопрос лишь в том, что быстрее: закладки или RecNo???
ava
Петрович | 11.08.2005, 17:48 #
Цитата (dsergey @ 11.8.2005, 15:13)
а если сначала проверять, то всё будет в ажуре, иначе нафига ж нужна проверка

Ну, нафига это вопрос риторический. На самом деле, нигде не сказано что присваивание закладки может вызвать исключение!
Например про Locate вообще явным образом сказано что возвращает False если искомая запись не найдена. Но, как я уже писал в другом топике, это иногда вызывает исключение smile.
Зарегистрируйтесь или войдите, чтобы написать.
Фирма дня
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Участники
advanced
Отправить