MQL5'te SQLite: yeni özellikler ve performans testi

 

2265 derlemesinde, SQLite 3.30.1'e dayalı düzenli veritabanı işlevleri uyguladık:


Veritabanları hem diskte hem de yalnızca DATABASE_OPEN_MEMORY bayrağı kullanılarak bellekte tutulabilir. Bir DatabaseTransactionBegin/Commit/Rollback işleminde büyük eklemeleri/değişiklikleri sarmak, işlemleri yüzlerce kez hızlandırır.

Mümkün olduğunca performansa odaklandığımız için, LLVM 9.0.0 ve MQL5 testlerinin sonuçları burada. Milisaniye cinsinden süre ne kadar düşükse o kadar iyidir:
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

MQL5'teki hız, en iyi derleyicilerden birine sahip yerel C++ ile kesinlikle aynıdır. Oynatma kıyaslama paketi ekli.


Ayrıca, toplu işlemleri basitleştiren ve hızlandıran kayıtları doğrudan bir yapıya okumanıza olanak tanıyan benzersiz bir DatabaseReadBind işlevi uyguladık.

İşte basit bir örnek:

 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



Dosyalar:
SqLiteTest.zip  2709 kb
 
Harika yeni tanıtım Renat! Böyle bir soru vardı.
.sqlite dosyası ME proje yapısına dahil edilebilir mi? .ex5'te sonraki paketleme için
Eğer öyleyse, .sqlite dosyasının boyutu büyüdüğünde .ex5 programı nasıl davranacak? önceden derlenmiş bir .ex5 programında
 

Yeni işlevsellik için teşekkürler.
Yeni bir işlevsellik hakkında iyi bir referansın, geliştirmedeki başarının anahtarı olduğunu düşünüyorum. Yardımın kendisinde nasıl çalışılacağına dair yeterli örnek yok.
Ayrıca, bulmayı başardığımız aşağıdaki eksikliklere dikkat etmenizi rica ediyorum:


1) DatabaseExecute işlevinin açıklaması doğru değil, DatabasePrepare'den kopyalandı.

2) DatabaseRead işlevinin ilk parametresinin eksik açıklaması: int database , // DatabaseOpen'da alınan veritabanı tanıtıcısı;
DatabasePrepare daha eksiksiz bilgi sağladığından: c daha sonra DatabaseRead() ile yürütülebilecek bir istek tanıtıcısı oluşturur .

3) DatabaseTransactionXXX işlevleri yukarıdaki GetLastError() hata listelerini gerçekten oluşturuyor mu, yoksa "önceki bir hatadan kaynaklanan takip hatası" mı yapıyorlar?

4) DatabaseTransactionХХХ işlevleri için iç içe işlemlerle çalışma hakkında hiçbir bilgi sağlanmaz.

5) DatabaseColumnName işlev parametresinin açıklamasında bir yazım hatası ("alan adını almak için" olmalıdır)
string& name // tablo adını almak için değişken referansı

 
Roman :
Harika yeni tanıtım Renat! Böyle bir soru vardı.
.sqlite dosyası ME proje yapısına dahil edilebilir mi? .ex5'te sonraki paketleme için
Eğer öyleyse, .sqlite dosyasının boyutu büyüdüğünde .ex5 programı nasıl davranacak? önceden derlenmiş bir .ex5 programında

Büyük olasılıkla, kaynaklara dahil edilmesine izin vereceğiz ve bu dosyalar program ilk başlatıldığında otomatik olarak diske ayıklanacak.

Yani ex5'in içinde taban şişmesi olmayacak. Dosya yalnızca disk üzerinde değiştirilebilir.

 
Renat Fatkhullin :

Veritabanları hem diskte hem de yalnızca DATABASE_OPEN_MEMORY bayrağı kullanılarak bellekte tutulabilir.

Bunun MT5 Terminalleri arasında (SSD dosyalarını öldürmek yerine) ve Terminal içindeki programlar arasında (kaynaklar yerine) veri alışverişi için resmi mekanizma olduğunu doğru anlıyor muyum?

 
Sergey Dzyublik :

Yeni işlevsellik için teşekkürler.
Yeni bir işlevsellik hakkında iyi bir referansın, geliştirmedeki başarının anahtarı olduğunu düşünüyorum. Yardımın kendisinde nasıl çalışılacağına dair yeterli örnek yok.
Ayrıca, bulmayı başardığımız aşağıdaki eksikliklere dikkat etmenizi rica ediyorum:


1) DatabaseExecute işlevinin açıklaması doğru değil, DatabasePrepare'den kopyalandı.

2) DatabaseRead işlevinin ilk parametresinin eksik açıklaması: int database , // DatabaseOpen'da alınan veritabanı tanıtıcısı;
DatabasePrepare daha eksiksiz bilgi sağladığından: c daha sonra DatabaseRead() ile yürütülebilecek bir istek tanıtıcısı oluşturur .

3) DatabaseTransactionXXX işlevleri yukarıdaki GetLastError() hata listelerini gerçekten oluşturuyor mu, yoksa "önceki bir hatadan kaynaklanan takip hatası" mı yapıyorlar?

4) DatabaseTransactionХХХ işlevleri için iç içe işlemlerle çalışma hakkında hiçbir bilgi sağlanmaz.

5) DatabaseColumnName işlev parametresinin açıklamasında bir yazım hatası ("alan adını almak için" olmalıdır)
string& name // tablo adını almak için değişken referansı

Yardım kısmen güncellendi, lütfen tekrar kontrol edin. Bu, yardımın ilk versiyonudur ve tamamlanacaktır.

SQLite'da iç içe geçmiş işlemler yoktur, bu yüzden onları yapmaya çalışmayın.

Konuda örnekler sunulmuştur, karmaşık bir şey yoktur. Daha sonra standart kütüphanede bir makale ve bir sarmalayıcı sınıfı yapacağız.

 
fxsaber :

Bunun MT5 Terminalleri arasında (SSD dosyalarını öldürmek yerine) ve Terminal içindeki programlar arasında (kaynaklar yerine) veri alışverişi için resmi mekanizma olduğunu doğru anlıyor muyum?

Dağıtmak için dar görüşlü kullanıcılardan "SSD'yi öldürmek" hakkında yeterince açık saçmalık.

Hayır, bunlar dosya tabanlarıdır - değiştirilebilirler, ancak veritabanları aynı anda açıkken potansiyel olarak özel erişim nedeniyle bunlara farklı Uzman Danışmanlardan aynı anda erişmek risklidir.

"Yalnızca DATABASE_OPEN_MEMORY bayrağı tarafından bellekte" açılan veritabanları yalnızca belirli bir program tarafından kullanılabilir ve kimseyle paylaşılmaz.


Veritabanlarının uygulama alanları:

  1. MQL5 programlarının ayarlarının ve durumlarının saklanması

  2. Büyük veri depolama

  3. Tarafta hazırlanan verilerin kullanımı

    Örneğin, Metatrader'dan SQLite'a veri aktardık, bu verileri Python'da hazır matematiksel paketler kullanarak hesapladık ve sonucu SQlite formatına da koyduk.

Bir sonraki sürüm, SQLite veritabanlarını doğrudan düzenleyicide görüntülemek ve düzenlemek için yerel desteğe sahip olacak ve bu veritabanlarının düzenli bir veri değişim mekanizması olarak kullanılmasına yol açacaktır.
 
Renat Fatkhullin :

Bir sonraki sürüm, SQLite veritabanlarını doğrudan düzenleyicide görüntülemek ve düzenlemek için yerel desteğe sahip olacak ve bu veritabanlarının düzenli bir veri değişim mekanizması olarak kullanılmasına yol açacaktır.

ME'deki veritabanı editörü, çok uygun olacak, teşekkürler.

 
Renat Fatkhullin :

Hayır, bunlar dosya tabanlarıdır - değiştirilebilirler, ancak veritabanları aynı anda açıkken potansiyel olarak özel erişim nedeniyle bunlara farklı Uzman Danışmanlardan aynı anda erişmek risklidir.

Python, çok iş parçacıklı bir veritabanıyla çalışırken iş parçacığı güvenli G/Ç için Sqlite3Worker kitaplığına sahiptir.
Birkaç uzmandan oluşan bir veritabanıyla eşzamansız olarak çalışabilmek için uygulamayı mql'ye taşımayı düşünmek mantıklı olabilir.
Peki, ya da fikri ödünç alın ve kendi asenkron giriş/çıkışınızı uygulayın.

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

Python, çok iş parçacıklı bir veritabanıyla çalışırken iş parçacığı güvenli G/Ç için Sqlite3Worker kitaplığına sahiptir.
Birkaç uzmandan oluşan bir veritabanıyla eşzamansız olarak çalışabilmek için uygulamayı mql'ye taşımayı düşünmek mantıklı olabilir.
Peki, ya da fikri ödünç alın ve kendi asenkron giriş/çıkışınızı uygulayın.

Yukarıdaki performans tablosunu gördünüz mü? Orada, MQL5'te genellikle C++'dan daha hızlı çalışır.

Elbette her şeye sahibiz, çok iş parçacıklı ve her şey doğru.

Tamamen farklı bir şey hakkında - farklı programlar / işlemler bağımsız olarak aynı veritabanı dosyasına tırmanırsa ne olur. Bir program (MQL5) değil, birbirini bilmeyen ve aynı veritabanı tanıtıcısını kullanmayan birkaç bağımsız program.

 
Ve ne, farklı programlardan / terminallerden bir veritabanına erişimin senkronizasyonunu organize etmede temel bir zorluk var mı? Yine mi ersatz?