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


Автор: Dayana 2.10.2003, 17:39
Есть TQuery. Как узнать номер активной на данный момент строки запроса? Т.е. номер строки, где сейчас стоит указатель запроса?

Автор: x77 2.10.2003, 17:48
никак. может быть в каких-то субд есть специально заточенные под это средства, в чём я сомневаюсь, но по идее - никак. в лучшем случае можно определить порядковый номер записи, отображаемый в гриде, можно, добраться до номера записи (индекса), под которым она храниться в буфере TDataSet, но что касается физического порядка хранения записей на сервере - мне кажется, это вообще чёрный ящик. переформулируйте задачу. может стоит клать нумерацию ручками в поле таблицы и потом просто просматривать это поле?

Автор: x77 2.10.2003, 17:49
я правльно понимаю, что под запросом имеется в виду результирующий набор данных, а под указателем - текущая запись?

Автор: Dayana 2.10.2003, 17:54
Мне именно и нужен порядковый номер текущей записи в результирующем наборе данных... мне не нужен физический порядок на сервере...

Автор: pigas 3.10.2003, 04:54
это с-во recno:integer /

Автор: pigas 3.10.2003, 04:55
или в этом духе

Автор: pigas 3.10.2003, 04:56
ок

Автор: x77 3.10.2003, 08:42
это я и имел в виду - RecNo. но вот вчера попробовал... пакость, однако, получается, господа.

RecNo - это действительно номер записи, на которой стоит курсор (DBI - курсор, не квери и тем более не грида!) но квери начитывает записи по несольку штук в буфер. в итоге значение RecNo скачет безобразным образом: открыли квери - 1, пролистали страницу - 10, ещё страницу - 20 и т.д. и самое обидное, что он прав - текущая физическая запись такая и есть. но мы то видим запись из буфера smile.gif и какой у неё номер - мне лично выкупить не удалось. существует тип (рекорд), описывающий каждую запись. в нём для каждой записи прошит номер последовательности либо номер самой записи. я вытаскивал эту запись через крэк класса, приводил к ней текущее значение буфера - и всё равно нефига smile.gif

так что если кто-то мне объяснит, как дорваться до номера записи в кэше bde - с меня пиво smile.gif

Автор: p0s0l 3.10.2003, 09:41
Цитата
существует тип (рекорд), описывающий каждую запись

Что за тип ?

Автор: x77 3.10.2003, 10:44
DbTables.pas
Код
 PRecInfo = ^TRecInfo;
 TRecInfo = packed record
   RecordNumber: Longint;
   UpdateStatus: TUpdateStatus;
   BookmarkFlag: TBookmarkFlag;
 end;


с буфером bde работает именно через этот тип.

Автор: Cashey 3.10.2003, 13:23
x77, не понимаю о чем ты. Св-во RecNo возвращаетномер записи не зависимо от того в квери оно или в тэйбдле. И в квери оно вернет номер текущей записи резулбтирующего набора данных, что кажется и требуется. И кэш здесь сам по себе не причем.

Автор: p0s0l 3.10.2003, 13:44
Думаю также, как и Cashey...
Сделал table, query, вывел запрос в грид. RecNo всегда показывает ту запись, которая выбрана в гриде. Если просто в цикле делать next или менять RecNo, тоже нормально всё. Я не понял, что имел в виду x77 тут:
Цитата
текущая физическая запись такая и есть. но мы то видим запись из буфера

где видим и что ?

Автор: x77 3.10.2003, 14:14
у меня он не является номером текущей записи. сначала равен одному. листаю грид. спустя сколько-то там строк становится равным десяти. дохожу до конца грида. делаю вставку. -1. отменяю вставку - 28. т.е. он меняется скачкообразно, по мере того, как квери начитывает данные из таблицы в буфер. и становится равным номеру последней, начитанной в буфер записи. что это за фигня - я пока не знаю, сейчас эксперементировать - времени нет, вечером разберусь и отвечу.

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

Автор: x77 3.10.2003, 17:29
кажется, есть. на *.db, *.dbf всё так, как полагается - в RecNo живёт номер текущей записи. а вот на Ib - полная фигня.
Код
procedure TForm1.Query1AfterScroll(DataSet: TDataSet);
begin
 Label1.Caption := IntToStr (Query1.RecNo);
end;


чушь это, конечно, работать с Ib через BDE, но тем не менее.

Автор: Dayana 3.10.2003, 17:54
Вот и я о том же... пробовала через RecNo... обрабатываю на событие OnCalcFields (или почти так называется), считываю RecNo, а он мне -1 всегда возвращает. Думаю, RecNo содержит номер выделеной строки на db гриде. А OnCalcFields с выделением строки в db гриде не связано.
Задача у меня такая. В db гриде надо вывести колонку с нумерацией всех строк. Я подумала, что самое простое это завести поле в TQuery, которое калькулейтит, и в OnCalcFields заносить туда номер текущей записи. Оказалось проблема с номером текущей записи. Но, и по-другому, как пересчитать строки я до сих пор не знаю. Ведь в db гриде напрямую добраться к ячейки нельзя (как, например, в string гриде). А квери у меня большой, и делать его еще больше, чтобы он сам возвращал номер строки не хочется, т.к. я выяснила, что это не так уж и тривиально. Может есть мысли на этот счет?

Автор: x77 3.10.2003, 18:39
для Ib у меня сейчас получилось так. я завёл генератор (my_gen_id), в основной запрос Query1 добавил его вызов

Код
select GEN_ID (my_gen_id, 1), comments
 from data


т.е. при каждом переоткрытии таблицы он начитывает номера полей из самого запроса. безотказная фишка. есть только одно но: значение генератора каждый раз увеличивается. поэтому я завёл ещё одну TQuery с таким SQL:

Код
SET GENERATOR MY_GEN_ID TO 0


и повесил обработчик на TQuery1.BeforeOpen:

Код
procedure TForm1.Query1BeforeOpen(DataSet: TDataSet);
begin
 Query2.ExecSQL;
end;


т.е. перед каждым открытием Query1 генератор сбрасывается в ноль. всё классно работает smile.gif

Автор: Dayana 3.10.2003, 18:54
x77, спасибо, я попробую... только в воскресенье буду на работе, тогда попробую...
и, честно говоря, я не знаю, что такое генератор и как его заводить?
и можно ли в Oracle заводить генератор?

Автор: x77 3.10.2003, 19:11
у меня оракла нет sad.gif а так, навскидку, я не помню. но возможность там такая однозначно есть, до выходных постараюсь уточнить, если никто раньше не напишет smile.gif

Автор: Fantasist 3.10.2003, 21:13
Цитата
Может есть мысли на этот счет?



У меня мысля такая: во-первых, выбросить dbGrid или найти нормальный. smile.gif Я эти db control'ы вообще не очень люблю, проблем с ними много. Во-вторых, делать не live query. То есть возвращать статический набор данных. Тоже предпочитаю так делать: запрос - отображение данных, редактирование их пользователем - запрос на изменение данных. Не уверен, правда, что это решит проблему с RecNo, даже скорее всего нет, но кто знает?

Вторая мысля - создать свой компонент наследованный от стандартного и в ключить в него свое свойство, которое уже считать самому при гулянии по квере. Так же можно попытаться контролировать номер строки в квере через событие OnScroll (кажется так), тоже через свою переменную.

Пока больше идей нет. smile.gif

Автор: Cashey 3.10.2003, 21:51
Ты с Oracle из дельфи работаешь через BDE?

Автор: Cashey 3.10.2003, 23:03
... если через BDE.
Значит делаешь так (если таблица известна на этапе проектирования):
1. В св-ве Query1.DatabaseName проставляешь имя БД, зарегистрированной в BDE.
2. Создаешь в Query1.SQL, набор команд, например
Код
select fieldname1 as fieldname1, fieldname2 as fieldname2, ... , fieldnameN as fieldnameN
from <tablename>

3. Создаешь в TQuery набор полей, соответствующий вышеприведенному запросу (двойной щелчок мыши на компоненте Query1, далее в открывшемся окне жмешь правую кнопку мыши и выбираешь пункт Add Fields (Add All Fields), во вновь открывшемся окне, если все поля синие, жми ОК), далее создаешь еще одно поле, New Field, назови 'num', тип Integer и Field Type - Calculated.
4. В гриде создаешь колонку (практически так же как и в TQuery) и связываешь его с этип полем.
5. В обработчике событий Query1CalcFields, пишешь следующее:
Код
Query1num.AsInteger := query1.RecNo;

6. Проверь, что б св-во Query1.AutoCalcFields было в true.
Все, после
Код
query1.Active := true;
у тебя будет грид с порядковой нумерацией результирующего набора данных. Вариант плох тем, что надо заранее знать структуру таблицы и, главное, сам запрос. Но я уверен, что и это преодолимо, может не просто, но преодолимо, щас на выходных из интереса попытаюсь добить, и если потребуется, вышлю демку.

PS
Цитата
Вот и я о том же... пробовала через RecNo... обрабатываю на событие OnCalcFields (или почти так называется), считываю RecNo, а он мне -1 всегда возвращает. Думаю, RecNo содержит номер выделеной строки на db гриде. А OnCalcFields с выделением строки в db гриде не связано.

Не знаю как себя введет Оракл, но можно прописать так
Код
Query1num.AsInteger := query1.RecNo + 1;

Цитата
Задача у меня такая. В db гриде надо вывести колонку с нумерацией всех строк. Я подумала, что самое простое это завести поле в TQuery, которое калькулейтит, и в OnCalcFields заносить туда номер текущей записи. Оказалось проблема с номером текущей записи.

В общем, ты на правильном пути, только у меня все тип-топ, могу демку выслать, если не веришь smile.gif

Автор: Dayana 3.10.2003, 23:36
Cashey, все было сделано так, как ты описал, а RecNo = -1.... если у тебя все хорошо, то я даже и не знаю, где искать... И вышли демку, если не сложно. :-)

Fantasist, идеи очень хорошие, но проблема в том, что проект уже готовый, а мне приходится к нему дописывать. Соответственно делать такие вещи, как менять грид и писать свой компонент, нельзя... (нет на это времени и.... могут уволить, а я только 4 дня работаю...).
Наверное придется сделать поле не fkCalculate, a fkData, и попробовать заполнить его на этапе открытия квери в цикле, проходясь по всем строчкам.
Интересно получится что-нибудь или нет?

Автор: Cashey 3.10.2003, 23:44
Цитата
Наверное придется сделать поле не fkCalculate, a fkData, и попробовать заполнить его на этапе открытия квери в цикле, проходясь по всем строчкам.
Интересно получится что-нибудь или нет?

Точно не получится, поле fkData, может бать только привязана к конкретному полю в таблице, а св-во Value в fkCalculate доступно только для чтения.
Дай мне названия и тип полей в твоей таблице, я тебе вышлю свою прожку, если в ней все будет ОК значит: не все делала как я писал.

Автор: Dayana 5.10.2003, 10:20
Cashey, у меня не одна таблица, квери берет данные из нескольких таблиц... Да в любом случае, пишу отдельный пример, все равно не получается... все равно -1...

Автор: Dayana 5.10.2003, 13:09
Cashey, ну вот например такой квери:
Код

select cc, id, nname, p_name from client c

где
сс varchar2[2]
id varchar2[10]
nname varchar2[15]
p_name varchar2[15]

вышли мне свой код, плиз...

Автор: x77 5.10.2003, 16:58
Cashey, все твои варианты прокатывают только для парадокса/дэбэйз/etc. я и сам на это поймался. через RecNo работать с ib/oracle вообще нельзя (почему - я не знаю, но оно всегда равно -1 или вообще чему-то левому). самое реальное и быстрое для oracle с минимум изменений в коде - это завести генератор и добавить его вызов в select. а перед открытием таблицы - обнулять этот генератор. в оракле генераторами являются "последовательности" - sequence. вот там и надо копать.

Автор: Dayana 5.10.2003, 17:18
x77, сделала через генератор (sequence)... все хорошо... а "начальство" сказало, что это плохой стиль... :-(( короче не нравится так.... надо делать по-другому...
Есть еще такая штука RxMemoryData. Туда загружаешь весь квери и можно работать с ним как с квери, и при этом получается тот же квери, но еще и writable. Можно, например после открытия, в цикле заполнить поле для нумерации. Т.е. сделать то, что нельзя с обычным квери. Но с этим у меня тоже проблема, т.к. в этом случае надо переписать много кода. Т.е. не переписать, а заменить в коде обработку существующего квери на новый RxMemoryData. Предложу тому же "начальству". Хотя уверена, что это тоже не подойдет....
Извиняюсь за оффтоп, но просто ужасно начинать работать с готовым проектом, когда над тобой стоит кто-то, кто хочет, что бы писалось так, как он хочет... а не так, как ты считаешь нужным... В этом случае перестаешь понимать, умеешь ли программировать вообще... вот...

Автор: x77 5.10.2003, 17:24
Цитата
а "начальство" сказало, что это плохой стиль


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

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

Автор: x77 5.10.2003, 18:21
сделал откровенно идиотский вариант, через закладки. работает, но... в общем, смотри сама. я завёл в квери одно левое поле, Number, типа fkCalculated. реально туда ничего не ложится, оно используется только для того, чтобы отрисовать порядковый номер записи.


Код
 TForm1 = class(TForm)
   DataSource1: TDataSource;
   DBGrid1: TDBGrid;
   Query1: TQuery;
   Query1Number: TIntegerField;
   Button1: TButton;
   ....
   procedure Query1AfterOpen(DataSet: TDataSet);
   procedure Query1AfterClose(DataSet: TDataSet);
   procedure DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
     DataCol: Integer; Column: TColumn; State: TGridDrawState);
   procedure Button1Click(Sender: TObject);
   procedure FormClose(Sender: TObject; var Action: TCloseAction);
 private
   { Private declarations }
   fBookmarks: array of TBookmark; // массив закладок
   fBookmarkCount: integer; //кол-во закладок
 public
   { Public declarations }
 end;

...

implementation

// заполняем массив: на каждую запись получаем закладку
procedure TForm1.Query1AfterOpen(DataSet: TDataSet);
begin
 Query1.DisableControls;
 try
   Query1.First;
   fBookmarkCount := 0;
   while not Query1.Eof do begin
     Inc (fBookmarkCount);
     SetLength (fBookmarks, fBookmarkCount);
     fBookmarks [fBookmarkCount - 1] := Query1.GetBookmark;
     Query1.Next;
   end;
   Query1.First;
 finally
   Query1.EnableControls;
 end;
end;

// освобождаем массив
procedure TForm1.Query1AfterClose(DataSet: TDataSet);
var
 i: integer;
begin
 for i := 0 to fBookmarkCount - 1 do
   Query1.FreeBookmark (fBookmarks [i]);
 Finalize (fBookmarks);
end;

// отрисовываем нумера:
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
 DataCol: Integer; Column: TColumn; State: TGridDrawState);
var
 bm: TBookmark;
 i: integer;
begin
 // если это не наше поле - сваливаем
 if Column.FieldName <> 'Number' then
   Exit;
 bm := Query1.GetBookmark;
 try
   for i := 0 to fBookmarkCount - 1 do begin
     // находим нужную закладку. её индекс в массиве - это и есть порядковый номер записи
     if Query1.CompareBookmarks (bm, fBookmarks[i]) = 0 then begin
       DbGrid1.Canvas.TextOut(Rect.Left + 2, Rect.Top + 1, IntToStr (i + 1));
       Break;
     end;
   end;
 finally
   Query1.FreeBookmark (bm);
 end;
end;

// для информации. у меня получилось по три кило на закладку :(
procedure TForm1.Button1Click(Sender: TObject);
begin
 ShowMessage (IntToStr (fBookmarkCount) + ' * ' + IntToStr ( TFake (Query1).BookmarkSize));
end;

// обязательно закрываем квери, чтобы отработало совобождение массива
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 Query1.Active := FALSE;
end;

end.


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

Автор: x77 5.10.2003, 18:23
да, забыл:

Код
type
 TFake = class (TDataset);


это чтобы добраться до proected property BookmarkSize DataSEt'а. в проекте это можно просто выбросить, вместе с обработчиком Button1Click.

Автор: Dayana 5.10.2003, 18:35
x77, идея понятна... А можно ли обработку букмарков засунуть не в отрисовку грида, а в OnCalcFiled квери, и заполнять калькулируемое поле Number?

Автор: x77 5.10.2003, 18:50
нельзя. OnCalc происходит не для какой-то конкретной записи, а для TDataset'а в целом. т.е. тебе понадобится в обработчике пробегать всю квери от первой до последней записи, попутно заполняя Number. я не уверен, что из этого может выйти что-то путное, но тормоза будут дикие однозначно.

Автор: x77 5.10.2003, 23:57
Fantasist, наследовать от стандартного - ты запаришься smile.gif все бде-шные датасеты работают исключительно через dbi-энжин. она возвращает уже готовую, начитанную квери (hDBICUR). а всё остальное служит для накрутки второстепенного функционала, переопределить же первичный функционал даже наследуясь от TComponent и переписав TDataSet не удасться. в том-то вся пакость: загруз квери происходит не на уровне vcl, а на уровне bde.

в общем, привет smile.gif

Автор: gray_k 6.10.2003, 09:06
Можно сделать FetchAll, и после этого смотреть RecNo. В этом случае в памяти будут все записи запроса, а не только те, которые видно в гриде. Тогда RecNo скакать не должен.

Автор: x77 6.10.2003, 09:15
попробовал для ib. RecNo = -1.

Автор: gray_k 6.10.2003, 09:49
Попробовал, всё работает.

Автор: x77 6.10.2003, 09:55
если есть возможность, сбрось мне свой экзешник (x77@pisem.net)

Автор: Dayana 7.10.2003, 12:23
Привет всем!

Что получилось... Самым подходящим способом, который придумался, оказалось создать в db гриде поле, например Num, и в методе OnDrawColumnCell, с которым приходит TRect, высчитать номер строки...
НО... оказалось все намного проще... Оказалось можно сделать так, на что я расчитывала в самом начале, но в инете не нашла об этом инфы. В Оракле есть такая штука, как ROWNUM, которая возвращает номер строки в результирующем наборе данных... Это же так логично... только я убила 2 дня, чтобы это найти...

Автор: Cashey 7.10.2003, 12:29
Dayana рад за тебя, тока с RecNo, что так не чего и не получилось?

Автор: x77 7.10.2003, 13:05
Dayana, зато повеселились smile.gif

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

Автор: Dayana 7.10.2003, 13:23
Cashey, нет... не работает... да мне все здесь (те, кто давно с Ораклом работает) твердят, что не будет RecNo работать....

x77, ага... очень было весело... :-)

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