在Delphi中频繁存储,搜索和修改大型数据集的最佳方法

时间:2009-05-05 15:39:22

标签: delphi dataset search storage

在delphi中,创建和存储经常被搜索和修改的数据的最佳方法是什么?

基本上,我想编写一个功能,在现有数据库中搜索电话号码,并跟踪每个电话号码的使用次数,使用的第一个日期以及使用的最新日期。正在搜索的数据库基本上是已下订单的日志,其中包含用于下订单的电话号码。它不是一个SQL数据库或任何可以轻易查询的东西(它是一个旧的btrieve数据库),因此我需要创建一种获取此信息的方法(最终输出到文本文件)。

我正在考虑创建一个包含电话号码,两个日期和使用次数的记录,然后将记录添加到每个电话号码的动态数组中。然后,我将按数据条目搜索数组,查找数据库中的每条记录,以查看当前记录的电话号码是否已在数组中。然后根据需要更新或创建记录。

这似乎可行,但由于数据库中有数万个条目,它可能不是最好的方式,而且是一种相当缓慢且低效的处理方式。考虑到我可以对数据库执行有限的操作,是否有更好的方法?

有人建议使用MySQL表来跟踪数字,而不是使用数组,然后查询每个数据库记录的每个数字。这看起来更像开销!

非常感谢你的时间。

5 个答案:

答案 0 :(得分:1)

我会在一个完全断开的TClientDataset(cds)中注册聚合,并在循环中获取它们时更新这些值。如果Btrieve可以按电话号码排序,那就更好了。然后使用cds上的数据生成报告。

(如果你这样做,我建议你从Midas SpeedFix获得Andreas Hausladen' blog,以及你可以找到的其他最好的东西。)

答案 1 :(得分:1)

好的,这是一个双通老式方法,运行良好,应该可以很好地扩展(我使用这种方法对一个数百万的记录数据库一​​次,需要时间,但给出了准确的结果)。

  1. 下载并安装Turbo Power SysTools - 排序引擎 这个过程非常有效。
  2. 使用固定记录创建排序 电话号码,你将使用 这要排序。
  3. 循环记录,在每个订单中添加 电话号码排序。
  4. 完成第一次迭代后,启动 从中弹出电话号码 排序,增加一个计数器如果 电话号码与上一个号码相同 一读,否则报告 号码并清理你的柜台。
  5. 此过程也可以使用任何SQL数据库完成,但我的经验是,排序方法比管理临时表更快并生成相同的结果。

    编辑 - 您声明这是一个BTrieve数据库,为什么不在电话号码上创建一个键,对该键进行排序,然后在此表上应用第4步(下一步而不是pop )。无论哪种方式,您都需要触摸数据库中的每条记录来获取计数,索引/排序只会使您的决策过程更加轻松。

    例如,假设您有两个表,一个是customer表,结果将存储在哪里,另一个表是orders表。按相同的电话号码排序。然后在两个列表的顶部开始一个光标,然后应用以下的伪代码:

    Count := 0;
    While (CustomerTable <> eof) and (OrderTable <> eof) do
      begin
        comp = comparetext( customer.phone, order.phone );
        while (comp = 0) and (not orderTable eof) do 
          begin
            inc( Count );
            order.next;
            comp = comparetext( customer.phone, order.phone );
          end;
        if comp < 0 then
          begin
            Customer.TotalCount = count;
            save customer;
            count := 0;
            Customer.next;
          end
        else if (Comp > 0) and (not OrderTable EOF) then
          begin
            Order.Next;  // order no customer
          end;  
       end;
    
    // handle case where end of orders reached
    if (OrdersTable EOF) and (not CustomersTable EOF) then
      begin
        Customer.TotalCount = count;
        save customer;
      end;
    

    此代码的好处是可以将两个列表都移动一次。没有必要进行查找,因为两个列表的排序方式相同,只有在必要时才可以从上到下进行操作。唯一的要求是两个列表都有一些共同点(在此示例中为电话号码),并且两个列表都可以进行排序。

    我没有处理订单而没有客户的情况。我的假设是,如果没有客户,订单就不存在,并且会被忽略进行计数。

答案 2 :(得分:0)

抱歉,无法编辑我的帖子(当时未注册)。一旦迭代完数据库中的所有记录,数据将被丢弃。该功能不会经常调用。它基本上将被用来确定人们在一段时间内从我们已经拥有的记录中订购的频率,所以实际上只需要生成一个一次性列表。

数据将在创建列表期间保持不变。也就是说,在读取最后一个数据库记录之前,需要搜索所有电话号码。

答案 3 :(得分:0)

如果你要将它保留在内存中并且不想要任何花哨的东西,那么最好使用TStringList,这样你就可以使用Find函数了。查找使用Hoare的选择或快速选择,O(n)定位器。例如,定义一个类型:

type
   TPhoneData = class
      private
         fPhone:string;
         fFirstCalledDate:TDateTime;
         fLastCalledDate:TDateTime;
         fCallCount:integer;
      public
         constructor Create(phone:string; firstDate, lastDate:TDateTime);
         procedure updateCallData(date:TDateTime);
         property phoneNumber:string read fPhone write fPhone;
         property firstCalledDate:TDateTime read fFirstCalledDate write fFirstCalledDate;
         property lastCalledDate:TDateTime read fLastCalledDate write fLastCalledDate;
         property callCount:integer read fCallCount write fCallCount;
      end;

{ TPhoneData }

constructor TPhoneData.Create(phone: string; firstDate, lastDate: TDateTime);
begin
fCallCount:=1;
fFirstCalledDate:=firstDate;
fLastCalledDate:=lastDate;
fPhone:=phone;
end;

procedure TPhoneData.updateCallData(date: TDateTime);
begin
inc(fCallCount);
if fFirstCalledDate<date then fFirstCalledDate:=date;
if date>fLastCalledDate then fLastCalledDate:=date;
end;

然后填写,报告:

procedure TForm1.btnSortExampleClick(Sender: TObject);
const phoneSeed:array[0..9] of string = ('111-111-1111','222-222-2222','333-333-3333','444-444-4444','555-555-5555','666-666-6666','777-777-7777','888-888-8888','999-999-9999','000-000-0000');

var TSL:TStringList;
    TPD:TPhoneData;
    i,index:integer;
    phone:string;
begin
randseed;
TSL:=TStringList.Create;
TSL.Sorted:=true;
for i := 0 to 100 do
   begin
   phone:=phoneSeed[random(9)];
   if TSL.Find(phone, index) then
      TPhoneData(TSL.Objects[index]).updateCallData(now-random(100))
   else
      TSL.AddObject(phone,TPhoneData.Create(phone,now,now));
   end;
for i := 0 to 9 do
   begin
   if TSL.Find(phoneSeed[i], index) then
      begin
      TPD:=TPhoneData(TSL.Objects[index]);
      ShowMessage(Format('Phone # %s, first called %s, last called %s, num calls %d', [TPD.PhoneNumber, FormatDateTime('mm-dd-yyyy',TPD.firstCalledDate), FormatDateTime('mm-dd-yyyy',TPD.lastCalledDate), TPD.callCount]));
      end;
   end;
end;

答案 4 :(得分:0)

我建议使用DeCAL's(在sf.net上)DMap将项目存储在内存中,而不是TStringList。您可以指定手机是键并存储包含记录其余部分的记录/类结构。

所以你的Record课程将是:


  TPhoneData = class
    number: string;
    access_count: integer;
    added: TDateTime.
     ...
  end;

然后在代码中:


  procedure TSomeClass.RegisterPhone(number, phoneData);
  begin
    //FStore created in Constructor as FStore := DMap.Create;
    FStore.putPair([number, phoneData])
  end;
  ...
  procedure TSoemClass.GetPhoneAndIncrement(number);
  var
    Iter: DIterator;
    lPhoneData: TPhoneData;
  begin
    Iter := FStore.locate([number]);
    if atEnd(Iter) then
      raise Exception.CreateFmt('Number %s not found',[number])
    else
    begin
      lPhoneData := GetObject(Iter) as TPhoneData;
      lPhoneData.access_count = lPhoneData.access_count + 1;
      //no need to save back to FStore as it holds a pointer to lPhoneData
    end;
  end;

DMap实现红/黑树,因此数据结构可以免费为您排序。您也可以使用DHashMap获得相同的效果和(可以说)提高速度。

DeCAL是我最喜欢的数据结构库之一,并建议任何人进行内存存储操作来查看。

希望有所帮助