应用网络函数,或无需 DLL 的 MySQL:第 I 部分 - 连通器·综合运用
📘

应用网络函数,或无需 DLL 的 MySQL:第 I 部分 - 连通器·综合运用

第 3/3 篇

「用脚本拉一张大洋洲面积榜」

在 MT5 里接 MySQL,最轻量的入口就是写一个脚本而非 EA。下面这段把 world 测试库当靶子:目标是取出属于 Oceania 大陆的国家,按 SurfaceArea 从大到小排,最多 10 行。 连通参数全部走 input,本地起服务时地址填 127.0.0.1、端口 3306 是 MySQL 默认 TCP 口,登录 admin / 12345 仅作示例,实盘环境务必换掉。CMySQLTransaction 这个类负责发 SELECT 并拿回响应。 查询拼出来后是 select Name,SurfaceArea from world.country where Continent='Oceania' order by SurfaceArea desc limit 10。成功路径里先确认 Responses() 等于 1,再取 Response() 指针,逐行用 Row(i).Double 读面积、PrintFormat 打出两位小数。 失败分三类:服务端错走 GetServerError,内部错走 EnumToString,其余用 GetLastError 抓码。若本地库数据齐整,脚本跑完终端会列出不多于 10 个国家名加面积值,可作为你验证连接器是否通的第一步。 更复杂的多查询与长连接放在后续篇章,这里先把单发单收跑顺。

MQL5 / C++
class=class="str">"cmt">//--- class="kw">input parameters
class="kw">input class="type">class="kw">string   inp_server     = "class="num">127.0.class="num">0.1";           class=class="str">"cmt">// MySQL server address
class="kw">input class="type">uint     inp_port       = class="num">3306;                  class=class="str">"cmt">// TCP port
class="kw">input class="type">class="kw">string   inp_login      = "admin";               class=class="str">"cmt">// Login
class="kw">input class="type">class="kw">string   inp_password   = "class="num">12345";               class=class="str">"cmt">// Password
class="kw">input class="type">class="kw">string   inp_db         = "world";               class=class="str">"cmt">// Database name
class=class="str">"cmt">//--- Connect MySQL transaction class
class="macro">#include   <MySQL\MySQLTransaction.mqh>
CMySQLTransaction mysqlt;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Script program start function                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
class=class="str">"cmt">//--- Configure MySQL transaction class
   mysqlt.Config(inp_server,inp_port,inp_login,inp_password);
class=class="str">"cmt">//--- Make a query
   class="type">class="kw">string q = "select `Name`,`SurfaceArea` "+
              "from `"+inp_db+"`.`country` "+
              "where `Continent`=&class="macro">#x27;Oceania&class="macro">#x27; "+
              "order by `SurfaceArea` desc limit class="num">10";
   if(mysqlt.Query(q)==true)
     {
      if(mysqlt.Responses()!=class="num">1)
         class="kw">return;
      CMySQLResponse *r = mysqlt.Response();
      if(r==NULL)
         class="kw">return;
      Print("Name: ","Surface Area");
      class="type">uint rows = r.Rows();
      for(class="type">uint i=class="num">0; i<rows; i++)
        {
         class="type">class="kw">double area;
         if(r.Row(i).Double("SurfaceArea",area)==false)
            class="kw">break;
         PrintFormat("%s: %.2f",r.Row(i)["Name"],area);
        }
     }
   else
     if(GetLastError()==(ERR_USER_ERROR_FIRST+MYSQL_ERR_SERVER_ERROR))
       {
        class=class="str">"cmt">// in case of a server error

MT5 里把 MySQL 与事务报错拆开打印

在 EA 或脚本里接 MySQL,最忌讳只打印一个笼统的 Error。下面这段把服务端错误和本地事务错误分两条路处理,开 MT5 把代码贴进脚本就能直接看输出。 服务端连不上或语句异常时,走 mysqlt.GetServerError(),把 code 和 message 一并打出,能省掉你反复猜「到底是权限还是语法」的时间。 本地侧用 GetLastError() 判断:若返回值 ≥ ERR_USER_ERROR_FIRST(常量 65536),说明是交易事务类错误,减去这个偏移后用 ENUM_TRANSACTION_ERROR 转成可读枚举名;否则直接打印原生错误码。这样排查订单发送失败,概率上更快定位到具体环节。

MQL5 / C++
      Print("MySQL Server Error: ",mysqlt.GetServerError().code," (",mysqlt.GetServerError().message,")");
      }
   else
      {
       if(GetLastError()>=ERR_USER_ERROR_FIRST)
         Print("Transaction Error: ",EnumToString(ENUM_TRANSACTION_ERROR(GetLastError()-ERR_USER_ERROR_FIRST)));
       else
         Print("Error: ",GetLastError());
      }
}

◍ MySQL 连接类的字段与计时器接口

在 MT5 的 MySQL 封装里,CMySQLTransaction 是实际跑业务请求的核心类。它暴露的 Config 只管基础连接参数,而 KeepAliveTimeout 与 PingPeriod 两个字段决定了空闲连接多久被探活一次,PingBeforeQuery 则控制每次发 SQL 前是否强制心跳。 OnTimer 是 EA 或脚本里驱动保活的逻辑入口,Handshake 完成握手,Query 发请求;Responses 和 Response 分别管批量与单条回包。GetServerError 能直接拿到 MySQLServerError 结构里的服务端错误码,排查连不上库或语句报错时最实用。 流量侧有三个硬数据点:RequestDuration 记录单次请求耗时(毫秒级),RxBytesTotal 与 TxBytesTotal 累计收发字节数,ResetBytesCounters 可清零。盯盘机器人跑久了,用这三个值就能算出平均查询延迟和带宽占用,不靠猜。 授权与数据解析另由 CMySQLLoginRequest、CMySQLResponse、CMySQLField、CMySQLRow 分担,登录态和行列映射解耦,改表结构时不用动业务类。

「盯住长连接里的心跳与字节账本」

在 MT5 EA 里直连 MySQL,最怕空闲后被服务端掐线。CMySQLTransaction 用 KeepAliveTimeout(秒)和 PingPeriod(秒)两套参数管“保持活动”,PingBeforeQuery 还能强制每次发 SQL 前先探活,降低静默断开导致 Query 失败的概率。 OnTimer 专门消化计时器事件来跑这套心跳,Handshake 拿回类指针做授权,Query 发完可用 Responses 看响应条数、Response 取指针细读,GetServerError 捞错误结构。 流量侧有三个硬指标值得常看:RequestDuration 给出单次业务微秒级耗时;RxBytesTotal / TxBytesTotal 是从程序启动累计的收发明文字节,ResetBytesCounters 可清零重算。外汇或贵金属相关的历史 tick 落库若走这条通道,需意识到直连数据库本身引入额外故障点,属高风险工程实践,建议先在模拟环境验证。

连通参数怎么填

做 MT5 外接数据库或消息中间件时,先调 Config 把通道建起来。它负责把服务器地址、端口、账号、库名和保活超时一次性提交,函数返回布尔量:true 代表握手成功,false 多半是字符串参数里塞了非法交易品种符号或库名,直接拦在连接前。 下面这段声明就是实际接口形状,参数顺序别乱:host 填服务名,port 是网络端口,user 和 password 走鉴权,base 指定库,keep_alive_tout 设常连超时,0 表示不保活。 外汇与贵金属行情转发走这条通道时波动大、延迟敏感,连不上先查 base 和符号合法性,比怀疑网络更快。

MQL5 / C++
class="type">bool  Config(
   class="type">class="kw">string host,        class=class="str">"cmt">// server name
   class="type">uint port,          class=class="str">"cmt">// port
   class="type">class="kw">string user,        class=class="str">"cmt">// user name
   class="type">class="kw">string password,    class=class="str">"cmt">// password
   class="type">class="kw">string base,        class=class="str">"cmt">// database name
   class="type">uint keep_alive_tout class=class="str">"cmt">// constant connection timeout(class="num">0 - not used)
   );

◍ 长连接保活超时怎么设

MT5 的 WebRequest 若走持续连接,靠 KeepAliveTimeout 控制空闲断线节奏。它从你发出上一个查询那一刻开始计时,超过设定秒数还没新请求,底层连接就关闭;若在超时前又发了请求,连接保持,不重新握手。 参数 tout 单位是秒,传 0 表示禁用保活,每次请求都新建连接。实际盯盘里频繁拉行情或信号源时,设 10~30 秒通常比每次新建更省资源,但外汇/贵金属行情高波动时段网络抖动大,保活过长可能卡在半死连接上,值要按你的请求频率调。 下面这段是接口原型,tout 直接决定保活窗口长度: void KeepAliveTimeout( uint tout // set the constant connection timeout in seconds (0 - disable) ); 逐行看:void 是返回类型,调用它不回传结果;KeepAliveTimeout 函数名即「保活超时」;uint tout 是无符号整型入参,注释写明以秒为单位的常量超时,0 代表关闭该功能。开 MT5 按 F4 进编辑器,搜这个函数名就能看到所属头文件与调用上下文。

MQL5 / C++
class="type">void  KeepAliveTimeout(
   class="type">uint tout           class=class="str">"cmt">// set the constant connection timeout in seconds(class="num">0 - disable)
   );

「持续连接下的 ping 心跳间隔」

MT5 的 WebRequest 在持续连接模式里,靠周期性 ping 保活,避免服务端因空闲直接断开 TCP。PingPeriod 就是用来设定这个心跳周期的,单位秒。 它只在最后一次查询或上一轮 ping 过去指定时长后才发下一个 ping,不是严格定时轮询。设得太长,连接可能在你发下一单请求前就被服务端掐掉;设得太短,又会无谓占用通道。 下方是原函数签名,参数只有秒数一个,调用后无返回值,纯粹改内部定时器配置。外汇与贵金属行情接口调用属高风险操作,参数异常可能导致连接不稳,实盘前请在模拟环境验证。

MQL5 / C++
class="type">void  PingPeriod(
   class="type">uint period           class=class="str">"cmt">// set the ping period in seconds(for the constant connection mode)
   );

查询前先 ping 保活连接

在 MT5 用 MySQL 持续连接跑策略时,两次查询的间隙连接可能被服务端掐断或网络抖动中断,等到真正发 SQL 才报错就会漏掉行情。PingBeforeQuery 就是专门在每次查询前先发一个 ping 包探活,确认链路还在再继续,避免静默断连。 参数只有一个 bool 型 st:传 true 开启查询前 ping,传 false 关闭。函数本身无返回值,调用后生效于后续所有查询。 外汇与贵金属行情高波动、断线成本高,这种保活机制建议默认开启;但注意它只解决连接存活探测,不保证查询逻辑正确,仍需自己在 EA 里捕获数据库异常。

MQL5 / C++
class="type">void  PingBeforeQuery(
   class="type">bool st                class=class="str">"cmt">// enable(true)/disable(false) ping before a query
   );

◍ OnTimer 里的保活节奏

持续连接模式下,OnTimer 是维系会话不中断的关键入口。它必须从计时器事件处理程序里被调用,否则底层连接可能在无交易活动时悄悄掉线。 计时器周期不能拍脑袋设。它必须小于等于 KeepAliveTimeout 与 PingPeriod 两者中的较小值,否则心跳包发出频率跟不上保活窗口,连接会被服务端判定为失效。 MQL5 里这个函数本身不返回任何值,职责只是触发,真正的重连与状态判断在别处。下面这行就是它的原型声明,看着空,但周期设错就白跑。

MQL5 / C++
class="type">void  OnTimer(class="type">void);

「握手阶段先拿到授权指针」

在 MT5 调用 MySQL 接口时,Handshake 是唯一能在连服务器前就介入的入口。它返回 CMySQLLoginRequest 类指针,此时连接尚未建立,你可以提前设置客户端职能和数据包最大尺寸标志。 授权完成后,同一个指针会回填服务器端的职能版本与标志位,相当于一次双向握手确认。 下面这行声明就是接口本体,返回类型是类指针而非普通句柄,意味着调用方要自己管理生命周期。 [CODE] CMySQLLoginRequest *Handshake(void); [/CODE] 逐行拆解:CMySQLLoginRequest * 表示函数返回指向该授权类的指针;Handshake 是函数名;(void) 在 MQL5 里显式声明无参数,避免隐式 int 旧式声明。 实操上,开 MT5 写 EA 调用前先判空:若 Handshake() 返回 NULL,说明底层 MySQL 组件未就绪,别直接访问成员函数,否则易崩。外汇与贵金属自动化接入外部库属高风险,实盘前务必在策略测试器跑通授权流程。

MQL5 / C++
CMySQLLoginRequest *Handshake(class="type">void);

用 Query 把指令丢给引擎

MQL5 里 Query 函数负责把一段查询文本直接发给底层分析引擎,调用方只管传字符串,不用关心内部解析链路。 它的返回值很直白:引擎成功受理并返回结果给 true,任意环节出错则返回 false,因此调用后必须判断返回值再继续,否则后续逻辑可能跑在空结果上。 外汇与贵金属行情受杠杆和跳空影响,自动查询返回 true 只代表通信成功,不代表信号有效,实盘前请在 MT5 策略测试器用历史数据验证查询稳定性。

MQL5 / C++
class="type">bool  Query(
   class="type">class="kw">string q                class=class="str">"cmt">// query body
   );

◍ 怎么数清服务器回了几次

在 MQL5 网络查询里,Responses() 用来拿服务器响应数量。它只统计类型为 "Ok" 或 "Data" 的数据包,这两类才算有效响应。 一次查询成功时,可能收到一个响应,也可能因为发了多个子查询而拿到多个响应。写监控逻辑时,不能默认只回一条,得用返回值判断实际条数。 外汇与贵金属行情接口走网络,延迟和丢包风险高,响应数为 0 或少于预期时,应触发重连或重试,而不是继续解析空数据。

MQL5 / C++
class="type">uint  Responses(class="type">void);

「拿到 MySQL 响应对象的指针」

在 MT5 的 MySQL 封装库里,想读服务器回的数据,第一步是拿到 CMySQLResponse 的指针。带索引的重载接收 uint 类型的 idx,用来指定第几条服务器响应;不传参的重载等效于 Response(),直接取默认那条。 两个重载的返回规则一致:正常时给到 CMySQLResponse 指针,调用方就能继续解析字段;若传了无效索引或当前根本没有响应,返回的是 NULL,使用前必须判空,否则解引用会直接崩 EA。 下面这段是库头文件里的原生声明,注意 void 重载没有参数列表,仅用于取默认响应:

MQL5 / C++
CMySQLResponse  *Response(
   class="type">uint idx                    class=class="str">"cmt">// server response index
   );
CMySQLResponse  *Response(class="type">void);

拿到服务器报错的具体内容

当业务类抛出 MYSQL_ERR_SERVER_ERROR 时,光看错误码还不够,得靠 GetServerError 把存储代码和服务器侧的消息文本一起取出来,才能定位是连接断了还是语句被拒。 这个函数没有入参,直接调用即可返回 MySQLServerError 结构,里面同时带着错误码与可读信息。在 MT5 的 MySQL 封装库里,只要返回值判定命中上述错误常量,下一步就该跑它而不是反复重试原语句。 [CODE]MySQLServerError GetServerError(void);[/CODE] 代码只有一行声明:返回类型是 MySQLServerError,函数名 GetServerError,形参表为 void 表示不接收任何参数。实盘接 MySQL 做行情落库时,建议在每次写操作失败分支里打印该结构,否则排查贵金属高频插入超时只会卡在「未知服务端错误」这一句。外汇与贵金属接口涉及实时资金与杠杆,任何服务端异常都属高风险信号,需结合日志概率性判断重连或熔断。

MQL5 / C++
MySQLServerError  GetServerError(class="type">void);

◍ 用 RequestDuration 卡住请求耗时

在 MT5 的 MQL5 网络层里,RequestDuration 用来拿单次请求从发出到处理完毕的真实耗时,单位是微秒。它不接收参数,直接返回 ulong 类型的计数值,适合塞进日志或做超时判断。 [CODE] ulong RequestDuration(void); [/CODE] 上面这行就是原生声明:ulong 是返回类型,代表无符号长整型;void 表示函数不带输入,纯查询内部计时。 实盘里你可以每隔 N 次请求打印一次这个值,比如某次返回 850000 微秒(即 0.85 秒),就说明那一轮网关响应偏慢。外汇与贵金属行情在重大数据发布时延迟可能陡增,这类微观耗时数据能帮你定位是策略慢还是通道慢,而不是瞎猜。

MQL5 / C++
class="type">class="kw">ulong  RequestDuration(class="type">void);

「读一下网卡吃进来的总字节」

在 MT5 的本地 TCP 会话里,RxBytesTotal() 返回的是从程序启动那一刻起、底层协议栈实际收进来的字节总数,不含任何应用层包装的二次统计。它直接反映终端跟交易服务器之间「下载」侧的真实流量负担。 这个计数器不会自己归零。如果你想对比某段行情会话内的增量接收量,得先调用 ResetBytesCounters() 把收发两端同时清掉,再在目标窗口内取差值,否则拿到的是全生命期累计值,容易把历史负载误判成当下拥堵。 外汇与贵金属行情推送受点差跳变和深度行情影响,RxBytesTotal 在重大数据发布时可能短时陡增,可作为终端侧网络压力的旁证指标,但高杠杆品种本身风险极高,该数值不直接预示价格方向。

MQL5 / C++
class="type">class="kw">ulong  RxBytesTotal(class="type">void);

统计 TCP 已发字节数

在 MT5 的 WebRequest 相关接口里,TxBytesTotal 用来拿程序启动后从 TCP 层发出去的总字节数。这个数不含应用层封装开销,只算实际走到网卡的负载,所以拿它评估实盘 EA 的对外通信量比较贴近真实。 返回值类型是 ulong,意味着理论上限极高,正常策略跑几个月也难触顶。若想重新计量某段时间的发送量,调用 ResetBytesCounters 即可把计数归零,再读 TxBytesTotal 就是重置后的增量。 贵金属与外汇 EA 调用外部 API 频繁时,盯着这个返回值能发现隐性重连或重复推送——这类行为在低点差时段可能拖慢执行,属高风险环境下的隐性成本。

MQL5 / C++
class="type">class="kw">ulong  TxBytesTotal(class="type">void);

◍ 清零收发字节计数

在 MT5 的网络统计接口里,ResetBytesCounters 负责把终端累计的已接收字节数与已发送字节数一次性归零。 调用它本身不返回任何值,只是清空计数器状态,便于你在一段自定义观测窗口内重新计量流量。 如果你在写监控脚本,典型做法是先调用一次 ResetBytesCounters,跑若干个心跳周期,再用 BytesReceived 与 BytesSent 读取净增量,避免历史累计干扰本次统计。 外汇与贵金属品种行情推送密集,字节计数波动大,该接口仅作诊断用途,实盘决策仍需结合价格行为,相关品种杠杆高、风险显著。

MQL5 / C++
class="type">void  ResetBytesCounters(class="type">void);

「扒开 CMySQLLoginRequest 的授权字段」

在 MT5 里做 MySQL 登录封装时,CMySQLLoginRequest 这个类负责把客户端能力、包上限和字符集先喂给握手协议。它不像普通 EA 参数那样直观,几个 Set 方法填错一个,连接就会在鉴权阶段直接断掉。 SetClientCapabilities 写入客户端职能标志,预定义值是 0x005FA685;SetMaxPacketSize 控制单包上限,默认填 16777215 字节(即 16 MB 边界);SetCharset 定字符集,预定义值为 8。这三行不显式赋值,多数第三方 MySQL 服务端会拒连。 连上之后可用 Version 拿服务端版本串,例如 "5.7.21-log";ThreadId 回当前连接线程 ID,对应 SQL 里的 CONNECTION_ID()。ServerCapabilities 和 ServerLanguage 则是只读,分别取服务端职能标志和编码/表述 ID,用来判断服务端是否支持你想要的压缩或长密码认证。 贵金属与外汇相关的数据库桥接涉及实时报价落库,属于高风险工程环节;建议先在本地 MySQL 实例用上述默认值跑通握手,再上真实账户环境。

拆开 MySQL 响应包的两种形态

在 MT5 的 MySQL 桥接里,服务端回传的数据包只分两类:"Ok" 和 "Data"。两者结构差异很大,所以 CMySQLResponse 类没有混着处理,而是给每种类型单独配了一套取数方法,避免调用方误读字段。 遇到 "Data" 包时,先用 Fields() 拿字段数、Rows() 拿行数,再用 Row(int) 定位行、Field(int) 或 Field(string) 定位列,最后用 Value() 按索引或名称取字符串。如果要整列灌进数组,ColumnToArray 提供了 string / int / long / double 四种重载,后三种会做类型校验,类型对不上就取不到数。 "Ok" 包没有结果集,重点在影响面和状态:AffectedRows() 返回上次操作动了几行,LastId() 对应 LAST_INSERT_ID,ServerStatus() 给服务器标志位,Warnings() 是警告计数,Message() 是文本消息。写 EA 落库后想确认写入是否真生效,这几个值比盲目重试更靠谱。 实际接 MT5 跑时,建议先判 Type() 是 MYSQL_RESPONSE_DATA 还是 MYSQL_RESPONSE_OK,再走对应分支——外汇和贵金属自动化策略接外部数据库本身有延迟与断连风险,响应类型判错可能导致信号逻辑误触发。

◍ MySQLServerError 的错误字段拆解

在 MT5 策略与外部 MySQL 通信时,服务端返回的错误会被封装进 MySQLServerError 结构。该结构只有三个字段,但每个都对应排错的关键入口。 code 是 ushort 类型的错误代码,用于程序内部分支判断;sqlstate 为 uint,承载 SQL 标准状态;message 是 string,直接给出服务器文本消息,人工看日志最常用。 实盘接数据库前,建议先在演示账户跑一次异常注入,确认这三个字段都能正确读出,否则出问题时你连错在哪一层都不知道。外汇与贵金属外接数据库存在断连风险,信号延迟可能放大亏损。

「CMySQLField 把结果集列元信息拆开给你看」

在 MT5 通过 MySQL 接口拉回结果集后,每一列的具体属性并不藏在数据里,而是由 CMySQLField 对象单独承载。这个类不负责存取单元格数值,只描述「这一列到底是什么」。 调用 Catalog 能拿到数据表所属目录名,Database 返回库名,Table 与 OriginalTable 分别给出字段所属表的别名和原始名——联表查询时这两者的差异很关键,别名错配会导致后续绑定字段失效。 Name 和 OriginalName 同理区分别名与原字段名;Charset 返回该列实施编码代号,Length 是数值长度,Type 为数值类型,Flags 定义值属性标志,Decimals 表示允许小数位。最后 MQLType 把类型映射成 ENUM_DATABASE_FIELD_TYPE(DATABASE_FIELD_TYPE_NULL 除外),方便在 EA 里用 switch 分流处理。 实际验证时,可在 OnTick 里跑一次 SELECT 后遍历 field(0)->MQLType(),对比返回值和你在数据库中建表时声明的类型,确认驱动层映射没偏。外汇与贵金属自动化涉及实盘资金,接口层字段误读可能引发错单,属高风险操作。

数据行取值与类型校验的坑

CMySQLRow 把一行查询结果封装成对象,对外暴露两套索引方式:按字段编号用 Value / MQLType / Text / Integer / Long / Double / Blob,按字段名称则用 operator[] 和同名重载。两套接口返回的内容一致,只是定位字段的钥匙不同,写 EA 时混用也不会冲突。 类型验证是这套设计的硬约束。比如 Integer() 只认 DATABASE_FIELD_TYPE_INTEGER,若底层 MySQL 字段实际是 float 或 varchar,方法直接返回 false 且不给值,不会帮你强转。这意味着你从数据库拉历史 tick 或订单流水时,字段类型声明错一位,回测逻辑就会静默漏数据。 字段类型 ID 到 MT5 枚举的映射集中在 CMySQLField::MQLType() 里完成。它把 MySQL 协议里的 0x00/0x04/0x05/0xf6 归为浮点,0x01~0x03/0x07~0x10 归为整数,0x0f/0xfd/0xfe 归文本,0xfb 归二进制,其余抛 INVALID。下面这段是映射源码,逐行看能少踩很多隐式类型坑。 类型不匹配时别指望自动兜底,开 MT5 接自己的 MySQL 行情库前,先 print 一遍每列的 MQLType() 返回值,确认和调用的方法配对再写取值逻辑。外汇与贵金属数据库对接涉及实盘资金,类型误读可能造成策略信号偏差,属高风险操作。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the field type as the ENUM_DATABASE_FIELD_TYPE value      |
class=class="str">"cmt">//+------------------------------------------------------------------+
ENUM_DATABASE_FIELD_TYPE CMySQLField::MQLType(class="type">void)
  {
   class="kw">switch(m_type)
     {
      case 0x00:  class=class="str">"cmt">// decimal
      case 0x04:  class=class="str">"cmt">// class="type">float
      case 0x05:  class=class="str">"cmt">// class="type">class="kw">double
      case 0xf6:  class=class="str">"cmt">// newdecimal
        class="kw">return DATABASE_FIELD_TYPE_FLOAT;
      case 0x01:  class=class="str">"cmt">// tiny
      case 0x02:  class=class="str">"cmt">// class="type">class="kw">short
      case 0x03:  class=class="str">"cmt">// class="type">long
      case 0x08:  class=class="str">"cmt">// longlong
      case 0x09:  class=class="str">"cmt">// int24
      case 0x10:  class=class="str">"cmt">// bit
      case 0x07:  class=class="str">"cmt">// timestamp
      case 0x0c:  class=class="str">"cmt">// class="type">class="kw">datetime
        class="kw">return DATABASE_FIELD_TYPE_INTEGER;
      case 0x0f:  class=class="str">"cmt">// varchar
      case 0xfd:  class=class="str">"cmt">// varstring
      case 0xfe:  class=class="str">"cmt">// class="type">class="kw">string
        class="kw">return DATABASE_FIELD_TYPE_TEXT;
      case 0xfb:  class=class="str">"cmt">// blob
        class="kw">return DATABASE_FIELD_TYPE_BLOB;
      class="kw">default:
        class="kw">return DATABASE_FIELD_TYPE_INVALID;
     }
  }

◍ 记住这一条就够了

前面用套接字手搓 MySQL 连通器的思路,核心就是绕开 DLL 直接拿网络函数跑查询,附件里 Include\MySQL\ 下的源码和 Scripts\test_mysql.mq5 示例能直接拖进 MT5 跑通基础连通。 下一篇会落地成信号属性采集服务,当前这版代码在 3550 构建之后有读者反馈部分函数已变动,连库时若缺 base 参数会直接连不上。 文末那段报错片段里 LastId、ChartId 等标识符在 Element.mqh 的 841–845 行全部未声明,说明图形元素基类的接口在你本地环境可能已被重构,编译前先对照自己终端的 Standard Library 版本查一遍。 外汇与贵金属交易本身高风险,这类底层连通实验只建议用在本地 localhost 做数据归档,别拿它直连实盘账户相关的任何链路。

MQL5 / C++
  m_id       =m_wnd.LastId()+class="num">1;
  m_chart_id =m_wnd.ChartId();
  m_subwin   =m_wnd.SubwindowNumber();
  m_corner   =(ENUM_BASE_CORNER)m_wnd.Corner();
  m_anchor   =(ENUM_ANCHOR_POINT)m_wnd.Anchor();
&class="macro">#x27;LastId&class="macro">#x27; - undeclared identifier        Element.mqh    class="num">841   class="num">22
&class="macro">#x27;)&class="macro">#x27; - expression expected        Element.mqh    class="num">841   class="num">29
&class="macro">#x27;ChartId&class="macro">#x27; - undeclared identifier        Element.mqh    class="num">842   class="num">22
&class="macro">#x27;)&class="macro">#x27; - expression expected        Element.mqh    class="num">842   class="num">30
&class="macro">#x27;SubwindowNumber&class="macro">#x27; - undeclared identifier        Element.mqh    class="num">843   class="num">22
&class="macro">#x27;)&class="macro">#x27; - expression expected        Element.mqh    class="num">843   class="num">38
&class="macro">#x27;Corner&class="macro">#x27; - undeclared identifier         Element.mqh    class="num">844   class="num">40
&class="macro">#x27;)&class="macro">#x27; - expression expected        Element.mqh    class="num">844   class="num">47
&class="macro">#x27;Anchor&class="macro">#x27; - undeclared identifier         Element.mqh    class="num">845   class="num">41
&class="macro">#x27;)&class="macro">#x27; - expression expected        Element.mqh    class="num">845   class="num">48
select c1, c2 from `db_name`.`table_name`;

常见问题

用内置网络函数直连MySQL,写个脚本发查询语句拿结果集,不需要任何第三方DLL,连通参数填好host、user、pass、db即可。
把事务报错和MySQL连接报错分开打印到不同日志,先查保活超时是否触发,再查事务回滚原因,别混在一起排。
可以,小布能替你监控长连接心跳与字节收发账本,异常时直接推提醒,你只管策略不用盯底层连接。
超时设5到10秒够用,保活间隔建议30秒发一次心跳;外汇贵金属高风险,断线可能漏单,按网络质量下调保活间隔。
在连接类里留发送/接收字节字段,挂一个计时器接口在查询前后打点,差值就是单次耗时,便于后面调优。