Компонент TFastTable

Вернуться к оглавлению

Невизуальный компонент. Основное назначение - хранение больших объемов данных, загружаемых из базы данных в результате запросов. Может эффективно использоваться как невизуальное хранилище данных визуального компонента TFastListView, использующегося в режиме полностью виртуального (TotalVirtually = TRUE). Фактически таблица устроена как список строк с индексами от 0 до Count-1, и с поименованными колонками.

Компонент разрабатывался для Delphi 5, но работает так же и в Delphi 6, и должен работать в Delphi 7. В более поздних версиях Delphi и его клонов не тестировался.

Подробное описание

Основная цель разработки компонента TFastTable - это компактное хранение огромного числа данных (миллионов и более значений). Экономия достигается за счет того, что тождественные строки сохраняются в единственном экземпляре. Второй целью была высокая скорость доступа к ячейкам таблицы. Для быстрого поиска соответствия между именем колонки и ее индексом в списке колонок используются хэш-таблицы. А для тех случаев, когда и этой скорости кажется недостаточно, есть набор свойств II, SS, FF, которые позволяют передать в списке параметров свою переменную типа TFastIndex, инициализированную значением NIL, в которой при первом же обращении сохраняется сопоставленный индекс колонки, что еще больше ускоряет дальнейшее обращение к ячейкам. Правда, запись исходного кода в этом случае становится несколько более громоздкой, и приходится вручную контролировать соответствие имен переменных типа TFastIndex и наименований колонок.

Дополнительные возможности TFastTable - это хранение целочисленных и вещественных (Extended) значений, сопоставленных ячейкам. Это позволяет не тратить время на внутренние преобразования значений в строчное представление и наоборот при работе с числовыми полями таблицы.

Используется вспомогательный компонент TFloatList, предназначенный для хранения списка вещественных чисел (типа Extended). Так же имеется вспомогательный класс TFastIndex, предназначенный для хранения в пользовательских переменных кэшированных индексов колонок.
 
TFastTable TFastIndex TFloatList

Свойства и методы времени исполнения класса TFastTable (секция PUBLIC).

Имя свойства / декларация метода Тип Только чтение Описание
constructor Create;     Типичное использование компонента в случае, когда он создаётся временно, в пределах одного метода:
var T: TFastTable;
...........
T := TFastTable.Create;
try
    // ...
работа с таблицей ...
finally
    T.Free;
end;
destructor Destroy;

(используйте метод Free)

   
procedure Clear;     Очищает таблицу, в том числе список колонок и все использовавшиеся с таблицей индексные переменные (см. TFastIndex).
procedure Assign( line: Integer; Q: TQuery );     Присваивает строке (с индексом line) содержимое текущей записи результата запроса Q. Если свойство AutoAllocateColumns=FALSE и таблица не содержит колонки с именем, соответствующим одному из имён полей возвращённого результата запроса, возникает исключительная ситуация. Если в качестве значения line указан индекс строки в таблице, превосходящий Count, такая строка создаётся вместе со всеми предыдущими. Типичный цикл построчного присваивания выглядит так:
Q.Open;
T.Clear;
while not Q.Eof do
begin
    T.Assign(T.Count, Q);
    Q.Next;
end;
procedure Assign( line: Integer; T: TFastTable; T_line: Integer );     Аналогично предыдущему, но присваивает строке с индексом line строку из другой таблицы TFastTable (из строки с индексом T_line таблицы T).
procedure AssignFirst( line: Integer; Q: TQuery );     Эта пара методов обеспечивает более быстрое присваивание строк из запроса за счет того, что вызове AssignFirst (это должен быть первый вызов) создаются колонки в том же порядке, в котором они имеют место быть в результатах запроса Q, а при последующих вызовах присваивание идет не по именам колонок, а по индексам, что намного быстрее. Но важным условием корректности работы в этом случае является то, что список колонок изначально пустой или совпадает со списком полей результата запроса. Это может быть обеспечено предварительным вызовом метода Clear. Q.Open;
T.Clear;
if not Q.Eof then
begin
    T.AssignFirst( 0, Q );
    Q.Next;
    while not Q.Eof do
    begin
        T.AssignFast(T.Count, Q);
        Q.Next;
    end;
end;
procedure AssignFast( line: Integer; Q: TQuery )    
procedure Assign( Q: TQuery );     Сразу присваивает таблице все результаты запроса Q. Таблица не обязана быть пустой на момент вызова (в этом случае строки в таблицу добавляются). В случае уже не пустой таблицы, для присваивания строк используется более медленный вариант - процедура Assign( line: Integer; Q: TQuery ). Поэтому в критичных для быстродействия случаях лучше использовать пару методов AssignFirst/AssignFast, полностью выписывая соответствующий цикл.
Values[ line: Integer; const column: String] String R/W Свойство, позволяющее получить доступ (как для чтения, так и для записи) к строковым значениям ячеек таблицы. Данное свойство является свойством по умолчанию, что означает, что при обращению к этому свойству его имя может быть опущено в коде, например, T[i, 'Дата_прибытия']. Для более быстрого доступа к строковым значениям ячеек см. свойство SS.
SS[ line: Integer; const column: String; var fast: TFastIndex ]   R/W Аналогично свойству Values, позволяет работать со строчными значениями полей таблицы, но доступ выполняется очень быстро, так как соответствие имени колонки индексу колонку происходит только один раз, после чего этот индекс сохраняется в создаваемом объекте типа TFastIndex, и при последующих обращениях с этой же индексной переменной в качестве третьего параметра индекс извлекается из неё, и процедура сопоставления пропускается. Запись кода становится несколько более громоздкой, т.к. теперь надо обязательно указывать имя свойства SS, и появляется дополнительный параметр (индексная переменная fast), и ко всему прочему необходимо вручную контролировать, что используется правильная переменная fast, соответствующая именно нужной колонке, а так же обеспечивать ее декларацию, и возможно, инициализацию, но это того стоит: быстродействие на больших таблицах данных возрастает весьма существенно.
ValByColIdx[ line, colidx: Integer ] String R/W Аналогично свойству Values, но доступ к колонкам осуществляется не по имени колонки, а по ее индексу. Так можно поступать в тех случаях, когда точно известен индекс определённых колонок. Такой способ доступа быстрее, чем по имени. Неудобство здесь заключено в том, что на программиста ложится обязанность помнить, какое числовое значение индекса соответствует какой именно колонке, или использовать именованные константы (если таблица имеет всегда одни и те же колонки и в том же порядке).
IntVal[ line: Integer; const column: String ] Integer R/W Свойство для работы с целочисленным значением, сопоставленным ячейке таблицы. В случае, когда таблица получает свои значения из результатов запросов, все поля, имеющие целочисленный тип данных (и не содержащие NULL), получают целочисленное значение. Работа с целыми значениями напрямую, разумеется, быстрее, чем в случае постоянного преобразования целых чисел в строки и наоборот.
II[ line: Integer; const column: String; var fast: TFastIndex ] Integer R/W Аналогично предыдущему (работа с целочисленным значением ячейки), но, как и для свойства SS, используется третий индексный параметр для кэширования индекса быстрого доступа к колонке.
IntValByColIdx[ line: Integer; colidx: Integer ] Integer R/W Аналогично IntVal (работа с целочисленным значением ячейки), но как и в случае ValByColIdx , доступ к колонкам осуществляется по индексу колонки.
FltVal[ line: Integer; const column: String ] Extended R/W Свойство для работы с вещественным значением, сопоставленным ячейке таблицы. В случае, когда таблица получает свои значения из результатов запросов, все поля, имеющие вещественный тип данных (и не содержащие NULL, например, DATE, TIME, DATETIME, CURRENCY, FLOAT, NUMERIC), получают вещественное значение. Работа с вещественными значениями напрямую, разумеется, быстрее, чем в случае постоянного преобразования вещественных чисел в строки и наоборот.
FF[ line: Integer; const column: String; var fast: TFastIndex ] Extended R/W Аналогично предыдущему (работа с вещественным значением ячейки), но, как и для свойства SS, используется третий индексный параметр для кэширования индекса быстрого доступа к колонке.
FltValByColIdx[ line: Integer; colidx: Integer ] Extended R/W Аналогично FltVal (работа с целочисленным значением ячейки), но как и в случае ValByColIdx, доступ к колонкам осуществляется по индексу колонки.
Count Integer R возвращает число строк в таблице.
Data[ i: Integer ] Pointer R/W Пользовательские данные, сопоставленные строке. Пользователь компонента полностью отвечает за время жизни объектов, на которые здесь хранятся ссылки.
AutoAllocateColumns Boolean R/W В случае значения TRUE (по умолчанию), если при присваивании значения одним из свойств, требующим имя колонки, указано новое имя, автоматически создаётся еще одна колонка с этим именем (добавляется в конец списка колонок). В случае значения свойства FALSE такая ситуация вызывает возбуждение исключительной ситуации.
function IndexOfColumn( const column: String ): Integer;     Возвращает индекс колонки по ее имени.
SaveStatsToFile( const Filename: String; Append: Boolean )     Сохраняет статистику в текстовом виде во внешний файл (в режиме добавления). Данный метод предназначен для отладки.
procedure SaveStatsToStream( Stream: TStream );     Аналогично, статистика сбрасывается в поток.
procedure Delete( idx: Integer );     Удаление строки. Внимание! До сих пор (вероятно) не устранёна ошибка, связанная с неверным удалением строк при наличии / использовании вещественного значения ячеек (свойства FltVal, FF, FltValByColIdx). Использование полной очистки работает безопасно.
procedure SwapLines( i1, i2: Integer );     Обменивает местами строки с индексами i1 и i2. Обычно используется для сортировки строк таблицы.
ReleaseIndeces( const ListOfReleasingIndeces: array of PTFastIndex );     Освобождает указанные индексные переменные, использовавшиеся со свойствами SS, II, FF. Если указан пустой список, очищаются все индексные переменные, с которыми до сих пор шла работа. Имеет смысл использовать данный метод только в случаях, когда работа с таблицей шла в процедуре, причем таблица не создавалась как локальный объект, а была передана извне в качестве параметра. Более подробно смотрим в описании класса TFastIndex.
function LineAsString( idx: Integer; ColumnNames: Boolean = TRUE): String;     Возвращает всю строку в виде имя_колонки1=значение, имя_колонки2=значение, ... . Может использоваться в целях отладки, для вывода строк таблицы в журнал.
 

Замечание. Индекс строки в таблице должен быть неотрицательным целым числом. В то же время, допускается выход за пределы имеющегося числа строк. При обращении на чтение строковое значение полей для всех отсутствующих строк с индексом >= Count - пустая строка, числовое значение - 0. При обращении на запись с индексом >= Count, недостающие строки автоматически создаются. Исключительная ситуация не возникает! (Кроме случаев, когда затребован невероятно большой индекс, и не хватило памяти для создания всех промежуточных строк). Собственно, поэтому и нет специальных методов для добавления строк: достаточно просто указать в качестве индекса очередной несуществующий (со значением Count или больший), и выполнить присваивание. Для вставки строки между существующими, однако, придётся потратить больше усилий. Лучше избегать использования алгоритмов, требующих вставки элементов.






Класс TFastIndex

Объекты данного класса предназначены для хранения индексов колонок компонента TFastTable, для быстрого доступа к ним в свойствах SS, FF, II. Этот класс не имеет никаких публичных свойств. Более того, программист не должен самостоятельно создавать или разрушать переменные этого типа. Их надо всего лишь объявить, и в случае, когда такая переменная используется и объявляется как локальная переменная процедуры или функции, проинициализировать до использования значением NIL.

При первом использовании (в качестве третьего, индексного параметра свойства SS, FF или II), когда значение такой переменной равно NIL, происходит сопоставление имени колонки (второй индексный параметр) ее индексу, создается индексная переменная типа TFastIndex, и в ней сохраняется найденный индекс. При следующем обращении к свойствам SS, II, FF с той же самой переменной в качестве третьего индексного параметра индекс уже не вычисляется, а выбирается из этой индексной переменной. В принципе, такой подход называется "кэширование".

Использование индексных кэширующих переменных приводит к существенному ускорению доступа к ячейкам таблицы TFastTable. При этом не теряется наглядность и гибкость кода, как, например, при использовании свойств ValByColIdx. Хотя код и увеличивается несколько за счет третьего параметра.

В общем случае, существует три основных случая использования компонентов TFastIndex совместно с TFastTable.

1. Таблица типа TFastTable является глобальной переменной, или объявлена как член класса (например, как поле формы). В этом случае использующиеся с ней индексные переменные типа TFastIndex следует объявить рядом с объявлением таблицы, как глобальные переменные или как члены того же класса. При этом система автоматически инициализирует их значением NIL, и использование сводится к употреблению ("называнию") имён индексных переменных при доступе к свойствам SS, FF, II. Не требуется так же никаких действий по очистке индексных переменных TFastIndex в этом случае, так как компонент TFastTable сохраняет список всех переменных типа TFastIndex, с которыми к нему происходили обращения, и сам очистит эти переменные (вызовет для них метод Free) при очистке таблицы, как и при разрушении таблицы.

2. Таблица типа TFastTable является локальной переменной, которая создаётся в пределах некоторой процедуры, после чего разрушается перед выходом из процедуры. В этом случае используемые индексные переменные правильнее всего объявить так же как локальные переменные этой процедуры. Но необходимо их до начала работы проинициализировать значениями NIL, так как для локальных переменных Delphi не обеспечивает их автоматическую очистку до начала работы. Далее все то же самое - используем индексные переменные типа TFastIndex. По окончании работы, когда разрушается объект временной таблицы TFastTable, она автоматически выполнит и разрушение использовавшихся с ней индексных переменных.

Как вариант, чтобы избежать инициализации индексных переменных значением NIL, их можно объявить вне тела процедуры, как глобальные переменные. При разрушении локальной таблицы, использовавшиеся с ней индексные переменные разрушаются и им всем присваивается значение NIL. Но учтите в этом случае, что данная процедура не должна вызывать себя рекурсивно, или другие процедуры, в которых могут использоваться те же индексные переменные, иначе может возникнуть проблема, если одна и та же индексная переменная будет использоваться различными экземплярами таблиц TFastTable, и у них окажутся разные наборы (и порядок) колонок.

3. Таблица TFastTable передается в процедуру в качестве параметра. Сама она может при этом являться глобальной переменной, членом класса или локальной переменной вызвавшей процедуры, важно лишь, что нельзя объявить и передать переменные индексирования TFastIndex в качестве параметров той же процедуры. В этом случае рекомендуется объявить индексные переменные как локальные переменные и проинициализировать, как в пункте 2. Но добавляется следующее требование: по окончании работы процедуры индексные переменные, которые использовались с таблицей TFastTable, должны быть очищены вызовом метода ReleaseIndeces. Если таких переменных много (большое число колонок в таблице), то во избежание тщательного перечисления всех переменных, которые надо очистить, в списке параметров ReleaseIndeces, лучше использовать полную очистку. То есть, указать в качестве параметра пустой список переменных.

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

Совет

Для обеспечения наглядности кода, давайте индексным переменным составное двухчастное имя, содержащее в левой части аббревиатуру имени той таблицы, для работы с которой предназначен данный индекс, а в правой - аббревиатуру имени колонки. Части лучше разделять символ подчеркивания. Например, bld_PayDate - для обращений Building.FF[ i, 'Дата_платежа', bld_PayDate ], prs_Family - для обращений Persons.SS[ j, 'Фамилия', prs_Family ].
 






Компонент TFloatList

Свойства, события и методы класса TFloatList.

Данный компонент инкапсулирует список значений типа Extended. По виду (по составу свойств и методов) он аналогичен обычному списку TList, но его элементами являются вещественные числа типа Extended (10 байт на число).

Имя свойства / метода Тип Атрибуты
доступа
(для свойств)
Описание
procedure Clear;      
Count Integer R Число элементов в списке
Capacity Integer R/W Выделено место для хранения указанного числа элементов
procedure Add( Value: Extended );     Добавление элемента в конец списка
procedure Insert( Idx: Integer; Value: Extended );     Вставка элемента перед элементом с указанным индексом
procedure Delete( Idx: Integer );     Удаление элемента по индексу
procedure DeleteRange( Idx, Cnt: Integer );     Удаление диапазона элементов начиная с заданного индекса (всего Cnt элементов)
Items[ Idx: Integer ] Extended   Доступ к элементам. Свойство имеет атрибут default - по умолчанию, т.е. при обращении к этому свойству имя свойства может быть опущено:
FltList[ i ] эквивалентно FltList.Items[ i ];

 


(C) by Vladimir Kladov, 2000-2010