SQLite in MQL5: neue Funktionen und Leistungstests

 

Im Build 2265 haben wir die regulären Datenbankfunktionen auf Basis von SQLite 3.30.1 implementiert:


Datenbanken können entweder auf der Festplatte oder nur mit dem Flag DATABASE_OPEN_MEMORY im Speicher gehalten werden. Die Einbindung umfangreicher Einfügungen/Änderungen in DatabaseTransactionBegin/Commit/Rollback-Transaktionen beschleunigt die Vorgänge um das Hundertfache.

Da wir uns maximal auf die Leistung konzentrieren, sind hier die Ergebnisse der LLVM 9.0.0 vs MQL5 Tests. Zeit in Millisekunden, je weniger, desto besser:
Windows 10 x64, Intel Xeon  E5-2690 v3 @ 2.60GHz
                                                        LLVM   MQL5
---------------------------------------------------------------------------------
Test  1: 1000 INSERTs:                                 11572   8488
Test  2: 25000 INSERTs in a transaction:                  59     60
Test  3: 25000 INSERTs into an indexed table:            102    105
Test  4: 100 SELECTs without an index:                   142    150
Test  5: 100 SELECTs on a string comparison:             391    390
Test  6: Creating an index:                               43     33
Test  7: 5000 SELECTs with an index:                     385    307
Test  8: 1000 UPDATEs without an index:                   58      54
Test  9: 25000 UPDATEs with an index:                    161    165
Test 10: 25000 text UPDATEs with an index:               124    120
Test 11: INSERTs from a SELECT:                           84     84
Test 12: DELETE without an index:                         25     74
Test 13: DELETE with an index:                            70     72
Test 14: A big INSERT after a big DELETE:                 62     66
Test 15: A big DELETE followed by many small INSERTs:     33     33
Test 16: DROP TABLE: finished.                            42     40

Die Geschwindigkeit in MQL5 ist absolut die gleiche wie in nativem C++ mit einem der besten Compiler. Eine Reihe von Benchmarks zum Nachspielen ist beigefügt.


Wir haben auch eine einzigartige DatabaseReadBind-Funktion implementiert, mit der Sie Datensätze direkt in die Struktur einlesen können, was Massenoperationen vereinfacht und beschleunigt.

Hier ist ein einfaches Beispiel:

struct Person
  {
   int               id;
   string            name;
   int               age;
   string            address;
   double            salary;
  };

//+------------------------------------------------------------------+
//| Test                                                             |
//+------------------------------------------------------------------+
bool TestDB(string filename,int flags)
  {
   int db;
//--- open
   db=DatabaseOpen(filename,flags);
   if(db==INVALID_HANDLE)
     {
      Print("DB: ",filename," open failed with code ",GetLastError());
      return(false);
     }
//--- create a table
   if(!DatabaseTableExists(db,"COMPANY"))
      if(!DatabaseExecute(db,"CREATE TABLE COMPANY("
                          "ID INT PRIMARY KEY     NOT NULL,"
                          "NAME           TEXT    NOT NULL,"
                          "AGE            INT     NOT NULL,"
                          "ADDRESS        CHAR(50),"
                          "SALARY         REAL );"))
        {
         Print("DB: ",filename," create table failed with code ",GetLastError());
         DatabaseClose(db);
         return(false);
        }
//--- insert data
   if(!DatabaseExecute(db,"INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (1, 'Paul', 32, 'California', 20000.00 ); "
                       "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (2, 'Allen', 25, 'Texas', 15000.00 ); "
                       "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );"
                       "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 );"))
     {
      Print("DB: ",filename," insert failed with code ",GetLastError());
      DatabaseClose(db);
      return(false);
     }
//--- prepare the request
   int request=DatabasePrepare(db,"SELECT * FROM COMPANY WHERE SALARY>15000");

   if(request==INVALID_HANDLE)
     {
      Print("DB: ",filename," request failed with code ",GetLastError());
      DatabaseClose(db);
      return(false);
     }
//--- выводим записи
   Person person;

   for(int i=0; DatabaseReadBind(request,person); i++)
      Print(i,":  ",person.id, " ", person.name, " ",person.age, " ",person.address, " ",person.salary);

   Print("");
//--- close all
   DatabaseFinalize(request);
   DatabaseClose(db);
   return(true);
  }

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   TestDB("test.sqlite",DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE |DATABASE_OPEN_COMMON);
  }


Output:
0:  1 Paul 32 California 20000.0
1:  3 Teddy 23 Norway 20000.0
2:  4 Mark 25 Rich-Mond  65000.0



Dateien:
SqLiteTest.zip  2709 kb
 
Tolle News-Einführung Renat! Es gibt eine solche Frage.
Kann die .sqlite-Datei in die ME-Projektstruktur aufgenommen werden? für die spätere Verpackung in .ex5
Wenn ja, wie verhält sich das .ex5-Programm, wenn die . sqlite-Datei ver größert wird? in einem bereits kompilierten .ex5-Programm
 

Vielen Dank für die neue Funktionalität.
Ich halte die gute Hilfe zu den neuen Funktionen für den Schlüssel zum Erfolg bei der Beherrschung dieser Funktionen. Ich vermisse wirklich Beispiele für die Arbeit in der Hilfe selbst.
Bitte beachten Sie auch die folgenden Mängel, die festgestellt wurden:


1) Die Beschreibung der Funktion DatabaseExecute ist nicht wahr, sondern vonDatabasePrepare kopiert.

2) Unvollständige Beschreibung des ersten Parameters der FunktionDatabaseRead:intdatabase,// database handle obtained in DatabaseOpen;
DaDatabasePrepare vollständigere Informationen liefert:Erzeugt ein Abfrage-Handle, das dann mit DatabaseRead()ausgeführt werden kann.

3) ErzeugenDatabaseTransactionXXX-Funktionen wirklich die angegebenen GetLastError()-Fehlerlisten oder führen sie einen "Folgefehler eines früheren Fehlers" aus?

4) Für DatabaseTransactionXXX-Funktionen gibt es keine Informationen über die Behandlung verschachtelter Transaktionen.

5) Es gibt einen Druckfehler in der Beschreibung des Parameters der FunktionDatabaseColumnName(es muss heißen "to get the field name")
string&name// Verweis auf die Variable zur Ermittlung desTabellennamens

 
Roman:
Tolle Neuigkeiten, Renat! Es hat sich eine Frage ergeben.
Kann eine .sqlite-Datei in die ME-Projektstruktur aufgenommen werden?
Wenn ja, wie wird sich das .ex5-Programm verhalten, wenn die .sqlite-Datei vergrößert wird? in einem bereits kompilierten .ex5-Programm

Höchstwahrscheinlich werden wir Ressourcen zulassen, und diese Dateien werden beim ersten Start des Programms automatisch auf die Festplatte extrahiert.

Das heißt, dass die Basis im Inneren von ex5 nicht anschwellen wird. Die Datei kann nur auf der Festplatte bearbeitet werden.

 
Renat Fatkhullin:

Die Basen können entweder auf der Festplatte oder nur im Speicher gehalten werden, indem das Flag DATABASE_OPEN_MEMORY verwendet wird.

Verstehe ich das richtig, dass dies ein offizieller Mechanismus für den Datenaustausch zwischen MT5-Terminals (anstelle des Tötens von SSD-Dateien) und zwischen Programmen innerhalb des Terminals (anstelle von Ressourcen) ist?

 
Sergey Dzyublik:

Vielen Dank für die neue Funktionalität.
Ich denke, eine gute Hilfe für die neue Funktionalität ist der Schlüssel zum Erfolg, um sie zu beherrschen. Ich vermisse wirklich die Beispiele für die Arbeit in der Hilfe selbst.
Bitte beachten Sie auch die folgenden Mängel, die ich festgestellt habe:


1) Die Beschreibung der Funktion DatabaseExecute ist nicht wahr, sondern von DatabasePrepare kopiert.

2) Unvollständige Beschreibung des ersten Parameters der FunktionDatabaseRead:intdatabase, // database handle obtained in DatabaseOpen;
Da DatabasePrepare vollständigere Informationen liefert: s erzeugt ein Abfrage-Handle, das dann mit DatabaseRead() ausgeführt werden kann.

3) Erzeugen dieDatabaseTransactionXXX-Funktionen wirklich die angegebenen Fehlerlisten GetLastError() oder führen sie "Folgefehler aus einem früheren Fehler" aus?

4) Für DatabaseTransactionXXX-Funktionen gibt es keine Informationen über die Behandlung verschachtelter Transaktionen.

5) Es gibt einen Druckfehler in der Beschreibung des Parameters der Funktion DatabaseColumnName (es muss heißen "to get the field name")
string&name// Verweis auf die Variable zur Ermittlung desTabellennamens

Die Hilfe ist bereits teilweise aktualisiert worden, schauen Sie noch einmal nach. Dies ist noch die erste Version der Hilfe und sie wird aktualisiert werden.

In SQLite gibt es keine verschachtelten Transaktionen, so dass es nicht nötig ist, diese zu versuchen.

Beispiele werden im Thema vorgestellt, es ist überhaupt nichts Kompliziertes dabei. Wir werden später einen Artikel und eine Wrapper-Klasse in der Standardbibliothek erstellen.

 
fxsaber:

Verstehe ich das richtig, dass dies ein offizieller Mechanismus für den Datenaustausch zwischen MT5-Terminals (anstelle von SSD-Killing-Dateien) und zwischen Programmen innerhalb des Terminals (anstelle von Ressourcen) ist?

Hören Sie auf, unverhohlenen Unsinn über "tödliche SSDs" von inkompetenten Benutzern zu verbreiten.

Nein, es handelt sich um Dateibasen - sie können ausgetauscht werden, aber es ist riskant, von verschiedenen Sachverständigen gleichzeitig darauf zuzugreifen, da es bei gleichzeitig geöffneten Datenbanken zu einem Monopol kommen kann.

Geöffnete "In-Memory-only by DATABASE_OPEN_MEMORY flag"-Datenbanken sind nur für ein bestimmtes Programm verfügbar und werden nicht mit anderen geteilt.


Datenbankanwendungen:

  1. Speichern von Einstellungen und Zuständen von MQL5-Programmen

  2. Speicherung von Massendaten

  3. Verwendung von extern aufbereiteten Daten

    . Zum Beispiel, exportierte Daten von Metatrader zu SQLite, in Python berechnet diese Daten mit gebrauchsfertigen mathematischen Paketen und legte das Ergebnis auch in SQlite-Format.

In der nächsten Version wird es native Unterstützung für die Anzeige und Bearbeitung von SQLite-Datenbanken direkt im Editor geben, was dazu führen wird, dass diese Datenbanken als regulärer Datenaustauschmechanismus verwendet werden können.
 
Renat Fatkhullin:

In der nächsten Version wird es native Unterstützung für die Anzeige und Bearbeitung von SQLite-Datenbanken direkt im Editor geben, was zur Verwendung dieser Datenbanken als regulärer Datenaustauschmechanismus führen wird.

Der Datenbank-Editor in ME wird, nun ja, sehr praktisch sein, vielen Dank.

 
Renat Fatkhullin:

Nein, es handelt sich um Dateibasen - sie können ausgetauscht werden, aber es ist riskant, von verschiedenen EAs aus gleichzeitig auf sie zuzugreifen, da bei gleichzeitig geöffneten Basen möglicherweise ein Monopol besteht.

Python verfügt über eine Bibliothek namensSqlite3Worker für Thread-sichere E/A bei der Arbeit mit einer Datenbank über mehrere Threads.
Vielleicht ist es sinnvoll, über eine Portierung der Implementierung auf mql nachzudenken, um asynchrones Arbeiten mit der Datenbank mehrerer Expert Advisors zu ermöglichen.
Nun, oder Sie leihen sich die Idee und implementieren Ihre eigene asynchrone E/A.

sqlite3worker
sqlite3worker
  • 2017.03.21
  • pypi.org
('Thread safe sqlite3 interface',)
 
Roman:

Python verfügt über die BibliothekSqlite3Worker für eine thread-sichere Ein- und Ausgabe, wenn mit der Datenbank in mehreren Threads gearbeitet wird.
Vielleicht sollte man eine Portierung der Implementierung auf mql in Betracht ziehen, um asynchrones Arbeiten mit der Datenbank mehrerer Expert Advisors zu ermöglichen.
Nun, oder Sie leihen sich die Idee und implementieren Ihre eigene asynchrone E/A.

Haben Sie die obige Leistungstabelle gesehen? In MQL5 ist es oft schneller als in C++.

Wir haben natürlich Multithreading, und alles ist korrekt.

Die Frage bezieht sich auf etwas anderes - was passiert, wenn verschiedene Programme/Prozesse unabhängig voneinander auf dieselbe Datenbankdatei zugreifen. Es handelt sich nicht um ein einziges Programm (MQL5), sondern um mehrere unabhängige Programme, die nichts voneinander wissen und nicht dasselbe Datenbank-Handle verwenden.

 
Ist es prinzipiell schwierig, den Zugriff auf dieselbe Datenbank von verschiedenen Programmen/Terminals aus zu synchronisieren? Werden Sie wieder zum Ersatzmann?