Обсуждение статьи "SQLite: нативная работа с базами данных на SQL в MQL5" - страница 8

 

И вопрос №2:

Пытаюсь использовать функции ATTACH/DETACH, чтобы передать данные между БД встроенным механизмом SQLite, в результате ошибка транзакции 5601...

При выполнении этой же транзакции в SQLiteStudio, все отлично.

Это сделано специально или что-то не работает? 

   m_db.Create("IndiTemp","indi",m_db_main.GetStruct(),true);
   string db_path=StringFormat("%s\\Files\\%s.sqlite",
                               TerminalInfoString(TERMINAL_COMMONDATA_PATH),
                               "Indi");
                               
   string query=StringFormat("ATTACH DATABASE '%s' AS X; ",db_path),
          tables[];
          
   int total=m_db.GetTables(tables);
   if(total>0)
     {
      for(int i=0; i<total; i++)
         StringAdd(query,StringFormat("INSERT OR IGNORE INTO %s SELECT * FROM X.%s; ",tables[i],tables[i]));
     }
     
   StringAdd(query,"DETACH X; ");
   m_db.TransactionExecute(query);
 
Daniil Kurmyshev #:

Добрый день, уважаемые разработчики!

Функция "DatabaseExport" никак не хочет работать...выдает ошибку 5601 (ошибка выполнения запроса, но запрос я не выполняю) когда указываю имя таблицы в параметрах,

а когда указываю SQL запрос, то ошибка 4022 (отмена выполнения программы), возможно ошибка внутри MQL функции, часть кода из моей библиотеки:


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

По ошибке 4022, Вы получаете её при тестировании, если да, то в тестере имеется ограничение на размер файла экспорта -  1GB общий размер записанных на диск данных ?

 
Daniil Kurmyshev #:

И вопрос №2:

Пытаюсь использовать функции ATTACH/DETACH, чтобы передать данные между БД встроенным механизмом SQLite, в результате ошибка транзакции 5601...

При выполнении этой же транзакции в SQLiteStudio, все отлично.

Это сделано специально или что-то не работает? 

Запрос должен работать, проверьте правильность путей
 
Ilyas #:

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

По ошибке 4022, Вы получаете её при тестировании, если да, то в тестере имеется ограничение на размер файла экспорта -  1GB общий размер записанных на диск данных ?

Ошибка 4022 появляется без тестера, если указать в функцию DatabaseExport запрос вместо имени таблицы.

Размер базы меньше 10 Мб, планировал использовать этот функционал для копирования данных из базы на диске в базу в памяти.
 
Ilyas #:
Запрос должен работать, проверьте правильность путей

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

О результате напишу.

 
Есть ли возможность подключения к базе в оперативной памяти по имени и если задействовать параметр общего кэша, знаю что такая возможность у SQLite точно есть, но хотелось бы узнать включена ли она в МТ5, спасибо.
 

Тест для многопоточной записи в БД, запускать несколько копий скрипта, каждую копию можно пометить параметром expert_id.

#property copyright "Aliaksandr Hryshyn"
#property link      "https://www.mql5.com/ru/users/greshnik1"
#property version   "1.00"
#property script_show_inputs

enum e_E
  {
   e_0,
   e_1,
   e_2,
   e_3,
  };
input e_E expert_id=0;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   int db_handle=DatabaseOpen("tmp//test.sqlite",DATABASE_OPEN_READWRITE|DATABASE_OPEN_CREATE|DATABASE_OPEN_COMMON);
   const string sql_create=
      "CREATE TABLE IF NOT EXISTS Demo("
      "Id INTEGER PRIMARY KEY AUTOINCREMENT,"
      "Expert_id INTEGER,"
      "demo_0 TEXT(2000),"
      "demo_1 TEXT(2000),"
      "demo_2 TEXT(2000),"
      "demo_3 TEXT(2000),"
      "demo_4 TEXT(2000),"
      "demo_5 TEXT(2000),"
      "demo_6 TEXT(2000),"
      "demo_7 TEXT(2000)"
      ");";
   const string sql_add=
      "INSERT INTO Demo(Expert_id,demo_%s) VALUES(%s,'%s')";
   if(!DatabaseExecute(db_handle,sql_create))//Создание таблицы если отсутсвует
     {
      DatabaseClose(db_handle);
      Print("Ошибка:",GetLastError());
      Print("Запрос:",sql_create);
      return;
     }
   string s1;
   string sql;
   int err_counter=100;//Счётчик ошибок добавления записи
   for(int i1=0; i1<1000; i1++)
     {
      s1="";
      for(int i2=0; i2<100; i2++)
        {
         s1+=string(MathRand());
        }
      sql=StringFormat(sql_add,string(MathRand()%8),string(expert_id),s1);
      if(DatabaseExecute(db_handle,sql))//Добавляем запись в случайный столбец из набора demo_..
        {
         err_counter=100;
        }
      else
        {
         if(err_counter==0)
           {
            DatabaseClose(db_handle);
            Print(expert_id,": end");
            return;
           }
         else
           {
            Print(expert_id,": ",err_counter);
            err_counter--;
            i1--;
           }
        }
      if(IsStopped())
         break;
      Sleep(1);//Немного спим
     }
   DatabaseClose(db_handle);
  }

Всё правильно пишется:


Только сыпятся ошибки о блокировке:

...
2021.09.18 13:08:05.252 Test_sql (EURUSD,M1)    2: 80
2021.09.18 13:08:05.267 Test_sql (EURUSD,M1)    database error, database is locked
2021.09.18 13:08:05.267 Test_sql (EURUSD,M1)    2: 79
2021.09.18 13:08:05.283 Test_sql (EURUSD,M1)    database error, database is locked
2021.09.18 13:08:05.283 Test_sql (EURUSD,M1)    2: 78
2021.09.18 13:08:05.299 Test_sql (EURUSD,M1)    database error, database is locked
2021.09.18 13:08:05.299 Test_sql (EURUSD,M1)    1: 100
2021.09.18 13:08:05.315 Test_sql (EURUSD,M1)    database error, database is locked
2021.09.18 13:08:05.315 Test_sql (EURUSD,M1)    1: 99
...

Как быть?  По идее должен быть механизм условной вставки записи без возврата ошибки.

 
Ilyas #:
Запрос должен работать, проверьте правильность путей

1. Проверил ATTACH DATABASE действительно работает! 

Проблема была в следующем, я пытался присоединить к базе созданной в оперативной памяти, физическую базу в этом случае ATTACH DATABASE не работает, а если присоединяем оперативную базу к физической, то работает!

Мне этого вполне хватает для создания копии базы в оперативной памяти, что ускоряет вычисления))


2. Решил пойти дальше... и проверить ещё одну возможность, а именно:

ATTACH DATABASE 'file:memdb1?mode=memory&cache=shared' AS M;

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

Во 1 я могу к ней присоединиться из любой MQL5 программы, советник, индикатор, скрипт, сервис,

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

П.С. Возможно из-за того, что я создавал базу из индикатора, а он работает в общем потоке?


Так то это крутая фишка, но вот хотелось бы узнать останется ли она в терминале в следующих обновлениях и после моего сообщения об этом?:))

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


В таком исполнении удаляется, после завершения MQL5 программы:

ATTACH DATABASE ':memory:?cache=shared' AS M;

ATTACH DATABASE ':memory:' AS M;

 

Добрый день!

Вопрос к разработчикам MT, возможно ли отключить атрибут Shared у библиотеки SQLite, опытным путем в других проектах определил, что именно в этом причина ошибки: database error, database is locked.

Если обращение происходит из разных потоков к базе, то это все решается на уровне самой базы, а именно выполнив эти два запроса сразу после открытия базы:

1. PRAGMA journal_mode = WAL;

2. PRAGMA busy_timeout = 1000;

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

 
Daniil Kurmyshev #:

Добрый день!

Вопрос к разработчикам MT, возможно ли отключить атрибут Shared у библиотеки SQLite, опытным путем в других проектах определил, что именно в этом причина ошибки: database error, database is locked.

Если обращение происходит из разных потоков к базе, то это все решается на уровне самой базы, а именно выполнив эти два запроса сразу после открытия базы:

1. PRAGMA journal_mode = WAL;

2. PRAGMA busy_timeout = 1000;

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

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

а так всё Ок :-) 

SQLite заточен под один поток или один писатель и много читателей. Много писателей не про него, про это другие базы которые не "лайт"

это безотносительно MQL - вот просто так оно и есть на самом деле и везде