уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Готовится к выходу обновленная версия программы “Пинговалка”. За время, прошедшее с момента выхода последней версии программы, было много исправлено, дописано, удалено кода. Причин для подготовки новой версии было как минимум две: обращение пользователей программы и вторая причина – я решил заняться рефакторингом проекта, в результате чего проект кое в каких местах “похудел” иногда даже значительно. Есть идея добавить в программу пару новых “фишек”, но когда дойдут руки до внедрения новых функций в программу пока не ясно. Пока могу лишь сказать, что в ближайшее время постараюсь выложить в доступ обновление “Пинговалки”. Если есть предложения по каким-либо новым функциям в программе, или заметили ошибки в работе о которых не сообщалось здесь – пишите. А пока поговорим немного о работе с SQLite в Delphi.

Некоторое время назад я уже писал про SQLite в Delphi. С того времени SQLite несколько раз обновлялся, а вместе с этим обновлялся и wrapper, который я использую для работы с SQLite, так что можете обновиться из репозитория и приступим к рассмотрению нескольких примеров использования SQLite.

Первое, на что следует обратить внимание – это на те примеры, которые можно в большом достатке увидеть в Сети по части работы с SQLite, ну, например, на такой (пример работы с SQLite в PHP):

if (!$query_table) exit("Невозможно создать таблицу в базе данных!");
  // Запишем что-нибудь в таблицу
  sqlite_query($db, "INSERT INTO table1(field1, field2) VALUES ('PHP5+', 'Apache');");
  sqlite_query($db, "INSERT INTO table1(field1, field2) VALUES ('SQLite – ', 'классная вещь');");
  sqlite_query($db, "INSERT INTO table1(field1, field2) VALUES ('Посетите ', 'sqlite.org');");
  // Сделаем выборку данных
  $res = sqlite_query($db, "SELECT * FROM table1;");

Что здесь, собственно, не так? На первый взгляд пример составлен правильно, да и если проверите – результат проверки будет положительным. Но есть одно большое “НО” – НО так делать не надо :). Действительно, можете проверить работу приведенного выше примера хоть в PHP, хоть в C#, хоть в Delphi (не суть) – скорость работы с БД оставит желать лучшего.

Приведу простой пример на Delphi – добавление 100 записей в таблицу:

unit main;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, SQLite3, SQLiteTable3, StdCtrls;
 
const SQLTable = 'CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, strfield TEXT, intfield INTEGER)';
      SQLInsert = 'INSERT INTO test (strfield, intfield) VALUES ("%s", %s)';
 
type
  TfMain = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    lbTime: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  fMain: TfMain;
 
implementation
 
{$R *.dfm}
 
procedure TfMain.Button1Click(Sender: TObject);
var Base: TSQLiteDatabase;
    iCounterPerSec: TLargeInteger;
    T1, T2: TLargeInteger; //значение счётчика ДО и ПОСЛЕ операции
    i:integer;
begin
  Base:=TSQLiteDatabase.Create('base.sqlite');
  try
  if not Base.TableExists('test') then //таблица в БД отсутствует - создаем
    Base.ExecSQL(SQLTable);
 
    QueryPerformanceFrequency(iCounterPerSec);
    QueryPerformanceCounter(T1);
 
    for I := 1 to 100 do
       Base.ExecSQL(Format(SQLInsert,['AnyText',IntToStr(i)]));
 
    QueryPerformanceCounter(T2);
    lbTime.Caption:=FormatFloat('0.0000', (T2 - T1) / iCounterPerSec) + ' сек.';
 
  finally
    base.Free;
  end;
end;
 
end.

 

Смотрим на время, которое потребовалось, чтобы вписать в таблицу 100 значений:

SQLite_1

Жесть да и только – 8 с лишним секунд на 100 записей, а что если надо записать не 100, а 1000 записей? Вот Вам и ответ, почему приведенный выше код PHP я считаю недостаточно корректным – идея верная, а реализация на поверку оказалась фиговая. И я, следуя вот таким примерам “наколбасил” в “Пинговалке” много чего нехорошего в плане быстродействия. А все потому, что не достаточно внимательно читал документацию по SQLite. Теперь ускорим работу нашего приложения, следуя официальным рекомендациям. Допишем в процедуру обработчик две строки:

Base.BeginTransaction;//начали транзакцию
for I := 1 to 100 do
   Base.ExecSQL(Format(SQLInsert,['AnyText',IntToStr(i)]));
Base.Commit;//завершли

 

Смотрим результат:

SQLite_2

Неплохо ускорились, да? А причина столь стремительного роста скорости объясняется все в той же официальной документации – здесь.

Теперь второй момент, который следует учитывать при работе с SQLite в Delphi (и не только). Смотрим на размер файла БД:

SQLite_3

Попробуем удалить из нашей таблицы все записи:

 Base.ExecSQL('DELETE FROM test');

 

Вроде бы удалили все записи и SELECT тоже вернет нам пустую таблицу, а размер файла не изменяется. Ответ на вопрос “Ошибка ли это?” я нашел на одном из сайтов в рунете. Следуя изложенному материалы у нас для решения вопроса с размером файла базы данных есть два варианта:

  1. Включить auto_vacuum, если у нас версия SQLite 3.1. или выше
  2. Самостоятельно вызывать команду VACUUM в нужный момент для “сжатия” файла базы данных

Реализация в Delphi:

Base.ExecSQL('PRAGMA auto_vacuum = 1');
Base.ExecSQL('VACUUM');

 

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

Вот два небольших, но думаю, важных момента работы с SQLite, которые помогут Вам, в случае чего, избежать досадных недоразумений при разработке своих программ.

Вообще тема ускорения работы SQLite довольно занимательная и ей уделено много статей как в Рунете так и по Сети в целом, есть что почитать и чему поучиться у профессионалов, использующих SQLite. Теперь вот сижу, дописываю wrapper для работы с SQLite в Delphi, исправляю кое-что в “Пинговалке” и, надеюсь, что следующий релиз программы будет работать значительно быстрее предыдущего, а вы сможете с помощью Пинговалки ускорять индексацию своих сайтов ещё удобнее :). До новых встреч»!

Книжная полка

Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
купить книгу delphi на ЛитРес
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
купить книгу delphi на ЛитРес
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
5 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
angryvitum
angryvitum
30/05/2011 08:26

Vlad, а не замечался ли при работе с этой «оберткой» SQLite такой баг: при сохранении строк кириллицы в таблицу SQLite в проектах в Delphi2009-XE строки сохраняются как есть, в Unicode? В результате если открыть такую базу в любом SQLite-редакторе, то вместо строк — крякозябры, т.е. редакторы пытаются восстановить строку из UTF-8 в win1251, и, естественно, у них ничего не получается. Вроде как указанный wrapper игнорирует кодировку по умолчанию, установленную в базе.

Антон
Антон
30/05/2011 15:36

Привет. Если просто вызывать 

for I := 1 to 100 do

Base.ExecSQL(Format(SQLInsert,[‘AnyText’,IntToStr(i)]));

то на каждый ExecSQL создается неявная транзакция, отсюда и такое большое время.
И вообще проще использовать аргументы в SQLIte. Т.е. заместо

 SQLInsert =’INSERT INTO test (strfield, intfield) VALUES («%s», %s)’

писать SQLInsert =’INSERT INTO test (strfield, intfield) VALUES (?, ?)’
а  вызывать как ExecSQL(SQLInsert, [‘AnyText’, i]). Или нечто похожее. Раньше использовал этот модуль для работы с SQLite.
Теперь самописное.

 

angryvitum
angryvitum
31/05/2011 08:53

Остановил свой выбор на SQLite ODBC Driver (http://www.ch-werner.de/sqliteodbc/). SQLite Wrapper удобен для небольших выборок, ПМСМ, а вот для полноценной работы с базами SQLite нужно все-таки что-то «помощнее».

HSerg
HSerg
31/05/2011 14:46

Из общедоступного удобен ASQLite от Aducom Software (http://www.aducom.com/cms/page.php?2). И транзакции есть, и параметры поддерживаются, и велосипедов изобретать не нужно :)