Лучший метод частого хранения, поиска и изменения большого набора данных в Delphi - PullRequest
2 голосов
/ 05 мая 2009

Как лучше всего было бы в Delphi создавать и хранить данные, которые часто будут искать и изменять?

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

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

Кажется, это будет работать, но, поскольку в базе данных есть десятки тысяч записей, это может быть не лучшим способом, а довольно медленным и неэффективным способом ведения дел. Есть ли лучший способ, учитывая ограниченные действия, которые я могу выполнять с базой данных?

Кто-то предложил, чтобы вместо использования массива использовалась таблица MySQL для отслеживания чисел, а затем запрашивать каждое число для каждой записи базы данных. Это похоже на еще большие накладные расходы!

Большое спасибо за ваше время.

Ответы [ 5 ]

1 голос
/ 05 мая 2009

Хорошо, вот метод старой школы с двойным проходом, который работает хорошо и должен хорошо масштабироваться (я однажды использовал этот подход против многомиллионной базы данных записей, это заняло время, но дало точные результаты).

  1. Скачать и установить Turbo Power SysTools - двигатель сортировки работает очень хорошо для этого процесса.
  2. создать сортировку с фиксированной записью номера телефона, который вы будете использовать это сортировать.
  3. Зацикливайте свои записи, в каждом заказе добавляйте номер телефона для сортировки.
  4. Как только первая итерация завершена, начните выскакивают телефонные номера из сортировать, увеличивать счетчик, если номер телефона такой же, как последний один читать, в противном случае сообщить номер и очистить счетчик.

Этот процесс также можно выполнить с любой базой данных SQL, но мой опыт показывает, что метод сортировки быстрее, чем управление временной таблицей, и выдает те же результаты.

РЕДАКТИРОВАТЬ - Вы заявили, что это база данных BTrieve, почему бы просто не создать ключ по номеру телефона, отсортировать по этому ключу, а затем применить шаг 4 к этой таблице (следующий вместо всплывающего окна) , В любом случае вам нужно будет коснуться каждой записи в вашей базе данных для подсчета, индекс / сортировка просто облегчит процесс принятия решения.

Например, предположим, что у вас есть две таблицы, одна таблица покупателя, где будут храниться результаты, а другая таблица заказов. Сортировать оба по одному номеру телефона. Затем наведите курсор вверху обоих списков и примените следующий псевдокод:

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;

Преимущество этого кода состоит в том, что он выполняет оба списка один раз. Нет необходимости в поиске, так как оба списка отсортированы одинаково, их можно перемещать сверху вниз, предпринимая действия только при необходимости. Единственное требование состоит в том, что оба списка имеют что-то общее (в этом примере номер телефона), и оба списка могут быть отсортированы.

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

1 голос
/ 05 мая 2009

Я бы регистрировал агрегаты в полностью отключенном TClientDataset (cds) и обновлял значения по мере их получения из цикла. Если Btrieve можно отсортировать по номеру телефона, гораздо лучше. Затем используйте данные на компакт-дисках для создания отчета.

(Если вы пойдете этим путем, я предлагаю получить Midas SpeedFix из блога Andreas Hausladen ' вместе с другими лучшими вещами, которые вы можете найти там).

0 голосов
/ 06 мая 2009

Вместо TStringList я бы рекомендовал использовать DeCAL's (на sf.net) DMap для хранения элементов в памяти. Вы можете указать телефон в качестве ключа и сохранить структуру записи / класса, содержащую остальную часть записи.

Итак, ваш класс записи будет:


  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 - одна из моих любимых библиотек структуры данных, которая рекомендует всем, кто выполняет операции хранения в памяти, взглянуть.

Надеюсь, это поможет

0 голосов
/ 05 мая 2009

Если вы собираетесь хранить это в памяти и не хотите ничего необычного, вам лучше использовать TStringList, чтобы использовать функцию Find. Поиск использует выбор Hoare или Quick-select, локатор 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;
0 голосов
/ 05 мая 2009

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

Данные будут постоянными в течение всего времени создания списка. То есть все телефонные номера должны присутствовать для поиска, пока не будет прочитана самая последняя запись базы данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...