市场模拟(第 18 部分):SQL 入门(一)
概述
在上一篇文章“市场模拟(第 17 部分):套接字(十一)”中,我们完成了允许 MetaTrader 5 与 Excel 之间信息交换的实现演示。我们没有深入探讨如何让 Excel 控制 MetaTrader 5,因为这需要非常谨慎。由于这完全取决于每个人的想法或计划,我们在文章末尾提供了几个参考资料,以便您对如何操作有所了解。
我们使用哪个 SQL 程序并不重要:MySQL、SQL Server、SQLite、OpenSQL 或其他。它们都有一个共同点,那就是都使用 SQL 语言。即使我们不打算使用 Workbench,我们也可以直接在 MetaEditor 中或通过 MQL5 操作数据库,以在 MetaTrader 5 中执行操作,但要做到这一点,你需要了解 SQL。关于这个话题,有许多专门的书籍,同时网上也有各种学习指南可供选择。您所需要做的就是理解一些概念,并学会如何使用您的编程语言与 SQL 进行通信。
然而,在开始使用 SQL 之前,我们也需要意识到它存在的一些局限性。为了学习 SQL,您至少必须能够创建一个数据库。这是您需要理解的基本出发点。
数据库入门
使用 SQL 的方法有很多种,而且都非常简单。第一种方法是直接在命令行中输入命令。第二种方法是使用 Workbench 或类似的程序,例如 MetaEditor。第三种方法是将 SQL 命令嵌入到用 MQL5 编写的程序中。当程序在 MetaTrader 5 中运行时,它可以访问数据库。最后,还有另一种基本方法,即使用套接字与 SQL 服务器通信。所有这些基本方法在使用 SQL 的所有数据库系统中都广泛可用。因此,所有将要展示和解释的内容都可以在任何兼容的系统中使用。不要认为某事无法完成。如果您使用的是纯 SQL 和原生 SQL,那么您就能做到。
但是,有一点需要注意。任何使用 SQL 的程序 — 无论是 OpenSQL、MySQL、SQLite、SQL Server 还是其他 SQL — 都可能有一些专门为该程序设计的命令。如果使用了这些命令,我们将无法在另一个 SQL 程序中执行相同的操作。
另需说明的是:初期所有工作都将使用 MySQL Workbench 完成。原因稍后会解释。
在了解了所有这些基础知识后,让我们来看一下最简单的命令:创建一个空数据库。我们首先要明确一点,创建任何文件并将其扩展名更改为 .DB 是绝对无用的,因为系统不会将其识别为数据库。要做到这一点,您需要使用一个命令,这其实非常简单。但在查看任何命令之前,我们先来看点别的。
在 SQL 编程中,保留字通常(几乎被视为一种良好习惯)以大写形式书写。然而,这对SQL来说并不重要。如果在编写代码时忘记将命令以大写形式输入,不用担心:您只需使用下图所示的按钮即可。这将把整个代码或 SQL 保留字转换为大写。

SQL 中的第一个命令
现在,我们终于可以开始研究这些命令了。我们需要学习的第一个命令是 CREATE DATABASE 。在下图中,您可以看到它的实际运行情况。

以蓝色和粗体突出显示的文本表示 SQL 代码,而以黑色突出显示的文本则是数据库的名称。这是您需要使用的第一个命令。输入此命令后,我们可以点击代码上方显示的其中一个闪电图标。这些闪电图标各有不同的功能。既然我们才刚刚开始,那就点击最左边的那个吧。尝试理解每个图标的用途以及它们的工作原理也是很有用的,因为这将非常有帮助,尤其是在调试脚本时。我们稍后会讨论这个问题。请大家注意。如果您想直接在 MetaEditor 中使用 SQL 创建数据库,则需要采用不同的方法。在这种情况下,您必须选择下图所示的选项。

在这种情况下,MetaEditor 会要求您指定要创建的数据库文件的位置和名称。这一步相当简单。我们还可以使用 MQL5 编写的代码来创建数据库文件。但我们稍后再讨论这个,因为掌握更多材料后,我们才能直接使用 MQL5 或我们将来处理 SQL 时将使用的其他语言来实现相关操作,这样会更好。然而,如果我们在 MQL5 代码中使用套接字并连接到 SQL 服务器,我们就可以发送在工作台中显示的命令,并获得相同的结果 — 即创建一个数据库。
但这是另一个讨论的话题。现在,让我们以最简单、最直接的方式进行操作:使用 MetaEditor、Workbench,甚至命令行。但是,使用命令行时,您确实需要了解元素将如何显示。但这只是需要习惯命令行的问题。
由于本文旨在尽可能清晰明了地阐述内容,我们将只使用 MetaEditor 和 Workbench。
因此,在 MetaEditor 中完成所有步骤后,我们将得到类似于下图的结果。如您所见,在此案例中,我们更改了数据库名称,以将其与 Workbench 数据库区分开来。

至此,MetaEditor 已准备好进行后续工作。但是,关于 Workbench,我们目前还无法进行下一步。首先,我们需要采取几个额外的步骤。要了解需要执行的操作,请在执行脚本命令后,单击下图中突出显示的项目。

使用这两种选项中的任意一种都会得到相同的结果。现在请看下面的图片:用作数据库的文件已经创建好了。但请注意,它尚未被选定为后续 SQL 命令所使用的数据库。

在数据库中执行任何操作之前,必须先选择数据库。实现此目的的一种方法是使用 SQL 命令。这条命令叫做 “USE” 。您可以在下面看到。

现在,在使用 Workbench 时,我们必须考虑一个重要细节。如果您使用命令行来执行操作,那么此时您只需输入第 02 行的内容,就可以进行下一步了。然而,在 Workbench 中,流程略有不同。请注意,我们已突出显示了两个闪电图标。现在,让我们来看看这是如何运作的。创建 SQL 脚本时,我们通常会编写多个命令。我们使用这个脚本是为了文档记录。然而,在使用脚本时,还有几点需要考虑。首先:如果脚本一次性执行完毕,我们就不会遇到任何问题。但如果我们打算逐步执行同一个脚本,那么如果我们尝试从头开始运行,就会遇到问题。这就是为什么很多人放弃学习 SQL 的原因,因为他们无法处理出现的问题。
好吧,我们希望执行第 02 行代码。为此,我们有三种方法。第一种方法是选择第 02 行,然后单击左侧突出显示的按钮。第二种方法是将文本光标放在第 02 行,然后单击右侧突出显示的按钮。请注意,旁边有一个光标图标。只有光标所在的那一行会被执行。
还有第三种方法。这主要用于需要从头开始强制执行代码的情况,即使代码已经部分执行过。为此,您需要在 SQL 中创建一个测试。如果代码尝试从头开始执行,则会在第 01 行发生错误。这怎么可能呢?原因是 SQL 会尝试重新创建数据库文件,但由于该文件已存在,因此该尝试会失败。因此,其余代码将不会执行。您可以尝试点击左侧图标,无需选择任何行。
由于我们经常使用已存在数据库的脚本,但希望在其中进行修改,因此对脚本本身进行更改意味着删除数据库是一种应避免的替代方案。为了解决这个问题,我们可以像下图所示那样,通过添加双破折号来注释掉第 01 行,或者做一些稍微不同的事情。

如上图所示,有两种方法可以对 SQL 代码进行注释。第一种写法如图所示:第 01 行现在被识别为注释。当您需要注释掉一行代码时,这种方法非常有用。不过,您也可以一次注释多行代码。为此,只需使用与 MQL5 中相同的方法,即斜杠和星号。
好吧,虽然之前的解决方案是可行的,但当 SQL 代码很长且包含许多可能因为已经在数据库中存在而失败的部分时,它实际上并不奏效。因此,我们需要一个更优雅的解决方案。此解决方案如下图所示。

注意,在第 01 行,我们有一个不同的命令。然而,此命令确保脚本将按预期正确执行。这是因为如果数据库文件不存在,则会创建该文件。但如果该文件已经存在,SQL 不会将其视为错误,因为我们正在检查文件是否存在。在 SQL 中,这种带有 IF 命令的构造非常常见,尤其是在数据库生命周期内将被使用(甚至重复使用)的脚本中。执行完成后,我们将得到图中所示的结果。也就是说,我们的数据库文件将被设置为当前数据库。
如您所见,一切都非常简单实用。我们只需了解每条指令或命令的作用。不要浪费时间死记硬背命令及其组合。您只需了解每个命令的功能,并在需要时使用它们。所以从现在开始,我们就达到了与 MetaEditor 相同的水平。因此,我们接下来要介绍的命令既可以在 MetaEditor 中使用,也可以在 Workbench 中使用。这个命令比我们迄今为止见过的那些要复杂一些。但请记住,知识是逐步积累起来的。
创建表
这个创建表的命令是我们最常用的命令之一,尤其是在学习阶段。其目的是创建一个表,然后使用另一条命令向该表输入数据。然而,与之前的命令不同,从这一点开始,我们将遇到一些复杂的情况,这些情况可能会在开始时给我们带来困难。但无需担心:这完全取决于习惯和学习,最终您会明白如何使用这个命令。
这个问题归结起来就是一件事。在创建表格之前,您需要提前考虑如何对要放入表格中的数据进行建模。毫无疑问,这部分内容是学习 SQL 初期最难的部分。原因就在于,在开始实施之前,您需要对您要做的事情有一个大致的了解。计划千般事务,却未能妥善规划表中将存放的数据类型,这绝对是毫无用处的。与选择正确的数据类型不同,数据的排列顺序并不那么重要。如果您选择了错误的类型,那么您最终创建的表格将无法适应新的数据。
然而,如果我们试图创建过于通用的东西,最终会消耗远超必要的资源和空间。因此,我建议首先研究现有的数据类型。我建议在尝试为特定目的设计数据库之前,先进行这一步。在本文结尾,我会提供一些链接,以便您知道从哪里开始。
就从这里开始,即使只是作为起点。您应该查找更多有关信息。幸运的是,有许多人愿意详细解释每种类型。所以,不要等待别人给您解决方案。
为了简化并仅出于演示目的,我们假设以下场景:我们希望为某些 B3(巴西证券交易所)股票代码创建并维护报价历史记录。最简单的实现方法如下所示,即使用 Workbench。

尽管我们的 SQL 代码创建表格时占用了很多行,但您需要明白,本质上这仍是一条 SQL 语句。结果显示在 Workbench 的 Schemas 面板中。然而,尽管这张表已经创建并且可以接受值,但在某些情况下它并不适用。原因稍后会解释。但在我们找出原因之前,先来看看如何在 MetaEditor 中创建相同的表格。如下图所示。

如您所见,命令完全相同,因此结果也相同。几乎所有可以在 Workbench 中执行的命令也可以在 MetaEditor 中执行,而且它们都能正常工作。但请注意,我说的是“几乎所有命令”,而不是“所有命令都将被执行”。有一个命令(此处将简要讨论)可以在 SQL 中执行,但不能通过 MetaEditor 执行。不过,我们不要着急。
首先,让我们来了解一下为什么我们创建的表不适合某些场景。第一个原因在于模型本身。这怎么可能呢?它不是应该代表报价吗?是的,但也许您误解了某些事情。我们想要获取报价,但同时也想保留该报价的历史记录,而这个模型并不能解决这个问题。毕竟,它只能保证报价本身。即使我们试图创建历史,这种模式也是不够的。
我们需要更多信息。例如,日期或时间,视具体情况而定。假设我们只需要当天的收盘价。我们每天都会向表中添加新数据。因此,之前我们看到的相同代码也需要进行修改。
您可能想知道:“为什么一开始不这样做呢?”为什么要在表创建完成后再做这件事呢?这是有意的,目的是为了演示如何向表中添加新列。正如我们已经提到的,对象的创建顺序并不重要。SQL 会自动负责将它们正确存储在数据库中。但是,回到需要执行的命令,它正是下面动画中展示的那个。

如果我们在 MetaEditor 的第 08 行执行这个相同的命令,我们将得到与图中所示相同的结果:创建一个新列,其中包含与获取报价日期相关的数据。如下所示:

完成上述步骤后,执行以下命令即可在 Workbench 中获得相同的结果。

至于这些字段中将要输入的值以及准备这些值的方法,将是另一个讨论的主题。事实上,在数据库中插入新值或删除值都是必须谨慎执行的操作。相信我:您不会想通过删除或添加内容来对数据库进行太多更改。如果您不按计划行事,数据库最终将不再适合长期使用。理想的方法是根据精心制定的计划来添加或删除元素,并主要将数据库用于查询。
先思考,后行动
这篇文章反复提到,尽管这个简单的数据库能够运行,但并不理想。这是因为,根据我们对其使用的计划,它可以在空间利用方面得到更好的优化。但您可能会想:“我为什么要担心空间问题?如果空间不够用了,我就买个新硬盘。”这是其中一种方法,但并不能解决问题。问题在于数据库的使用方式或设计方式。
这里只是对这一问题本质的简要提示如果您在数据库方面有更丰富的经验,您就会明白这个问题其实要严重得多。事实上,有专门针对那些想成为该领域专业人士的课程。但我们不会深入细节,因为这个话题的涵盖范围远超我一两篇文章所能涉及。
我想告诉您,仅仅学习 SQL 编程或翻阅一些数据库教科书,然后声称自己知道如何使用它们,这是毫无用处的。这样的陈述只有在该行业工作多年后才能做出。即便如此,您也很少会找到有人如此自信地做出这样的断言。
原因很简单:数据库代表了一个足够广阔的细分市场,其中许多方面已经得到了研究。每个案例都是独一无二的。事实上,并没有单一的解决方案。
现在,我们来分析一下这个简单的八行脚本所存在的问题之一,该脚本用于创建一个数据库,以存储在 B3(巴西证券交易所)交易的股票的报价历史。第一个问题是,在我们存储价格的字段中,只能输入 0.00 到 999.99 之间的值。这意味着某些交易品种无法存储在此表中。这是因为这些交易品种的报价与表的预期值不同。可以通过增加一列来填补这些空白,从而解决这个问题。然而,这会导致两列中的一列 — 无论是代码列还是新创建的列 — 在数据库中占用不必要的空间。
第二个问题是,存储报价日期的记录值采用 ISO 8601 格式。那么,这对数据库有何重要性?要回答这个问题,我们首先必须回答另一个问题:我们将从哪个点向表添加报价?根据答案的不同,可以使用不同的格式。这样一来,我们就能占用更少的空间,并提升数据库的整体性能。
为了理解这一点,想象一下一年有 365 天,但交易所并非每天都进行交易。但假设交易每天都在进行。因此,如果我们使用像 SmallInt 这样的值(在本例中是一个有符号值),我们可以从 0 数到 32767。这将涵盖总共 89 年多的时间。如果我们使用相同的数据类型,但使用无符号数,即从 0 到 65535,我们总共可以涵盖略超过 179 年的时间。然而,我们只需要使用两个字节,而不是 ISO 8601 格式使用的所有字节。想象一下,179 年每日报价之间的差异所代表的数据量会有多大。
第三个问题与前一个问题类似。这也涉及到存储问题,但这次的记录字段有所不同。在这种情况下,它与交易品种名称有关。这个问题比之前的那个问题还要复杂。原因是有些交易品种会不时地更改名称。但是,如果我们认为更改交易品种名称并不意味着它所代表的公司会改变其行为,那么更改交易品种名称并不会改变其历史。
好吧,无论市场如何,交易品种的数量都将受到限制。在这些交易品种中,只有少数交易品种能够引起我们的注意,让我们想要了解它们的报价历史。既然可以用 1 个字节来表示一个交易品种,为什么还要用 6 个字节呢?这是因为 1 个字节最多可以表示 255 个交易品种。
现在让我们回到之前的问题。想想看,从长远来看,这将节省多少字节。而且,这还没考虑到,如果一个交易品种更改了名称,引入新名称只需要更改数据库中的一个条目,而不是整个报价历史记录。
最后的想法
在今天的文章中,我们尝试以最简单、最易懂的方式展示如何创建一个简单易懂的数据库。我们证明,Workbench 中几乎所有可用的命令都可以在 MetaEditor 中使用,以达到相同的效果。但是,有一条指令在 MetaEditor 中无法使用。本文未对此进行说明,也未介绍在数据库中输入数据和执行查询的方法。
但鉴于这个话题涉及面非常广,我建议您通过在线研究、课程或相关书籍来学习。无论如何,在此我们将展示更多命令,以便我们拥有在 MQL5 中使用 SQL 所需的最基本材料集。下一篇文章我们将更详细地探讨 SQL。
参考
| 文件 | 描述 |
|---|---|
| Experts\Expert Advisor.mq5 | 演示 Chart Trade 与 EA 之间的交互(交互需要 Mouse Study)。 |
| Indicators\Chart Trade.mq5 | 创建一个窗口,用于配置要发送的订单(需要 Mouse Study 才能进行交互) |
| Indicators\Market Replay.mq5 | 创建用于与回放/模拟服务交互的控件(交互需要 Mouse Study)。 |
| Indicators\Mouse Study.mq5 | 提供图形控件与用户之间的交互(回放系统和真实市场操作都需要)。 |
| Services\Market Replay.mq5 | 创建并维护市场回放/模拟服务(整个系统的主文件)。 |
| Code VS C++\Servidor.cpp | 创建并维护一个用 C++ 开发的套接字服务器(迷你聊天版本)。 |
| Python Server.py | 创建并维护用于 MetaTrader 5 和 Excel 之间通信的 Python 套接字。 |
| Indicators\Mini Chat.mq5 | 通过指标启用迷你聊天功能(需要服务器支持) |
| Experts\Mini Chat.mq5 | 使用 EA(需服务器支持)启用迷你聊天功能。 |
| Scripts\SQLite.mq5 | 演示如何使用 MQL5 编写 SQL 脚本 |
| Files\Script 01.sql | 演示如何创建带有外键的简单表。 |
| Files\Script 02.sql | 显示向表中添加值的过程 |
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/12926
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
您应该了解的MQL5向导技巧(第六十九部分):使用SAR与RVI的形态
新手在交易中的10个基本错误
交易中的神经网络:频域异常检测(终篇)