市場シミュレーション(第19回):SQL入門(II)
はじめに
皆さん、こんにちは。レプリケーション/モデリングシステム構築シリーズの新しい記事へようこそ。
前回の「市場シミュレーション(第18回):SQL入門(I)」では、SQLで使用できる最初のコマンドについて学び始めました。その目的は、情報取得や今後のクエリに使用する初期データベースを作成することでした。また、MetaEditorでは、一般的なSQLプログラムやSQLスクリプトで利用できるものと同じ機能を使用できることも示しました。つまり、同じコードをMQL5で作成した実行ファイル内でも利用できるということです。その結果、MetaTrader5から直接実行することも可能になります。
ただし、SQLには、MetaEditor上で実行しようとしても動作しないコマンドがあります。実行するとエラーが返されます。しかし、一つはっきりさせておきたいことがあります。MQL5ベースのソリューションでは、すべてのSQLコマンドを使用できます。これは、MetaTrader5がSQLiteを使用しているためです。ただし、そのすべてをMetaEditorから直接実行できるわけではありません。誤解を避けるために、どのコマンドについて話しているのかを確認してみましょう。
MetaEditorでは決して実行されないSQLコマンド
それでは、実際に試してみましょう。まだ学習を始めたばかりなので、影響は最小限で済みます。ただし、どのようなSQLコマンドであっても、その動作を理解する前に重要なデータベースに対して実行してはいけません。新しいことを試す場合は、後で問題にならないよう、できるだけ安全な形でおこなってください。ことわざにもあるように、「信念は山を動かせても、失われたデータは戻してくれません」。
では、この「魔法の」コマンドを見てみましょう。以下のアニメーションで、それが何であるか確認できます。

SQLでDROPコマンドを使用すると、何かを永久に削除できます。このコマンドを十分注意せずに使用すると、深刻な問題を引き起こす可能性があります。SQLは確認を求めません。指定されたものをそのまま削除します。
ただし、MetaEditorを使用している場合、データベースファイル自体は削除されません。しかし、アニメーション内でエラーになっていたのと同じコマンドをSQLとして実行すると、データベースファイルは実際に削除されます。
最初は、MetaEditorがDROPコマンドの実行を拒否していると誤解する人もいるかもしれません。しかし、アニメーションで確認できたように、それは事実ではありません。実際、以前作成したテーブルを削除することは可能でした。
また、MetaEditorでCREATE DATABASEコマンドを使用しようとしても、USEコマンドと同様に動作しないと考えるかもしれません。そして、それは事実です。ただし、これら二つのコマンドは、手動で入力する必要がないように実装されています。
つまり、MetaEditorはディスク上のデータベースファイル自体を削除しません。ここに重要な違いがあります。安全上の理由から、MetaEditorではDROP DATABASEコマンドの実行が許可されていないのです。SQLにおいてこれは、データベースファイルそのものを削除することを意味します。
これで、MetaEditorが実行しないSQLコマンドが存在することを確認できました(実際に試して確認することもできます)。しかし、このコマンドを除けば、他のコマンドは問題なく動作します。これはDROPコマンドの意味を理解する助けにもなります。このコマンドは単に何かを永久に削除するためのものであり、それが列であれ、テーブルであれ、あるいはデータベース全体であれ同じです。実行前に確認を求めないため、このコマンドの使用には十分注意してください。
適切な権限なしで誤って削除が行われないようにする方法も存在します。しかし、それを説明するには、SQLの扱い方についてさらに深い説明が必要になります。それは今回の目的ではありません。今回の目的は、次におこなうこと、つまりMQL5で作成した実行ファイルの中でSQLを使用するために必要な基礎を皆さんに提供することです。
データベースへのデータの挿入
次に見ていくコマンドは、データベースへ情報を挿入するためのものです。私は、データベースファイルを手動で編集することを推奨しません。正しい方法は、常にSQLコマンドを使用することです。データベースが小規模であっても、列やレコード数が少なくても、手動で編集してはいけません。必ずSQLコマンドを使用してください。
データベースへデータを挿入するためのコマンド自体は非常にシンプルですが、注意して使用する必要があります。実際、このコマンドは「キーと値」の原則で動作するため、使う際には十分気を付ける必要があります。Pythonでプログラミングをしたことがある人なら、この考え方はすぐ理解できるでしょう。
これは、SQLでデータを挿入するコマンドが、Pythonの辞書(dictionary)を作成する方法と非常によく似ているためです。つまり、キーを用意し、それに値を割り当てるという考え方です。ただし、SQLでは動作の仕組みが少し異なります。それでも、基本的な考え方は同じです。実際にどのように動作するのか見てみましょう。
ここでは、このコマンドをMetaEditor上では表示しません。実行する必要があるコマンドは同じであることは、すでに十分理解できていると思うので、同じことを繰り返す意味はありません。コマンドの説明を簡単にするために、非常にシンプルで理解しやすい小さなSQLコード例を使用します。
以下のコードをご覧ください。
01. CREATE DATABASE IF NOT EXISTS MT5_Tutor_DB;
02. USE MT5_Tutor_DB;
03.
04. CREATE TABLE IF NOT EXISTS tb_Quotes
05. (
06. of_day DATE,
07. symbol CHAR(6),
08. price DECIMAL(5, 2)
09. );
10.
11. INSERT INTO tb_Quotes (of_day, symbol , price)
12. VALUES ('2023-07-06', 'BOVA11' , 105.61);
13.
14. SELECT * FROM tb_Quotes; SQLスクリプト
今の段階では、14行目については気にしないでください。この時点では、結果を見やすくするためだけに存在しています。このコードは、MetaEditorでもSQLサーバー上でも使用できます。ただし、MetaEditorで使用する場合は、01行目と02行目を前回までの記事で説明した方法とは異なる形で実行する必要があることに注意してください。もし不明な場合は、前の記事を読み返して、MetaEditor上でどのように実行すれば同じコードを動かせるのかを確認してください。ここで重要なのは、04行目と11行目です。これらは、それぞれテーブルの作成とデータの挿入をおこなっています。
このコードは動作しますが、データベース作成時に使うべきものではありません。その理由は、データの重複を無制限に許してしまうからです。データベースについて少しでも知識があるなら、重複値は避けるべきだと理解しているでしょう。データベースは、本来そのような状況を防ぐために設計されています。しかし、その目的があるにもかかわらず、SQLスクリプトを素人っぽい方法で書いてしまうと、重複値は簡単に発生します。上のコードには、まさにその未熟な実装が見られます。それでも、ここでの目的はデータベースへ値を追加する方法を説明することなので、重複やNULL値を含む挿入エラーを一旦許容しています。
何が起きているのかを理解するために、04行目に注目してください。ここでは、データベース内にまだ存在しない場合のみテーブルを作成しています。このテーブルには、次のような列が含まれます。たとえば06行目では、日付を表す内容を格納する列を定義しています。
07行目では、幅6文字の列を定義しています。これは銘柄名を格納するためのものです。この命名規則は、B3(ブラジル証券取引所)で使用されている標準に従っています。
08行目では、幅5文字、そのうち2文字を小数部に使用する列を定義しています。これにより、0から999.99までの価格を格納できます。これはB3で取引される銘柄に対して十分以上です。ここで注目してほしいのは、キーや制約を一切定義していない点です。この制約が存在しないことによって、重複データが許可されてしまいます。
では、11行目で何が行われているのか見てみましょう。テーブルへのデータ挿入コマンドはINSERT INTOから始まっています。その直後に、どのテーブルへ値を挿入するかを指定しています。この場合は、先ほど作成したtb_Quotesです。その後、値を受け取る列名を宣言しています。
ここで重要なのは、列は任意の順番で並べられるという点です。ただし、テーブル定義時と完全に同じ名前を使用しなければなりません。異なる名前を使うとエラーになります。列の順番を定義した後、VALUESという単語を書き、その後に挿入する値を指定します。このとき、値は宣言した列と同じ順番で並べなければなりません。つまり、symbol列より前にprice列を宣言したなら、値リストでもsymbolより前にpriceを書く必要があります。
少し混乱するかもしれませんが、以下のコードを使用しても、先ほどのスクリプトと同じ結果が得られます。
INSERT INTO tb_Quotes (of_day, price, symbol)
VALUES ('2023-07-06', 105.61, 'BOVA11'); SQLスクリプト
列の宣言順を変更したことに注意してください。そのため、値の順番も変更する必要がありました。ここが最も注意すべきポイントです。ところで、なぜ日付がこの形式で指定されているのか疑問に思うかもしれません。その理由は、MySQLがこの形式を期待しているからです。つまり、「年→月→日」の順です。一部のデータベースでは別形式も使用できますが、保存時にはこの形式へ変換されます。たとえばSQL Serverでは「日→月→年」の形式も使用できます。しかし、データベース内部では常に「年→月→日」の形式になります。
上のスクリプトを実行すると、結果はその通りに表示されます。

そして、ここで注意してください。もし11行目をもう一度実行すると、同じ情報が再びデータベースへ追加されます。その結果、データが重複した状態になります。

これを見て、「別に問題ではない。重複行を削除すればいい」と思うかもしれません。確かに削除自体は簡単です。しかし、この例は非常に単純なケースであることを理解してください。実際のデータベースには膨大な数のレコードが存在する可能性があります。その中に重複データがあると、データベース全体が使い物にならなくなることさえあります。また、特定レコードの変更や更新も困難になります。だからこそ、データベース作成には慎重な設計と計画が必要なのです。
上のスクリプトは概念的には正しいものです(実際に動作するため)。しかし、重複データを許可してしまうため理想的ではありません。また、レコードの変更や更新にも対応できません。これをより理解するために、別の方法でデータベースへデータを挿入してみましょう。すべての列と値を一度に宣言するのではなく、段階的におこないます。実際、多くの場面ではこちらの方法が一般的です。すると、先ほどと同じ値を挿入するコードは次のようになります。
01. CREATE DATABASE IF NOT EXISTS MT5_Tutor_DB;
02. USE MT5_Tutor_DB;
03.
04. CREATE TABLE IF NOT EXISTS tb_Quotes
05. (
06. of_day DATE,
07. symbol CHAR(6),
08. price DECIMAL(5, 2)
09. );
10.
11. INSERT INTO tb_Quotes (of_day) VALUES ('2023-07-06');
12. INSERT INTO tb_Quotes (symbol) VALUES ('BOVA11');
13. INSERT INTO tb_Quotes (price) VALUES (105.61);
14.
15. SELECT * FROM tb_Quotes; SQLスクリプト
ここでは、Pythonの辞書を作成するときと同じように、「キー–値」という概念を文字通り使っていることに注目してください。そして実際、SQLでもこれをおこなうことができ、データベースは指定されたテーブルに値を保存します。ただし、このスクリプトを実行した結果に注意してください。結果はすぐ下に示されています。

「しかし、何が起きたのでしょうか。なぜ前回とは違う結果になったのでしょう。」理由は2つです。第一に、私たちは単にデータベースへデータを追加しているだけだからです。第二に、データベース自体が重複値を許可する構造になっているからです。もしレコードを変更しようとしても、このスクリプトのように作成されたデータベースではそれができません。特定のレコードを選択し、その値を操作・変更するための仕組みが必要です。そうすることで、「キー–値」形式を使って1件ずつデータを挿入できるようになります。では、それをどう実現すればよいのでしょうか。
最初のステップは、いずれかの列を一意キーにすることです。これが出発点になります。そうすることで、データベース内の重複レコードを防げます。しかし、ここで重要な疑問が生じます。最適な方法は何か、という点です。
この目的のためだけに専用のカラムを作る人もいます。それは決して悪い考えではありません。ただし、どの解決策を選ぶべきかはケースごとに異なります。今回の場合、単に一意キー用の追加カラムを作る必要はありません。既存の列を適切に選ぶだけで十分です。では考えてみましょう。
「Price」列は、値が異なる場合もあれば同じ場合もあるため、一意キーとしては適切ではないかもしれません。一方、銘柄名を含む列は、新しいレコードごとに繰り返されます。すると、候補となるのは日付列です。
しかし、なぜ「候補」であって断定できないのでしょうか。その理由は、データベースへどのくらいの頻度で新しいレコードを追加する予定なのかに関係しています。そして、なぜそれが重要なのでしょうか。もし非常に短い間隔でレコードを追加するなら、そのため専用の別列を作る必要があるかもしれないからです。しかし、1日に1回だけレコードを追加するのであれば、日付だけで十分な場合があります。
したがって、銘柄の日次価格を保存したい場合、先ほどの最初のスクリプトは次のように変更できます。
01. CREATE DATABASE IF NOT EXISTS MT5_Tutor_DB;
02. USE MT5_Tutor_DB;
03.
04. CREATE TABLE IF NOT EXISTS tb_Quotes
05. (
06. of_day DATE PRIMARY KEY,
07. symbol CHAR(6),
08. price DECIMAL(5, 2)
09. );
10.
11. INSERT INTO tb_Quotes (of_day, symbol , price)
12. VALUES ('2023-07-06', 'BOVA11' , 105.61);
13.
14. SELECT * FROM tb_Quotes; SQLスクリプト
最初のスクリプトから変更されたのは、06行目にPRIMARY KEYを追加した点だけです。ここでは、of_day列の値をこのテーブルの主キーとして使用することを示しています。複数の列を主キーとして設定することも可能です。しかし、主キーには重複値を持たせることはできません。もしそうしようとすると、SQLはそれをエラーとして扱い、コマンドは実行されません。
そのため、データベースに既に存在する同じ日付で再びINSERT INTOコマンドを実行しようとしても成功しません。SQLでエラーにならないようにするには、別の日付を指定する必要があります。この非常にシンプルな対策だけで、データベースの整合性を維持し、重複レコードを防ぐことができます。先ほど述べたように、たった一つの簡単な対策で、多くの問題を解決できるのです。
上記スクリプトを実行した結果は、以下の画像のようになります。

クエリ結果自体は以前と同じですが、表示内容が異なることに気づくでしょう。ここで、「これで「キー–値」形式、あるいはより正確には「列–値」形式でデータを挿入できるようになる」と思うでしょう。はい、その通りです。そこで最初に思いつくのは、次のようなスクリプトでしょう。
01. CREATE DATABASE IF NOT EXISTS MT5_Tutor_DB;
02. USE MT5_Tutor_DB;
03.
04. CREATE TABLE IF NOT EXISTS tb_Quotes
05. (
06. of_day DATE PRIMARY KEY,
07. symbol CHAR(6),
08. price DECIMAL(5, 2)
09. );
10.
11. INSERT INTO tb_Quotes (of_day) VALUES ('2023-07-07');
12. INSERT INTO tb_Quotes (symbol) VALUES ('BOVA11');
13. INSERT INTO tb_Quotes (price) VALUES (105.61);
14.
15. SELECT * FROM tb_Quotes; SQLスクリプト
このスクリプトは、of_dayの値を取得した直後に実行されることを想定しています。そのため、SQLによってレコード作成を拒否されないよう、日付を変更しています。つまり、主キーの仕組みを理解し、それに合わせてロジックを組み立てているわけです。しかし、このスクリプトを実行するとエラーが発生します。そして、これは実務上も重要なことなので、データベース内で何が起きたのか確認してみると、次のような状態になります。

ここで、「なぜレコードは作成されたのにエラーが発生したのか?」と思うかもしれません。その理由は、12行目で再び情報を挿入しようとしているからです。このときSQLは、新しいレコードを作成しようとします。ここで少し整理してみましょう。なぜSQLは新しいレコードを作成するのでしょうか。私は新しい主キーを作るとは指定していません。主キーを使っているのだから、同じレコードを扱っていると思っていたはずです。実際、多くの人が最初はこの誤解をします。概念の本質を理解していないからです。
では、状況を整理しましょう。INSERT INTOコマンドを使うとき、私たちはデータベース内に新しいレコードを作成しています。しかし、そのレコードを変更するには別のコマンドを使う必要があります。今回まさに必要なのはこれです。そこで次は、この2つを分けて別のトピックで見ていきましょう。
レコードの変更と更新
SQLがどのレコードを変更または更新すべきかを判断するためには、一意キー、つまり主キーが必要です。今回、of_dayの値を主キーとして定義しているため、最初におこなうべきことは、その値をデータベースへ追加することです。もし既に存在している場合、SQLは新しいレコードの作成を許可しません。
主キーを定義し、データベースへ登録した後で、他の列の値を変更または更新できます。注意してください。問題を避けるため、主キーの値を変更しようとしてはいけません。まずキーを作成し、その後でレコードを更新する必要があります。以下は、キー–値の概念、より正確にはカラム–値の概念を使って、新しいレコードを作成するための更新処理をおこなうコードです。
01. CREATE DATABASE IF NOT EXISTS MT5_Tutor_DB;
02. USE MT5_Tutor_DB;
03.
04. CREATE TABLE IF NOT EXISTS tb_Quotes
05. (
06. of_day DATE PRIMARY KEY,
07. symbol CHAR(6),
08. price DECIMAL(5, 2)
09. );
10.
11. INSERT INTO tb_Quotes (of_day) VALUES ('2023-07-07');
12. UPDATE tb_Quotes SET symbol = 'BOVA11' WHERE of_day = '2023-07-07';
13. UPDATE tb_Quotes SET price = 105.61 WHERE of_day = '2023-07-07';
14.
15. SELECT * FROM tb_Quotes; SQLスクリプト
このスクリプトを実行した結果は、以下のようになります。

ここで注目してほしいのは、上記のスクリプトを実行する前の時点で、すでに1件のレコードが存在しているという点です。そのため、このような方法を取っています。そして11行目では、新しいレコードをデータベースへ追加するためのキーを作成しています。このキーを作成したあと、UPDATEコマンドを次のように使用します。
- コマンド名の直後に、対象となるテーブル名を指定します。
- テーブル名のあとに SET コマンドを使用します。これは、ある列を変更または更新することを示します。
- 次に、列名と、その列へ設定する値を指定します。
- SQLが更新対象となるレコードを見つけられるように、WHEREコマンドを使用し、どのキーを使うかを指定します。
ここでさらにいくつか疑問が出てくるでしょう。しかし、あまり複雑にはしません。できる限りシンプルなまま進めます。私たちの目的は、MetaTrader 5と組み合わせてSQLを利用するための基礎を提供することであり、本格的なSQL解説をすることではありません。とはいえ、本日の内容については、もう少しだけ深く掘り下げてみる価値があります。というのも、SQL を高速化し、非効率なクエリによる負荷を避けるために、ここでできることはまだ多くあるからです。ただし、ここで重視しているのは効率性ではなく、あくまで学習と理解です。
これでレコードの挿入、変更、更新の方法は理解できました。あとは、レコードを削除する方法だけです。しかし今回も混乱を避けるために、内容を分けて説明していきましょう。
レコードの削除
データベースからレコードを削除することは、とてもシンプルな処理です。実際には、データベース内のレコード単位で削除する方法はいくつか存在します。しかしここでは、少なくともレコードを削除できるようになることを目的として、最も基本的な方法だけを扱います。
例として次のような状況を考えます。あなたは2023年7月7日のデータを追加しましたが、その後、その日は取引がおこなわれていなかったことに気づいたとします。そのため、このレコードはデータベースに残すべきではなく、削除する必要があります。もし無効なデータが残っていると、データベース全体の整合性を壊してしまう可能性があるからです。このような場合、次のスクリプトを使用します。
1. USE MT5_Tutor_DB; 2. 3. DELETE FROM tb_Quotes WHERE of_day = '2023-07-07'; 4. 5. SELECT * FROM tb_Quotes;
SQLスクリプト
まず1行目で使用するデータベースを指定します。続いて3行目で、実際にレコードを削除するコマンドを実行します。処理の流れは非常に単純です。つまり、まずコマンド名を指定します。まず最初にコマンド名としてDELETE FROMを指定します。そのあとにテーブル名を記述します。この場合 tb_Quotesです。次に、どのデータを削除するのかを明確にする必要があります。そのためにWHEREを使用し、その後ろに条件式を記述します。今回の例では、削除対象を特定するために主キーである日付を条件として指定しています。
そしてスクリプトの最後では、5行目でテーブルの中身を表示しています。見てわかる通り、レコードの削除自体は非常にシンプルです。しかし、ここで示した方法はあくまで数ある方法の中の一つにすぎません。
最後に
この記事では、できるだけ内容を簡略化しようとしました。もっと良い方法があることは私も理解しています。すでにSQLプログラムの経験や知識を持っている方は、ここで提示されたすべての内容がSQLの本質的な基礎であることをよくご存じでしょう。知らない方のために説明すると、rice and beansという表現は、必要最低限のものはあるが、同時に満足感を得るための最低限のバリエーションもある、という意味の地域的な表現です。
しかし話を元に戻しましょう。私は、これらの記事を通じてSQLを使って何かを作れるように、少し準備を整えてほしいと思っています。すでにSQLで実現できることを、無意味なものをプログラムしたり、不要なサブルーチンを作ったりして時間を無駄にしてほしくありません。
これは、あなたが常に適切でより広い知識を求め続け、快適な領域にとどまって、より簡単・効率的・安全に解決できる問題をわざわざ複雑にプログラムして時間を浪費しないようにするためです。
しばしば、多くの人はプログラマーやチームがあるツールを使っているのを見て、それが自分の好みと合わないという理由で眉をひそめることがあります。しかし、専門のプログラマーを目指す読者の皆さんに伝えたいのは、見栄は捨てるべきだということです。利用可能なツールを使い始めてください。そうすれば、より生産的になり、自分自身と自分の仕事に対してより自信を持つことができます。そして最も重要なのは、より高く評価されるようになるということです。なぜなら、他の人が解決策を探して頭を悩ませている間に、あなたはより広い知識のおかげで正しいツールをすぐに見つけ、顧客が必要とする解決策を簡単に作ることができるからです。この文章のメッセージは次の通りです。
テクノロジーの世界は非常に速く進化しています。私たちが何かを理解した時には、それはすでに時代遅れになっていることもあります。
このことを考えてみてください。それでは、次回の記事でお会いしましょう。SQLについてまだいくつか説明すべきポイントが残っており、それらを終えたら、SQLとMQL5を組み合わせてリプレイ/シミュレーションシステムを作り始める予定です。それでは、またお会いしましょう。
| ファイル | 説明 |
|---|---|
| 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 | 市場リプレイ/シミュレーションサービス(システム全体のメインファイル)を作成および維持する |
| VS C++ Code Server.cpp | C++で作成されたソケットサーバーを作成および維持する(ミニチャット版) |
| Python Code 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/12927
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
初級から中級まで:構造体(VII)
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
MQL5における建値機能の実装(第1回):基底クラスと固定ポイントの建値モード
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索