Initial Commit.

This commit is contained in:
ZhijuCen
2026-06-23 21:47:51 +08:00
commit d17f68e979
5201 changed files with 318318 additions and 0 deletions
@@ -0,0 +1,109 @@
# Working with databases
The functions for working with databases apply the popular and easy-to-use [SQLite](https://www.sqlite.org/index.html) engine. The convenient feature of this engine is that the entire database is located in a single file on a user PC's hard disk.
The functions allow for convenient creation of tables, adding data to them, performing modifications and sampling using simple SQL requests:
- receiving trading history and quotes from any formats,
- saving optimization and test results,
- preparing and exchanging data with other analysis packages,
- storing MQL5 application settings and status.
Queries allow using [statistical](/en/docs/database#math) and [mathematical](/en/docs/database#stats) functions.
The functions for working with databases allow you to replace the most repetitive large data array handling operations with SQL requests, so that it is often possible to use the [DatabaseExecute](/en/docs/database/databaseexecute)/[DatabasePrepare](/en/docs/database/databaseprepare) calls instead of programming complex loops and comparisons. Use the [DatabaseReadBind](/en/docs/database/databasereadbind) function to conveniently obtain query results in a ready-made structure. The function allows reading all record fields at once within a single call.
To accelerate reading, writing and modification, a database can be opened/created in RAM with the DATABASE_OPEN_MEMORY flag, although such a database is available only to a specific application and is not shared. When working with databases located on the hard disk, bulk data inserts/changes should be wrapped in transactions using [DatabaseTransactionBegin](/en/docs/database/databasetransactionbegin)/DatabaseTransactionCommit/DatabaseTransactionRollback. This accelerates the process hundreds of times.
To start working with the functions, read the article [SQLite: Native handling of SQL databases in MQL5](https://www.mql5.com/en/articles/7463).
| Function | Action |
| --- | --- |
| DatabaseOpen | Opens or creates a database in a specified file |
| DatabaseClose | Closes a database |
| DatabaseImport | Imports data from a file into a table |
| DatabaseExport | Exports a table or an SQL request execution result to a CSV file |
| DatabasePrint | Prints a table or an SQL request execution result in the Experts journal |
| DatabaseTableExists | Checks the presence of the table in a database |
| DatabaseExecute | Executes a request to a specified database |
| DatabasePrepare | Creates a handle of a request, which can then be executed using DatabaseRead() |
| DatabaseReset | Resets a request, like after calling DatabasePrepare() |
| DatabaseBind | Sets a parameter value in a request |
| DatabaseBindArray | Sets an array as a parameter value |
| DatabaseRead | Moves to the next entry as a result of a request |
| DatabaseReadBind | Moves to the next record and reads data into the structure from it |
| DatabaseFinalize | Removes a request created in DatabasePrepare() |
| DatabaseTransactionBegin | Starts transaction execution |
| DatabaseTransactionCommit | Completes transaction execution |
| DatabaseTransactionRollback | Rolls back transactions |
| DatabaseColumnsCount | Gets the number of fields in a request |
| DatabaseColumnName | Gets a field name by index |
| DatabaseColumnType | Gets a field type by index |
| DatabaseColumnSize | Gets a field size in bytes |
| DatabaseColumnText | Gets a field value as a string from the current record |
| DatabaseColumnInteger | Gets the int type value from the current record |
| DatabaseColumnLong | Gets the long type value from the current record |
| DatabaseColumnDouble | Gets the double type value from the current record |
| DatabaseColumnBlob | Gets a field value as an array from the current record |
Statistical functions:
- mode [mode](https://en.wikipedia.org/wiki/Mode_(statistics))
- median [median](https://en.wikipedia.org/wiki/Median) (50th percentile)
- percentile_25 25th [percentile](https://en.wikipedia.org/wiki/Quantile)
- percentile_75
- percentile_90
- percentile_95
- percentile_99
- stddev or stddev_samp — sample standard deviation
- stddev_pop — population standard deviation
- variance or var_samp — sample variance
- var_pop — population variance
Mathematical functions
- [acos(X)](https://sqlite.org/lang_mathfunc.html#acos) arccosine in radians
- [acosh(X)](https://sqlite.org/lang_mathfunc.html#acosh) hyperbolic arccosine
- [asin(X)](https://sqlite.org/lang_mathfunc.html#asin) arcsine in radians
- [asinh(X)](https://sqlite.org/lang_mathfunc.html#asinh) hyperbolic arcsine
- [atan(X)](https://sqlite.org/lang_mathfunc.html#atan) arctangent in radians
- [atan2(X,Y)](https://sqlite.org/lang_mathfunc.html#atan2) arctangent in radians of the X/Y ratio
- [atanh(X)](https://sqlite.org/lang_mathfunc.html#atanh) hyperbolic arctangent
- [ceil(X)](https://sqlite.org/lang_mathfunc.html#ceil) rounding up to an integer
- [ceiling(X)](https://sqlite.org/lang_mathfunc.html#ceil) rounding up to an integer
- [cos(X)](https://sqlite.org/lang_mathfunc.html#cos) angle cosine in radians
- [cosh(X)](https://sqlite.org/lang_mathfunc.html#cosh) hyperbolic cosine
- [degrees(X)](https://sqlite.org/lang_mathfunc.html#degrees) convert radians into the angle
- [exp(X)](https://sqlite.org/lang_mathfunc.html#exp) exponent
- [floor(X)](https://sqlite.org/lang_mathfunc.html#floor) rounding down to an integer
- [ln(X)](https://sqlite.org/lang_mathfunc.html#ln) natural logarithm
- [log(B,X)](https://sqlite.org/lang_mathfunc.html#log) logarithm to the indicated base
- [log(X)](https://sqlite.org/lang_mathfunc.html#log) decimal logarithm
- [log10(X)](https://sqlite.org/lang_mathfunc.html#log) decimal logarithm
- [log2(X)](https://sqlite.org/lang_mathfunc.html#log2) logarithm to base 2
- [mod(X,Y)](https://sqlite.org/lang_mathfunc.html#mod) remainder of division
- [pi()](https://sqlite.org/lang_mathfunc.html#pi) approximate Pi
- [pow(X,Y)](https://sqlite.org/lang_mathfunc.html#pow) power by the indicated base
- [power(X,Y)](https://sqlite.org/lang_mathfunc.html#pow)  power by the indicated base
- [radians(X)](https://sqlite.org/lang_mathfunc.html#radians)  convert the angle into radians
- [sin(X)](https://sqlite.org/lang_mathfunc.html#sin) angle sine in radians
- [sinh(X)](https://sqlite.org/lang_mathfunc.html#sinh) hyperbolic sine
- [sqrt(X)](https://sqlite.org/lang_mathfunc.html#sqrt) square root
- [tan(X)](https://sqlite.org/lang_mathfunc.html#tan) angle tangent in radians
- [tanh(X)](https://sqlite.org/lang_mathfunc.html#tanh) hyperbolic tangent
- [trunc(X)](https://sqlite.org/lang_mathfunc.html#trunc) truncate to an integer closest to 0
Example:
```
select
  count(*) as book_count,
  cast(avg(parent) as integer) as mean,
  cast(median(parent) as integer) as median,
  mode(parent) as mode,
  percentile_90(parent) as p90,
  percentile_95(parent) as p95,
  percentile_99(parent) as p99
from moz_bookmarks;
```
@@ -0,0 +1,60 @@
# DatabaseOpen
Opens or creates a database in a specified file.
```
int  DatabaseOpen(
   string  filename,      // file name
   uint    flags          // combination of flags
   );
```
Parameters
filename
[in]  File name relative to the "MQL5\Files" folder.
flags
[in]  Combination of flags from the [ENUM_DATABASE_OPEN_FLAGS](/en/docs/database/databaseopen#enum_database_open_flags) enumeration.
Return Value
If executed successfully, the function returns the database handle, which is then used to access the database. Otherwise, it returns [INVALID_HANDLE](/en/docs/constants/namedconstants/otherconstants). To get the error code, use GetLastError(), the possible responses are:
- ERR_INTERNAL_ERROR (4001)                       critical runtime error;
- ERR_WRONG_INTERNAL_PARAMETER (4002)  - internal error, while accessing the "MQL5\Files" folder;
- ERR_INVALID_PARAMETER (4003)                   path to the database file contains an empty string, or an incompatible combination of flags is set;
- ERR_NOT_ENOUGH_MEMORY (4004)              - insufficient memory;
- ERR_WRONG_FILENAME (5002)                     - wrong database file name;
- ERR_TOO_LONG_FILENAME (5003)                 - absolute path to the database file exceeds the maximum length;
- ERR_DATABASE_TOO_MANY_OBJECTS (5122) - exceeded the maximum acceptable number of Database objects;
- ERR_DATABASE_CONNECT (5123)                  - database connection error;
- ERR_DATABASE_MISUSE (5621)                      - incorrect use of the SQLite library.
Note
If the filename parameter features NULL or the empty string "", a temporary file is created on the disk. It is automatically deleted after closing the database connection.
If the filename parameter features ":memory:", the database is created in the memory and is automatically deleted after the connection to it is closed.
If the flags parameter features none of the DATABASE_OPEN_READONLY or DATABASE_OPEN_READWRITE flags, the DATABASE_OPEN_READWRITE flag is used.
If the file extension is not specified, ".sqlite" is used.
ENUM_DATABASE_OPEN_FLAGS
| ID | Description |
| --- | --- |
| DATABASE_OPEN_READONLY | Read only |
| DATABASE_OPEN_READWRITE | Open for reading and writing |
| DATABASE_OPEN_CREATE | Create the file on a disk if necessary |
| DATABASE_OPEN_MEMORY | Create a database in RAM |
| DATABASE_OPEN_COMMON | The file is in the common folder of all terminals |
See also
[DatabaseClose](/en/docs/database/databaseclose)
@@ -0,0 +1,30 @@
# DatabaseClose
Closes a database.
```
void  DatabaseClose(
   int  database      // database handle received in DatabaseOpen
   );
```
Parameters
database
[in]  Database handle received in [DatabaseOpen()](/en/docs/database/databaseopen).
Return Value
None.
Note
After calling DatabaseClose, all [handles of requests ](/en/docs/database/databaseprepare) to the database are automatically removed and become invalid.
If the handle is invalid, the function sets the ERR_DATABASE_INVALID_HANDLE error. You can check the error using GetLastError().
See also
[DatabaseOpen](/en/docs/database/databaseopen), [DatabasePrepare](/en/docs/database/databaseprepare)
@@ -0,0 +1,135 @@
# DatabaseImport
Imports data from a file into a table.
```
long  DatabaseImport(
   int           database,          // database handle received in DatabaseOpen
   const string  table,             // name of a table to insert data
   const string  filename,          // name of a file to import data
   uint          flags,             // combination of flags
   const string  separator,         // data separator 
   ulong         skip_rows,         // how many initial strings to skip 
   const string  skip_comments      // string of characters defining comments
   );
```
Parameters
database
[in]  Database handle received in [DatabaseOpen()](/en/docs/database/databaseopen).
table
[in]  Name of a table the data from a file is to be added to.
filename
[in]  CSV file or ZIP archive for reading data. The name may contain subdirectories and is set relative to the MQL5\Files folder.
flags
[in]  Combination of flags from the [ENUM_DATABASE_IMPORT_FLAGS](/en/docs/database/databaseimport#enum_database_import_flags) enumeration.
separator
[in]  Data separator in CSV file.
skip_rows
[in]  Number of initial strings to be skipped when reading data from the file.
skip_comments
[in]  String of characters for designating strings as comments. If any character from skip_comments is detected at the beginning of a string, such a string is considered a comment and is not imported.
Return Value
Return the number of imported strings or -1 in case of an error. To get the error code, use [GetLastError()](/en/docs/check/getlasterror), the possible responses are:
- ERR_INVALID_PARAMETER (4003)                no table name specified (empty string or NULL);
- ERR_DATABASE_INTERNAL (5120)               internal database error;
- ERR_DATABASE_INVALID_HANDLE (5121)   - invalid database handle.
Note
If there is no table named table, it is generated automatically. Names and field types in the created table are defined automatically based on the file data.
If there is no table named table, it is generated automatically. Names and field types in the created table are defined automatically based on the file data.
ENUM_DATABASE_IMPORT_FLAGS
| ID | Description |
| --- | --- |
| DATABASE_IMPORT_HEADER | The first line contains the names of the table fields |
| DATABASE_IMPORT_CRLF | CRLF (the default is LF) is considered a string break |
| DATABASE_IMPORT_APPEND | Add data to the end of an existing table |
| DATABASE_IMPORT_QUOTED_STRINGS | String values enclosed in double quotes |
| DATABASE_IMPORT_COMMON_FOLDER | The file is stored in the common folder of all client terminals \Terminal\Common\File. |
Example of reading the table from the file created by the code from the [DatabaseExport](/en/docs/database/databaseexport) example:
```
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
 {
  string csv_filename;
//--- get the names of text files for downloading from the common folder of the client terminals
  string filenames[];
  if(FileSelectDialog("Select a CSV file to download a table", NULL,
                      "Text files (*.csv)|*.csv",
                      FSD_WRITE_FILE|FSD_COMMON_FOLDER, filenames, "data.csv")>0)
   {
    //--- display the name of each selected file
    if(ArraySize(filenames)==1)
      csv_filename=filenames[0];
    else
     {
      Print("Unknown error while selecting file. Error code ", GetLastError());
      return;
     }
   }
  else
   {
    Print("CSV file not selected");
    return;
   }
//--- create or open a database
  string db_filename="test.sqlite";
  int db=DatabaseOpen(db_filename, DATABASE_OPEN_READWRITE|DATABASE_OPEN_CREATE);
//--- check if the TEST table exists
  if(DatabaseTableExists(db, "TEST"))
   {
    //--- remove the TEST table
    if(!DatabaseExecute(db, "DROP TABLE IF EXISTS TEST"))
     {
      Print("Failed to drop the TEST table with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
   }
  //--- import entries from the file to the TEST table
  long imported=DatabaseImport(db, "TEST", csv_filename, DATABASE_IMPORT_HEADER|DATABASE_IMPORT_COMMON_FOLDER|DATABASE_IMPORT_APPEND, ";", 0, NULL);
  if(imported>0)
   {
    Print(imported," lines imported in table TEST");
    DatabasePrint(db,"SELECT * FROM TEST",DATABASE_PRINT_NO_INDEX);
   }
  else
    {
     Print("DatabaseImport() failed. Error ",GetLastError());
    }
//--- close the database file and inform of that
  DatabaseClose(db);
  PrintFormat("Database: %s closed", db_filename);
 }
```
See also
[DatabaseOpen](/en/docs/database/databaseopen), [DatabasePrint](/en/docs/database/databaseprint)
@@ -0,0 +1,272 @@
# DatabaseExport
Exports a table or an SQL request execution result to a CSV file. The file is created in the UTF-8 encoding.
```
long  DatabaseExport(
   int           database,           // database handle received in DatabaseOpen
   const string  table_or_sql,       // a table name or an SQL request
   const string  filename,           // a name of a CSV file for data export
   uint          flags,              // combination of flags
   const string  separator           // data separator in the CSV file
   );
```
Parameters
database
[in]  Database handle received in [DatabaseOpen()](/en/docs/database/databaseopen).
table_or_sql
[in]   A name of a table or a text of an SQL request whose results are to be exported to a specified file.
filename
[in]  A file name for data export. The path is set relative to the MQL5\Files folder.
flags
[in]  Combination of flags from the [ENUM_DATABASE_EXPORT_FLAGS](/en/docs/database/databaseexport) enumeration.
separator
[in]  Data separator. If NULL is specified, the '\t' tabulation character is used as a separator.  An empty string "" is considered a valid separator but the obtained CSV file cannot be read as a table it is considered as a set of strings.
Return Value
Return the number of exported entries or a negative value in case of an error. To get the error code, use [GetLastError()](/en/docs/check/getlasterror), the possible responses are:
- ERR_INTERNAL_ERROR (4001)                       critical runtime error;
- ERR_INVALID_PARAMETER (4003)                   path to the database file contains an empty string, or an incompatible combination of flags is set;
- ERR_NOT_ENOUGH_MEMORY (4004)              - insufficient memory;
- ERR_FUNCTION_NOT_ALLOWED(4014)           specified pipe is not allowed;
- ERR_PROGRAM_STOPPED(4022)                     operation canceled (MQL program stopped);
- ERR_WRONG_FILENAME (5002)                     - invalid file name;
- ERR_TOO_LONG_FILENAME (5003)                 - absolute path to the file exceeds the maximum length;
- ERR_CANNOT_OPEN_FILE(5004)                     unable to open the file for writing;
- ERR_FILE_WRITEERROR(5026)                       unable to write to the file;
- ERR_DATABASE_INTERNAL (5120)                 internal database error;
- ERR_DATABASE_INVALID_HANDLE (5121)      - invalid database handle;
- ERR_DATABASE_QUERY_PREPARE(5125)         request generation error;
- ERR_DATABASE_QUERY_NOT_READONLY       read-only request is allowed.
Note
If request results are exported, the SQL request should begin with "SELECT" or "select". In other words, the SQL request cannot alter the database status, otherwise DatabaseExport() fails with an error.
Database string values may contain the conversion character ('\r' or '\r\n' ), as well as the value separator character set in the separator parameter. In this case, be sure to use the DATABASE_EXPORT_QUOTED_STRINGS flag in the 'flags' parameter. If this flag is present, all displayed strings are enclosed in double quotes. If a string contains a double quote, it is replaced by two double quotes.
ENUM_DATABASE_EXPORT_FLAGS
| ID | Description |
| --- | --- |
| DATABASE_EXPORT_HEADER | Display field names in the first string |
| DATABASE_EXPORT_INDEX | Display string indices |
| DATABASE_EXPORT_NO_BOM | Do not insert BOM mark at the beginning of the file (BOM is inserted by default) |
| DATABASE_EXPORT_CRLF | Use CRLF for string break (the default is LF) |
| DATABASE_EXPORT_APPEND | Add data to the end of an existing file (by default, the file is overwritten). If the file does not exist, it will be created. |
| DATABASE_EXPORT_QUOTED_STRINGS | Display string values in double quotes. |
| DATABASE_EXPORT_COMMON_FOLDER | A CSV file is created in the common folder of all client terminals \Terminal\Common\File. |
Example:
```
input int InpRates=100;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
 {
  MqlRates rates[];
//--- remember the start time before receiving bars
  ulong start=GetMicrosecondCount();
//--- request the last 100 bars on H1
  if(CopyRates(Symbol(), PERIOD_H1, 1, InpRates, rates)<InpRates)
   {
    Print("CopyRates() failed,, Error ", GetLastError());
    return;
   }
  else
   {
    //--- how many bars were received and how much time it took to receive them
    PrintFormat("%s: CopyRates received %d bars in %d ms ",
                _Symbol, ArraySize(rates), (GetMicrosecondCount()-start)/1000);
   }
//--- set the file name for storing the database
  string filename=_Symbol+"_"+EnumToString(PERIOD_H1)+"_"+TimeToString(TimeCurrent())+".sqlite";
  StringReplace(filename, ":", "-"); // ":" character is not allowed in file names
//--- open/create the database in the common terminal folder
  int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE|DATABASE_OPEN_CREATE|DATABASE_OPEN_COMMON);
  if(db==INVALID_HANDLE)
   {
    Print("Database: ", filename, " open failed with code ", GetLastError());
    return;
   }
  else
    Print("Database: ", filename, " opened successfully");
 
//--- check if the RATES table exists
  if(DatabaseTableExists(db, "RATES"))
   {
    //--- remove the RATES table
    if(!DatabaseExecute(db, "DROP TABLE IF EXISTS RATES"))
     {
      Print("Failed to drop the RATES table with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
   }
//--- create the RATES table
  if(!DatabaseExecute(db, "CREATE TABLE RATES("
                      "SYMBOL             CHAR(10),"
                      "TIME               INT NOT NULL,"
                      "OPEN               REAL,"
                      "HIGH               REAL,"
                      "LOW                REAL,"
                      "CLOSE              REAL,"
                      "TICK_VOLUME        INT,"
                      "SPREAD             INT,"
                      "REAL_VOLUME        INT);"))
   {
    Print("DB: ", filename, " create table RATES with code ", GetLastError());
    DatabaseClose(db);
    return;
   }
//--- display the list of all fields in the RATES table
  if(DatabasePrint(db, "PRAGMA TABLE_INFO(RATES)", 0)<0)
   {
    PrintFormat("DatabasePrint(\"PRAGMA TABLE_INFO(RATES)\") failed, error code=%d at line %d", GetLastError(), __LINE__);
    DatabaseClose(db);
    return;
   }
//--- create a parametrized request to add bars to the RATES table
  string sql="INSERT INTO RATES (SYMBOL,TIME,OPEN,HIGH,LOW,CLOSE,TICK_VOLUME,SPREAD,REAL_VOLUME)"
             " VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9)"; // request parameters
  int request=DatabasePrepare(db, sql);
  if(request==INVALID_HANDLE)
   {
    PrintFormat("DatabasePrepare() failed with code=%d", GetLastError());
    Print("SQL request: ", sql);
    DatabaseClose(db);
    return;
   }
//--- set the value of the first request parameter
  DatabaseBind(request, 0, _Symbol);
//--- remember the start time before adding bars to the RATES table
  start=GetMicrosecondCount();
  DatabaseTransactionBegin(db);
  int total=ArraySize(rates);
  bool request_error=false;
  for(int i=0; i<total; i++)
   {
    //--- set the values of the remaining parameters before adding the entry
    ResetLastError();
    if(!DatabaseBind(request, 1, rates[i].time))
     {
      PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
      PrintFormat("Bar #%d line=%d", i+1, __LINE__);
      request_error=true;
      break;
     }
    //--- if the previous DatabaseBind() call was successful, set the next parameter
    if(!request_error && !DatabaseBind(request, 2, rates[i].open))
     {
      PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
      PrintFormat("Bar #%d line=%d", i+1, __LINE__);
      request_error=true;
      break;
     }
    if(!request_error && !DatabaseBind(request, 3, rates[i].high))
     {
      PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
      PrintFormat("Bar #%d line=%d", i+1, __LINE__);
      request_error=true;
      break;
     }
    if(!request_error && !DatabaseBind(request, 4, rates[i].low))
     {
      PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
      PrintFormat("Bar #%d line=%d", i+1, __LINE__);
      request_error=true;
      break;
     }
    if(!request_error && !DatabaseBind(request, 5, rates[i].close))
     {
      PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
      PrintFormat("Bar #%d line=%d", i+1, __LINE__);
      request_error=true;
      break;
     }
    if(!request_error && !DatabaseBind(request, 6, rates[i].tick_volume))
     {
      PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
      PrintFormat("Bar #%d line=%d", i+1, __LINE__);
      request_error=true;
      break;
     }
    if(!request_error && !DatabaseBind(request, 7, rates[i].spread))
     {
      PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
      PrintFormat("Bar #%d line=%d", i+1, __LINE__);
      request_error=true;
      break;
     }
    if(!request_error && !DatabaseBind(request, 8, rates[i].real_volume))
     {
      PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
      PrintFormat("Bar #%d line=%d", i+1, __LINE__);
      request_error=true;
      break;
     }
 
    //--- execute a request for inserting the entry and check for an error
    if(!request_error && !DatabaseRead(request) && (GetLastError()!=ERR_DATABASE_NO_MORE_DATA))
     {
      PrintFormat("DatabaseRead() failed with code=%d", GetLastError());
      DatabaseFinalize(request);
      request_error=true;
      break;
     }
    //--- reset the request before the next parameter update
    if(!request_error && !DatabaseReset(request))
     {
      PrintFormat("DatabaseReset() failed with code=%d", GetLastError());
      DatabaseFinalize(request);
      request_error=true;
      break;
     }
   } //--- done going through all the bars
 
//--- transactions status
  if(request_error)
   {
    PrintFormat("Table RATES: failed to add %d bars ", ArraySize(rates));
    DatabaseTransactionRollback(db);
    DatabaseClose(db);
    return;
   }
  else
   {
    DatabaseTransactionCommit(db);
    PrintFormat("Table RATES: added %d bars in %d ms",
                ArraySize(rates), (GetMicrosecondCount()-start)/1000);
   }
//--- save the RATES table to a CSV file
  string csv_filename=Symbol()+".csv";
  long saved=DatabaseExport(db, "SELECT * FROM RATES", csv_filename, DATABASE_EXPORT_HEADER|DATABASE_EXPORT_INDEX|DATABASE_EXPORT_COMMON_FOLDER, ";");
  if(saved>0)
    Print("Table RATES saved in ", Symbol(), ".csv");
  else
    Print("DatabaseExport() failed. Error ", GetLastError());
//--- close the database file and inform of that
  DatabaseClose(db);
  PrintFormat("Database: %s created and closed", filename);
```
See also
[DatabasePrint](/en/docs/database/databaseprint), [DatabaseImport](/en/docs/database/databaseimport)
@@ -0,0 +1,231 @@
# DatabasePrint
Prints a table or an SQL request execution result in the Experts journal.
```
long  DatabasePrint(
   int           database,          // database handle received in DatabaseOpen
   const string  table_or_sql,      // a table or an SQL request
   uint          flags              // combination of flags
   );
```
Parameters
database
[in]  Database handle received in [DatabaseOpen()](/en/docs/database/databaseopen).
table_or_sql
[in]  A name of a table or a text of an SQL request whose results are displayed in the Experts journal.
flags
[in]  Combination of flags defining the output formatting. The flags are defined as follows:
DATABASE_PRINT_NO_HEADER       do not display table column names (field names)
DATABASE_PRINT_NO_INDEX         do not display string indices
DATABASE_PRINT_NO_FRAME         do not display a frame separating a header and data
DATABASE_PRINT_STRINGS_RIGHT align strings to the right.
If flags=0, the columns and the strings are displayed, the header and the data are separated by the frame, while the strings are aligned to the left.
Return Value
Return the number of exported strings or -1 in case of an error. To get the error code, use [GetLastError()](/en/docs/check/getlasterror), the possible responses are:
- ERR_INTERNAL_ERROR (4001)                       critical runtime error;
- ERR_NOT_ENOUGH_MEMORY (4004)              - insufficient memory;
- ERR_DATABASE_INTERNAL (5120)                 internal database error;
- ERR_DATABASE_INVALID_HANDLE (5121)      - invalid database handle;
Note
If the journal displays request results, the SQL request should begin with "SELECT" or "select". In other words, the SQL request cannot alter the database status, otherwise DatabasePrint() fails with an error.
Example:
```
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   string filename="departments.sqlite";
//--- create or open the database in the common terminal folder
   int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE |DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)
     {
      Print("DB: ", filename, " open failed with code ", GetLastError());
      return;
     }
 
//--- create the COMPANY table
   if(!CreataTableCompany(db))
     {
      DatabaseClose(db);
      return;
     }
//--- create the DEPARTMENT table
   if(!CreataTableDepartment(db))
     {
      DatabaseClose(db);
      return;
     }
 
//--- display the list of all fields in the COMPANY and DEPARTMENT tables
   PrintFormat("Try to print request \"PRAGMA TABLE_INFO(COMPANY);PRAGMA TABLE_INFO(DEPARTMENT)\"");
   if(DatabasePrint(db, "PRAGMA TABLE_INFO(COMPANY);PRAGMA TABLE_INFO(DEPARTMENT)", 0)<0)
     {
      PrintFormat("DatabasePrint(\"PRAGMA TABLE_INFO()\") failed, error code=%d", GetLastError());
      DatabaseClose(db);
      return;
     }
//--- display the COMPANY table in the log
   PrintFormat("Try to print request \"SELECT * from COMPANY\"");
   if(DatabasePrint(db, "SELECT * from COMPANY", 0)<0)
     {
      Print("DatabasePrint failed with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
//--- request text for combining the COMPANY and DEPARTMENT tables
   string request="SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT "
                  "ON COMPANY.ID = DEPARTMENT.EMP_ID";
//--- display the table combining result
   PrintFormat("Try to print request \"SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT\"");
   if(DatabasePrint(db, request, 0)<0)
     {
      Print("DatabasePrint failed with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
//--- close the database
   DatabaseClose(db);
  }
/*
Conclusion:
Try to print request "PRAGMA TABLE_INFO(COMPANY);PRAGMA TABLE_INFO(DEPARTMENT)"
#| cid name    type     notnull dflt_value pk
-+-------------------------------------------
1|   0 ID      INT            1             1 
2|   1 NAME    TEXT           1             0 
3|   2 AGE     INT            1             0 
4|   3 ADDRESS CHAR(50)       0             0 
5|   4 SALARY  REAL           0             0 
#| cid name   type     notnull dflt_value pk
-+------------------------------------------
1|   0 ID     INT            1             1 
2|   1 DEPT   CHAR(50)       1             0 
3|   2 EMP_ID INT            1             0 
Try to print request "SELECT * from COMPANY"
#| ID NAME  AGE ADDRESS     SALARY
-+--------------------------------
1|  1 Paul   32 California 25000.0 
2|  2 Allen  25 Texas      15000.0 
3|  3 Teddy  23 Norway     20000.0 
4|  4 Mark   25 Rich-Mond  65000.0 
5|  5 David  27 Texas      85000.0 
6|  6 Kim    22 South-Hall 45000.0 
7|  7 James  24 Houston    10000.0 
Try to print request "SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT"
#| EMP_ID NAME  DEPT       
-+-------------------------
1|      1 Paul  IT Billing  
2|      2 Allen Engineering 
3|        Teddy             
4|        Mark              
5|        David             
6|        Kim               
7|      7 James Finance     
*/
//+------------------------------------------------------------------+
//| Create the COMPANY table                           |
//+------------------------------------------------------------------+
bool CreateTableCompany(int database)
  {
//--- if the COMPANY table exists, delete it
   if(DatabaseTableExists(database, "COMPANY"))
     {
      //--- delete the table
      if(!DatabaseExecute(database, "DROP TABLE COMPANY"))
        {
         Print("Failed to drop table COMPANY with code ", GetLastError());
         return(false);
        }
     }
//--- create the COMPANY table
   if(!DatabaseExecute(database, "CREATE TABLE COMPANY("
                       "ID INT PRIMARY KEY     NOT NULL,"
                       "NAME           TEXT    NOT NULL,"
                       "AGE            INT     NOT NULL,"
                       "ADDRESS        CHAR(50),"
                       "SALARY         REAL );"))
     {
      Print("DB: create table COMPANY failed with code ", GetLastError());
      return(false);
     }
 
//--- enter data to the COMPANY table
   if(!DatabaseExecute(database, "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (1, 'Paul', 32, 'California', 25000.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); "
                       "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (5, 'David', 27, 'Texas', 85000.0); "
                       "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (6, 'Kim', 22, 'South-Hall', 45000.0); "
                       "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (7, 'James', 24, 'Houston', 10000.00); "))
     {
      Print("COMPANY insert failed with code ", GetLastError());
      return(false);
     }
//--- success
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the DEPARTMENT table                       |
//+------------------------------------------------------------------+
bool CreateTableDepartment(int database)
  {
//--- if the DEPARTMENT table exists, delete it
   if(DatabaseTableExists(database, "DEPARTMENT"))
     {
      //--- delete the table
      if(!DatabaseExecute(database, "DROP TABLE DEPARTMENT"))
        {
         Print("Failed to drop table DEPARTMENT  with code ", GetLastError());
         return(false);
        }
     }
//--- create the DEPARTMENT table
   if(!DatabaseExecute(database, "CREATE TABLE DEPARTMENT ("
                       "ID      INT PRIMARY KEY   NOT NULL,"
                       "DEPT    CHAR(50)          NOT NULL,"
                       "EMP_ID  INT               NOT NULL);"))
     {
      Print("DB: create table DEPARTMENT failed with code ", GetLastError());
      return(false);
     }
 
//--- enter data to the DEPARTMENT table
   if(!DatabaseExecute(database, "INSERT INTO DEPARTMENT (ID,DEPT,EMP_ID) VALUES (1, 'IT Billing', 1); "
                       "INSERT INTO DEPARTMENT (ID,DEPT,EMP_ID) VALUES (2, 'Engineering', 2); "
                       "INSERT INTO DEPARTMENT (ID,DEPT,EMP_ID) VALUES (3, 'Finance', 7);"))
     {
      Print("DEPARTMENT insert failed with code ", GetLastError());
      return(false);
     }
//--- success
   return(true);
  }
//+-------------------------------------------------------------------
```
See also
[DatabaseExport](/en/docs/database/databaseexport), [DatabaseImport](/en/docs/database/databaseimport)
@@ -0,0 +1,36 @@
# DatabaseTableExists
Checks the presence of the table in a database.
```
bool  DatabaseTableExists(
   int     database,      // database handle received in DatabaseOpen
   string  table          // table name
   );
```
Parameters
database
[in]  Database handle received in [DatabaseOpen()](/en/docs/database/databaseopen).
table
[in]  Table name.
Return Value
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_INVALID_PARAMETER (4003)                no table name specified (empty string or NULL);
- ERR_WRONG_STRING_PARAMETER (5040)   error converting a request into a UTF-8 string;
- ERR_DATABASE_INTERNAL (5120)               internal database error;
- ERR_DATABASE_INVALID_HANDLE (5121)   - invalid database handle;
- ERR_DATABASE_EXECUTE (5124)               -  request execution error;
- ERR_DATABASE_NO_MORE_DATA (5126)    - no table exists (not an error, normal completion).
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseFinalize](/en/docs/database/databasefinalize)
@@ -0,0 +1,557 @@
# DatabaseExecute
Executes a request to a specified database.
```
bool  DatabaseExecute(
   int     database,      // database handle received in DatabaseOpen
   string  sql            // SQL request
   );
```
Parameters
database
[in]  Database handle received in [DatabaseOpen()](/en/docs/database/databaseopen).
sql
[in]  SQL request.
Return Value
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_INTERNAL_ERROR (4001)                    critical runtime error;
- ERR_INVALID_PARAMETER (4003)                sql parameter contains an empty string;
- ERR_NOT_ENOUGH_MEMORY (4004)            insufficient memory;
- ERR_WRONG_STRING_PARAMETER (5040)   error converting a request into a UTF-8 string;
- ERR_DATABASE_INTERNAL (5120)               internal database error;
- ERR_DATABASE_INVALID_HANDLE (5121)   invalid database handle;
- ERR_DATABASE_EXECUTE (5124)                request execution error.
Example:
```
//--- symbol statistics
struct Symbol_Stats
  {
   string            name;             // symbol name
   int               trades;           // number of trades for the symbol
   double            gross_profit;     // total profit for the symbol
   double            gross_loss;       // total loss for the symbol
   double            total_commission; // total commission for the symbol
   double            total_swap;       // total swaps for the symbol
   double            total_profit;     // total profit excluding swaps and commissions
   double            net_profit;       // net profit taking into account swaps and commissions
   int               win_trades;       // number of profitable trades
   int               loss_trades;      // number of losing trades
   double            expected_payoff;  // expected payoff for the trade excluding swaps and commissions
   double            win_percent;      // percentage of winning trades
   double            loss_percent;     // percentage of losing trades
   double            average_profit;   // average profit
   double            average_loss;     // average loss
   double            profit_factor;    // profit factor
  };
 
//--- Magic Number statistics
struct Magic_Stats
  {
   long              magic;            // EA's Magic Number
   int               trades;           // number of trades for the symbol
   double            gross_profit;     // total profit for the symbol
   double            gross_loss;       // total loss for the symbol
   double            total_commission; // total commission for the symbol
   double            total_swap;       // total swaps for the symbol
   double            total_profit;     // total profit excluding swaps and commissions
   double            net_profit;       // net profit taking into account swaps and commissions
   int               win_trades;       // number of profitable trades
   int               loss_trades;      // number of losing trades
   double            expected_payoff;  // expected payoff for the trade excluding swaps and commissions
   double            win_percent;      // percentage of winning trades
   double            loss_percent;     // percentage of losing trades
   double            average_profit;   // average profit
   double            average_loss;     // average loss
   double            profit_factor;    // profit factor
  };
 
//--- entry hour statistics
struct Hour_Stats
  {
   char              hour_in;          // market entry hour
   int               trades;           // number of trades in this entry hour
   double            volume;           // volume of trades in this entry hour
   double            gross_profit;     // total profit in this entry hour
   double            gross_loss;       // total loss in this entry hour
   double            net_profit;       // net profit taking into account swaps and commissions
   int               win_trades;       // number of profitable trades
   int               loss_trades;      // number of losing trades
   double            expected_payoff;  // expected payoff for the trade excluding swaps and commissions
   double            win_percent;      // percentage of winning trades
   double            loss_percent;     // percentage of losing trades
   double            average_profit;   // average profit
   double            average_loss;     // average loss
   double            profit_factor;    // profit factor
  };
 
int ExtDealsTotal=0;;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- create the file name
   string filename=IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN))+"_stats.sqlite";
//--- open/create the database in the common terminal folder
   int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)
     {
      Print("DB: ", filename, " open failed with code ", GetLastError());
      return;
     }
//--- create the DEALS table
   if(!CreateTableDeals(db))
     {
      DatabaseClose(db);
      return;
     }
   PrintFormat("Deals in the trading history: %d ", ExtDealsTotal);
 
//--- get trading statistics per symbols
   int request=DatabasePrepare(db, "SELECT r.*,"
                               "   (case when r.trades != 0 then (r.gross_profit+r.gross_loss)/r.trades else 0 end) as expected_payoff,"
                               "   (case when r.trades != 0 then r.win_trades*100.0/r.trades else 0 end) as win_percent,"
                               "   (case when r.trades != 0 then r.loss_trades*100.0/r.trades else 0 end) as loss_percent,"
                               "   r.gross_profit/r.win_trades as average_profit,"
                               "   r.gross_loss/r.loss_trades as average_loss,"
                               "   (case when r.gross_loss!=0.0 then r.gross_profit/(-r.gross_loss) else 0 end) as profit_factor "
                               "FROM "
                               "   ("
                               "   SELECT SYMBOL,"
                               "   sum(case when entry =1 then 1 else 0 end) as trades,"
                               "   sum(case when profit > 0 then profit else 0 end) as gross_profit,"
                               "   sum(case when profit < 0 then profit else 0 end) as gross_loss,"
                               "   sum(swap) as total_swap,"
                               "   sum(commission) as total_commission,"
                               "   sum(profit) as total_profit,"
                               "   sum(profit+swap+commission) as net_profit,"
                               "   sum(case when profit > 0 then 1 else 0 end) as win_trades,"
                               "   sum(case when profit < 0 then 1 else 0 end) as loss_trades "
                               "   FROM DEALS "
                               "   WHERE SYMBOL <> '' and SYMBOL is not NULL "
                               "   GROUP BY SYMBOL"
                               "   ) as r");
   if(request==INVALID_HANDLE)
     {
      Print("DB: ", filename, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
   Symbol_Stats stats[], symbol_stats;
   ArrayResize(stats, ExtDealsTotal);
   int i=0;
//--- get records from request results
   for(; DatabaseReadBind(request, symbol_stats) ; i++)
     {
      stats[i].name=symbol_stats.name;
      stats[i].trades=symbol_stats.trades;
      stats[i].gross_profit=symbol_stats.gross_profit;
      stats[i].gross_loss=symbol_stats.gross_loss;
      stats[i].total_commission=symbol_stats.total_commission;
      stats[i].total_swap=symbol_stats.total_swap;
      stats[i].total_profit=symbol_stats.total_profit;
      stats[i].net_profit=symbol_stats.net_profit;
      stats[i].win_trades=symbol_stats.win_trades;
      stats[i].loss_trades=symbol_stats.loss_trades;
      stats[i].expected_payoff=symbol_stats.expected_payoff;
      stats[i].win_percent=symbol_stats.win_percent;
      stats[i].loss_percent=symbol_stats.loss_percent;
      stats[i].average_profit=symbol_stats.average_profit;
      stats[i].average_loss=symbol_stats.average_loss;
      stats[i].profit_factor=symbol_stats.profit_factor;
     }
   ArrayResize(stats, i);
   Print("Trade statistics by Symbol");
   ArrayPrint(stats);
   Print("");
//--- delete the request
   DatabaseFinalize(request);
 
//--- get trading statistics for Expert Advisors by Magic Numbers
   request=DatabasePrepare(db, "SELECT r.*,"
                           "   (case when r.trades != 0 then (r.gross_profit+r.gross_loss)/r.trades else 0 end) as expected_payoff,"
                           "   (case when r.trades != 0 then r.win_trades*100.0/r.trades else 0 end) as win_percent,"
                           "   (case when r.trades != 0 then r.loss_trades*100.0/r.trades else 0 end) as loss_percent,"
                           "   r.gross_profit/r.win_trades as average_profit,"
                           "   r.gross_loss/r.loss_trades as average_loss,"
                           "   (case when r.gross_loss!=0.0 then r.gross_profit/(-r.gross_loss) else 0 end) as profit_factor "
                           "FROM "
                           "   ("
                           "   SELECT MAGIC,"
                           "   sum(case when entry =1 then 1 else 0 end) as trades,"
                           "   sum(case when profit > 0 then profit else 0 end) as gross_profit,"
                           "   sum(case when profit < 0 then profit else 0 end) as gross_loss,"
                           "   sum(swap) as total_swap,"
                           "   sum(commission) as total_commission,"
                           "   sum(profit) as total_profit,"
                           "   sum(profit+swap+commission) as net_profit,"
                           "   sum(case when profit > 0 then 1 else 0 end) as win_trades,"
                           "   sum(case when profit < 0 then 1 else 0 end) as loss_trades "
                           "   FROM DEALS "
                           "   WHERE SYMBOL <> '' and SYMBOL is not NULL "
                           "   GROUP BY MAGIC"
                           "   ) as r");
   if(request==INVALID_HANDLE)
     {
      Print("DB: ", filename, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
   Magic_Stats EA_stats[], magic_stats;
   ArrayResize(EA_stats, ExtDealsTotal);
   i=0;
//--- print
   for(; DatabaseReadBind(request, magic_stats) ; i++)
     {
      EA_stats[i].magic=magic_stats.magic;
      EA_stats[i].trades=magic_stats.trades;
      EA_stats[i].gross_profit=magic_stats.gross_profit;
      EA_stats[i].gross_loss=magic_stats.gross_loss;
      EA_stats[i].total_commission=magic_stats.total_commission;
      EA_stats[i].total_swap=magic_stats.total_swap;
      EA_stats[i].total_profit=magic_stats.total_profit;
      EA_stats[i].net_profit=magic_stats.net_profit;
      EA_stats[i].win_trades=magic_stats.win_trades;
      EA_stats[i].loss_trades=magic_stats.loss_trades;
      EA_stats[i].expected_payoff=magic_stats.expected_payoff;
      EA_stats[i].win_percent=magic_stats.win_percent;
      EA_stats[i].loss_percent=magic_stats.loss_percent;
      EA_stats[i].average_profit=magic_stats.average_profit;
      EA_stats[i].average_loss=magic_stats.average_loss;
      EA_stats[i].profit_factor=magic_stats.profit_factor;
     }
   ArrayResize(EA_stats, i);
   Print("Trade statistics by Magic Number");
   ArrayPrint(EA_stats);
   Print("");
//--- delete the request
   DatabaseFinalize(request);
 
//--- make sure that hedging system for open position management is used on the account
   if((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
     {
      //--- deals cannot be transformed to trades using a simple method through transactions, therefore complete operation
      DatabaseClose(db);
      return;
     }
 
//--- now create the TRADES table based on the DEALS table
   if(!CreateTableTrades(db))
     {
      DatabaseClose(db);
      return;
     }
//--- fill in the TRADES table using an SQL query based on DEALS table data
   if(DatabaseTableExists(db, "DEALS"))
      //--- populate the TRADES table
      if(!DatabaseExecute(db, "INSERT INTO TRADES(TIME_IN,HOUR_IN,TICKET,TYPE,VOLUME,SYMBOL,PRICE_IN,TIME_OUT,PRICE_OUT,COMMISSION,SWAP,PROFIT) "
                          "SELECT "
                          "   d1.time as time_in,"
                          "   d1.hour as hour_in,"
                          "   d1.position_id as ticket,"
                          "   d1.type as type,"
                          "   d1.volume as volume,"
                          "   d1.symbol as symbol,"
                          "   d1.price as price_in,"
                          "   d2.time as time_out,"
                          "   d2.price as price_out,"
                          "   d1.commission+d2.commission as commission,"
                          "   d2.swap as swap,"
                          "   d2.profit as profit "
                          "FROM DEALS d1 "
                          "INNER JOIN DEALS d2 ON d1.position_id=d2.position_id "
                          "WHERE d1.entry=0 AND d2.entry=1     "))
        {
         Print("DB: fillng the table TRADES failed with code ", GetLastError());
         return;
        }
 
//--- get trading statistics by market entry hours
   request=DatabasePrepare(db, "SELECT r.*,"
                           "   (case when r.trades != 0 then (r.gross_profit+r.gross_loss)/r.trades else 0 end) as expected_payoff,"
                           "   (case when r.trades != 0 then r.win_trades*100.0/r.trades else 0 end) as win_percent,"
                           "   (case when r.trades != 0 then r.loss_trades*100.0/r.trades else 0 end) as loss_percent,"
                           "   r.gross_profit/r.win_trades as average_profit,"
                           "   r.gross_loss/r.loss_trades as average_loss,"
                           "   (case when r.gross_loss!=0.0 then r.gross_profit/(-r.gross_loss) else 0 end) as profit_factor "
                           "FROM "
                           "   ("
                           "   SELECT HOUR_IN,"
                           "   count() as trades,"
                           "   sum(volume) as volume,"
                           "   sum(case when profit > 0 then profit else 0 end) as gross_profit,"
                           "   sum(case when profit < 0 then profit else 0 end) as gross_loss,"
                           "   sum(profit) as net_profit,"
                           "   sum(case when profit > 0 then 1 else 0 end) as win_trades,"
                           "   sum(case when profit < 0 then 1 else 0 end) as loss_trades "
                           "   FROM TRADES "
                           "   WHERE SYMBOL <> '' and SYMBOL is not NULL "
                           "   GROUP BY HOUR_IN"
                           "   ) as r");
   if(request==INVALID_HANDLE)
     {
      Print("DB: ", filename, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
   Hour_Stats hours_stats[], h_stats;
   ArrayResize(hours_stats, ExtDealsTotal);
   i=0;
//--- print
   for(; DatabaseReadBind(request, h_stats) ; i++)
     {
      hours_stats[i].hour_in=h_stats.hour_in;
      hours_stats[i].trades=h_stats.trades;
      hours_stats[i].volume=h_stats.volume;
      hours_stats[i].gross_profit=h_stats.gross_profit;
      hours_stats[i].gross_loss=h_stats.gross_loss;
      hours_stats[i].net_profit=h_stats.net_profit;
      hours_stats[i].win_trades=h_stats.win_trades;
      hours_stats[i].loss_trades=h_stats.loss_trades;
      hours_stats[i].expected_payoff=h_stats.expected_payoff;
      hours_stats[i].win_percent=h_stats.win_percent;
      hours_stats[i].loss_percent=h_stats.loss_percent;
      hours_stats[i].average_profit=h_stats.average_profit;
      hours_stats[i].average_loss=h_stats.average_loss;
      hours_stats[i].profit_factor=h_stats.profit_factor;
     }
   ArrayResize(hours_stats, i);
   Print("Trade statistics by entry hour");
   ArrayPrint(hours_stats);
   Print("");
//--- delete the request
   DatabaseFinalize(request);
 
//--- close database
   DatabaseClose(db);
   return;
  }
/*
Deals in the trading history: 2771 
Trade statistics by Symbol
      [name] [trades] [gross_profit] [gross_loss] [total_commission] [total_swap] [total_profit] [net_profit] [win_trades] [loss_trades] [expected_payoff] [win_percent] [loss_percent] [average_profit] [average_loss] [profit_factor]
[0] "AUDUSD"      112      503.20000   -568.00000           -8.83000    -24.64000      -64.80000    -98.27000           70            42          -0.57857      62.50000       37.50000          7.18857      -13.52381         0.88592
[1] "EURCHF"      125      607.71000   -956.85000          -11.77000    -45.02000     -349.14000   -405.93000           54            71          -2.79312      43.20000       56.80000         11.25389      -13.47676         0.63512
[2] "EURJPY"      127     1078.49000  -1057.83000          -10.61000    -45.76000       20.66000    -35.71000           64            63           0.16268      50.39370       49.60630         16.85141      -16.79095         1.01953
[3] "EURUSD"      233     1685.60000  -1386.80000          -41.00000    -83.76000      298.80000    174.04000          127           106           1.28240      54.50644       45.49356         13.27244      -13.08302         1.21546
[4] "GBPCHF"      125     1881.37000  -1424.72000          -22.60000    -51.56000      456.65000    382.49000           80            45           3.65320      64.00000       36.00000         23.51712      -31.66044         1.32052
[5] "GBPJPY"      127     1943.43000  -1776.67000          -18.84000    -52.46000      166.76000     95.46000           76            51           1.31307      59.84252       40.15748         25.57145      -34.83667         1.09386
[6] "GBPUSD"      121     1668.50000  -1438.20000           -7.96000    -49.93000      230.30000    172.41000           77            44           1.90331      63.63636       36.36364         21.66883      -32.68636         1.16013
[7] "USDCAD"       99      405.28000   -475.47000           -8.68000    -31.68000      -70.19000   -110.55000           51            48          -0.70899      51.51515       48.48485          7.94667       -9.90563         0.85238
[8] "USDCHF"      206     1588.32000  -1241.83000          -17.98000    -65.92000      346.49000    262.59000          131            75           1.68199      63.59223       36.40777         12.12458      -16.55773         1.27902
[9] "USDJPY"      107      464.73000   -730.64000          -35.12000    -34.24000     -265.91000   -335.27000           50            57          -2.48514      46.72897       53.27103          9.29460      -12.81825         0.63606
 
Trade statistics by Magic Number
    [magic] [trades] [gross_profit] [gross_loss] [total_commission] [total_swap] [total_profit] [net_profit] [win_trades] [loss_trades] [expected_payoff] [win_percent] [loss_percent] [average_profit] [average_loss] [profit_factor]
[0]     100      242     2584.80000  -2110.00000          -33.36000    -93.53000      474.80000    347.91000          143            99           1.96198      59.09091       40.90909         18.07552      -21.31313         1.22502
[1]     200      254     3021.92000  -2834.50000          -29.45000    -98.22000      187.42000     59.75000          140           114           0.73787      55.11811       44.88189         21.58514      -24.86404         1.06612
[2]     300      250     2489.08000  -2381.57000          -34.37000    -96.58000      107.51000    -23.44000          134           116           0.43004      53.60000       46.40000         18.57522      -20.53078         1.04514
[3]     400      224     1272.50000  -1283.00000          -24.43000    -64.80000      -10.50000    -99.73000          131            93          -0.04687      58.48214       41.51786          9.71374      -13.79570         0.99182
[4]     500      198     1141.23000  -1051.91000          -27.66000    -63.36000       89.32000     -1.70000          116            82           0.45111      58.58586       41.41414          9.83819      -12.82817         1.08491
[5]     600      214     1317.10000  -1396.03000          -34.12000    -68.48000      -78.93000   -181.53000          116            98          -0.36883      54.20561       45.79439         11.35431      -14.24520         0.94346
 
Trade statistics by entry hour
     [hour_in] [trades] [volume] [gross_profit] [gross_loss] [net_profit] [win_trades] [loss_trades] [expected_payoff] [win_percent] [loss_percent] [average_profit] [average_loss] [profit_factor]
[ 0]         0       50  5.00000      336.51000   -747.47000   -410.96000           21            29          -8.21920      42.00000       58.00000         16.02429      -25.77483         0.45020
[ 1]         1       20  2.00000      102.56000    -57.20000     45.36000           12             8           2.26800      60.00000       40.00000          8.54667       -7.15000         1.79301
[ 2]         2        6  0.60000       38.55000    -14.60000     23.95000            5             1           3.99167      83.33333       16.66667          7.71000      -14.60000         2.64041
[ 3]         3       38  3.80000      173.84000   -200.15000    -26.31000           22            16          -0.69237      57.89474       42.10526          7.90182      -12.50938         0.86855
[ 4]         4       60  6.00000      361.44000   -389.40000    -27.96000           27            33          -0.46600      45.00000       55.00000         13.38667      -11.80000         0.92820
[ 5]         5       32  3.20000      157.43000   -179.89000    -22.46000           20            12          -0.70187      62.50000       37.50000          7.87150      -14.99083         0.87515
[ 6]         6       18  1.80000       95.59000   -162.33000    -66.74000           11             7          -3.70778      61.11111       38.88889          8.69000      -23.19000         0.58886
[ 7]         7       14  1.40000       38.48000   -134.30000    -95.82000            9             5          -6.84429      64.28571       35.71429          4.27556      -26.86000         0.28652
[ 8]         8       42  4.20000      368.48000   -322.30000     46.18000           24            18           1.09952      57.14286       42.85714         15.35333      -17.90556         1.14328
[ 9]         9      118 11.80000     1121.62000   -875.21000    246.41000           72            46           2.08822      61.01695       38.98305         15.57806      -19.02630         1.28154
[10]        10      206 20.60000     2280.59000  -2021.80000    258.79000          115            91           1.25626      55.82524       44.17476         19.83122      -22.21758         1.12800
[11]        11      138 13.80000     1377.02000   -994.18000    382.84000           84            54           2.77420      60.86957       39.13043         16.39310      -18.41074         1.38508
[12]        12      152 15.20000     1247.56000  -1463.80000   -216.24000           84            68          -1.42263      55.26316       44.73684         14.85190      -21.52647         0.85227
[13]        13       64  6.40000      778.27000   -516.22000    262.05000           36            28           4.09453      56.25000       43.75000         21.61861      -18.43643         1.50763
[14]        14       62  6.20000      536.93000   -427.47000    109.46000           38            24           1.76548      61.29032       38.70968         14.12974      -17.81125         1.25606
[15]        15       50  5.00000      699.92000   -413.00000    286.92000           28            22           5.73840      56.00000       44.00000         24.99714      -18.77273         1.69472
[16]        16       88  8.80000      778.55000   -514.00000    264.55000           51            37           3.00625      57.95455       42.04545         15.26569      -13.89189         1.51469
[17]        17       76  7.60000      533.92000  -1019.46000   -485.54000           44            32          -6.38868      57.89474       42.10526         12.13455      -31.85813         0.52373
[18]        18       52  5.20000      237.17000   -246.78000     -9.61000           24            28          -0.18481      46.15385       53.84615          9.88208       -8.81357         0.96106
[19]        19       52  5.20000      407.67000   -150.36000    257.31000           30            22           4.94827      57.69231       42.30769         13.58900       -6.83455         2.71129
[20]        20       18  1.80000       65.92000    -89.09000    -23.17000            9             9          -1.28722      50.00000       50.00000          7.32444       -9.89889         0.73993
[21]        21       10  1.00000       41.86000    -32.38000      9.48000            7             3           0.94800      70.00000       30.00000          5.98000      -10.79333         1.29277
[22]        22       14  1.40000       45.55000    -83.72000    -38.17000            6             8          -2.72643      42.85714       57.14286          7.59167      -10.46500         0.54408
[23]        23        2  0.20000        1.20000     -1.90000     -0.70000            1             1          -0.35000      50.00000       50.00000          1.20000       -1.90000         0.63158
*/  
  
//+------------------------------------------------------------------+
//| Creates the DEALS table                                          |
//+------------------------------------------------------------------+
bool CreateTableDeals(int database)
  {
//--- if the DEALS table already exists, delete it
   if(!DeleteTable(database, "DEALS"))
     {
      return(false);
     }
//--- check if the table exists
   if(!DatabaseTableExists(database, "DEALS"))
      //--- create the table
      if(!DatabaseExecute(database, "CREATE TABLE DEALS("
                          "ID          INT KEY NOT NULL,"
                          "ORDER_ID    INT     NOT NULL,"
                          "POSITION_ID INT     NOT NULL,"
                          "TIME        INT     NOT NULL,"
                          "TYPE        INT     NOT NULL,"
                          "ENTRY       INT     NOT NULL,"
                          "SYMBOL      CHAR(10),"
                          "VOLUME      REAL,"
                          "PRICE       REAL,"
                          "PROFIT      REAL,"
                          "SWAP        REAL,"
                          "COMMISSION  REAL,"
                          "MAGIC       INT,"
                          "HOUR        INT,"
                          "REASON      INT);"))
        {
         Print("DB: create the DEALS table  failed with code ", GetLastError());
         return(false);
        }
//---  request the entire trading history
   datetime from_date=0;
   datetime to_date=TimeCurrent();
//--- request the history of deals in the specified interval
   HistorySelect(from_date, to_date);
   ExtDealsTotal=HistoryDealsTotal();
//--- add deals to the table
   if(!InsertDeals(database))
      return(false);
//--- the table has been successfully created
   return(true);
  }
//+------------------------------------------------------------------+
//| Deletes a table with the specified name from the database        |
//+------------------------------------------------------------------+
bool DeleteTable(int database, string table_name)
  {
   if(!DatabaseExecute(database, "DROP TABLE IF EXISTS "+table_name))
     {
      Print("Failed to drop the DEALS table with code ", GetLastError());
      return(false);
     }
//--- the table has been successfully deleted
   return(true);
  }
//+------------------------------------------------------------------+
//| Adds deals to the database table                                 |
//+------------------------------------------------------------------+
bool InsertDeals(int database)
  {
//--- Auxiliary variables
   ulong    deal_ticket;         // deal ticket
   long     order_ticket;        // the ticket of the order by which the deal was executed
   long     position_ticket;     // ID of the position to which the deal belongs
   datetime time;                // deal execution time
   long     type ;               // deal type
   long     entry ;              // deal direction
   string   symbol;              // the symbol fro which the deal was executed
   double   volume;              // operation volume
   double   price;               // price
   double   profit;              // financial result
   double   swap;                // swap
   double   commission;          // commission
   long     magic;               // Magic number (Expert Advisor ID)
   long     reason;              // deal execution reason or source
   char     hour;                // deal execution hour
   MqlDateTime time_strusture;
//--- go through all deals and add them to the database
   bool failed=false;
   int deals=HistoryDealsTotal();
// --- lock the database before executing transactions
   DatabaseTransactionBegin(database);
   for(int i=0; i<deals; i++)
     {
      deal_ticket=    HistoryDealGetTicket(i);
      order_ticket=   HistoryDealGetInteger(deal_ticket, DEAL_ORDER);
      position_ticket=HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID);
      time= (datetime)HistoryDealGetInteger(deal_ticket, DEAL_TIME);
      type=           HistoryDealGetInteger(deal_ticket, DEAL_TYPE);
      entry=          HistoryDealGetInteger(deal_ticket, DEAL_ENTRY);
      symbol=         HistoryDealGetString(deal_ticket, DEAL_SYMBOL);
      volume=         HistoryDealGetDouble(deal_ticket, DEAL_VOLUME);
      price=          HistoryDealGetDouble(deal_ticket, DEAL_PRICE);
      profit=         HistoryDealGetDouble(deal_ticket, DEAL_PROFIT);
      swap=           HistoryDealGetDouble(deal_ticket, DEAL_SWAP);
      commission=     HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION);
      magic=          HistoryDealGetInteger(deal_ticket, DEAL_MAGIC);
      reason=         HistoryDealGetInteger(deal_ticket, DEAL_REASON);
      TimeToStruct(time, time_strusture);
      hour= (char)time_strusture.hour;
      //--- add each deal to the table using the following request
      string request_text=StringFormat("INSERT INTO DEALS (ID,ORDER_ID,POSITION_ID,TIME,TYPE,ENTRY,SYMBOL,VOLUME,PRICE,PROFIT,SWAP,COMMISSION,MAGIC,REASON,HOUR)"
                                       "VALUES (%d, %d, %d, %d, %d, %d, '%s', %G, %G, %G, %G, %G, %d, %d,%d)",
                                       deal_ticket, order_ticket, position_ticket, time, type, entry, symbol, volume, price, profit, swap, commission, magic, reason, hour);
      if(!DatabaseExecute(database, request_text))
        {
         PrintFormat("%s: failed to insert deal #%d with code %d", __FUNCTION__, deal_ticket, GetLastError());
         PrintFormat("i=%d: deal #%d  %s", i, deal_ticket, symbol);
         failed=true;
         break;
        }
     }
//--- check for transaction execution errors
   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(database);
      PrintFormat("%s: DatabaseExecute() failed with code ", __FUNCTION__, GetLastError());
      return(false);
     }
//--- all transactions have been performed successfully - record changes and unlock the database
   DatabaseTransactionCommit(database);
   return(true);
  }
//+------------------------------------------------------------------+
//| Creates the TRADES table                                         |
//+------------------------------------------------------------------+
bool CreateTableTrades(int database)
  {
//--- if the TRADES table already exists, delete it
   if(!DeleteTable(database, "TRADES"))
      return(false);
//--- check if the table exists
   if(!DatabaseTableExists(database, "TRADES"))
      //--- create the table
      if(!DatabaseExecute(database, "CREATE TABLE TRADES("
                          "TIME_IN     INT     NOT NULL,"
                          "HOUR_IN     INT     NOT NULL,"
                          "TICKET      INT     NOT NULL,"
                          "TYPE        INT     NOT NULL,"
                          "VOLUME      REAL,"
                          "SYMBOL      CHAR(10),"
                          "PRICE_IN    REAL,"
                          "TIME_OUT    INT     NOT NULL,"
                          "PRICE_OUT   REAL,"
                          "COMMISSION  REAL,"
                          "SWAP        REAL,"
                          "PROFIT      REAL);"))
        {
         Print("DB: create the TRADES table failed with code ", GetLastError());
         return(false);
        }
//--- the table has been successfully created
   return(true);
  }
//+------------------------------------------------------------------+
```
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseFinalize](/en/docs/database/databasefinalize)
@@ -0,0 +1,409 @@
# DatabasePrepare
Creates a handle of a request, which can then be executed using [DatabaseRead()](/en/docs/database/databaseread).
```
int  DatabasePrepare(
   int     database,      // database handle received in DatabaseOpen
   string  sql,           // SQL request
           ...            // request parameters
   );
```
Parameters
database
[in]  Database handle received in [DatabaseOpen()](/en/docs/database/databaseopen).
sql
[in]  SQL request that may contain automatically substituted parameters named ?1,?2,...
...
[in]  Automatically substituted request parameters.
Return Value
If successful, the function returns a handle for the SQL request. Otherwise, it returns [INVALID_HANDLE](/en/docs/constants/namedconstants/otherconstants). To get the error code, use GetLastError(), the possible responses are:
- ERR_INVALID_PARAMETER (4003)                   path to the database file contains an empty string, or an incompatible combination of flags is set;
- ERR_NOT_ENOUGH_MEMORY (4004)              - insufficient memory;
- ERR_WRONG_STRING_PARAMETER (5040)       error converting a request into a UTF-8 string;
- ERR_DATABASE_INVALID_HANDLE (5121)       - invalid database handle;
- ERR_DATABASE_TOO_MANY_OBJECTS (5122) - exceeded the maximum acceptable number of Database objects;
- ERR_DATABASE_PREPARE (5125)                   - request generation error.
Note
The DatabasePrepare() function does not perform a request to a database. Its purpose is to verify the request parameters and return the handle for executing the SQL request based on the verification results. The request itself is set during the [DatabaseRead()](/en/docs/database/databaseread) first call.
Example:
```
//--- Structure to store the deal
struct Deal
  {
   ulong             ticket;           // DEAL_TICKET
   long              order_ticket;     // DEAL_ORDER
   long              position_ticket;  // DEAL_POSITION_ID
   datetime          time;             // DEAL_TIME
   char              type;             // DEAL_TYPE
   char              entry;            // DEAL_ENTRY
   string            symbol;           // DEAL_SYMBOL
   double            volume;           // DEAL_VOLUME
   double            price;            // DEAL_PRICE
   double            profit;           // DEAL_PROFIT
   double            swap;             // DEAL_SWAP
   double            commission;       // DEAL_COMMISSION
   long              magic;            // DEAL_MAGIC
   char              reason;           // DEAL_REASON
  };
//--- Structure to store the trade: the order of members corresponds to the position in the terminal
struct Trade
  {
   datetime          time_in;          // entry time
   ulong             ticket;           // position ID
   char              type;             // buy or sell
   double            volume;           // volume
   string            symbol;           // symbol
   double            price_in;         // entry price
   datetime          time_out;         // exit time
   double            price_out;        // exit price
   double            commission;       // entry and exit commission
   double            swap;             // swap
   double            profit;           // profit or loss
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- create the file name
   string filename=IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN))+"_trades.sqlite";
//--- open/create the database in the common terminal folder
   int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)
     {
      Print("DB: ", filename, " open failed with code ", GetLastError());
      return;
     }
//--- create the DEALS table
   if(!CreateTableDeals(db))
     {
      DatabaseClose(db);
      return;
     }
//---  request the entire trading history
   datetime from_date=0;
   datetime to_date=TimeCurrent();
//--- request the history of deals in the specified interval
   HistorySelect(from_date, to_date);
   int deals_total=HistoryDealsTotal();
   PrintFormat("Deals in the trading history: %d ", deals_total);
//--- add deals to the table
   if(!InsertDeals(db))
      return;
//--- show the first 10 deals
   Deal deals[], deal;
   ArrayResize(deals, 10);
   int request=DatabasePrepare(db, "SELECT * FROM DEALS");
   if(request==INVALID_HANDLE)
     {
      Print("DB: ", filename, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
   int i;
   for(i=0; DatabaseReadBind(request, deal); i++)
     {
      if(i>=10)
         break;
      deals[i].ticket=deal.ticket;
      deals[i].order_ticket=deal.order_ticket;
      deals[i].position_ticket=deal.position_ticket;
      deals[i].time=deal.time;
      deals[i].type=deal.type;
      deals[i].entry=deal.entry;
      deals[i].symbol=deal.symbol;
      deals[i].volume=deal.volume;
      deals[i].price=deal.price;
      deals[i].profit=deal.profit;
      deals[i].swap=deal.swap;
      deals[i].commission=deal.commission;
      deals[i].magic=deal.magic;
      deals[i].reason=deal.reason;
     }
//--- print the deals
   if(i>0)
     {
      ArrayResize(deals, i);
      PrintFormat("The first %d deals:", i);
      ArrayPrint(deals);
     }
 
//--- delete request after use
   DatabaseFinalize(request);
 
//--- make sure that hedging system for open position management is used on the account
   if((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
     {
      //--- deals cannot be transformed to trades using a simple method through transactions, therefore complete operation
      DatabaseClose(db);
      return;
     }
 
//--- now create the TRADES table based on the DEALS table
   if(!CreateTableTrades(db))
     {
      DatabaseClose(db);
      return;
     }
//--- fill in the TRADES table using an SQL query based on DEALS table data
   ulong start=GetMicrosecondCount();
   if(DatabaseTableExists(db, "DEALS"))
      //--- populate the TRADES table
      if(!DatabaseExecute(db, "INSERT INTO TRADES(TIME_IN,TICKET,TYPE,VOLUME,SYMBOL,PRICE_IN,TIME_OUT,PRICE_OUT,COMMISSION,SWAP,PROFIT) "
                          "SELECT "
                          "   d1.time as time_in,"
                          "   d1.position_id as ticket,"
                          "   d1.type as type,"
                          "   d1.volume as volume,"
                          "   d1.symbol as symbol,"
                          "   d1.price as price_in,"
                          "   d2.time as time_out,"
                          "   d2.price as price_out,"
                          "   d1.commission+d2.commission as commission,"
                          "   d2.swap as swap,"
                          "   d2.profit as profit "
                          "FROM DEALS d1 "
                          "INNER JOIN DEALS d2 ON d1.position_id=d2.position_id "
                          "WHERE d1.entry=0 AND d2.entry=1     "))
        {
         Print("DB: fillng the TRADES table failed with code ", GetLastError());
         return;
        }
   ulong transaction_time=GetMicrosecondCount()-start;
   
//--- show the first 10 deals
   Trade trades[], trade;
   ArrayResize(trades, 10);
   request=DatabasePrepare(db, "SELECT * FROM TRADES");
   if(request==INVALID_HANDLE)
     {
      Print("DB: ", filename, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
   for(i=0; DatabaseReadBind(request, trade); i++)
     {
      if(i>=10)
         break;
      trades[i].time_in=trade.time_in;
      trades[i].ticket=trade.ticket;
      trades[i].type=trade.type;
      trades[i].volume=trade.volume;
      trades[i].symbol=trade.symbol;
      trades[i].price_in=trade.price_in;
      trades[i].time_out=trade.time_out;
      trades[i].price_out=trade.price_out;
      trades[i].commission=trade.commission;
      trades[i].swap=trade.swap;
      trades[i].profit=trade.profit;
     }
//--- print trades
   if(i>0)
     {
      ArrayResize(trades, i);
      PrintFormat("\r\nThe first %d trades:", i);
      ArrayPrint(trades);
      PrintFormat("Filling the TRADES table took %.2f milliseconds",double(transaction_time)/1000);
     }
//--- delete request after use
   DatabaseFinalize(request);
 
//--- close the database
   DatabaseClose(db);
  }
/*
Results:
   Deals in the trading history: 2741 
   The first 10 deals:
       [ticket] [order_ticket] [position_ticket]              [time] [type] [entry] [symbol] [volume]   [price]   [profit] [swap] [commission] [magic] [reason]
   [0] 34429573              0                 0 2019.09.05 22:39:59      2       0 ""        0.00000   0.00000 2000.00000 0.0000      0.00000       0        0
   [1] 34432127       51447238          51447238 2019.09.06 06:00:03      0       0 "USDCAD"  0.10000   1.32320    0.00000 0.0000     -0.16000     500        3
   [2] 34432128       51447239          51447239 2019.09.06 06:00:03      1       0 "USDCHF"  0.10000   0.98697    0.00000 0.0000     -0.16000     500        3
   [3] 34432450       51447565          51447565 2019.09.06 07:00:00      0       0 "EURUSD"  0.10000   1.10348    0.00000 0.0000     -0.18000     400        3
   [4] 34432456       51447571          51447571 2019.09.06 07:00:00      1       0 "AUDUSD"  0.10000   0.68203    0.00000 0.0000     -0.11000     400        3
   [5] 34432879       51448053          51448053 2019.09.06 08:00:00      1       0 "USDCHF"  0.10000   0.98701    0.00000 0.0000     -0.16000     600        3
   [6] 34432888       51448064          51448064 2019.09.06 08:00:00      0       0 "USDJPY"  0.10000 106.96200    0.00000 0.0000     -0.16000     600        3
   [7] 34435147       51450470          51450470 2019.09.06 10:30:00      1       0 "EURUSD"  0.10000   1.10399    0.00000 0.0000     -0.18000     100        3
   [8] 34435152       51450476          51450476 2019.09.06 10:30:00      0       0 "GBPUSD"  0.10000   1.23038    0.00000 0.0000     -0.20000     100        3
   [9] 34435154       51450479          51450479 2019.09.06 10:30:00      1       0 "EURJPY"  0.10000 118.12000    0.00000 0.0000     -0.18000     200        3
 
   The first 10 trades:
                 [time_in] [ticket] [type] [volume] [symbol] [price_in]          [time_out] [price_out] [commission]   [swap]  [profit]
   [0] 2019.09.06 06:00:03 51447238      0  0.10000 "USDCAD"    1.32320 2019.09.06 18:00:00     1.31761     -0.32000  0.00000 -42.43000
   [1] 2019.09.06 06:00:03 51447239      1  0.10000 "USDCHF"    0.98697 2019.09.06 18:00:00     0.98641     -0.32000  0.00000   5.68000
   [2] 2019.09.06 07:00:00 51447565      0  0.10000 "EURUSD"    1.10348 2019.09.09 03:30:00     1.10217     -0.36000 -1.31000 -13.10000
   [3] 2019.09.06 07:00:00 51447571      1  0.10000 "AUDUSD"    0.68203 2019.09.09 03:30:00     0.68419     -0.22000  0.03000 -21.60000
   [4] 2019.09.06 08:00:00 51448053      1  0.10000 "USDCHF"    0.98701 2019.09.06 18:00:01     0.98640     -0.32000  0.00000   6.18000
   [5] 2019.09.06 08:00:00 51448064      0  0.10000 "USDJPY"  106.96200 2019.09.06 18:00:01   106.77000     -0.32000  0.00000 -17.98000
   [6] 2019.09.06 10:30:00 51450470      1  0.10000 "EURUSD"    1.10399 2019.09.06 14:30:00     1.10242     -0.36000  0.00000  15.70000
   [7] 2019.09.06 10:30:00 51450476      0  0.10000 "GBPUSD"    1.23038 2019.09.06 14:30:00     1.23040     -0.40000  0.00000   0.20000
   [8] 2019.09.06 10:30:00 51450479      1  0.10000 "EURJPY"  118.12000 2019.09.06 14:30:00   117.94100     -0.36000  0.00000  16.73000
   [9] 2019.09.06 10:30:00 51450480      0  0.10000 "GBPJPY"  131.65300 2019.09.06 14:30:01   131.62500     -0.40000  0.00000  -2.62000
   Filling the TRADES table took 12.51 milliseconds
*/  
//+------------------------------------------------------------------+
//| Creates the DEALS table                                          |
//+------------------------------------------------------------------+
bool CreateTableDeals(int database)
  {
//--- if the DEALS table already exists, delete it
   if(!DeleteTable(database, "DEALS"))
     {
      return(false);
     }
//--- check if the table exists
   if(!DatabaseTableExists(database, "DEALS"))
      //--- create the table
      if(!DatabaseExecute(database, "CREATE TABLE DEALS("
                          "ID          INT KEY NOT NULL,"
                          "ORDER_ID    INT     NOT NULL,"
                          "POSITION_ID INT     NOT NULL,"
                          "TIME        INT     NOT NULL,"
                          "TYPE        INT     NOT NULL,"
                          "ENTRY       INT     NOT NULL,"
                          "SYMBOL      CHAR(10),"
                          "VOLUME      REAL,"
                          "PRICE       REAL,"
                          "PROFIT      REAL,"
                          "SWAP        REAL,"
                          "COMMISSION  REAL,"
                          "MAGIC       INT,"
                          "REASON      INT );"))
        {
         Print("DB: create the DEALS table failed with code ", GetLastError());
         return(false);
        }
//--- the table has been successfully created
   return(true);
  }
//+------------------------------------------------------------------+
//| Deletes a table with the specified name from the database        |
//+------------------------------------------------------------------+
bool DeleteTable(int database, string table_name)
  {
   if(!DatabaseExecute(database, "DROP TABLE IF EXISTS "+table_name))
     {
      Print("Failed to drop the DEALS table  with code ", GetLastError());
      return(false);
     }
//--- the table has been successfully deleted
   return(true);
  }
//+------------------------------------------------------------------+
//| Adds deals to the database table                                 |
//+------------------------------------------------------------------+
bool InsertDeals(int database)
  {
//--- Auxiliary variables
   ulong    deal_ticket;         // deal ticket
   long     order_ticket;        // the ticket of the order by which the deal was executed
   long     position_ticket;     // ID of the position to which the deal belongs
   datetime time;                // deal execution time
   long     type ;               // deal type
   long     entry ;              // deal direction
   string   symbol;              // the symbol fro which the deal was executed
   double   volume;              // operation volume
   double   price;               // price
   double   profit;              // financial result
   double   swap;                // swap
   double   commission;          // commission
   long     magic;               // Magic number (Expert Advisor ID)
   long     reason;              // deal execution reason or source
//--- go through all deals and add them to the database
   bool failed=false;
   int deals=HistoryDealsTotal();
// --- lock the database before executing transactions
   DatabaseTransactionBegin(database);
   for(int i=0; i<deals; i++)
     {
      deal_ticket=    HistoryDealGetTicket(i);
      order_ticket=   HistoryDealGetInteger(deal_ticket, DEAL_ORDER);
      position_ticket=HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID);
      time= (datetime)HistoryDealGetInteger(deal_ticket, DEAL_TIME);
      type=           HistoryDealGetInteger(deal_ticket, DEAL_TYPE);
      entry=          HistoryDealGetInteger(deal_ticket, DEAL_ENTRY);
      symbol=         HistoryDealGetString(deal_ticket, DEAL_SYMBOL);
      volume=         HistoryDealGetDouble(deal_ticket, DEAL_VOLUME);
      price=          HistoryDealGetDouble(deal_ticket, DEAL_PRICE);
      profit=         HistoryDealGetDouble(deal_ticket, DEAL_PROFIT);
      swap=           HistoryDealGetDouble(deal_ticket, DEAL_SWAP);
      commission=     HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION);
      magic=          HistoryDealGetInteger(deal_ticket, DEAL_MAGIC);
      reason=         HistoryDealGetInteger(deal_ticket, DEAL_REASON);
      //--- add each deal to the table using the following request
      string request_text=StringFormat("INSERT INTO DEALS (ID,ORDER_ID,POSITION_ID,TIME,TYPE,ENTRY,SYMBOL,VOLUME,PRICE,PROFIT,SWAP,COMMISSION,MAGIC,REASON)"
                                       "VALUES (%d, %d, %d, %d, %d, %d, '%s', %G, %G, %G, %G, %G, %d, %d)",
                                       deal_ticket, order_ticket, position_ticket, time, type, entry, symbol, volume, price, profit, swap, commission, magic, reason);
      if(!DatabaseExecute(database, request_text))
        {
         PrintFormat("%s: failed to insert deal #%d with code %d", __FUNCTION__, deal_ticket, GetLastError());
         PrintFormat("i=%d: deal #%d  %s", i, deal_ticket, symbol);
         failed=true;
         break;
        }
     }
//--- check for transaction execution errors
   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(database);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
      return(false);
     }
//--- all transactions have been performed successfully - record changes and unlock the database
   DatabaseTransactionCommit(database);
   return(true);
  }
//+------------------------------------------------------------------+
//| Creates the TRADES table                                          |
//+------------------------------------------------------------------+
bool CreateTableTrades(int database)
  {
//--- if the TRADES table already exists, delete it
   if(!DeleteTable(database, "TRADES"))
      return(false);
//--- check if the table exists
   if(!DatabaseTableExists(database, "TRADES"))
      //--- create the table
      if(!DatabaseExecute(database, "CREATE TABLE TRADES("
                          "TIME_IN     INT     NOT NULL,"
                          "TICKET      INT     NOT NULL,"
                          "TYPE        INT     NOT NULL,"
                          "VOLUME      REAL,"
                          "SYMBOL      CHAR(10),"
                          "PRICE_IN    REAL,"
                          "TIME_OUT    INT     NOT NULL,"
                          "PRICE_OUT   REAL,"
                          "COMMISSION  REAL,"
                          "SWAP        REAL,"
                          "PROFIT      REAL);"))
        {
         Print("DB: create the TRADES table failed with code ", GetLastError());
         return(false);
        }
//--- the table has been successfully created
   return(true);
  }
//+------------------------------------------------------------------+
```
See also
[DatabaseExecute](/en/docs/database/databaseexecute), [DatabaseFinalize](/en/docs/database/databasefinalize)
@@ -0,0 +1,290 @@
# DatabaseReset
Resets a request, like after calling [DatabasePrepare()](/en/docs/database/databaseprepare).
```
int  DatabaseReset(
   int  request      // request handle received in DatabasePrepare
   );
```
Parameters
request
[in]  The handle of the request obtained in [DatabasePrepare()](/en/docs/database/databaseprepare).
Return Value
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_DATABASE_INVALID_HANDLE (5121) - invalid database handle;
- SQLite error codes starting with ERR_DATABASE_ERROR(5601).
Note
The DatabaseReset() function is intended for multiple execution of a request with different parameter values. For example, when adding data to the table in bulk using the INSERT command, a custom set of field values should be formed for each entry.
Unlike [DatabasePrepare()](/en/docs/database/databaseprepare), the DatabaseReset() call does not compile the string with SQL commands into a new request, therefore DatabaseReset() is executed much faster than DatabasePrepare().
DatabaseReset() is used together with the [DatabaseBind()](/en/docs/database/databasebind) functions and/or [DatabaseBindArray()](/en/docs/database/databasebindarray) if the request parameter values should be changed after executing [DatabaseRead()](/en/docs/database/databaseread). This means that before setting new values of the request parameters (before the block of DatabaseBind/DatabaseBindArray calls), DatabaseReset() should be called to reset it. The parametrized request itself should be created using [DatabasePrepare()](/en/docs/database/databaseprepare).
Just like DatabasePrepare(), DatabaseReset() does not make a database request. A direct request execution is performed when calling [DatabaseRead()](/en/docs/database/databaseread) or [DatabaseReadBind()](/en/docs/database/databasereadbind).
DatabaseReset() call does not lead to resetting parameter values in the request if they were set by calling DatabaseBind()/DatabaseBindArray(), i.e. the parameters retain their values. Thus, the value of only a single parameter can be changed. There is no need to set all request parameters anew after calling DatabaseReset().
A handle of a request removed using [DatabaseFinalize()](/en/docs/database/databasefinalize) cannot be passed to DatabaseReset(). This will result in an error.
Example:
```
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- create or open a database
   string filename="symbols.sqlite";
   int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE);
   if(db==INVALID_HANDLE)
     {
      Print("DB: ", filename, " open failed with code ", GetLastError());
      return;
     }
   else
      Print("Database: ", filename, " opened successfully");
//--- if the SYMBOLS table exists, delete it
   if(DatabaseTableExists(db, "SYMBOLS"))
     {
      //--- delete the table
      if(!DatabaseExecute(db, "DROP TABLE SYMBOLS"))
        {
         Print("Failed to drop table SYMBOLS with code ", GetLastError());
         DatabaseClose(db);
         return;
        }
     }
//--- create the SYMBOLS table
   if(!DatabaseExecute(db, "CREATE TABLE SYMBOLS("
                       "NAME           TEXT    NOT NULL,"
                       "DESCRIPTION    TEXT            ,"
                       "PATH           TEXT            ,"
                       "SPREAD         INT             ,"
                       "POINT          REAL    NOT NULL,"
                       "DIGITS         INT     NOT NULL,"
                       "JSON           BLOB );"))
     {
      Print("DB: ", filename, " create table failed with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
//--- display the list of all fields in the SYMBOLS table
   if(DatabasePrint(db, "PRAGMA TABLE_INFO(SYMBOLS)", 0)<0)
     {
      PrintFormat("DatabasePrint(\"PRAGMA TABLE_INFO(SYMBOLS)\") failed, error code=%d at line %d", GetLastError(), __LINE__);
      DatabaseClose(db);
      return;
     }
 
//--- create a parametrized request to add symbols to the SYMBOLS table
   string sql="INSERT INTO SYMBOLS (NAME,DESCRIPTION,PATH,SPREAD,POINT,DIGITS,JSON)"
              " VALUES (?1,?2,?3,?4,?5,?6,?7);"; // request parameters
   int request=DatabasePrepare(db, sql);
   if(request==INVALID_HANDLE)
     {
      PrintFormat("DatabasePrepare() failed with code=%d", GetLastError());
      Print("SQL request: ", sql);
      DatabaseClose(db);
      return;
     }
 
//--- go through all the symbols and add them to the SYMBOLS table
   int symbols=SymbolsTotal(false);
   bool request_error=false;
   DatabaseTransactionBegin(db);   
   for(int i=0; i<symbols; i++)
     {
      //--- set the values of the parameters before adding a symbol
      ResetLastError();
      string symbol=SymbolName(i, false);
      if(!DatabaseBind(request, 0, symbol))
        {
         PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
         request_error=true;
         break;
        }
      //--- if the previous DatabaseBind() call was successful, set the next parameter
      if(!DatabaseBind(request, 1, SymbolInfoString(symbol, SYMBOL_DESCRIPTION)))
        {
         PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
         request_error=true;
         break;
        }
      if(!DatabaseBind(request, 2, SymbolInfoString(symbol, SYMBOL_PATH)))
        {
         PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
         request_error=true;
         break;
        }
      if(!DatabaseBind(request, 3, SymbolInfoInteger(symbol, SYMBOL_SPREAD)))
        {
         PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
         request_error=true;
         break;
        }
      if(!DatabaseBind(request, 4, SymbolInfoDouble(symbol, SYMBOL_POINT)))
        {
         PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
         request_error=true;
         break;
        }
      if(!DatabaseBind(request, 5, SymbolInfoInteger(symbol, SYMBOL_DIGITS)))
        {
         PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
         request_error=true;
         break;
        }
      if(!DatabaseBind(request, 6, GetSymBolAsJson(symbol)))
        {
         PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
         request_error=true;
         break;
        }
 
      //--- execute a request for inserting the entry and check for an error
      if(!DatabaseRead(request)&&(GetLastError()!=ERR_DATABASE_NO_MORE_DATA))
        {
         PrintFormat("DatabaseRead() failed with code=%d", GetLastError());
         DatabaseFinalize(request);
         request_error=true;
         break;
        }
      else
         PrintFormat("%d: added %s", i+1, symbol);
      //--- reset the request before the next parameter update
      if(!DatabaseReset(request))
        {
         PrintFormat("DatabaseReset() failed with code=%d", GetLastError());
         DatabaseFinalize(request);
         request_error=true;
         break;
        }
     } //--- done going through all the symbols
 
//--- transactions status
   if(request_error)
     {
      PrintFormat("Table SYMBOLS: failed to add %d symbols", symbols);
      DatabaseTransactionRollback(db);
      DatabaseClose(db);
      return;
     }
   else
     {
      DatabaseTransactionCommit(db);
      PrintFormat("Table SYMBOLS: added %d symbols",symbols);
     }
 
//--- save the SYMBOLS table to a CSV file
   string csv_filename="symbols.csv";
   if(DatabaseExport(db, "SELECT * FROM SYMBOLS", csv_filename,
                     DATABASE_EXPORT_HEADER|DATABASE_EXPORT_INDEX|DATABASE_EXPORT_QUOTED_STRINGS, ";"))
      Print("Database: table SYMBOLS saved in ", csv_filename);
   else
      Print("Database: DatabaseExport(\"SELECT * FROM SYMBOLS\") failed with code", GetLastError());
 
//--- close the database file and inform of that
   DatabaseClose(db);
   PrintFormat("Database: %s created and closed", filename);
  }
//+------------------------------------------------------------------+
//| Return a symbol specification as JSON                            |
//+------------------------------------------------------------------+
string GetSymBolAsJson(string symbol)
  {
//--- indents
   string indent1=Indent(1);
   string indent2=Indent(2);
   string indent3=Indent(3);
//---
   int digits=(int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
   string json="{"+
               "\n"+indent1+"\"ConfigSymbols\":["+
               "\n"+indent2+"{"+
               "\n"+indent3+"\"Symbol\":\""+symbol+"\","+
               "\n"+indent3+"\"Path\":\""+SymbolInfoString(symbol, SYMBOL_PATH)+"\","+
               "\n"+indent3+"\"CurrencyBase\":\""+SymbolInfoString(symbol, SYMBOL_CURRENCY_BASE)+"\","+
               "\n"+indent3+"\"CurrencyProfit\":\""+SymbolInfoString(symbol, SYMBOL_CURRENCY_PROFIT)+"\","+
               "\n"+indent3+"\"CurrencyMargin\":\""+SymbolInfoString(symbol, SYMBOL_CURRENCY_MARGIN)+"\","+
               "\n"+indent3+"\"ColorBackground\":\""+ColorToString((color)SymbolInfoInteger(symbol, SYMBOL_BACKGROUND_COLOR))+"\","+
               "\n"+indent3+"\"Digits\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_DIGITS))+"\","+
               "\n"+indent3+"\"Point\":\""+DoubleToString(SymbolInfoDouble(symbol, SYMBOL_POINT), digits)+"\","+
               "\n"+indent3+"\"TickBookDepth\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_TICKS_BOOKDEPTH))+"\","+
               "\n"+indent3+"\"ChartMode\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_CHART_MODE))+"\","+
               "\n"+indent3+"\"TradeMode\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_TRADE_EXEMODE))+"\","+
               "\n"+indent3+"\"TradeCalcMode\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_TRADE_CALC_MODE))+"\","+
               "\n"+indent3+"\"OrderMode\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_ORDER_MODE))+"\","+
               "\n"+indent3+"\"CalculationMode\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_TRADE_CALC_MODE))+"\","+
               "\n"+indent3+"\"ExecutionMode\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_TRADE_EXEMODE))+"\","+
               "\n"+indent3+"\"ExpirationMode\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_EXPIRATION_MODE))+"\","+
               "\n"+indent3+"\"FillFlags\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE))+"\","+
               "\n"+indent3+"\"ExpirFlags\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_EXPIRATION_MODE))+"\","+
               "\n"+indent3+"\"Spread\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_SPREAD))+"\","+
               "\n"+indent3+"\"TickValue\":\""+StringFormat("%G", (SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE)))+"\","+
               "\n"+indent3+"\"TickSize\":\""+StringFormat("%G", (SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE)))+"\","+
               "\n"+indent3+"\"ContractSize\":\""+StringFormat("%G",(SymbolInfoDouble(symbol, SYMBOL_TRADE_CONTRACT_SIZE)))+"\","+
               "\n"+indent3+"\"StopsLevel\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL))+"\","+
               "\n"+indent3+"\"VolumeMin\":\""+StringFormat("%G",(SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN)))+"\","+
               "\n"+indent3+"\"VolumeMax\":\""+StringFormat("%G",(SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX)))+"\","+
               "\n"+indent3+"\"VolumeStep\":\""+StringFormat("%G",(SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP)))+"\","+
               "\n"+indent3+"\"VolumeLimit\":\""+StringFormat("%G",(SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP)))+"\","+
               "\n"+indent3+"\"SwapMode\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_SWAP_MODE))+"\","+
               "\n"+indent3+"\"SwapLong\":\""+StringFormat("%G",(SymbolInfoDouble(symbol, SYMBOL_SWAP_LONG)))+"\","+
               "\n"+indent3+"\"SwapShort\":\""+StringFormat("%G",(SymbolInfoDouble(symbol, SYMBOL_SWAP_SHORT)))+"\","+
               "\n"+indent3+"\"Swap3Day\":\""+IntegerToString(SymbolInfoInteger(symbol, SYMBOL_SWAP_ROLLOVER3DAYS))+"\""+
               "\n"+indent2+"}"+
               "\n"+indent1+"]"+
               "\n}";
   return(json);
  }
//+------------------------------------------------------------------+
//| Form an indent made of spaces                                    |
//+------------------------------------------------------------------+
string Indent(const int number, const int characters=3)
  {
   int length=number*characters;
   string indent=NULL;
   StringInit(indent, length, ' ');
   return indent;
  }
/*
 Result:
  Database: symbols.sqlite opened successfully
  #| cid name        type notnull dflt_value pk
  -+-------------------------------------------
  1|   0 NAME        TEXT       1             0 
  2|   1 DESCRIPTION TEXT       0             0 
  3|   2 PATH        TEXT       0             0 
  4|   3 SPREAD      INT        0             0 
  5|   4 POINT       REAL       1             0 
  6|   5 DIGITS      INT        1             0 
  7|   6 JSON        BLOB       0             0 
  1: added EURUSD
  2: added GBPUSD
  3: added USDCHF
  ...
  82: added USDCOP
  83: added USDARS
  84: added USDCLP
  Table SYMBOLS: added 84 symbols
  Database: table SYMBOLS saved in symbols.csv
  Database: symbols.sqlite created and closed
*/
 
```
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseBind](/en/docs/database/databasebind), [DatabaseBindArray](/en/docs/database/databasebindarray), [DatabaseFinalize](/en/docs/database/databasefinalize)
@@ -0,0 +1,250 @@
# DatabaseBind
Sets a parameter value in a request.
```
bool  DatabaseBind(
   int  request,      // the handle of a request created in DatabasePrepare
   int  index,        // the parameter index in the request
   T    value         // the value of a simple type parameter
   );
```
Parameters
request
[in]  The handle of a request created in [DatabasePrepare()](/en/docs/database/databaseprepare).
index
[in]  The parameter index in the request a value should be set for. The numbering starts with zero.
value
[in]  The value to be set. Extended types: bool, char, uchar, short, ushart, int, uint, color, datetime, long, ulong, float, double, string.
Return Value
Returns true if successful, otherwise - false. To get the error code, use GetLastError(), the possible responses are:
- ERR_INVALID_PARAMETER (4003)             unsupported type;
- ERR_DATABASE_INVALID_HANDLE (5121)  - invalid database handle;
- ERR_DATABASE_NOT_READY (5128)         - cannot use the function to make a request at the moment. The request is being executed or already complete. [DatabaseReset()](/en/docs/database/databasereset) should be called.
Note
The function is used in case an SQL request contains "?" or "?N" parameterizable values where N means the parameter index (starting from one). At the same time, parameters indexing in DatabaseBind() starts from zero.
For example:
```
     INSERT INTO table VALUES (?,?,?)
     SELECT * FROM table WHERE id=?
```
The function may be called immediately after a parametrized request is created in [DatabasePrepare()](/en/docs/database/databaseprepare) or after the request is reset using [DatabaseReset()](/en/docs/database/databasereset).
Use this function together with [DatabaseReset()](/en/docs/database/databasereset) to execute the request as many times as needed with different parameter values.
The function is designed to work with simple type parameters. If a parameter should be checked against an array, use the [DatabaseBindArray()](/en/docs/database/databasebindarray) function.
Example:
```
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   MqlTick ticks[];
//--- remember the start time before receiving the ticks
   uint start=GetTickCount();
//--- request the tick history per day
   ulong to=TimeCurrent()*1000;
   ulong from=to-PeriodSeconds(PERIOD_D1)*1000;
   if(CopyTicksRange(_Symbol, ticks, COPY_TICKS_ALL, from, to)==-1)
     {
      PrintFormat("%s: CopyTicksRange(%s - %s) failed, error=%d",
                  _Symbol, TimeToString(datetime(from/1000)), TimeToString(datetime(to/1000)), _LastError);
      return;
     }
   else
     {
      //--- how many ticks were received and how much time it took to receive them
      PrintFormat("%s: CopyTicksRange received %d ticks in %d ms (from %s to %s)",
                  _Symbol, ArraySize(ticks), GetTickCount()-start,
                  TimeToString(datetime(from/1000)), TimeToString(datetime(to/1000)));
     }
 
//--- set the file name for storing the database
   string filename=_Symbol+" "+TimeToString(datetime(from/1000))+" - "+TimeToString(datetime(to/1000))+".sqlite";
   StringReplace(filename, ":", "."); // ":" character is not allowed in file names
//--- open/create the database in the common terminal folder
   int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)
     {
      Print("Database: ", filename, " open failed with code ", GetLastError());
      return;
     }
   else
      Print("Database: ", filename, " opened successfully");
 
//--- create the TICKS table
   if(!DatabaseExecute(db, "CREATE TABLE TICKS("
                       "SYMBOL             CHAR(10),"
                       "TIME               INT NOT NULL,"
                       "BID                REAL,"
                       "ASK                REAL,"
                       "LAST               REAL,"
                       "VOLUME             INT,"
                       "TIME_MSC           INT,"
                       "VOLUME_REAL        REAL);"))
     {
      Print("DB: ", filename, " create table TICKS failed with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
//--- display the list of all fields in the TICKS table
   if(DatabasePrint(db, "PRAGMA TABLE_INFO(TICKS)", 0)<0)
     {
      PrintFormat("DatabasePrint(\"PRAGMA TABLE_INFO(TICKS)\") failed, error code=%d at line %d", GetLastError(), __LINE__);
      DatabaseClose(db);
      return;
     }
//--- create a parametrized request to add ticks to the TICKS table
   string sql="INSERT INTO TICKS (SYMBOL,TIME,BID,ASK,LAST,VOLUME,TIME_MSC,VOLUME_REAL)"
              " VALUES (?1,?2,?3,?4,?5,?6,?7,?8)"; // request parameters
   int request=DatabasePrepare(db, sql);
   if(request==INVALID_HANDLE)
     {
      PrintFormat("DatabasePrepare() failed with code=%d", GetLastError());
      Print("SQL request: ", sql);
      DatabaseClose(db);
      return;
     }
//--- set the value of the first request parameter
   DatabaseBind(request, 0, _Symbol);
//--- remember the start time before adding ticks to the TICKS table
   start=GetTickCount();
   DatabaseTransactionBegin(db);
   int total=ArraySize(ticks);
   bool request_error=false;
   for(int i=0; i<total; i++)
     {
      //--- set the values of the remaining parameters before adding the entry
      ResetLastError();
      if(!DatabaseBind(request, 1, ticks[i].time))
        {
         PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
         PrintFormat("Tick #%d line=%d", i+1, __LINE__);
         request_error=true;
         break;
        }
      //--- if the previous DatabaseBind() call was successful, set the next parameter       
      if(!request_error && !DatabaseBind(request, 2, ticks[i].bid))
        {
         PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
         PrintFormat("Tick #%d line=%d", i+1, __LINE__);
         request_error=true;
         break;
        }
      if(!request_error && !DatabaseBind(request, 3, ticks[i].ask))
        {
         PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
         PrintFormat("Tick #%d line=%d", i+1, __LINE__);
         request_error=true;
         break;
        }
      if(!request_error && !DatabaseBind(request, 4, ticks[i].last))
        {
         PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
         PrintFormat("Tick #%d line=%d", i+1, __LINE__);
         request_error=true;
         break;
        }
      if(!request_error && !DatabaseBind(request, 5, ticks[i].volume))
        {
         PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
         PrintFormat("Tick #%d line=%d", i+1, __LINE__);
         request_error=true;
         break;
        }
      if(!request_error && !DatabaseBind(request, 6, ticks[i].time_msc))
        {
         PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
         PrintFormat("Tick #%d line=%d", i+1, __LINE__);
         request_error=true;
         break;
        }
      if(!request_error && !DatabaseBind(request, 7, ticks[i].volume_real))
        {
         PrintFormat("DatabaseBind() failed with code=%d", GetLastError());
         PrintFormat("Tick #%d line=%d", i+1, __LINE__);
         request_error=true;
         break;
        }
 
      //--- execute a request for inserting the entry and check for an error
      if(!request_error && !DatabaseRead(request) && (GetLastError()!=ERR_DATABASE_NO_MORE_DATA))
        {
         PrintFormat("DatabaseRead() failed with code=%d", GetLastError());
         DatabaseFinalize(request);
         request_error=true;
         break;
        }
      //--- reset the request before the next parameter update
      if(!request_error && !DatabaseReset(request))
        {
         PrintFormat("DatabaseReset() failed with code=%d", GetLastError());
         DatabaseFinalize(request);
         request_error=true;
         break;
        }
     } //--- done going through all the ticks
 
//--- transactions status
   if(request_error)
     {
      PrintFormat("Table TICKS: failed to add %d ticks ", ArraySize(ticks));
      DatabaseTransactionRollback(db);
      DatabaseClose(db);
      return;
     }
   else
     {
      DatabaseTransactionCommit(db);
      PrintFormat("Table TICKS: added %d ticks in %d ms",
                  ArraySize(ticks), GetTickCount()-start);
     }
 
//--- close the database file and inform of that
   DatabaseClose(db);
   PrintFormat("Database: %s created and closed", filename);
  }
/*
 Result:
  EURUSD: CopyTicksRange received 268061 ticks in 47 ms (from 2020.03.18 12:40 to 2020.03.19 12:40)
  Database: EURUSD 2020.03.18 12.40 - 2020.03.19 12.40.sqlite opened successfully
  #| cid name        type     notnull dflt_value pk
  -+-----------------------------------------------
  1|   0 SYMBOL      CHAR(10)       0             0 
  2|   1 TIME        INT            1             0 
  3|   2 BID         REAL           0             0 
  4|   3 ASK         REAL           0             0 
  5|   4 LAST        REAL           0             0 
  6|   5 VOLUME      INT            0             0 
  7|   6 TIME_MSC    INT            0             0 
  8|   7 VOLUME_REAL REAL           0             0 
  Table TICKS: added 268061 ticks in 797 ms
  Database: EURUSD 2020.03.18 12.40 - 2020.03.19 12.40.sqlite created and closed
  OnCalculateCorrelation=0.87 2020.03.19 13:00:  EURUSD vs GBPUSD  PERIOD_M30 
*/
```
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseReset](/en/docs/database/databasereset), [DatabaseRead](/en/docs/database/databaseread), [DatabaseBindArray](/en/docs/database/databasebindarray)
@@ -0,0 +1,232 @@
# DatabaseBindArray
Sets an array as a parameter value.
```
bool  DatabaseBind(
   int  request,      // the handle of a request created in DatabasePrepare
   int  index,        // the parameter index in the request
   T&   array[]       // parameter value as an array
   );
```
Parameters
request
[in]  The handle of a request created in [DatabasePrepare()](/en/docs/database/databaseprepare).
index
[in]  The parameter index in the request a value should be set for. The numbering starts with zero.
array[]
[in]  The array to be set as a request parameter value.
Return Value
Returns true if successful, otherwise - false. To get the error code, use GetLastError(), the possible responses are:
- ERR_INVALID_PARAMETER (4003)               unsupported type;
- ERR_ARRAY_BAD_SIZE (4011)                    - array size in bytes exceeds INT_MAX;
- ERR_DATABASE_INVALID_HANDLE (5121)  - invalid database handle;
- ERR_DATABASE_NOT_READY (5128)    - cannot use the function to make a request at the moment (the request is being executed or already complete, DatabaseReset should be called).
Note
The function is used in case an SQL request contains "?" or "?N" parameterizable values where N means the parameter index (starting from one). At the same time, parameters indexing in DatabaseBindArray() starts from zero.
For example:
```
     INSERT INTO table VALUES (?,?,?)
```
The function may be called immediately after a parametrized request is created in [DatabasePrepare()](/en/docs/database/databaseprepare) or after the request is reset using [DatabaseReset()](/en/docs/database/databasereset).
Use this function together with [DatabaseReset()](/en/docs/database/databasereset) to execute the request as many times as needed with different parameter values.
Example:
```
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- open the dialog for selecting files with the DAT extension
   string selected_files[];
   if(!FileSelectDialog("Select files to download", NULL,
                       "Data files (*.dat)|*.dat|All files (*.*)|*.*",
                       FSD_ALLOW_MULTISELECT, selected_files, "tester.dat")>0)
     {
      Print("Files not selected. Exit");
      return;
     }
//--- get the size of files
   ulong filesize[];
   int filehandle[];
   int files=ArraySize(selected_files);
   ArrayResize(filesize, files);
   ZeroMemory(filesize);
   ArrayResize(filehandle, files);
   double total_size=0;
   for(int i=0; i<files; i++)
     {
      filehandle[i]=FileOpen(selected_files[i], FILE_READ|FILE_BIN);
      if(filehandle[i]!=INVALID_HANDLE)
        {
         filesize[i]=FileSize(filehandle[i]);
         //PrintFormat("%d, %s handle=%d %d bytes", i, selected_files[i], filehandle[i], filesize[i]);
         total_size+=(double)filesize[i];
        }
     }
//--- check the common size of files
   if(total_size==0)
     {
      PrintFormat("Total files size is 0. Exit");
      return;
     }
 
//--- create or open the database in the common terminal folder
   string filename="dat_files.sqlite";
   int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE);
   if(db==INVALID_HANDLE)
     {
      Print("DB: ", filename, " open failed with code ", GetLastError());
      return;
     }
   else
      Print("Database: ", filename, " opened successfully");
//--- if the FILES table exists, delete it
   if(DatabaseTableExists(db, "FILES"))
     {
      //--- delete the table
      if(!DatabaseExecute(db, "DROP TABLE FILES"))
        {
         Print("Failed to drop table FILES with code ", GetLastError());
         DatabaseClose(db);
         return;
        }
     }
//--- create the FILES table
   if(!DatabaseExecute(db, "CREATE TABLE FILES("
                       "NAME           TEXT NOT NULL,"
                       "SIZE           INT  NOT NULL,"
                       "PERCENT_SIZE   REAL NOT NULL,"
                       "DATA           BLOB NOT NULL);"))
     {
      Print("DB: failed to create table FILES with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
//--- display the list of all fields in the FILES table
   if(DatabasePrint(db, "PRAGMA TABLE_INFO(FILES)", 0)<0)
     {
      PrintFormat("DatabasePrint(\"PRAGMA TABLE_INFO(FILES)\") failed, error code=%d at line %d", GetLastError(), __LINE__);
      DatabaseClose(db);
      return;
     }
 
//--- create a parametrized request to add files to the FILES table
   string sql="INSERT INTO FILES (NAME,SIZE,PERCENT_SIZE,DATA)"
              " VALUES (?1,?2,?3,?4);"; // request parameters
   int request=DatabasePrepare(db, sql);
   if(request==INVALID_HANDLE)
     {
      PrintFormat("DatabasePrepare() failed with code=%d", GetLastError());
      Print("SQL request: ", sql);
      DatabaseClose(db);
      return;
     }
 
//--- go through all the files and add them to the FILES table
   bool request_error=false;
   DatabaseTransactionBegin(db);
   int count=0;
   uint size;
   for(int i=0; i<files; i++)
     {
      if(filehandle[i]!=INVALID_HANDLE)
        {
         char data[];
         size=FileReadArray(filehandle[i], data);
         if(size==0)
           {
            PrintFormat("FileReadArray(%s) failed with code %d", selected_files[i], GetLastError());
            continue;
           }
 
         count++;
         //--- set the values of the parameters before adding the file to the table
         if(!DatabaseBind(request, 0, selected_files[i]))
           {
            PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
            request_error=true;
            break;
           }
         if(!DatabaseBind(request, 1, size))
           {
            PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
            request_error=true;
            break;
           }
         if(!DatabaseBind(request, 2, double(size)*100./total_size))
           {
            PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
            request_error=true;
            break;
           }
         if(!DatabaseBindArray(request, 3, data))
           {
            PrintFormat("DatabaseBind() failed at line %d with code=%d", __LINE__, GetLastError());
            request_error=true;
            break;
           }
         //--- execute a request for inserting the entry and check for an error
         if(!DatabaseRead(request)&&(GetLastError()!=ERR_DATABASE_NO_MORE_DATA))
           {
            PrintFormat("DatabaseRead() failed with code=%d", GetLastError());
            DatabaseFinalize(request);
            request_error=true;
            break;
           }
         else
            PrintFormat("%d. %s: %d bytes", count, selected_files[i],size);
         //--- reset the request before the next parameter update
         if(!DatabaseReset(request))
           {
            PrintFormat("DatabaseReset() failed with code=%d", GetLastError());
            DatabaseFinalize(request);
            request_error=true;
            break;
           }
        }
     }
//--- transactions status
   if(request_error)
     {
      PrintFormat("Table FILES: failed to add %d files", count);
      DatabaseTransactionRollback(db);
      DatabaseClose(db);
      return;
     }
   else
     {
      DatabaseTransactionCommit(db);
      PrintFormat("Table FILES: added %d files", count);
     }
 
//--- close the database file and inform of that
   DatabaseClose(db);
   PrintFormat("Database: %s created and closed", filename);
  }
```
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseReset](/en/docs/database/databasereset), [DatabaseRead](/en/docs/database/databaseread), [DatabaseBind](/en/docs/database/databasebind)
@@ -0,0 +1,31 @@
# DatabaseRead
Moves to the next entry as a result of a request.
```
bool  DatabaseRead(
   int  request      // request handle received in DatabasePrepare
   );
```
Parameters
request
[in]  Request handle received in [DatabasePrepare()](/en/docs/database/databaseprepare).
Return Value
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_INVALID_PARAMETER (4003)                no table name specified (empty string or NULL);
- ERR_WRONG_STRING_PARAMETER (5040)   error converting a request into a UTF-8 string;
- ERR_DATABASE_INTERNAL (5120)               internal database error;
- ERR_DATABASE_INVALID_HANDLE (5121)     invalid database handle;
- ERR_DATABASE_EXECUTE (5124)                  request execution error;
- ERR_DATABASE_NO_MORE_DATA (5126)     no table exists (not an error, normal completion).
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseReadBind](/en/docs/database/databasereadbind)
@@ -0,0 +1,169 @@
# DatabaseReadBind
Moves to the next record and reads data into the structure from it.
```
bool  DatabaseReadBind(
   int    request,           // the handle of a request created in DatabasePrepare
   void&  struct_object      // the reference to the structure for reading the record 
   );
```
Parameters
request
[in]  The handle of a request created in [DatabasePrepare()](/en/docs/database/databaseprepare).
struct_object
[out]  The reference to the structure the data from the current record is to be read to. The structure should only have numerical types and/or strings (arrays are not allowed) as members and cannot be a descendant.
Return Value
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_INVALID_PARAMETER (4003)                no table name specified (empty string or NULL);
- ERR_WRONG_STRING_PARAMETER (5040)   error converting a request into a UTF-8 string;
- ERR_DATABASE_INTERNAL (5120)               internal database error;
- ERR_DATABASE_INVALID_HANDLE (5121)     invalid database handle;
- ERR_DATABASE_EXECUTE (5124)                  request execution error;
- ERR_DATABASE_NO_MORE_DATA (5126)     no table exists (not an error, normal completion).
Note
A number of fields in the struct_object structure should not exceed [DatabaseColumnsCount()](/en/docs/database/databasecolumnscount). If the number of fields in the struct_object structure is less than the number of fields in the record, the partial reading is performed. The remaining data can be explicitly obtained using the corresponding [DatabaseColumnText()](/en/docs/database/databasecolumntext), [DatabaseColumnInteger()](/en/docs/database/databasecolumninteger) and other functions.
Example:
```
struct Person
  {
   int               id;
   string            name;
   int               age;
   string            address;
   double            salary;
  };
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   int db;
   string filename="company.sqlite";
//--- open
   db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE |DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)
     {
      Print("DB: ", filename, " open failed with code ", GetLastError());
      return;
     }
//--- if the table COMPANY exists then drop the table
   if(DatabaseTableExists(db, "COMPANY"))
     {
      //--- delete the table
      if(!DatabaseExecute(db, "DROP TABLE COMPANY"))
        {
         Print("Failed to drop table COMPANY with code ", GetLastError());
         DatabaseClose(db);
         return;
        }
     }
//--- create table
   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;
     }
 
//--- insert data
   if(!DatabaseExecute(db, "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (1, 'Paul', 32, 'California', 25000.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;
     }
 
//--- 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;
     }
//--- print records
   Person person;
   Print("Persons with salary > 15000:");
   for(int i=0; DatabaseReadBind(request, person); i++)
      Print(i, ":  ", person.id, " ", person.name, " ", person.age, " ", person.address, " ", person.salary);
//--- delete request after use
   DatabaseFinalize(request);
 
   Print("Some statistics:");
//--- prepare new request about total salary
   request=DatabasePrepare(db, "SELECT SUM(SALARY) FROM COMPANY");
   if(request==INVALID_HANDLE)
     {
      Print("DB: ", filename, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return;
     }
   while(DatabaseRead(request))
     {
      double total_salary;
      DatabaseColumnDouble(request, 0, total_salary);
      Print("Total salary=", total_salary);
     }
//--- delete request after use
   DatabaseFinalize(request);
 
//--- prepare new request about average salary
   request=DatabasePrepare(db, "SELECT AVG(SALARY) FROM COMPANY");
   if(request==INVALID_HANDLE)
     {
      Print("DB: ", filename, " request failed with code ", GetLastError());
      ResetLastError();
      DatabaseClose(db);
      return;
     }
   while(DatabaseRead(request))
     {
      double aver_salary;
      DatabaseColumnDouble(request, 0, aver_salary);
      Print("Average salary=", aver_salary);
     }
//--- delete request after use
   DatabaseFinalize(request);
 
//--- close database
   DatabaseClose(db);
  }
//+-------------------------------------------------------------------
/*
Output:
Persons with salary > 15000:
0:  1 Paul 32 California 25000.0
1:  3 Teddy 23 Norway 20000.0
2:  4 Mark 25 Rich-Mond  65000.0
Some statistics:
Total salary=125000.0
Average salary=31250.0
*/
```
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseRead](/en/docs/database/databaseread)
@@ -0,0 +1,28 @@
# DatabaseFinalize
Removes a request created in [DatabasePrepare(](/en/docs/database/databaseprepare)).
```
void  DatabaseFinalize(
   int  request      // request handle received in DatabasePrepare
   );
```
Parameters
request
[in]  Request handle received in DatabasePrepare().
Return Value
None.
Note
If the handle is invalid, the function sets the ERR_DATABASE_INVALID_HANDLE error. You can check the error using GetLastError().
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseExecute](/en/docs/database/databaseexecute)
@@ -0,0 +1,234 @@
# DatabaseTransactionBegin
Starts transaction execution.
```
bool  DatabaseTransactionBegin(
   int  database      // database handle received in DatabaseOpen
   );
```
Parameters
database
[in]  Database handle received in [DatabaseOpen()](/en/docs/database/databaseopen).
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_INTERNAL_ERROR (4001)                    critical runtime error;
- ERR_INVALID_PARAMETER (4003)                sql parameter contains an empty string;
- ERR_NOT_ENOUGH_MEMORY (4004)            insufficient memory;
- ERR_WRONG_STRING_PARAMETER (5040)   error converting a request into a UTF-8 string;
- ERR_DATABASE_INTERNAL (5120)               internal database error;
- ERR_DATABASE_INVALID_HANDLE (5121)   invalid database handle;
- ERR_DATABASE_EXECUTE (5124)                request execution error.
Note
The DatabaseTransactionBegin() function should be called before a transaction execution. Any transaction should start with calling DatabaseTransactionBegin() and end with calling DatabaseTransactionCommit().
Example:
```
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- create the file name
   string filename=AccountInfoString(ACCOUNT_SERVER) +"_"+IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN))+".sqlite";
//--- open/create the database in the common terminal folder
   int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)
     {
      Print("DB: ", filename, " open failed with code ", GetLastError());
      return;
     }
//--- if the DEALS table already exists, delete it
   if(!DeleteTable(db, "DEALS"))
     {
      DatabaseClose(db);
      return;
     }
//--- create the DEALS table
   if(!CreateTableDeals(db))
     {
      DatabaseClose(db);
      return;
     }
//---  request the entire trading history
   datetime from_date=0;
   datetime to_date=TimeCurrent();
//--- request the history of deals in the specified interval
   HistorySelect(from_date, to_date);
   int deals_total=HistoryDealsTotal();
   PrintFormat("Deals in the trading history: %d ", deals_total);
 
//--- measure the transaction execution speed using DatabaseTransactionBegin/DatabaseTransactionCommit
   ulong start=GetMicrosecondCount();
   bool fast_transactions=true;
   InsertDeals(db, fast_transactions);
   double fast_transactions_time=double(GetMicrosecondCount()-start)/1000;
   PrintFormat("Transations WITH    DatabaseTransactionBegin/DatabaseTransactionCommit: time=%.1f milliseconds", fast_transactions_time);
 
//--- delete the DEALS table, and then create it again
   if(!DeleteTable(db, "DEALS"))
     {
      DatabaseClose(db);
      return;
     }
//--- create a new DEALS table
   if(!CreateTableDeals(db))
     {
      DatabaseClose(db);
      return;
     }
 
//--- test again, this time without using DatabaseTransactionBegin/DatabaseTransactionCommit
   fast_transactions=false;
   start=GetMicrosecondCount();
   InsertDeals(db, fast_transactions);
   double slow_transactions_time=double(GetMicrosecondCount()-start)/1000;
   PrintFormat("Transations WITHOUT DatabaseTransactionBegin/DatabaseTransactionCommit: time=%.1f milliseconds", slow_transactions_time);
//--- report gain in time
   PrintFormat("Use of DatabaseTransactionBegin/DatabaseTransactionCommit provided acceleration by %.1f times", double(slow_transactions_time)/fast_transactions_time);
//--- close the database
   DatabaseClose(db);
  }
/*
Results:
   Deals in the trading history: 2737
   Transations WITH    DatabaseTransactionBegin/DatabaseTransactionCommit: time=48.5 milliseconds
   Transations WITHOUT DatabaseTransactionBegin/DatabaseTransactionCommit: time=25818.9 milliseconds
   Use of DatabaseTransactionBegin/DatabaseTransactionCommit provided acceleration by 532.8 times
*/
//+------------------------------------------------------------------+
//| Deletes a table with the specified name from the database        |
//+------------------------------------------------------------------+
bool DeleteTable(int database, string table_name)
  {
   if(!DatabaseExecute(database, "DROP TABLE IF EXISTS "+table_name))
     {
      Print("Failed to drop table with code ", GetLastError());
      return(false);
     }
//--- the table has been successfully deleted
   return(true);
  }
//+------------------------------------------------------------------+
//| Creates the DEALS table                                          |
//+------------------------------------------------------------------+
bool CreateTableDeals(int database)
  {
//--- check if the table exists
   if(!DatabaseTableExists(database, "DEALS"))
      //--- create the table
      if(!DatabaseExecute(database, "CREATE TABLE DEALS("
                          "ID          INT KEY NOT NULL,"
                          "ORDER_ID    INT     NOT NULL,"
                          "POSITION_ID INT     NOT NULL,"
                          "TIME        INT     NOT NULL,"
                          "TYPE        INT     NOT NULL,"
                          "ENTRY       INT     NOT NULL,"
                          "SYMBOL      CHAR(10),"
                          "VOLUME      REAL,"
                          "PRICE       REAL,"
                          "PROFIT      REAL,"
                          "SWAP        REAL,"
                          "COMMISSION  REAL,"
                          "MAGIC       INT,"
                          "REASON      INT );"))
        {
         Print("DB: create the table DEALS failed with code ", GetLastError());
         return(false);
        }
//--- the table has been successfully created
   return(true);
  }
//+------------------------------------------------------------------+
//| Adds deals to the database table                                 |
//+------------------------------------------------------------------+
bool InsertDeals(int database, bool begintransaction=true)
  {
//--- Auxiliary variables
   ulong    deal_ticket;         // deal ticket
   long     order_ticket;        // the ticket of the order by which the deal was executed
   long     position_ticket;     // ID of the position to which the deal belongs
   datetime time;                // deal execution time
   long     type ;               // deal type
   long     entry ;              // deal direction
   string   symbol;              // the symbol fro which the deal was executed
   double   volume;              // operation volume
   double   price;               // price
   double   profit;              // financial result
   double   swap;                // swap
   double   commission;          // commission
   long     magic;               // Magic number
   long     reason;              // deal execution reason or source
//--- go through all deals and add to the database
   bool failed=false;
   int deals=HistoryDealsTotal();
//--- if fast transaction performance method is used
   if(begintransaction)
     {
      // --- lock the database before executing transactions
      DatabaseTransactionBegin(database);
     }
   for(int i=0; i<deals; i++)
     {
      deal_ticket=    HistoryDealGetTicket(i);
      order_ticket=   HistoryDealGetInteger(deal_ticket, DEAL_ORDER);
      position_ticket=HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID);
      time= (datetime)HistoryDealGetInteger(deal_ticket, DEAL_TIME);
      type=           HistoryDealGetInteger(deal_ticket, DEAL_TYPE);
      entry=          HistoryDealGetInteger(deal_ticket, DEAL_ENTRY);
      symbol=         HistoryDealGetString(deal_ticket, DEAL_SYMBOL);
      volume=         HistoryDealGetDouble(deal_ticket, DEAL_VOLUME);
      price=          HistoryDealGetDouble(deal_ticket, DEAL_PRICE);
      profit=         HistoryDealGetDouble(deal_ticket, DEAL_PROFIT);
      swap=           HistoryDealGetDouble(deal_ticket, DEAL_SWAP);
      commission=     HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION);
      magic=          HistoryDealGetInteger(deal_ticket, DEAL_MAGIC);
      reason=         HistoryDealGetInteger(deal_ticket, DEAL_REASON);
      //--- add each deal using the following request
      string request_text=StringFormat("INSERT INTO DEALS (ID,ORDER_ID,POSITION_ID,TIME,TYPE,ENTRY,SYMBOL,VOLUME,PRICE,PROFIT,SWAP,COMMISSION,MAGIC,REASON)"
                                       "VALUES (%d, %d, %d, %d, %d, %d, '%s', %G, %G, %G, %G, %G, %d, %d)",
                                       deal_ticket, order_ticket, position_ticket, time, type, entry, symbol, volume, price, profit, swap, commission, magic, reason);
      if(!DatabaseExecute(database, request_text))
        {
         PrintFormat("%s: failed to insert deal #%dwith code %d", __FUNCTION__, deal_ticket, GetLastError());
         PrintFormat("i=%d: deal #%d  %s", i, deal_ticket, symbol);
         failed=true;
         break;
        }
     }
//--- check for transaction execution errors
   if(failed)
     {
      //--- if fast transaction performance method is used
      if(begintransaction)
        {
         //--- roll back all transactions and unlock the database
         DatabaseTransactionRollback(database);
        }
      Print("%s: DatabaseExecute() failed with code ", __FUNCTION__, GetLastError());
      return(false);
     }
//--- if fast transaction performance method is used
   if(begintransaction)
     {
      //--- all transactions have been performed successfully - record changes and unlock the database
      DatabaseTransactionCommit(database);
     }
//--- successful completion
   return(true);
  }
//+------------------------------------------------------------------+
```
See also
[DatabaseExecute](/en/docs/database/databaseexecute),[ DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseTransactionCommit](/en/docs/database/databasetransactioncommit), [DatabaseTransactionRollback](/en/docs/database/databasetransactionrollback)
@@ -0,0 +1,34 @@
# DatabaseTransactionCommit
Completes transaction execution.
```
bool  DatabaseTransactionCommit(
   int  database      // database handle received in DatabaseOpen
   );
```
Parameters
database
[in]  Database handle received in [DatabaseOpen()](/en/docs/database/databaseopen).
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_INTERNAL_ERROR (4001)                    critical runtime error;
- ERR_INVALID_PARAMETER (4003)                sql parameter contains an empty string;
- ERR_NOT_ENOUGH_MEMORY (4004)            insufficient memory;
- ERR_WRONG_STRING_PARAMETER (5040)   error converting a request into a UTF-8 string;
- ERR_DATABASE_INTERNAL (5120)               internal database error;
- ERR_DATABASE_INVALID_HANDLE (5121)   invalid database handle;
- ERR_DATABASE_EXECUTE (5124)                request execution error.
Note
The DatabaseTransactionCommit() function completes all transactions executed after calling the [DatabaseBeginTransaction()](/en/docs/database/databasetransactionbegin) function. Any transaction should start with calling DatabaseTransactionBegin() and end with calling DatabaseTransactionCommit() for successful completion.
See also
[DatabaseExecute](/en/docs/database/databaseexecute),[ DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseTransactionBegin](/en/docs/database/databasetransactionbegin), [DatabaseTransactionRollback](/en/docs/database/databasetransactionrollback)
@@ -0,0 +1,34 @@
# DatabaseTransactionRollback
Rolls back transactions.
```
bool  DatabaseTransactionRollback(
   int  database      // database handle received in DatabaseOpen
   );
```
Parameters
database
[in]  Database handle received in [DatabaseOpen()](/en/docs/database/databaseopen).
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_INTERNAL_ERROR (4001)                      critical runtime error;
- ERR_INVALID_PARAMETER (4003)                sql parameter contains an empty string;
- ERR_NOT_ENOUGH_MEMORY (4004)            insufficient memory;
- ERR_WRONG_STRING_PARAMETER (5040)   error converting a request into a UTF-8 string;
- ERR_DATABASE_INTERNAL (5120)               internal database error;
- ERR_DATABASE_INVALID_HANDLE (5121)     invalid database handle;
- ERR_DATABASE_EXECUTE (5124)                  request execution error.
Note
DatabaseTransactionRollback() call cancels all transactions executed after calling the DatabaseTransactionBegin() function. The DatabaseTransactionRollback() function is necessary for rolling back changes in a database in case errors occur when executing a transaction.
See also
[DatabaseExecute](/en/docs/database/databaseexecute),[ DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseTransactionBegin](/en/docs/database/databasetransactionbegin), [DatabaseTransactionCommit](/en/docs/database/databasetransactioncommit)
@@ -0,0 +1,30 @@
# DatabaseColumnsCount
Gets the number of fields in a request.
```
int  DatabaseColumnsCount(
   int  request      // request handle received in DatabasePrepare
   );
```
Parameters
request
[in]  Request handle received in [DatabasePrepare()](/en/docs/database/databaseprepare).
Return Value
Number of fields or -1 in case of an error. To get the error code, use GetLastError(), the possible responses are:
- ERR_DATABASE_INVALID_HANDLE (5121) - invalid request handle.
Note
There is no need to call the [DatabaseRead()](/en/docs/database/databaseread) function to get the number of fields of a request created in DatabasePrepare(). For the remaining DatabaseColumnXXX() functions, DatabaseRead() should be preliminarily called.
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseFinalize](/en/docs/database/databasefinalize), [DatabaseClose](/en/docs/database/databaseclose)
@@ -0,0 +1,41 @@
# DatabaseColumnName
Gets a field name by index.
```
bool  DatabaseColumnName(
   int      request,     // request handle received in DatabasePrepare
   int      column,      // field index in the request
   string&  name         // the reference to the variable for receiving the field name
   );
```
Parameters
request
[in]  Request handle received in [DatabasePrepare()](/en/docs/database/databaseprepare).
column
[in]  Field index in the request. Field numbering starts from zero and cannot exceed [DatabaseColumnsCount()](/en/docs/database/databasecolumnscount) - 1.
name
[out]  Variable for writing the field name.
Return Value
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_DATABASE_INVALID_HANDLE (5121) invalid request handle;
- ERR_DATABASE_NO_MORE_DATA (5126)   'column' index exceeds DatabaseColumnsCount() -1.
Note
The value can be obtained only if at least one [DatabaseRead()](/en/docs/database/databaseread) call has been preliminarily made for 'request'.
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseColumnsCount](/en/docs/database/databasecolumnscount), [DatabaseColumnType](/en/docs/database/databasecolumntype)
@@ -0,0 +1,47 @@
# DatabaseColumnType
Gets a field type by index.
```
ENUM_DATABASE_FIELD_TYPE  DatabaseColumnType(
   int  request,     // request handle received in DatabasePrepare
   int  column       // field index in the request
   );
```
Parameters
request
[in]  Request handle received in [DatabasePrepare()](/en/docs/database/databaseprepare).
column
[in]  Field index in the request. Field numbering starts from zero and cannot exceed [DatabaseColumnsCount()](/en/docs/database/databasecolumnscount) - 1.
Return Value
Return the field type from the [ENUM_DATABASE_FIELD_TYPE](/en/docs/database/databasecolumntype#enum_database_field_type) enumeration. To get the error code, use GetLastError(), the possible responses are:
- ERR_DATABASE_INVALID_HANDLE (5121) invalid request handle;
- ERR_DATABASE_NO_MORE_DATA (5126)    'column' index exceeds DatabaseColumnsCount() -1.
Note
The value can be obtained only if at least one [DatabaseRead()](/en/docs/database/databaseread) call has been preliminarily made for 'request'.
ENUM_DATABASE_FIELD_TYPE
| ID | Description |
| --- | --- |
| DATABASE_FIELD_TYPE_INVALID | Error getting type, the error code can be obtained using int GetLastError() |
| DATABASE_FIELD_TYPE_INTEGER | Integer type |
| DATABASE_FIELD_TYPE_FLOAT | Real type |
| DATABASE_FIELD_TYPE_TEXT | String type |
| DATABASE_FIELD_TYPE_BLOB | Binary type |
| DATABASE_FIELD_TYPE_NULL | Special NULL type |
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseColumnsCount](/en/docs/database/databasecolumnscount), [DatabaseColumnName](/en/docs/database/databasecolumnname)
@@ -0,0 +1,36 @@
# DatabaseColumnSize
Gets a field size in bytes.
```
int  DatabaseColumnSize(
   int  request,     // request handle received in DatabasePrepare
   int  column       // field index in the request
   );
```
Parameters
request
[in]  Request handle received in [DatabasePrepare()](/en/docs/database/databaseprepare).
column
[in]  Field index in the request. Field numbering starts from zero and cannot exceed [DatabaseColumnsCount()](/en/docs/database/databasecolumnscount) - 1.
Return Value
If successful, the field size in bytes is returned, otherwise -1. To get the error code, use GetLastError(), the possible responses are:
- ERR_DATABASE_INVALID_HANDLE (5121) invalid request handle;
- ERR_DATABASE_NO_MORE_DATA (5126)   'column' index exceeds DatabaseColumnsCount() -1.
Note
The value can be obtained only if at least one [DatabaseRead()](/en/docs/database/databaseread) call has been preliminarily made for 'request'.
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseColumnBlob](/en/docs/database/databasecolumnblob), [DatabaseColumnsCount](/en/docs/database/databasecolumnscount), [DatabaseColumnName](/en/docs/database/databasecolumnname), [DatabaseColumnType](/en/docs/database/databasecolumntype)
@@ -0,0 +1,43 @@
# DatabaseColumnText
Gets a field value as a string from the current record.
```
bool  DatabaseColumnText(
   int      request,     // request handle received in DatabasePrepare
   int      column,      // field index in the request
   string&  value        // the reference to the variable for receiving the value
   );
```
Parameters
request
[in]  Request handle received in [DatabasePrepare()](/en/docs/database/databaseprepare).
column
[in]  Field index in the request. Field numbering starts from zero and cannot exceed [DatabaseColumnsCount()](/en/docs/database/databasecolumnscount) - 1.
value
[out]  Reference to the variable for writing the field value.
Return Value
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_DATABASE_INVALID_HANDLE (5121) invalid request handle;
- ERR_DATABASE_NO_MORE_DATA (5126)   'column' index exceeds DatabaseColumnsCount() -1.
Note
The value can be obtained only if at least one [DatabaseRead()](/en/docs/database/databaseread) call has been preliminarily made for 'request'.
To read the value from the next record, call [DatabaseRead()](/en/docs/database/databaseread) preliminarily.
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseColumnsCount](/en/docs/database/databasecolumnscount), [DatabaseColumnType](/en/docs/database/databasecolumntype), [DatabaseColumnName](/en/docs/database/databasecolumnname)
@@ -0,0 +1,43 @@
# DatabaseColumnInteger
Gets the int type value from the current record.
```
bool  DatabaseColumnInteger(
   int   request,     // request handle received in DatabasePrepare
   int   column,      // field index in the request
   int&  value        // the reference to the variable for receiving the value
   );
```
Parameters
request
[in]  Request handle received in [DatabasePrepare()](/en/docs/database/databaseprepare).
column
[in]  Field index in the request. Field numbering starts from zero and cannot exceed [DatabaseColumnsCount()](/en/docs/database/databasecolumnscount) - 1.
value
[out]  Reference to the variable for writing the field value.
Return Value
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_DATABASE_INVALID_HANDLE (5121) invalid request handle;
- ERR_DATABASE_NO_MORE_DATA (5126)   'column' index exceeds DatabaseColumnsCount() -1.
Note
The value can be obtained only if at least one [DatabaseRead()](/en/docs/database/databaseread) call has been preliminarily made for 'request'.
To read the value from the next record, call [DatabaseRead()](/en/docs/database/databaseread) preliminarily.
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseColumnsCount](/en/docs/database/databasecolumnscount), [DatabaseColumnType](/en/docs/database/databasecolumntype), [DatabaseColumnName](/en/docs/database/databasecolumnname)
@@ -0,0 +1,43 @@
# DatabaseColumnLong
Gets the long type value from the current record.
```
bool  DatabaseColumnLong(
   int    request,     // request handle received in DatabasePrepare
   int    column,      // field index in the request
   long&  value        // the reference to the variable for receiving the value
   );
```
Parameters
request
[in]  Request handle received in [DatabasePrepare()](/en/docs/database/databaseprepare).
column
[in]  Field index in the request. Field numbering starts from zero and cannot exceed [DatabaseColumnsCount()](/en/docs/database/databasecolumnscount) - 1.
value
[out]  Reference to the variable for writing the field value.
Return Value
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_DATABASE_INVALID_HANDLE (5121) invalid request handle;
- ERR_DATABASE_NO_MORE_DATA (5126)   'column' index exceeds DatabaseColumnsCount() -1.
Note
The value can be obtained only if at least one [DatabaseRead()](/en/docs/database/databaseread) call has been preliminarily made for 'request'.
To read the value from the next record, call [DatabaseRead()](/en/docs/database/databaseread) preliminarily.
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseColumnsCount](/en/docs/database/databasecolumnscount), [DatabaseColumnType](/en/docs/database/databasecolumntype), [DatabaseColumnName](/en/docs/database/databasecolumnname)
@@ -0,0 +1,43 @@
# DatabaseColumnDouble
Gets the double type value from the current record.
```
bool  DatabaseColumnDouble(
   int      request,     // request handle received in DatabasePrepare
   int      column,      // field index in the request
   double&  value        // the reference to the variable for receiving the value
   );
```
Parameters
request
[in]  Request handle received in [DatabasePrepare()](/en/docs/database/databaseprepare).
column
[in]  Field index in the request. Field numbering starts from zero and cannot exceed [DatabaseColumnsCount()](/en/docs/database/databasecolumnscount) - 1.
value
[out]  Reference to the variable for writing the field value.
Return Value
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_DATABASE_INVALID_HANDLE (5121) invalid request handle;
- ERR_DATABASE_NO_MORE_DATA (5126)   'column' index exceeds DatabaseColumnsCount() -1.
Note
The value can be obtained only if at least one [DatabaseRead()](/en/docs/database/databaseread) call has been preliminarily made for 'request'.
To read the value from the next record, call [DatabaseRead()](/en/docs/database/databaseread) preliminarily.
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseColumnsCount](/en/docs/database/databasecolumnscount), [DatabaseColumnType](/en/docs/database/databasecolumntype), [DatabaseColumnName](/en/docs/database/databasecolumnname)
@@ -0,0 +1,43 @@
# DatabaseColumnBlob
Gets a field value as an array from the current record.
```
bool  DatabaseColumnBlob(
   int    request,     // request handle received in DatabasePrepare
   int    column,      // field index in the request
   void&  data[]       // the reference to the variable for receiving the value
   );
```
Parameters
request
[in]  Request handle received in [DatabasePrepare()](/en/docs/database/databaseprepare).
column
[in]  Field index in the request. Field numbering starts from zero and cannot exceed [DatabaseColumnsCount()](/en/docs/database/databasecolumnscount) - 1.
data[]
[out]  Reference to the array for writing the field value.
Return Value
Return true if successful, otherwise false. To get the error code, use GetLastError(), the possible responses are:
- ERR_DATABASE_INVALID_HANDLE (5121) invalid request handle;
- ERR_DATABASE_NO_MORE_DATA (5126)   'column' index exceeds DatabaseColumnsCount() -1.
Note
The value can be obtained only if at least one [DatabaseRead()](/en/docs/database/databaseread) call has been preliminarily made for 'request'.
To read the value from the next record, call [DatabaseRead()](/en/docs/database/databaseread) preliminarily.
See also
[DatabasePrepare](/en/docs/database/databaseprepare), [DatabaseColumnSize](/en/docs/database/databasecolumnsize), [DatabaseColumnsCount](/en/docs/database/databasecolumnscount), [DatabaseColumnType](/en/docs/database/databasecolumntype), [DatabaseColumnName](/en/docs/database/databasecolumnname)