如何从 MQL5 (MQL4) 访问 MySQL 数据库(基础篇)
📘

如何从 MQL5 (MQL4) 访问 MySQL 数据库(基础篇)

第 1/2 篇

◍ 在 MT5 里直连 MySQL 的可行性

很多做 EA 的人想把报价、信号或成交记录甩进外部 MySQL,而不是只靠 MT5 本地文件。官方终端并没有内置 MySQL 驱动,所以核心思路是用 MQL5 调用编译好的 DLL,把 SQL 请求转交给 C/C++ 层去跑。 这套方案在 2014 年就有开发者验证过,相关讨论帖累计浏览约 14,137 次、回帖 292 条,说明社区里长期有真实需求。外汇和贵金属杠杆高、滑点随机,任何把下单逻辑依赖外部库的设计都要先在模拟盘压测,避免 DLL 卡死拖垮平仓。 具体落地时,MQL5 侧只负责拼 SQL 字符串和收结果,连接池、鉴权、超时全部放在 DLL 内。这样 MT5 重启不影响数据库会话结构,也方便你换服务器不用改 EA 代码。

为什么多数 MQL 接 MySQL 方案都卡在查询

MQL 和数据库打通不是新话题,但在 MT4/MT5 上依然刚需:存价格历史、跨平台搬交易、服务端算深度指标、远程看账户,这些都绕不开库。 代码库里早有尝试,比如 MT4 的「MySQL 包装」链接库,很多程序员自己改过,但它的短板很具体——专门分配特殊数组来接数据库返回,主程序一改查询就得重管内存。 另一个 MT4 的「MySQL 日志 EA」不走包装、直接调 libmysql.dll,结果在编译版 600+ 跑不起来:char 换 wchar_t、int 替了结构指针,内存分配失控漏内存。MT5 的「EAX_Mysql」写得漂亮,但作者自己列了强制限制。 还有更荒谬的:用 libmysql.dll 做写操作,SELECT 却走 inet.dll 发 HTTP 到 PHP 脚本查,SQL 写死在 PHP 里。这意味着跑起来要 MySQL+Web 服务器+脚本全就位,只为拉个数据,维护成本离谱。 插入、建表这类操作大部分方案都没问题,真正的雷区是数据查询的返回。用数组接结果在开发调试时极不方便——查询一变你就得重新算内存分配,这事能避就该避。 下文要说的接口参照 PL/SQL、T-SQL 的游标思路,做成 DLL 包 libmysql.dll + 一个 .mqh,部件最少、好维护,专门解决查询返回这个痛点。

「用 MQL 直连 MySQL 的三层组件与连接陷阱」

在 MT5 里用 MQL 程序碰 MySQL,实际是三层 DLL 接力:MQLMySQL.mqh 只做函数导入和错误处理;MQLMySQL.dll 是包装器,负责管连接和游标,支持多程序并发连多个库,用互斥锁隔开共享资源;最底层的 libmysql.dll 才是真正发查询收结果的本地驱动,可从系统目录或终端 Libraries 拷入。 开连接靠 MySqlConnect,典型写在 OnInit() 里,传 host、user、password、database、port(一般 3306)、socket、clientflag(一般 0),成功返 ID,失败返 -1。关连接 MySqlDisconnect 放 OnDeinit()。这里有个实打实的坑:MySQL 自己会在长时间无查询时超时断连,默认 8 小时。周末休市时 OnTick 不触发,连接空挂,周一开市就报连错。所以得在运行期定时探活并重连,间隔略小于服务端 timeout。 非 SELECT 的 SQL(INSERT/UPDATE/DELETE/CREATE 等)走 MySqlExecute,返回 bool。想一次发多条语句,连库时 clientflag 带 CLIENT_MULTI_STATEMENTS,用分号拼包,能省网络往返。MySQL 的 INSERT 支持 ON DUPLICATE KEY,做历史迁移时以柱线时间做主键就不必先查重,重复自动转 UPDATE。 读数据走游标:MySqlCursorOpen 开游标拿 ID,MySqlCursorRows 看行数,MySqlCursorFetchRow 取行,再用 MySqlGetFieldAsInt/Double/Datetime/String 按列号(从 0 起)取字段。游标必须关,MySqlCursorClose 漏掉的话每次 tick 开新游标占内存,MQLMySQL.dll 到上限就拒开。日常保持 2–3 个打开游标足够覆盖多数维度,复杂检索可以下推成服务端视图。 连接参数别硬编码进 EA,用 ReadIni 从 INI 读 host/port/user 等,服务器迁了只改文件。调试时开追踪模式,所有 DB 调用会 Print 到终端控制台,定位烂 SQL 很直接。

MQL5 / C++
class="type">int ClientFlag = CLIENT_MULTI_STATEMENTS; class=class="str">"cmt">// Setting the multi-statements flag
class="type">int DB;
DB = MySqlConnect(Host, User, Password, Database, Port, Socket, ClientFlag); class=class="str">"cmt">// Connection to the database
if (DB == -class="num">1)
  {
  class=class="str">"cmt">// Handling the connection error
  }

class=class="str">"cmt">// Preparing a SQL query to insert data(class="num">3 rows in one query)
class="type">class="kw">string SQL;
SQL = "INSERT INTO EURUSD(Ask,Bid) VALUES(class="num">1.3601,class="num">1.3632);";
SQL = SQL + "INSERT INTO EURUSD(Ask,Bid) VALUES(class="num">1.3621,class="num">1.3643);";
SQL = SQL + "INSERT INTO EURUSD(Ask,Bid) VALUES(class="num">1.3605,class="num">1.3629);";

◍ 把连接配置塞进本地 ini 再跑 SQL

MT5 里直连 MySQL 时,别把服务器地址、端口、账密写死在 EA 源码里。更稳的做法是读本地 ini:比如从 C:\MetaTrader5\MQL5\Experts\MyConnection.ini 的 [MYSQL] 段取 Server 字段,实测默认本地环回地址是 127.0.0.1、端口 3306。 执行 SQL 前用 if (!MySqlExecute(DB,SQL)) 判错,括号内只留注释位接错误提示,避免崩溃时无声。下面这段是最小可验证片段,复制进 MQL5 工程即可改参数跑。 SQLTrace 可临时置 true 看底层通信,稳定后改 false 降开销。外汇与贵金属接口测试属高风险操作,参数错连可能拖慢终端,建议先用本地库练手。

MQL5 / C++
if (!MySqlExecute(DB,SQL))
  {
    class=class="str">"cmt">// Showing an error message
  }
...
[MYSQL]
Server = class="num">127.0.class="num">1
User = root
Password = Adm1n1str@t0r
Database = mysql
Port = class="num">3306
class="type">class="kw">string vServer = ReadIni("C:\MetaTrader5\MQL5\Experts\MyConnection.ini", "MYSQL", "Server");
SQLTrace = true;
SQLTrace = class="kw">false;

常见问题

可以,但需用 DLL 封装 MySQL C API 供 MQL 调用,纯脚本层无法直接连;先确认平台允许 DLL 导入。
多数卡在字符集不匹配、连接未异步释放或查询超时;把连接和 SQL 执行放独立线程并设 3 秒超时更稳。
小布可接管重复的记录与导出流程,你只需配置字段映射,它把每笔信号写库,你专注分析。
放本地 ini 文件,MQL 启动时读入,别进版本库;权限设为仅当前用户可读。
缺连接池或结果集回收层就会挂起;客户端库、连接管理、结果解析三层必须齐备并正确释放。