English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
SQL ve MQL5: SQLite Veritabanı ile Çalışmak

SQL ve MQL5: SQLite Veritabanı ile Çalışmak

MetaTrader 5Örnekler | 14 Ocak 2022, 10:46
135 0
---
---

Küçük. Hızlı. Güvenilir.
Üçünden birini seçin.

 

Giriş

Pek çok geliştirici, projelerinde veri depolama amacıyla veritabanlarını kullanmayı düşünür, ancak SQL sunucu kurulumunun ne kadar fazladan zaman gerektirebileceğini bilerek bu konuda tereddütlü kalırlar. Ayrıca programcılar için o kadar sorun olmasa da (başka amaçlar için bir veritabanı yönetim sistemi (DBMS) zaten kurulmuşsa), sonunda yazılımı tamamen kurmaktan vazgeçebilecek olan ortak bir kullanıcı için kesinlikle bir sorun olacaktır.

Pek çok geliştirici, şu anda üzerinde çalıştıkları çözümlerin çok az kişi tarafından kullanılacağını fark ederek DBMS ile uğraşmamayı tercih ediyor. Sonuç olarak dosyalarla çalışmaya yönelirler (kullanılan verilerin çeşitliliği göz önüne alındığında, genellikle birden fazla dosyayla uğraşmak zorunda kalırlar): CSV, daha az sıklıkla XML veya JSON veya katı yapı boyutuna sahip ikili veri dosyaları vb.

Ancak SQL sunucusuna harika bir alternatif olduğu ortaya çıktı! Ayrıca, SQL'in tüm gücünü kullanmanıza izin verirken, projenizde her şey yerel olarak yapıldığından ek yazılım yüklemeniz bile gerekmez. Burada SQLite'dan bahsediyoruz.

Bu makalenin amacı, SQLite ile hızlı bir şekilde başlamanızı sağlamaktır. Bu nedenle, inceliklere ve akla gelebilecek tüm parametre kümelerine ve işlev bayraklarına girmeyeceğim, bunun yerine SQL komutlarını yürütmek için hafif bir bağlantı sarmalayıcı oluşturacağım ve kullanımını göstereceğim.

Makaleye devam etmek için yapmanız gerekenler:

  • İyi bir ruh halinde olun ;)
  • Makaleye eklenen arşiv dosyalarını MetaTrader 5 istemci terminal klasörüne çıkarın
  • Herhangi bir uygun SQLite Görüntüleyici'yi kurun (ör. SQLiteStudio)
  • SQLite http://www.sqlite.org ile ilgili resmi belgeleri Favoriler'e ekleyin

İçindekiler

1. SQLite İlkeleri
2. SQLite3 API
    2.1. Bir Veritabanı Açma ve Kapatma
    2.2. SQL Sorgularının Yürütülmesi
    2.3. Tablolardan Veri Alma
    2.4. Bağlama ile Parametre Verisi Yazma
    2.5. İşlemler/Çok Satırlı Ekler (Bir Alım Satım Hesabının İşlem Tablosunu Oluşturma Örneği)
3. 64 Bit Sürümünü Derleme (sqlite3_64.dll)


1. SQLite İlkeleri

SQLite, temel özelliği yerel olarak kurulmuş bir SQL sunucusunun olmaması olan bir RDBMS'dir. Uygulamanız burada bir sunucu olarak görülüyor. SQLite veritabanıyla çalışmak temel olarak bir dosyayla çalışmaktır (bir disk sürücüsünde veya bellekte). Tüm veriler, herhangi bir özel şekilde yüklemeye gerek kalmadan arşivlenebilir veya başka bir bilgisayara taşınabilir.

SQLite ile geliştiriciler ve kullanıcılar, inkar edilemez birçok avantajdan yararlanabilir:

  • ek yazılım yüklemeye gerek yok;
  • veriler yerel bir dosyada saklanır, böylece yönetim şeffaflığı sunar, yani uygulamanızdan bağımsız olarak bunları görüntüleyebilir ve düzenleyebilirsiniz;
  • tabloları diğer DBMS'ye alma ve verme yeteneği;
  • kod, uygulamayı istediğiniz zaman diğer DBMS ile çalışmaya zorlamanıza olanak tanıyan tanıdık SQL sorguları kullanır.

SQLite ile çalışmanın üç yolu vardır:

  1. DLL dosyasını eksiksiz bir API işlevleri seti ile kullanabilirsiniz;
  2. bir EXE dosyasına kabuk komutlarını kullanabilirsiniz;
  3. projenizi SQLite API kaynak kodları ile derleyebilirsiniz.

Bu yazıda, MQL5'te en yaygın olan ilk seçeneği anlatacağım.

 

2. SQLite3 API

Bağlayıcı işlemi aşağıdaki SQLite'ın kullanılmasını gerektirecektir functions:

//--- general functions
sqlite3_open
sqlite3_prepare
sqlite3_step
sqlite3_finalize
sqlite3_close
sqlite3_exec

//--- functions for getting error descriptions
sqlite3_errcode
sqlite3_extended_errcode
sqlite3_errmsg

//--- functions for saving data
sqlite3_bind_null
sqlite3_bind_int
sqlite3_bind_int64
sqlite3_bind_double
sqlite3_bind_text
sqlite3_bind_blob

//--- functions for getting data
sqlite3_column_count
sqlite3_column_name
sqlite3_column_type
sqlite3_column_bytes
sqlite3_column_int
sqlite3_column_int64
sqlite3_column_double
sqlite3_column_text
sqlite3_column_blob

İşaretçilerle çalışmak için düşük seviyeli msvcrt.dll işlevlerine de ihtiyacınız olacak:

strlen
strcpy
memcpy

32 ve 64 bit terminallerde çalışması gereken bir bağlayıcı oluşturduğum için, API işlevlerine gönderilen işaretçinin boyutunu dikkate almak önemlidir. Şimdi bunların adlarını ayıralım:

// for a 32 bit terminal
#define PTR32                int
#define sqlite3_stmt_p32     PTR32
#define sqlite3_p32          PTR32
#define PTRPTR32             PTR32

// for a 64 bit terminal
#define PTR64                long
#define sqlite3_stmt_p64     PTR64
#define sqlite3_p64          PTR64
#define PTRPTR64             PTR64

Gerekirse 32 ve 64 bit işaretçiler için tüm API işlevleri aşırı yüklenecektir. Lütfen bağlayıcının tüm işaretçilerinin 64 bit olacağını unutmayın. Aşırı yüklenmiş API işlevlerinde doğrudan 32 bit'e dönüştürülecektir. API işlevi içe aktarma kaynak kodu, SQLite3Import.mqh içinde sağlanır


SQLite Veri Türleri

SQLite Sürüm 3'te beş veri türü vardır

Tür
Açıklama
NULL NULL değeri.
TAMSAYI Depolanan değerin büyüklüğüne bağlı olarak 1, 2, 3, 4, 6 veya 8 bayt olarak depolanan tamsayı.
GERÇEK 8 baytlık gerçek sayı.
METİN UTF-8 veya UTF-16 kodlaması kullanılarak saklanan, bitiş karakteri \0 olan metin dizesi.
BLOB Keyfi ikili veri


Diğer tür adlarını da kullanabilirsiniz, ör. bir SQL sorgusundan bir tablo oluştururken bir alanın veri tipini belirtmek için çeşitli DBMS'lerde kabul edilen BIGINT veya INT. Bu durumda, SQLite onları kendi iç türlerinden birinde, bu durumda TAMSAYI'ya dönüştürür. Veri türleri ve ilişkileri hakkında daha fazla bilgi için lütfen dokümantasyonu http://www.sqlite.org/datatype3.html okuyun.


2.1. Bir Veritabanı Açma ve Kapatma

Bildiğiniz gibi, SQLite3'teki bir veritabanı normal bir dosyadır. Yani bir veritabanını açmak aslında bir dosyayı açıp onun tanıtıcısını almaya eşittir.

sqlite3_open işlevi kullanılarak yapılır:

int sqlite3_open(const uchar &filename[], sqlite3_p64 &ppDb);

filename [in]  - a pathname or file name if the file is being opened at the current location.
ppDb     [out] - variable that will store the file handle address.

The function returns SQLITE_OK in case of success or else an error code.

sqlite3_close işlevi kullanılarak bir veritabanı dosyası kapatılır:

int sqlite3_close(sqlite3_p64 ppDb);

ppDb [in] - file handle

The function returns SQLITE_OK in case of success or else an error code.


Bağlayıcıda veritabanı açma ve kapama fonksiyonları oluşturalım.

//+------------------------------------------------------------------+
//| CSQLite3Base class                                               |
//+------------------------------------------------------------------+
class CSQLite3Base
  {
   sqlite3_p64       m_db;             // pointer to database file
   bool              m_bopened;        // flag "Is m_db handle valid"
   string            m_dbfile;         // path to database file

public:
                     CSQLite3Base();   // constructor
   virtual          ~CSQLite3Base();   // destructor


public:
   //--- connection to database 
   bool              IsConnected();
   int               Connect(string dbfile);
   void              Disconnect();
   int               Reconnect();
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSQLite3Base::CSQLite3Base()
  {
   m_db=NULL;
   m_bopened=false;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSQLite3Base::~CSQLite3Base()
  {
   Disconnect();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CSQLite3Base::IsConnected()
  {
   return(m_bopened && m_db);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CSQLite3Base::Connect(string dbfile)
  {
   if(IsConnected())
      return(SQLITE_OK);
   m_dbfile=dbfile;
   return(Reconnect());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CSQLite3Base::Disconnect()
  {
   if(IsConnected())
      ::sqlite3_close(m_db);
   m_db=NULL;
   m_bopened=false;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CSQLite3Base::Reconnect()
  {
   Disconnect();
   uchar file[];
   StringToCharArray(m_dbfile,file);
   int res=::sqlite3_open(file,m_db);
   m_bopened=(res==SQLITE_OK && m_db);
   return(res);
  }

Bağlayıcı artık bir veritabanını açıp kapatabilir. Şimdi performansını basit bir script dosyasıyla kontrol edin:

#include <MQH\Lib\SQLite3\SQLite3Base.mqh>

CSQLite3Base sql3; // database connector
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
{   
//--- open database connection
   if(sql3.Connect("SQLite3Test.db3")!=SQLITE_OK)
      return;
//--- close connection
    sql3.Disconnect();
}

Komut dosyasını hata ayıklama modunda çalıştırın, derin bir nefes alın ve her dizenin çalışmasını kontrol edin. Sonuç olarak MetaTrader 5 terminal kurulum klasöründe bir veritabanı dosyası görünür. Bu başarı için kendinizi tebrik edin ve bir sonraki bölüme geçin.


2.2. SQL Sorgularının Yürütülmesi

SQLite3'teki herhangi bir SQL sorgusu en az üç aşamadan geçmelidir:

  1. sqlite3_prepare - ifadelerin listesini doğrulama ve alma;
  2. sqlite3_step - bu ifadelerin yürütülmesi;
  3. sqlite3_finalize - sonlandırma ve bellek temizleme.

Bu yapı esas olarak tabloları oluşturmak veya silmek ve ayrıca ikili olmayan verileri yazmak için, yani bir SQL sorgusunun yürütme başarı durumu dışında herhangi bir veri döndürmeyi gerektirmediği durumlar için uygundur.

Sorgu veri almayı veya ikili veri yazmayı içeriyorsa sırasıyla ikinci aşamada sqlite3_column_хх veya sqlite3_bind_хх işlevi kullanılır. Bu işlevler bir sonraki bölümde ayrıntılı olarak açıklanmaktadır.

Basit bir SQL sorgusu yürütmek için CSQLite3Base::Query yöntemini yazalım:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CSQLite3Base::Query(string query)
  {
//--- check connection
   if(!IsConnected())
      if(!Reconnect())
         return(SQLITE_ERROR);
//--- check query string
   if(StringLen(query)<=0)
      return(SQLITE_DONE);
   sqlite3_stmt_p64 stmt=0; // variable for pointer
//--- get pointer
   PTR64 pstmt=::memcpy(stmt,stmt,0);
   uchar str[];
   StringToCharArray(query,str);
//--- prepare statement and check result
   int res=::sqlite3_prepare(m_db,str,-1,pstmt,NULL);
   if(res!=SQLITE_OK)
      return(res);
//--- execute
   res=::sqlite3_step(pstmt);
//--- clean
   ::sqlite3_finalize(pstmt);
//--- return result
   return(res);
  }

Gördüğünüz gibi sqlite3_prepare, sqlite3_step ve sqlite3_finalize işlevleri birbirini takip ediyor.

SQLite'da tablolarla çalışırken CSQLite3Base::Query çalıştırmayı düşünün:

// Create the table (CREATE TABLE)
sql3.Query("CREATE TABLE IF NOT EXISTS `TestQuery` (`ticket` INTEGER, `open_price` DOUBLE, `comment` TEXT)");

Bu komutu yürüttükten sonra, veritabanında tablo belirir:

// Rename the table  (ALTER TABLE  RENAME)
sql3.Query("ALTER TABLE `TestQuery` RENAME TO `Trades`");

// Add the column (ALTER TABLE  ADD COLUMN)
sql3.Query("ALTER TABLE `Trades` ADD COLUMN `profit`");

Bu komutları yürüttükten sonra, tabloyu yeni bir ad ve ek bir alanla alırız:

// Add the row (INSERT INTO)
sql3.Query("INSERT INTO `Trades` VALUES(3, 1.212, 'info', 1)");

// Update the row (UPDATE)
sql3.Query("UPDATE `Trades` SET `open_price`=5.555, `comment`='New price'  WHERE(`ticket`=3)")

Yeni satır eklenip değiştirildikten sonra tabloda aşağıdaki giriş görünür:

Son olarak veritabanını temizlemek için aşağıdaki komutlar birbiri ardına yürütülmelidir.

// Delete all rows from the table (DELETE FROM)
sql3.Query("DELETE FROM `Trades`")

// Delete the table (DROP TABLE)
sql3.Query("DROP TABLE IF EXISTS `Trades`");

// Compact database (VACUUM)
sql3.Query("VACUUM");

Bir sonraki bölüme geçmeden önce, hata açıklaması alan yönteme ihtiyacımız vardır. Kendi deneyimlerime göre, hata kodunun bol miktarda bilgi sağlayabileceğini söyleyebilirim, ancak hatanın açıklaması, SQL sorgu metninde, tespitini ve düzeltmesini basitleştiren bir hatanın göründüğü yeri gösterir.

const PTR64 sqlite3_errmsg(sqlite3_p64 db);

db [in] - handle received by function sqlite3_open

The pointer is returned to the string containing the error description.

Bağlayıcıda, strcpy ve strlen kullanarak işaretçiden bu dizeyi alma yöntemini eklemeliyiz.

//+------------------------------------------------------------------+
//| Error message                                                    |
//+------------------------------------------------------------------+
string CSQLite3Base::ErrorMsg()
  {
   PTR64 pstr=::sqlite3_errmsg(m_db);  // get message string
   int len=::strlen(pstr);             // length of string
   uchar str[];
   ArrayResize(str,len+1);             // prepare buffer
   ::strcpy(str,pstr);                 // read string to buffer
   return(CharArrayToString(str));     // return string
  }


2.3. Tablolardan Veri Alma

Bölüm 2.2'nin başında belirttiğim gibi, veri okuma sqlite3_column_хх fonksiyonları kullanılarak gerçekleştirilir. Bu şematik olarak aşağıdaki gibi gösterilebilir:

  1. sqlite3_prepare
  2. sqlite3_column_count - elde edilen tablonun sütun sayısını bulun
  3. Mevcut adım sonucu sqlite3_step == SQLITE_ROW iken
    1. sqlite3_column_хх - dize hücrelerini okuyun
  4. sqlite3_finalize

Veri okuma ve yazma ile ilgili kapsamlı bir bölüme yaklaştığımız için, şimdi de tüm veri alışverişinde kullanılan üç kapsayıcı sınıfını tanımlayalım. Gerekli veri modeli, verilerin veritabanında nasıl saklandığına bağlıdır:

Veritabanı
|
Tablo bir satır dizisidir.
|
Satır bir hücre dizisidir.
|
Hücre , isteğe bağlı uzunlukta bir bayt arabelleğidir.


//+------------------------------------------------------------------+
//| CSQLite3Table class                                              |
//+------------------------------------------------------------------+
class CSQLite3Table
  {

public:
   string            m_colname[]; // column name
   CSQLite3Row       m_data[];    // database rows
//...
  };
//+------------------------------------------------------------------+
//| CSQLite3Row class                                                |
//+------------------------------------------------------------------+
class CSQLite3Row
  {

public:
   CSQLite3Cell      m_data[];
//...
  };
//+------------------------------------------------------------------+
//| CSQLite3Cell class                                               |
//+------------------------------------------------------------------+
class CSQLite3Cell
  {

public:
   enCellType        type;
   CByteImg          buf;
//...
  };

Gördüğünüz gibi, CSQLite3Row ve CSQLite3Table bağlantıları ilkseldir; bunlar geleneksel veri dizileridir. CSQLite3Cell hücre sınıfı ayrıca uchar veri dizisi + Veri türü alanına sahiptir. Bayt dizisi CByteImage sınıfında uygulanır (iyi bilinen CFastFile ile benzer).

Bağlayıcının çalışmasını kolaylaştırmak ve hücre veri türlerini yönetmek için aşağıdaki numaralandırmayı oluşturdum:

enum enCellType
  {
   CT_UNDEF,
   CT_NULL,
   CT_INT,
   CT_INT64,
   CT_DBL,
   CT_TEXT,
   CT_BLOB,
   CT_LAST
  };

İlk hücre durumunu tanımlamak için beş temel SQLite3 türüne CT_UNDEF türünün eklendiğini unutmayın. Tüm TAMSAYI türü, benzer şekilde bölünmüş sqlite3_bind_intXX ve sqlite3_column_intXX işlevlerine göre CT_INT ve CT_INT64 olarak ayrılır.

Veri Alma

Hücreden veri almak için sqlite3_column_хх türü fonksiyonları genelleyen metodu oluşturmalıyız. Veri tipini ve boyutunu kontrol edecek ve bunu CSQLite3Cell hücresine yazacaktır.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CSQLite3Base::ReadStatement(sqlite3_stmt_p64 stmt,int column,CSQLite3Cell &cell)
  {
   cell.Clear();
   if(!stmt || column<0)
      return(false);
   int bytes=::sqlite3_column_bytes(stmt,column);
   int type=::sqlite3_column_type(stmt,column);
//---
   if(type==SQLITE_NULL)
      cell.type=CT_NULL;
   else if(type==SQLITE_INTEGER)
     {
      if(bytes<5)
         cell.Set(::sqlite3_column_int(stmt,column));
      else
         cell.Set(::sqlite3_column_int64(stmt,column));
     }
   else if(type==SQLITE_FLOAT)
      cell.Set(::sqlite3_column_double(stmt,column));
   else if(type==SQLITE_TEXT || type==SQLITE_BLOB)
     {
      uchar dst[];
      ArrayResize(dst,bytes);
      PTR64 ptr=0;
      if(type==SQLITE_TEXT)
         ptr=::sqlite3_column_text(stmt,column);
      else
         ptr=::sqlite3_column_blob(stmt,column);
      ::memcpy(dst,ptr,bytes);
      if(type==SQLITE_TEXT)
         cell.Set(CharArrayToString(dst));
      else
         cell.Set(dst);
     }
   return(true);
  }

İşlev oldukça büyüktür, ancak yalnızca geçerli ifadedeki verileri okur ve bunları bir hücrede saklar.

Ayrıca alınan veriler için ilk parametre olarak CSQLite3Table kapsayıcı tablosunu ekleyerek CSQLite3Base::Query işlevini aşırı yüklemeliyiz.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CSQLite3Base::Query(CSQLite3Table &tbl,string query)
  {
   tbl.Clear();
//--- check connection
   if(!IsConnected())
      if(!Reconnect())
         return(SQLITE_ERROR);
//--- check query string
   if(StringLen(query)<=0)
      return(SQLITE_DONE);
//---
   sqlite3_stmt_p64 stmt=NULL;
   PTR64 pstmt=::memcpy(stmt,stmt,0);
   uchar str[]; StringToCharArray(query,str);
   int res=::sqlite3_prepare(m_db, str, -1, pstmt, NULL); if(res!=SQLITE_OK) return(res);
   int cols=::sqlite3_column_count(pstmt); // get column count
   bool b=true;
   while(::sqlite3_step(pstmt)==SQLITE_ROW) // in loop get row data
     {
      CSQLite3Row row; // row for table
      for(int i=0; i<cols; i++) // add cells to row
        {
         CSQLite3Cell cell;
         if(ReadStatement(pstmt,i,cell)) row.Add(cell); else { b=false; break; }
        }
      tbl.Add(row); // add row to table
      if(!b) break; // if error enabled
     }
// get column name
   for(int i=0; i<cols; i++)
     {
      PTR64 pstr=::sqlite3_column_name(pstmt,i); if(!pstr) { tbl.ColumnName(i,""); continue; }
      int len=::strlen(pstr);
      ArrayResize(str,len+1);
      ::strcpy(str,pstr);
      tbl.ColumnName(i,CharArrayToString(str));
     }
   ::sqlite3_finalize(stmt);  // clean
   return(b?SQLITE_DONE:res); // return result code
  }

Veri almak için gerekli tüm fonksiyonlara sahibiz. Örneklerine geçelim:

// Read data (SELECT)
CSQLite3Table tbl;
sql3.Query(tbl, "SELECT * FROM `Trades`")

Aşağıdaki komutu Yazdır(TablePrint(tbl)) kullanarak terminalde sorgunun sonucunu yazdırın. Dergide aşağıdaki girişleri göreceğiz (sıralama aşağıdan yukarıya doğrudur):

// Sample calculation of stat. data from the tables (COUNT, MAX, AVG ...)
sql3.Query(tbl, "SELECT COUNT(*) FROM `Trades` WHERE(`profit`>0)")   
sql3.Query(tbl, "SELECT MAX(`ticket`) FROM `Trades`")
sql3.Query(tbl, "SELECT SUM(`profit`) AS `sumprof`, AVG(`profit`) AS `avgprof` FROM `Trades`")

// Get the names of all tables in the base
sql3.Query(tbl, "SELECT `name` FROM `sqlite_master` WHERE `type`='table' ORDER BY `name`;");

Sorgu sonucu, Yazdır(TablePrint(tbl)) kullanılarak aynı şekilde yazdırılır. Mevcut tabloyu görebiliriz:

Örneklerden de anlaşılacağı gibi sorgu yürütme sonuçları tbl değişkenine yerleştirilir. Bunun ardından, bunları kendi takdirinize göre kolayca alabilir ve işleyebilirsiniz.


2.4. Bağlama ile Parametre Verisi Yazma

Yeni başlayanlar için önemli olabilecek bir diğer konu da veri tabanına "uygunsuz" formatta veri yazmaktır. Tabii ki, burada ikili verileri kastediyoruz. İlk sıfırla karşılaşıldığında bir dize tamamlanmış olarak kabul edildiğinden, ortak bir EKLE veya GÜNCLLE metin ifadesinde doğrudan iletilemez. Aynı sorun, dizenin kendisi tek tırnak ' içerdiğinde ortaya çıkar.

Geç bağlama, özellikle tablo geniş olduğunda bazı durumlarda faydalı olabilir. Bir şeyi kolayca kaçırabileceğiniz için tüm alanları tek bir satıra yazmak zor ve güvenilmez olur. sqlite3_bind_хх serisinin işlevleri, bağlama işlemi için gereklidir.

Bağlama uygulamak için iletilen veriler yerine bir şablon eklenmelidir. "?" işareti durumlarından birini ele alacağım. Başka bir deyişle, GÜNCLLE sorgusu aşağıdaki gibi görünecektir:

UPDATE `Trades` SET `open_price`=?, `comment`=? WHERE(`ticket`=3)


Ardından, sqlite3_bind_double ve sqlite3_bind_text işlevleri open_price ve comment hücreleri veri yerleştirmek için birbiri ardına yürütülmelidir. Genellikle bind işlevleriyle çalışmak şu şekilde temsil edilebilir:

  1. sqlite3_prepare
  2. sqlite3_bind_хх sayfasını açın ve gerekli verileri ifadeye yazın
  3. sqlite3_step
  4. sqlite3_finalize

Tür sayısı ile sqlite3_bind_xx, yukarıda açıklanan okuma işlevlerini tamamen tekrarlar. Böylece, CSQLite3Base::BindStatement konumundaki bağlayıcıda kolayca birleştirebilirsiniz:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CSQLite3Base::BindStatement(sqlite3_stmt_p64 stmt,int column,CSQLite3Cell &cell)
  {
   if(!stmt || column<0)
      return(false);
   int bytes=cell.buf.Len();
   enCellType type=cell.type;
//---
   if(type==CT_INT)        return(::sqlite3_bind_int(stmt, column+1, cell.buf.ViewInt())==SQLITE_OK);
   else if(type==CT_INT64) return(::sqlite3_bind_int64(stmt, column+1, cell.buf.ViewInt64())==SQLITE_OK);
   else if(type==CT_DBL)   return(::sqlite3_bind_double(stmt, column+1, cell.buf.ViewDouble())==SQLITE_OK);
   else if(type==CT_TEXT)  return(::sqlite3_bind_text(stmt, column+1, cell.buf.m_data, cell.buf.Len(), SQLITE_STATIC)==SQLITE_OK);
   else if(type==CT_BLOB)  return(::sqlite3_bind_blob(stmt, column+1, cell.buf.m_data, cell.buf.Len(), SQLITE_STATIC)==SQLITE_OK);
   else if(type==CT_NULL)  return(::sqlite3_bind_null(stmt, column+1)==SQLITE_OK);
   else                    return(::sqlite3_bind_null(stmt, column+1)==SQLITE_OK);
  }

Bu yöntemin tek amacı, iletilen hücrenin arabelleğini ifadeye yazmaktır.

CQLite3Table::QueryBind yöntemini benzer şekilde ekleyelim. İlk argümanı, yazmak için veri dizesidir:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CSQLite3Base::QueryBind(CSQLite3Row &row,string query) // UPDATE <table> SET <row>=?, <row2>=?  WHERE (cond)
  {
   if(!IsConnected())
      if(!Reconnect())
         return(SQLITE_ERROR);
//---
   if(StringLen(query)<=0 || ArraySize(row.m_data)<=0)
      return(SQLITE_DONE);
//---
   sqlite3_stmt_p64 stmt=NULL;
   PTR64 pstmt=::memcpy(stmt,stmt,0);
   uchar str[];
   StringToCharArray(query,str);
   int res=::sqlite3_prepare(m_db, str, -1, pstmt, NULL);
   if(res!=SQLITE_OK)
      return(res);
//---
   bool b=true;
   for(int i=0; i<ArraySize(row.m_data); i++)
     {
      if(!BindStatement(pstmt,i,row.m_data[i]))
        {
         b=false;
         break;
        }
     }
   if(b)
      res=::sqlite3_step(pstmt); // executed
   ::sqlite3_finalize(pstmt);    // clean
   return(b?res:SQLITE_ERROR);   // result
  }

Amacı, dizeyi uygun parametrelere yazmaktır.


2.5. İşlemler / Çok Satırlı Ekler

Bu konuya geçmeden önce bir SQLite API fonksiyonunu daha bilmeniz gerekiyor. Önceki bölümde, isteklerin üç aşamalı olarak ele alınmasını anlatmıştım: hazırla+adım+sonlandır. Ancak, bir alternatif (bazı durumlarda basit ve hatta kritik) bir çözüm vardır; sqlite3_exec işlevi:

int sqlite3_exec(sqlite3_p64 ppDb, const char &sql[], PTR64 callback, PTR64 pvoid, PTRPTR64 errmsg);

ppDb [in] - database handle
sql  [in] - SQL query
The remaining three parameters are not considered yet in relation to MQL5.

It returns SQLITE_OK in case of success or else an error code.

Bunun temel amacı, üç aşamalı yapılar oluşturmadan sorguyu tek bir çağrıda yürütmektir.

Şimdi çağrıyı bağlayıcıya ekleyelim:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CSQLite3Base::Exec(string query)
  {
   if(!IsConnected())
      if(!Reconnect())
         return(SQLITE_ERROR);
   if(StringLen(query)<=0)
      return(SQLITE_DONE);
   uchar str[];
   StringToCharArray(query,str);
   int res=::sqlite3_exec(m_db,str,NULL,NULL,NULL);
   return(res);
  }

Ortaya çıkan yöntemin kullanımı kolaydır. Örneğin aşağıdaki şekilde tablo silme (DROP TABLE) veya veritabanı kompakt (VACUUM) komutunu çalıştırabilirsiniz:

sql3.Exec("DROP TABLE `Trades`");

sql3.Exec("VACUUM");


İşlemler

Şimdi, tabloya birkaç bin satır eklememiz gerektiğini varsayalım. Tüm bunları döngüye eklersek:

for (int i=0; i<N; i++)
   sql3.Query("INSERT INTO `Table` VALUES(1, 2, 'text')");

yürütme çok yavaş olacaktır (10(!) saniyeden fazla). Bu nedenle SQLite'ta bu tür bir uygulama önerilmez. Buradaki en uygun çözüm işlemleri kullanmaktır: tüm SQL deyimleri ortak bir listeye girilir ve ardından tek bir sorgu olarak iletilir.

İşlemin başlangıcını ve bitişini yazmak için aşağıdaki SQL deyimleri kullanılır:

BEGIN
...
COMMIT

Tüm içerikler son COMMIT ifadesinde yürütülür. ROLLBACK ifadesi, döngünün kesintiye uğraması veya önceden eklenmiş deyimlerin yürütülmemesi durumunda kullanılır.

Örnek olarak, tüm hesap anlaşmaları tabloya eklenir.

#include <MQH\Lib\SQLite3\SQLite3Base.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
   CSQLite3Base sql3;

//--- open database connection
   if(sql3.Connect("Deals.db3")!=SQLITE_OK) return;
//---
   if(sql3.Query("CREATE TABLE IF NOT EXISTS `Deals` (`ticket` INTEGER PRIMARY KEY, `open_price` DOUBLE, `profit` DOUBLE, `comment` TEXT)")!=SQLITE_DONE)
     {
      Print(sql3.ErrorMsg());
      return;
     }

//--- create transaction
   if(sql3.Exec("BEGIN")!=SQLITE_OK)
     {
      Print(sql3.ErrorMsg());
      return;
     }
   HistorySelect(0,TimeCurrent());
//--- dump all deals from terminal to table 
   for(int i=0; i<HistoryDealsTotal(); i++)
     {
      CSQLite3Row row;
      long ticket=(long)HistoryDealGetTicket(i);
      row.Add(ticket);
      row.Add(HistoryDealGetDouble(ticket, DEAL_PRICE));
      row.Add(HistoryDealGetDouble(ticket, DEAL_PROFIT));
      row.Add(HistoryDealGetString(ticket, DEAL_COMMENT));
      if(sql3.QueryBind(row,"REPLACE INTO `Deals` VALUES("+row.BindStr()+")")!=SQLITE_DONE)
        {
         sql3.Exec("ROLLBACK");
         Print(sql3.ErrorMsg());
         return;
        }
     }
//--- end transaction
   if(sql3.Exec("COMMIT")!=SQLITE_OK)
      return;

//--- get statistical information from table
   CSQLite3Table tbl;
   CSQLite3Cell cell;

   if(sql3.Query(tbl,"SELECT COUNT(*) FROM `Deals` WHERE(`profit`>0)")!=SQLITE_DONE)
     {
      Print(sql3.ErrorMsg());
      return;
     }
   tbl.Cell(0,0,cell);
   Print("Count(*)=",cell.GetInt64());
//---
   if(sql3.Query(tbl,"SELECT SUM(`profit`) AS `sumprof`, AVG(`profit`) AS `avgprof` FROM `Deals`")!=SQLITE_DONE)
     {
      Print(sql3.ErrorMsg());
      return;
     }
   tbl.Cell(0,0,cell);
   Print("SUM(`profit`)=",cell.GetDouble());
   tbl.Cell(0,1,cell);
   Print("AVG(`profit`)=",cell.GetDouble());
  }

Script dosyası hesaba uygulandıktan sonra, hesap anlaşmalarını anında tabloya ekler.

İstatistikler terminal günlüğünde görüntülenir

Komut dosyasıyla oynayabilirsiniz: BEGIN, ROLLBACK ve COMMIT içeren satırları yorumlayın. Hesabınızda yüzlerce fırsat varsa farkı hemen göreceksiniz. Bu arada some tests adresine göre, SQLite işlemleeri MySQL veya PostgreSQL'den daha hızlı çalışıyor.


3. 64-Bit Sürümünü Derleme (sqlite3_64.dll)

  1. SQLite kaynak kodunu indirin (birleşme) ve sqlite3.c dosyasını bulun.
  2. sqlite-dll-win32 dosyasını indirin ve buradan sqlite3.dll dosyasını çıkarın.
  3. Dll dosyasının ayıklandığı klasörde LIB.EXE /DEF:sqlite3.def konsol komutunu yürütün. lib.exe dosyasının yollarının PATH sistem değişkeninde ayarlandığından emin olun veya Visual Studio'nuzda bulun.
  4. 64 bit platformlar için Sürüm yapılandırmasını seçerek DLL projesi oluşturun.
  5. İndirilen sqlite3.c ve elde edilen sqlite3.def dosyalarını projeye ekleyin. Derleyici def dosyasından bazı işlevleri kabul etmezse bunları yorumlayın.
  6. Proje ayarlarında aşağıdaki parametreler ayarlanmalıdır:
    C/C++ Genel Hata Ayıklama Bilgi Formatı = Program Veritabanı (/Zi)
    C/C++ Önceden Derlenmiş Başlıklar Önceden Derlenmiş Başlık Oluşturun/Kullanın = Önceden Derlenmiş Başlıkları Kullanma (/Yu)
  7. Derleyin ve 64 bit dll alın.


Sonuç

Umarım makale, SQLite'de uzmanlaşmanız için vazgeçilmez rehberiniz olur. Belki ileride projelerinizde kullanırsınız. Bu kısa genel bakış, uygulamalar için mükemmel ve güvenilir bir çözüm olarak SQLite'ın işlevselliği hakkında bazı bilgiler sağlamıştır.

Bu yazıda, alım satım verilerini işlerken karşılaşabileceğiniz tüm durumları anlattım. Bir ev ödevi olarak, her sembol için tabloya onay işareti ekleyen basit bir onay işareti toplayıcı geliştirmenizi tavsiye ederim. Aşağıdaki ekte sınıf kitaplığı kaynak kodunu ve test komut dosyalarını bulabilirsiniz.

Size iyi şanslar ve büyük kazançlar diliyorum!

MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/862

Ekli dosyalar |
MQL5.zip (790.12 KB)
Üç Satır Kesme Grafiği Oluşturma Göstergesi Üç Satır Kesme Grafiği Oluşturma Göstergesi
Bu makale Steve Nison'ın "Candlesticks Ötesinde" adlı kitabında önerilen Üç Satır Arası tablosuna ayrılmıştır. Bu grafiğin en büyük avantajı, bir önceki harekete göre bir fiyattaki küçük dalgalanmaları filtrelemeye izin vermesidir. Grafik oluşturma ilkesini, gösterge kodunu ve buna dayalı bazı alım satım stratejileri örneklerini tartışacağız.
Sinir Ağları Ucuz ve Neşeli - NeuroPro'yu MetaTrader 5 ile Bağlayın Sinir Ağları Ucuz ve Neşeli - NeuroPro'yu MetaTrader 5 ile Bağlayın
Alım satım için belirli sinir ağı programları pahalı ve karmaşık görünüyorsa veya tam tersine çok basitse NeuroPro'yu deneyin. Ücretsizdir ve amatörler için en uygun işlevsellik setini içerir. Bu makale size MetaTrader 5'in nasıl kullanılacağını anlatacaktır.
Alım Satım Robotlarının Hikayeleri: Daha Az mı Daha Fazla mı? Alım Satım Robotlarının Hikayeleri: Daha Az mı Daha Fazla mı?
İki yıl önce "Son Haçlı Seferi"nde piyasa bilgilerini görüntülemek için oldukça ilginç ancak şu anda yaygın olarak kullanılmayan bir yöntem olan nokta ve şekil grafiklerini inceledik. Şimdi nokta ve şekil grafiğinde tespit edilen formasyonlara göre bir alım satım robotu yazmaya çalışmanızı öneririm.
Öğretici video: MetaTrader Sinyalleri Hizmeti Öğretici video: MetaTrader Sinyalleri Hizmeti
Sadece 15 dakikada bu eğitim videosu MetaTrader Sinyalleri Hizmetinin ne olduğunu açıklar ve alım satım sinyallerine nasıl abone olunacağını ve hizmetimizde nasıl sinyal sağlayıcı olunacağını ayrıntılı olarak gösterir. Bu öğreticiyi izleyerek herhangi bir alım satım sinyaline abone olabilir veya hizmetimizde kendi sinyallerinizi yayınlayabilir ve tanıtabilirsiniz.