Понадобилось мне хранить результаты измерений в базе данных. Просто сохранить их в таблицу не получится, т.к. количество строк с информацией в одном измерении быстро перевалит за миллион, и при сохранении их в одной транзакции пользователь успеет попить кофе. Плодить множество мелких транзакций по несколько тысяч записей тоже не сильно прибавит скорости. Скорость при последующей вычитке из базы данных миллионов строк тоже будет печальной. Поэтому единственный очевидный для меня выход - это сохранить результаты измерения в поле типа BLOB. Измерения у меня хранились в многомерном массиве, но я упрощу код и объявлю динамический одномерный массив целых чисел:
Var
aData: Array of Integer;
Процедура записи динамического массива в базу данных получилась примерно такая:
Var
ms: TMemoryStream;
...
begin
...
Try
ms := TMemoryStream.Create;
// Запись содержимого массива aData в поток ms
ms.WriteBuffer(aData[0], Length(aData) * SizeOf(aData[0]));
// Запись содержимого потока ms в параметр query для вставки данных
qInsertResearch.Params[5].LoadFromStream(ms, ftBlob);
Finally
ms.Free;
End;
...
А процедура чтения массива из базы данных не сложнее процедуры записи:
Var
ms: TMemoryStream;
...
begin
...
Try
ms := TMemoryStream.Create;
// Запись содержимого поля qResearchDATA типа TBlobField в поток ms
qResearchDATA.SaveToStream(ms);
// Установка размера динамического массива aData
SetLength(aData, ms.Size div SizeOf(aData[0]));
ms.Position := 0;
// Запись содержимого потока ms в массив aData
ms.ReadBuffer(aData[0], ms.Size);
Finally
ms.Free;
End;
...
Теперь на запись/чтение нескольких миллионов записей моя программа тратит секунду. Вместо размера первого элемента массива SizeOf(aData[0]) вы можете использовать размер типа элементов массива (для приведенного мной примера - SizeOf(Integer)). Но такой вариант кода будет менее универсальным, т.к. будет зависеть от используемого типа данных.
P.S. При использовании такого способа хранения данных не забывайте, что размер некоторых типов данных зависит от операционной системы, для которой скомпилирована программа. Иначе информация, которую прочтет из базы данных ваша 64-битная программа перекомпилированная из 32-битной, вас неприятно удивит.
Var
aData: Array of Integer;
Процедура записи динамического массива в базу данных получилась примерно такая:
Var
ms: TMemoryStream;
...
begin
...
Try
ms := TMemoryStream.Create;
// Запись содержимого массива aData в поток ms
ms.WriteBuffer(aData[0], Length(aData) * SizeOf(aData[0]));
// Запись содержимого потока ms в параметр query для вставки данных
qInsertResearch.Params[5].LoadFromStream(ms, ftBlob);
Finally
ms.Free;
End;
...
А процедура чтения массива из базы данных не сложнее процедуры записи:
Var
ms: TMemoryStream;
...
begin
...
Try
ms := TMemoryStream.Create;
// Запись содержимого поля qResearchDATA типа TBlobField в поток ms
qResearchDATA.SaveToStream(ms);
// Установка размера динамического массива aData
SetLength(aData, ms.Size div SizeOf(aData[0]));
ms.Position := 0;
// Запись содержимого потока ms в массив aData
ms.ReadBuffer(aData[0], ms.Size);
Finally
ms.Free;
End;
...
Теперь на запись/чтение нескольких миллионов записей моя программа тратит секунду. Вместо размера первого элемента массива SizeOf(aData[0]) вы можете использовать размер типа элементов массива (для приведенного мной примера - SizeOf(Integer)). Но такой вариант кода будет менее универсальным, т.к. будет зависеть от используемого типа данных.
P.S. При использовании такого способа хранения данных не забывайте, что размер некоторых типов данных зависит от операционной системы, для которой скомпилирована программа. Иначе информация, которую прочтет из базы данных ваша 64-битная программа перекомпилированная из 32-битной, вас неприятно удивит.
Продолжение...
"4" в коде напрягает. Пост может читать молодежь, которая скопипастит исходник.
ОтветитьУдалитьПожалуйста, ввиду лёгко-доступности 64 битного кода в Delphi XE2, а также целесообразности именно универсализации кода относительно платформы, поправьте публикацию. Тем более, что Вы это и так делаете в последнем абзаце.
Согласен - спасем копипастеров :) Переделал код и концовку.
ОтветитьУдалитьА не проще было бы сделать без MemoryStream-a. что-то типа:
ОтветитьУдалитьvar
tmpBlobStream: TStream;
begin
...
tmpBlobStream = Dataset.CreateBlobStream(...);
tmpBlobStream .WriteBuffer(aData[0], Length(aData) * SizeOf(aData[0]))
Dataset.Post;
Если бы я редактировал DataSet, то можно и так. Но я же задаю значение не полю, а параметру, т.к. для добавления записей в базу данных я использую Query с параметрами (insert into x (..., DATA) values(..., :DATA)). Но без TMemoryStream действительно можно обойтись и мой код можно значительно упростить - http://it-blackcat.blogspot.com/2012/06/blob-2.html
ОтветитьУдалитьСпасибо за мысль!