![SQL ve MQL5: SQLite Veritabanı ile Çalışmak](https://c.mql5.com/i/articles/overlay.png)
SQL ve MQL5: SQLite Veritabanı ile Çalışmak
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:
- DLL dosyasını eksiksiz bir API işlevleri seti ile kullanabilirsiniz;
- bir EXE dosyasına kabuk komutlarını kullanabilirsiniz;
- 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:
- sqlite3_prepare - ifadelerin listesini doğrulama ve alma;
- sqlite3_step - bu ifadelerin yürütülmesi;
- 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:
- sqlite3_prepare
- sqlite3_column_count - elde edilen tablonun sütun sayısını bulun
- Mevcut adım sonucu sqlite3_step == SQLITE_ROW iken
- sqlite3_column_хх - dize hücrelerini okuyun
- 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:
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:
- sqlite3_prepare
- sqlite3_bind_хх sayfasını açın ve gerekli verileri ifadeye yazın
- sqlite3_step
- 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)
- SQLite kaynak kodunu indirin (birleşme) ve sqlite3.c dosyasını bulun.
- sqlite-dll-win32 dosyasını indirin ve buradan sqlite3.dll dosyasını çıkarın.
- 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.
- 64 bit platformlar için Sürüm yapılandırmasını seçerek DLL projesi oluşturun.
- İndirilen sqlite3.c ve elde edilen sqlite3.def dosyalarını projeye ekleyin. Derleyici def dosyasından bazı işlevleri kabul etmezse bunları yorumlayın.
- 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)
- 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
![Üç Satır Kesme Grafiği Oluşturma Göstergesi](https://c.mql5.com/2/10/logo.png)
![Sinir Ağları Ucuz ve Neşeli - NeuroPro'yu MetaTrader 5 ile Bağlayın](https://c.mql5.com/2/12/NeuroPro_MetaTrader4_neural_net.png)
![Alım Satım Robotlarının Hikayeleri: Daha Az mı Daha Fazla mı?](https://c.mql5.com/2/0/tales.png)
![Öğretici video: MetaTrader Sinyalleri Hizmeti](https://c.mql5.com/2/0/signal-video.png)
![MQL5 - MetaTrader 5 müşteri terminalinde yerleşik ticaret stratejileri dili](https://c.mql5.com/i/registerlandings/logo-2.png)
- Ücretsiz ticaret uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz