- Знакомство с принципами работы с базой данных в MQL5
- Основы SQL
- Структура (схема) таблиц: типы данных и ограничения
- Интеграция ООП (MQL5) и SQL: концепция ORM
- Создание, открытие и закрытие базы данных
- Выполнение запросов без привязки к данным MQL5
- Проверка существования таблицы в базе данных
- Подготовка запросов с привязкой: DatabasePrepare
- Удаление и сброс подготовленных запросов
- Привязка данных к параметрам запроса:DatabaseBind/Array
- Выполнение подготовленных запросов: DatabaseRead/Bind
- Раздельное чтение полей: DatabaseColumn-функции
- Примеры CRUD-операций в SQLite через объекты ORM
- Транзакции
- Импорт и экспорт таблицы базы данных
- Печать таблиц и SQL-запросов в журнал
- Пример поиска торговой стратегии средствами SQLite
Транзакции
SQLite поддерживает механизм транзакций — логически связанных наборов действий, которые могут быть выполнены либо целиком, либо не выполнены вовсе, что обеспечивает непротиворечивость данных в базе.
Понятие транзакция имеет в контексте баз данных новый смысл, отличающийся от того, что мы использовали при описании торговых транзакций: там под транзакцией подразумевалась отдельная операция над сущностями торгового счета — ордерами, сделками, позициями.
Транзакции обеспечивают 4 основных характеристики изменений базы:
- Атомарность (неделимость) — при успешном завершении транзакции в базу попадут все входящие в неё изменения, а в случае ошибки — не попадет ничего;
- Согласованность — текущее правильное состояние базы может измениться только на другое правильное (промежуточные, по прикладной логике, состояния исключены);
- Изолированность — изменения в транзакции текущего подключения не видны до конца этой транзакции в других подключениях к той же базе и наоборот — изменения из других подключений не видны в текущем подключении, пока здесь есть незавершенная транзакция;
- Надежность — изменения из успешной транзакции гарантированно сохраняются в базе.
Англоязычные термины этих характеристик — Atomic, Consistent, Isolated, Durable — формируют известный в теории баз данных акроним ACID.
Даже если нормальный ход программы будет прерван из-за системного сбоя, база данных сохранит свое рабочее состояние.
Наиболее часто использование транзакций иллюстрируют примером банковской системы, в базе которой выполняется перевод средств со счета одного клиента на счет другого. Он должен затронуть две записи с балансами клиентов: в одной остаток уменьшается на размер перевода, а в другой — увеличивается. Ситуация, когда применилось бы только одно из этих изменений, нарушило бы баланс банковских счетов: в зависимости от того, какая операция отказала, перечисленная сумма могла бы пропасть или, наоборот, взяться ниоткуда.
Можно привести более приближенный к трейдерской практике пример, но по принципу "от противного". Дело в том, что система учета ордеров, сделок и позиций в MetaTrader 5 не является транзакционной.
В частности, как мы знаем из главы про Создание экспертов, сработавший ордер (рыночный или отложенный), пропав из списка активных, может не сразу отобразиться в списке позиций. Поэтому для анализа фактического результата приходится в MQL-программе реализовывать ожидание обновления (актуализации) торгового окружения. Если бы система учета строилась на транзакциях, то исполнение ордера, регистрация сделки в истории и появление позиции были бы заключены в транзакцию и согласованы друг с другом. Разработчики терминала выбрали другой подход: максимально быстро и асинхронно возвращать любые модификации торгового окружения, а за их целостностью должна следить MQL-программа.
Любая SQL-команда, которая изменяет базу (то есть фактически все, кроме SELECT), автоматически будет обернута в транзакцию, если это не было сделано предварительно явным образом.
MQL5 API предоставляет 3 функции для управления транзакциями: DatabaseTransactionBegin, DatabaseTransactionCommit, DatabaseTransactionRollback. Все функции возвращают true в случае успеха или false в случае ошибки.
bool DatabaseTransactionBegin(int database)
Функция DatabaseTransactionBegin начинает выполнение транзакции в базе данных с указанным дескриптором, полученным из DatabaseOpen.
Все последующие изменения, производимые в базе, накапливаются во внутреннем кэше транзакции и не попадают в базу, пока не будет вызвана функция DatabaseTransactionCommit.
Транзакции в MQL5 не могут быть вложенными: если транзакция уже начата, то повторный вызов DatabaseTransactionBegin вернет признак ошибки и выдаст сообщение в журнал.
database error, cannot start a transaction within a transaction
|
Соответственно, нельзя пытаться и завершить транзакцию многократно.
bool DatabaseTransactionCommit(int database)
Функция DatabaseTransactionCommit завершает транзакцию, предварительно начатую в базе с указанным дескриптором, и применяет все накопленные изменения (сохраняет их). Если MQL-программа начнет транзакцию, но не применит её до закрытия базы, все изменения будут потеряны.
При необходимости программа может отменить транзакцию и, тем самым, все изменения с начала транзакции.
bool DatabaseTransactionRollback(int database)
Функция DatabaseTransactionRollback выполняет "откат" всех действий, попавших в начатую ранее транзакцию для базы с дескриптором database.
Дополним класс DBSQLite методами для работы с транзакциями, с учетом ограничения на их вложенность, которую будем подсчитывать в переменной transaction. Если она равна 0, метод begin начинает транзакцию вызовом DatabaseTransactionBegin. Все последующие попытки начать транзакцию просто увеличивают счетчик. В методе commit уменьшаем счетчик, и по достижении 0 вызываем DatabaseTransactionCommit.
class DBSQLite
|
Кроме того, создадим класс DBTransaction, который позволит описывать внутри блоков (например, функций) объекты, обеспечивающие автоматическое начало транзакции с её последующим применением (или отменой) при выходе программы из блока.
class DBTransaction
|
Политика использования таких объектов избавляет от необходимости обрабатывать различные варианты выхода из блока (функции).
void DataFunction(DBSQLite &db)
|
Чтобы объект автоматически применял изменения на любой стадии, следует передать true во втором параметре его конструктора.
void DataFunction(DBSQLite &db)
|
Вы можете описать объект DBTransaction внутри цикла, и тогда на каждой итерации будет начинаться и закрываться отдельная транзакция.
Демонстрация транзакций будет приведена в разделе Пример поиска торговой стратегии средствами SQLite.