Начало » Использование СУБД » Firebird, HQbird, InterBase » IBX: буферизация записей 
	| 
		
 |  
	
		
		
			| Re: IBX: буферизация записей [сообщение #1508 является ответом на сообщение #1506] | 
			Sun, 29 January 2023 11:41    | 
		 
		
			
				
				
				  | 
					
						  
						Док
						 Сообщений: 101 Зарегистрирован: June 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		МорскойДесант писал(а) Sun, 29 January 2023 03:36Что-то меня сомнения терзать начали.  
А скажи, что значит "дерево загружается"? Ты что, записи из мемдатасета ещё и "внутрь" дерева переносишь?  
Покажи, если не жалко. 
Не жалко, за свой говнокод стыдно   
 
По поводу мемдатасета - это просто кеш, с которым можно делать все, что угодно. И заполняется он достаточно быстро. И главное, это не визуальный компонент(можно работать в доп.потоке без прокладок) и не требует коннекта для доступа к БД. 
 
Я попробовал загрузку напрямую из IBSQL в дерево. 
  
Начало: 
const
  MinCount = 100000;
var
  Form1: TForm1;
implementation 
...
procedure TForm1.Button2Click(Sender: TObject);
var
  ExecSQL: TIBSQL = Nil;
  startTick: PtrInt = 0;
  endTick: PtrInt = 0;
  MyThread: TMyThread = nil;
  i: Integer;
  rNode: PVirtualNode = nil;
  rNodeData: PMyRec = nil;
begin
  for i:= 0 to Pred(StatusBar1.Panels.Count) do
    StatusBar1.Panels[i].Text:= '';
  if not IBDatabase1.Connected then IBDatabase1.Connected:= True;
  ExecSQL:= TIBSQL.Create(Self);
  try
    try
      startTick:= GetTickCount64;
      IBTransaction1.StartTransaction;
      ExecSQL.Database:= IBDatabase1;
      ExecSQL.Transaction:= IBTransaction1;
      ExecSQL.SQL.Text:= 'SELECT COUNT(ID) CNT FROM TEST';
      ExecSQL.ExecQuery;
      if (ExecSQL.FieldByName('CNT').AsInteger > MinCount) then
      begin
        MyThread:= TMyThread.Create(True);
        MyThread.Start;
      end;
      ExecSQL.SQL.Text:= 'SELECT ID, NAME FROM TEST';
      ExecSQL.ExecQuery;
      VST_partial.BeginUpdate;
      try
        VST_partial.Clear;
        while (ExecSQL.RecordCount < Succ(MinCount)) do
        begin
          rNode:= VST_partial.AddChild(nil);
          rNodeData:= VST_partial.GetNodeData(rNode);
          rNodeData^.ID:= ExecSQL.FieldByName('ID').AsInteger;
          rNodeData^.Name:= ExecSQL.FieldByName('NAME').AsString;
          VST_partial.AddChild(rNode);
          ExecSQL.Next;
        end;
      finally
        VST_partial.EndUpdate;
        VST_partial.ScrollIntoView(rNode,True);
        VST_partial.AddToSelection(rNode);
        VST_partial.Expanded[rNode]:= False;
      end;
      endTick:= GetTickCount64;
      StatusBar1.Panels[0].Text:= Format('Executing time of inserting %d records into the VST_partial is %d msec',
                                    [MinCount, (endTick - startTick)]);
      StatusBar1.Panels[0].Width:= StatusBar1.Canvas.TextWidth(StatusBar1.Panels[0].Text)
                        + StatusBar1.Canvas.TextWidth('W');
      IBTransaction1.Commit;
    except
      on E:Exception do
      begin
        IBTransaction1.Rollback;
        {$IFDEF MSWINDOWS}
        ShowMessage(WinCPToUTF8(E.Message));
        {$ELSE}
        ShowMessage(E.Message);
        {$ENDIF}
      end;
    end;
  finally
    FreeAndNil(ExecSQL);
  end;
end;  
Доп.поток: 
procedure TMyThread.Execute;
begin
  try
    if not Fdbase.Connected then Fdbase.Connected:= True;
    FTrans.StartTransaction;
    FexecSQL.ExecQuery;//SQL.Text:= 'SELECT ID, NAME FROM TEST';
    Form1.VST_full.Clear;
    while not FexecSQL.Eof do
    begin
      Synchronize(@AddNodeToExtVST);
      Sleep(1);
      FexecSQL.Next;
    end;
    FTrans.Commit;
  except
    on E:Exception do
    begin
      FTrans.Rollback;
      {$IFDEF MSWINDOWS}
      ShowMessage(WinCPToUTF8(E.Message));
      {$ELSE}
      ShowMessage(E.Message);
      {$ENDIF}
    end;
  end;
end; 
procedure TMyThread.AddNodeToExtVST;
var
  aNode: PVirtualNode = nil;
  aNodeData: PMyRec = nil;
begin
  with Form1 do
  begin
    //VST_full.BeginUpdate;
    try
      aNode:= VST_full.AddChild(nil);
      aNodeData:= VST_full.GetNodeData(aNode);
      if Assigned(aNodeData) then
      begin
        aNodeData^.ID:= FexecSQL.FieldByName('ID').AsInteger;
        aNodeData^.Name:= FexecSQL.FieldByName('NAME').AsString;
        VST_full.AddChild(aNode);
      end;
    finally
      //VST_full.EndUpdate;
      //VST_full.ScrollIntoView(aNode,True);
    end;
  end;
end;  
Справа дерево заполняется в потоке 
  
		
	- 
	
 
	Вложение: vst_1.gif
	 
	(Размер: 241.79KB, Загружено 2495 раз)
 
 
		
  FPC/Lazarus (trunk) | Win10 x64 Ultim/Debian 11 amd64/Darwin x86_64 Monterey | Firebird 3.0.10 x64 | IBX by TonyWhyman 
 
https://zoltanleo.blogspot.com/
		[Обновления: Sun, 29 January 2023 12:40] Известить модератора  
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	
		
		
			| Re: IBX: буферизация записей [сообщение #1516 является ответом на сообщение #1515] | 
			Mon, 30 January 2023 18:26    | 
		 
		
			
				
				
				
					
						  
						
						 Сообщений: 204 Зарегистрирован: September 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		>нет там мемдатасета. 
TIBSQL, точно, я невнимателен. 
 
>Насчёт дерева: покажи, как правильно? 
Выше писал: 
Цитата: 
сообщаешь ему, что у тебя в выборке 1 млн записей. Этого достаточно, чтобы дерево правильно отображало скролл. 
Дерево хочет показать тебе данные и сразу выдает коллбэк: "А покажи мне данные узлов номер 1, 2, 3 ... <номер последнего видимого узла>".  
 
Т.е., ты не загружаешь в NodeData, не создаешь список в самом vst,  а сообщаешь ему, что "вот столько узлов", и всё. А при отображении, vst запрашивает подробности.
		
		
		
 |  
	| 
		
	 | 
 
 
 |  
	
		
		
			| Re: IBX: буферизация записей [сообщение #1524 является ответом на сообщение #1516] | 
			Wed, 01 February 2023 15:36    | 
		 
		
			
				
				
				  | 
					
						  
						Док
						 Сообщений: 101 Зарегистрирован: June 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		МорскойДесант писал(а) Mon, 30 January 2023 18:26Т.е., ты не загружаешь в NodeData, не создаешь список в самом vst,  а сообщаешь ему, что "вот столько узлов", и всё. А при отображении, vst запрашивает подробности. 
Вообще-то, я ждал от тебя пример кода, хотя бы схематично. В результате, пришлось, как обычно, делать самому о_О 
 
  
 
Сорцы сложил сюда 
https://bitbucket.org/zoltanleo/vtv_fill/src/main/
		
	- 
	
 
	Вложение: vst_2.gif
	 
	(Размер: 96.50KB, Загружено 2295 раз)
 
 
		
  FPC/Lazarus (trunk) | Win10 x64 Ultim/Debian 11 amd64/Darwin x86_64 Monterey | Firebird 3.0.10 x64 | IBX by TonyWhyman 
 
https://zoltanleo.blogspot.com/
		
 |  
	| 
		
	 | 
 
 
 |  
	
		
		
			| Re: IBX: буферизация записей [сообщение #1527 является ответом на сообщение #1524] | 
			Wed, 01 February 2023 18:04    | 
		 
		
			
				
				
				
					
						  
						
						 Сообщений: 204 Зарегистрирован: September 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		Док писал(а) Wed, 01 February 2023 15:36МорскойДесант писал(а) Mon, 30 January 2023 18:26Т.е., ты не загружаешь в NodeData, не создаешь список в самом vst,  а сообщаешь ему, что "вот столько узлов", и всё. А при отображении, vst запрашивает подробности. 
Вообще-то, я ждал от тебя пример кода, хотя бы схематично. В результате, пришлось, как обычно, делать самому о_О 
... 
Ну, хорошо. Смотри аттачмент tstVST.7z. 
Streets.fbk - архив файла базы, восстановишь сам. Две таблички: city (id, name) и street (id, city_id, name).  
Выборка улица + город:  
select S.ID s_id, S.NAME s_name, C.NAME c_name from STREET S
join CITY C on C.ID = S.CITY_ID
  
155 000 записей в выборке. 
--- 
Программка на Delphi. VST, FIB+ (заменишь на ibx сам, у меня таких бананов нет).  
В константах указана строка коннекта с базой и путь к клиентской библиотеке, заменишь на свои: 
const 
// Строка коннекта
  c_dbn = '127.0.0.1/3100:D:/Trash/tstVST/streets.fdb';
// Путь к библиотеке fbclient.dll
  c_lbn = 'D:/Program Design/Delphi2007/EstExe/bcSupport/fbClient/fbclient.dll';
  
В vst дереве - два столбца: название улицы [0] и название города [1].  
Инициализация дерева: с помощью qSelectIds (TpFIBQuery)  заливаем все идентификаторы улиц  в массив 
Код: 
 
procedure TForm1.LoadIds;
var
  i: Integer;
begin
  qSelectIds.ExecQuery;
  SetLength(fIds, 10000);
  i := 0;
  while not qSelectIds.Eof do
  begin
    if i = Length(fIds) then
      SetLength(fIds, Length(fIds) + 10000);
    fIds[i] := qSelectIds.Fields[0].AsInteger;
    Inc(i);
    qSelectIds.Next;
  end;
  SetLength(fIds, i);
end;
  - можно ускорить, если очень захочется, разными способами, хоть в отдельной нити или выбирать одним блоком/блобом и т.д. и т.п. 
 
Сообщаем дереву, сколько у нас записей: 
  vstMain.RootNodeCount := Length(fIds);
  
Всё. В событии OnGetText дерево запрашивает, что показывать.  
Выбираем значения полей в qSelectAll (TpFIBQuery) 
select S.NAME, C.NAME
from STREET S
join CITY C on C.ID = S.CITY_ID
where S.ID = :ID   
  
Код (без кэширования): 
 
procedure TForm1.vstMainGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
begin
  if not Assigned(Node) then Exit;
  if not (Column in [0, 1]) then Exit;
  if qSelectAll.Params[0].AsInteger <> fIds[Node.Index] then
  begin
    qSelectAll.Params[0].AsInteger := fIds[Node.Index];
    qSelectAll.ExecQuery;
  end;
  CellText := qSelectAll.Fields[Column].AsString;
end;
 
Дабы не травмировать тонкие струны, перед обращением к qSelectAll лезем в кэш который, ты пишешь сам. Например, используя разреженные массивы или словари из https://github.com/fundamentalslib/fundamentals5  
 
  
 
 
 
 
		
	- 
	
 
	Вложение: tstVST.7z
	 
	(Размер: 284.98KB, Загружено 707 раз)
 - 
	
 
	Вложение: Безымянный.png
	 
	(Размер: 16.86KB, Загружено 2143 раза)
 
 
		
		[Обновления: Wed, 01 February 2023 18:15] Известить модератора  
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	| 
		
 |  
	
		
		
			| Re: IBX: буферизация записей [сообщение #1533 является ответом на сообщение #1531] | 
			Thu, 02 February 2023 00:47    | 
		 
		
			
				
				
				
					
						  
						
						 Сообщений: 204 Зарегистрирован: September 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		Док писал(а) Wed, 01 February 2023 21:58Т.е. ты на отображение каждого узла вызываешь ExecQuery?     
Какой-то чат-ГПТ без обратной связи, честное слово. Я тебе про принципы работы с vst, а ты на какую-то фигню смотришь.  
Вообще-то, в приведенном коде запрос может быть отправлен даже не для каждого узла, а для каждого поля и вообще сдуру можно и член сломать.  
Про кэш я уже три раза писал. И - запрос может быть не для каждого узла, а для набора отображаемых в данный момент узлов (тех, которых нет в кэше, конечно) - включи фантазию. Я вот тестировал - при включенном кэше не ощутил разницы, по одной записи тянуть, или по 40 штук за раз (если запросы препарированные), именно для данной задачи. Прикинь, даже без кэша нормально, если не совсем тухлая локалка и юзеров всего пара десятков, но что мешает добавить кэш? 
		
		
		
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	| 
		
 |  
	
		
		
			| Re: IBX: буферизация записей [сообщение #1538 является ответом на сообщение #1533] | 
			Thu, 02 February 2023 13:40    | 
		 
		
			
				
				
				  | 
					
						  
						Док
						 Сообщений: 101 Зарегистрирован: June 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		МорскойДесант писал(а) Thu, 02 February 2023 00:47Док писал(а) Wed, 01 February 2023 21:58Т.е. ты на отображение каждого узла вызываешь ExecQuery?     
Какой-то чат-ГПТ без обратной связи, честное слово. Я тебе про принципы работы с vst, а ты на какую-то фигню смотришь.  
Вообще-то, это должны быть мои слова     
 
Я выложил готовый проект, где данные только отображаются деревом (из датасета, как и предложил ПВП), и приложил гифку на 15 сек (которую, похоже, ты не досмотрел до конца), где первые 1000 записей фетчатся/отображаются в основном потоке, а оставшиеся почти 1 000 000 записей фетчатся/отображаются в доп.потоке. Но ты не вник   
 
Ладно, в любом случае, спасибо тебе за участие и идеи. Я и правда не знал, что VTV может просто отображать записи без хранения    
		
		
  FPC/Lazarus (trunk) | Win10 x64 Ultim/Debian 11 amd64/Darwin x86_64 Monterey | Firebird 3.0.10 x64 | IBX by TonyWhyman 
 
https://zoltanleo.blogspot.com/
		[Обновления: Thu, 02 February 2023 13:43] Известить модератора  
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	
		
		
			| Re: IBX: буферизация записей [сообщение #1564 является ответом на сообщение #1501] | 
			Mon, 06 February 2023 12:24   | 
		 
		
			
				
				
				
					
						  
						pastor
						 Сообщений: 100 Зарегистрирован: June 2022  Географическое положение: Калуга
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		Док писал(а) Sat, 28 January 2023 18:56 
1. искать записи по меморидатасету проще, чем писать функции поиска по VTV - посмотрю 
2. тогда при скроле придется отслеживать, достигли ли мы последний(нижний) узел дерева, и фетчить записи, начиная с определенного ID. А если ранее загруженные записи были изменены, то в целом данные в дереве уже будут не актуальны. Проще обновлять мемдатасет и грузить актуальные записи. К тому же, для экспериментов я беру самые экстремальные условия: без фильтрации, огромное кол-во записей - в реальности вряд ли такое встретится 
 
и поиск быстрый и фильтр тоже быстрый. 
		
		
		
 |  
	| 
		
	 | 
 
 
 |   
Переход к форуму:
 
 Текущее время: Tue Nov 04 06:11:48 GMT+3 2025 
 Общее время, затраченное на создание страницы: 0.01937 секунд 
 |