Поиск по блогу

суббота, 26 января 2013 г.

SQLite и Delphi 7: работа с БД с помощью DISQLite

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

В предыдущей серии я рассказала про компонент для браузера, который буду использовать. Настало время определиться с данными.

Сначала хотела вообще обойтись без БД, а заодним и описать работу с XML, но, подумав, решила, что без базы будет сложно (и неудобно, если пользователей заведется много). Особенно, если разрабатывать с перспективами. Поэтому сегодня речь пойдет о SQLite.

Чем руководствовалась при выборе БД?
  • Максимальная простота и легкость.
  • Минимум телодвижений для работы приложения на другой машине: надо просто скопировать все содержимое папки к себе (кроме файлов БД нужна только dll).

Библиотека DISQLite для работы с SQLite в Delphi 7

Их существует несколько. Например, я встречала упоминания о DISQLite, ASQLite, ZeosDBO. Я пробовала только первую, поэтому про нее сейчас и расскажу. Если кто-нибудь использовал другие, поделитесь, пожалуйста, впечатлениями в комментариях.

Установка DISQLite

Скачиваем файлы библиотеки. Подробно процесс установки описан в хэлпе, который прилагается к либе, поэтому переводить и размещать его здесь не имеет смысла. В принципе, вся установка может быть сведена к добавлению библиотек в Tools — Environment Options — Library. Но я устанавливала еще и компоненты. После установки пакета DISQLite3_D7.dpk появляется вкладка Yunga с тремя компонентами:
Компоненты DISQLite

Создание базы данных

Прикинем, что нам надо хранить в базе данных. Пока по-минимуму.

Во-первых, таблица с юзерами.
Во-вторых, таблица с задачами (постинг картинок, текстов и т.д.).
В-третьих, таблица для хранения данных о приглашенных в друзья. Чтобы не повторяться и не приглашать повторно одних и тех же. Через некоторое время, если юзера взаимно не зафрендили, отписываться от него и позже уже не приглашать.

Все это только наброски, я еще сама точно не знаю, чем потом дополню базу.

Я покажу, как можно открыть базу (или создать, если такой базы нет) из приложения. Как вариант, можно создать ее в каком-нибудь менеджере БД (мне нравится бесплатная программа SQLite Expert Personal, ее функционала вполне достаточно), а дальше работать с созданной базой через IDE-компоненты.

Вот как выглядит окно SQLite Expert Personal с открытой БД:
SQLite Expert Personal - менеджер для работы с БД SQLite

Итак, код создания базы данных SQLite с помощью DISQLite в Delphi7:

var
  vkBase  : TDISQLite3Database;

procedure TfrmMain.FormCreate(Sender: TObject);
var
  sSQL  : String;
begin
  vkBase  := TDISQLite3Database.Create(nil);
  vkBase.DatabaseName := 'vkBase.db';
  try 
    vkBase.Open; // пытаемся открыть базу, если она не существует — создаем
    // если база не существует, будет возвращено EFOpenError
  except
    on e: EFOpenError do
      begin
        vkBase.CreateDatabase;
        vkBase.Execute('PRAGMA legacy_file_format=OFF;');
      end;
  end;
  
  try
  //  создаем все нужные таблицы, если их еще нет
      sSQL:='CREATE TABLE if not exists vk_users ( '+
          'id INTEGER NOT NULL PRIMARY KEY,'+
          'user_name CHAR(50) NOT NULL,'+
          'user_email CHAR(20) NOT NULL,'+
          'user_pass CHAR(20) NOT NULL,'+
          'user_url CHAR(20) NOT NULL,'+
          'create_date DATETIME,'+
          'dsc TEXT);';
      vkBase.Execute(sSQL);
      sSQL:='CREATE TABLE if not exists vk_invitations ( '+
          'id INTEGER NOT NULL PRIMARY KEY,'+
          'user_id INTEGER,'+
          'invited_url CHAR(30),'+
          'invitation_fd DATETIME,'+
          'invitation_td DATETIME,'+
          'status SMALLINT NOT NULL DEFAULT 0)';
      vkBase.Execute(sSQL);
      sSQL:='CREATE TABLE if not exists vk_tasks ( '+
          'id INTEGER NOT NULL PRIMARY KEY,'+
          'user_id INTEGER,'+
          'task_code INTEGER,'+
          'task_fd DATETIME,'+
          'task_td DATETIME,'+
          'image_dir CHAR(50),'+
          'text_to_post TEXT,'+
          'status SMALLINT NOT NULL DEFAULT 0)';
      vkBase.Execute(sSQL);
  except
    MessageBox(0,'Ошибка при создании таблиц.','Ошибка',MB_OK+MB_ICONERROR);
    Application.Terminate;
  end;
end;

В FormDestroy не забудьте очистить память, выделенную под vkBase.

Во время написания этого поста столкнулась с одной странной штуковиной, касающейся ключей и автоинкремента.

Сначала следует сказать, что в таблицах SQLite каждая запись снабжается уникальным в рамках этой таблицы ROWID. При добавлении записи этот ключ по умолчанию автоинкрементируется. Чтобы воспользоваться этим механизмом "встроенного инкремента", можно или назвать поле ROWID (еще варианты: _ROWID_, OID), или создать поле типа INTEGER PRIMARY KEY.

Так вот, если в менеджере SQL-код:

CREATE TABLE if not exists vk_users (
          id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
          user_name CHAR(50) NOT NULL,
          user_email CHAR(20) NOT NULL,
          user_pass CHAR(20) NOT NULL,
          user_url CHAR(20) NOT NULL,
          create_date DATETIME,
          dsc TEXT);

прокатывает, то при открытии такой базы через DISQLite (или при попытке исполнить этот запрос через Execute), выскакивает ошибка SQLite Error 11 - malformed database schema (vk_users) near AUTOINCREMENT. Может, это такой баг (особенность?) DISQLite?

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

SELECT в DISQLite

Чтобы делать выборку SELECT, в библиотеке есть TDISQLite3UniDirQuery. Небольшой пример, как работать с экземпляром класса безотносительно к конкретно этому приложению. Кто знаком с работой с БД, увидит, что все стандартно, ничего нового и особенного.

var
  Query : TDISQLite3UniDirQuery;
  ...
begin
...
  Query := TDISQLite3UniDirQuery.Create(nil);
    try
      Query.Database := vkBase;
      Query.SelectSQL := 'select * from vk_users';
      Query.Open;
      while not Query.EOF do
        begin
          StringList.Add(Query.FieldByName('user_name').AsString);
          Query.Next;
        end;
  finally
    Query.Free;
  end;
...
end;
Можно создавать экземпляр TDISQLite3UniDirQuery динамически (как выше), а можно разместить его на форме и обращаться к его свойствам в редакторе.

Чтобы быть в курсе обновлений блога, можно подписаться на RSS.

Статьи схожей тематики:



9 комментариев:

  1. Не подскажешь, чем можно синхронизировать БД? Что-то не нашел таких компонентов под delphi. Короче как сделать репликацию sqlite?

    ОтветитьУдалить
  2. Не, не знаю, я не писала многопользовательских приложений с sqlite. Может, кто из читателей знает, возможно ли это.

    ОтветитьУдалить
  3. ZeosDBO хорош, если пользоваться последней версией (альфа,бетта)
    Можно использовать как визуально так и рантайм немного напугало отсутствие документации но пару форумов поправят это дело

    ОтветитьУдалить
  4. Извините, если Вы уже это знаете... :)
    Для локальной работы удобно использовать встроенные в Окошки движок базы Access.
    Достоинства (в свете использования в Дельфи и распространения):
    - Он есть в системе всегда, даже если Офис не установлен.
    - Никаких дополнительных компонентов не надо, доступ через ADODataSet.
    - Это один файл. Достаточно создать его в Аксесе и приложить к дистрибутиву.
    Недостаток: Никаких "серверных" скриптов, триггеров и т.п. нету. Только Запрос/Ответ

    FireBird: Опенсорсный наследник Interbase. Полноценная мощная база, с триггерами, функциями и т.д. и т.п.
    Никаких специальных компонентов не требуется. Доступ через dbExpress (драйвер Interbase). База - один файл. Можно работать как с локальной базой, так и по сети с сервером. Причем ни базу, ни приложение переделывать не надо, только использовать разные DLLки.

    ОтветитьУдалить
    Ответы
    1. Сергей, спасибо за развернутый комментарий :) Да, я это знаю, как-никак 9 лет преимущественно с базами данных работаю, но, может, кому-то он поможет сделать выбор БД.

      Удалить
  5. спасибо за статью! как раз думал с какой стороны подходить к disqlite, уж очень тяжеловесно всё в её родных примерах.

    ОтветитьУдалить
  6. Нащет AUTOINCREMENT: http://habrahabr.ru/qa/18641/
    Цитата:
    How do I create an AUTOINCREMENT field.
    Short answer: A column declared INTEGER PRIMARY KEY will autoincrement.

    ОтветитьУдалить
    Ответы
    1. Про INTEGER PRIMARY KEY есть в посте, зачем дублировать в комментариях? :)

      Удалить
  7. В прдолжение к предыдещему. После использования DISQLite вылазит куча утечек памяти. Как бороться с утечками после DISQLite3Database нарыл на официальном форуме. А вот после TDISQLite3UniDirQuery остается много не унитоженых widestring'ов. Может встречали как с этим бороться? У меня DISQLite3 4.2.0 personal и Delphi XE3

    ОтветитьУдалить

Комментарии модерируются, вопросы не по теме удаляются, троллинг тоже.

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

Поделиться