Marktsimulation (Teil 24): Erste Schritte mit SQL (VII)
Einführung
Im vorherigen Artikel „Marktsimulation (Teil 23): Erste Schritte mit SQL (VI)“ wurden einige wichtige Punkte erläutert, die man über SQL wissen muss. Damit verfügen wir über die minimale Grundlage, die wir für die Arbeit mit Datenbanken brauchen. Da wir also noch viel zu besprechen haben und das Thema ziemlich komplex sein wird, wollen wir keine Zeit mit einer Einleitung verschwenden. Kommen wir gleich zur Sache.
Das Problem verstehen
Datenbanken sind nichts anderes als eine Sammlung von Datensätzen in einer Datei. Um jedoch zu verstehen, was wir hier erklären wollen, verwenden wir den folgenden Code:
01. PRAGMA FOREIGN_KEYS = ON; 02. 03. DROP TABLE IF EXISTS tb_Quotes; 04. DROP TABLE IF EXISTS tb_Symbols; 05. 06. CREATE TABLE IF NOT EXISTS tb_Symbols 07. ( 08. id PRIMARY KEY, 09. symbol NOT NULL UNIQUE 10. ); 11. 12. CREATE TABLE IF NOT EXISTS tb_Quotes 13. ( 14. of_day NOT NULL, 15. price NOT NULL, 16. fk_id NOT NULL 17. ); 18. 19. INSERT INTO tb_Symbols (id, symbol) VALUES 20. (2, 'PETR4'), 21. (1, 'ITUB3'), 22. (3, 'VALE3'); 23. 24. INSERT INTO tb_Quotes (of_day, price, fk_id) VALUES 25. ('2023-07-10', '22.00', 1), 26. ('2023-07-11', '22.20', 1), 27. ('2023-07-12', '22.40', 1), 28. ('2023-07-13', '22.30', 1), 29. ('2023-07-14', '22.60', 1), 30. ('2023-07-10', '26.00', 2), 31. ('2023-07-11', '26.20', 2), 32. ('2023-07-12', '26.40', 2), 33. ('2023-07-13', '26.30', 2), 34. ('2023-07-14', '26.60', 2), 35. ('2023-07-10', '62.00', 3), 36. ('2023-07-11', '62.20', 3), 37. ('2023-07-12', '62.40', 3), 38. ('2023-07-13', '62.30', 3), 39. ('2023-07-14', '62.60', 3); 40. 41. SELECT tq.of_day AS 'Data da cotação', 42. tq.price AS 'Preço Atual', 43. ts.symbol AS 'Nome do Ativo' 44. FROM tb_Quotes AS tq, tb_Symbols AS ts 45. WHERE tq.fk_id = ts.id 46. ORDER BY price DESC;
Code 01
Code 01 ist Ihnen wahrscheinlich bereits bekannt, da das darin verwendete Material in früheren Artikeln ausführlich beschrieben wurde. Wenn wir diesen Code also ausführen, erhalten wir folgendes Ergebnis:

Abbildung 01
Gut, diese Abbildung zeigt genau das, was wir erwartet hatten, da wir drei Symbole erstellt und jedem fünf Kurse hinzugefügt haben, was insgesamt 15 Datensätze ergibt. Aber kommen wir nun zum Hauptproblem. Das Problem tritt vor allem dann auf, wenn wir versuchen, etwas aus der Datenbank zu löschen. „Aber warum sollten beim Löschen von Datensätzen aus der Datenbank plötzlich Probleme auftreten? Das ergibt nicht viel Sinn.“ Im Grunde genommen nicht. Bei der Verwendung logisch aufeinander bezogener Tabellen tritt jedoch ein Problem auf, das bei einem anderen Schemaentwurfsansatz nicht auftritt. Eines dieser Probleme lässt sich durch Ausführen des unten gezeigten Programms feststellen:
1. DELETE FROM tb_Symbols WHERE symbol = 'PETR4'; 2. 3. SELECT tq.of_day AS 'Data da cotação', 4. tq.price AS 'Preço Atual', 5. ts.symbol AS 'Nome do Ativo' 6. FROM tb_Quotes AS tq, tb_Symbols AS ts 7. WHERE tq.fk_id = ts.id 8. ORDER BY price DESC;
Code 02
Vielleicht denken Sie: „Na gut, aber was ist daran falsch? Mein Ziel war es, alle PETR4-Datensätze zu löschen.“ Tatsächlich, liebe Leser, löscht SQL bei der Ausführung der ersten Zeile die PETR4-Datensätze. Genau das passiert, und wenn wir also den SELECT-Befehl in Zeile 3 ausführen, erhalten wir folgendes Ergebnis:

Abbildung 02
Diese Abbildung belegt, dass der Befehl in Zeile 01 funktioniert hat. Aber wissen Sie wirklich, was der Befehl in Zeile 01 bewirkt hat? Und außerdem: Wissen Sie wirklich, was in der Datenbank vor sich ging? Viele würden sagen: „Natürlich weiß ich, was passiert ist.“ Also, liebe Leser, ich möchte, dass Sie die folgende Frage beantworten: Erstellt Code 01 eine relationale Datenbank oder nicht? Wenn ja, wo wird diese Beziehung hergestellt? Wenn die Antwort „Nein“ lautet, wie können wir diese Beziehung dann sichern? Ich weiß, dass viele Leute glauben, sie wüssten, wie man mit Datenbanken umgeht, nur weil sie ein paar SQL-Befehle kennen. Aber es ist nicht alles so einfach, wie es scheint. Es ist auch gar nicht so schwer, wie viele glauben. Auf jeden Fall möchte ich, dass Sie eines verstehen:
Code 01 ERSTELLT KEINE RELATIONALE DATENBANK.

Abbildung 03
Auch wenn wir nicht alle 30 möglichen Kombinationen sehen, können wir feststellen, dass es keinen Verweis auf das Symbol PETR4 gibt. Der Wert „fk_id“ mit dem Wert 2 ist zwar vorhanden, dieser Wert ist jedoch keinem Symbolbezeichner mehr zugeordnet. Dadurch wird die Datenbank mit der Zeit anfälliger für Fehler bei der Datenkonsistenz; der Grund dafür wird etwas später erläutert. Schauen wir uns nun einmal genauer an, was der DELETE-Befehl in der Datenbank bewirkt. Da es sich nicht um eine relationale Datenbank handelt, sind die beiden Tabellen voneinander unabhängig, und die Daten sind lediglich durch eine einfache Referenz miteinander verbunden.
Dieses Schema hat seine Vorteile, auch wenn es nicht garantiert, dass wir tatsächlich eine relationale Datenbank erstellen. Nun wollen wir uns den wahren Grund ansehen. Wie wir wissen, ist ID 2 frei. Um dies zu verstehen, müssen wir uns lediglich die Tabelle „tb_Symbols“ ansehen, wie unten gezeigt.

Abbildung 04
Mit dieser Information können wir ein weiteres Symbol, zum Beispiel BBDC4, unter dieser freien ID anlegen. Dies geschieht wie folgt:

Abbildung 05
Frage: Stimmt daran etwas nicht? Im Prinzip nein, denn ID 2 war im Grunde frei. Doch genau hier liegt das Problem. Wenn Daten in die Tabelle „tb_Quotes“ eingefügt werden, können in unserer Datenbank ungültige Werte auftreten. Und es ist sehr wahrscheinlich, dass wir dies erst bemerken, nachdem wir ziemlich viel Zeit damit verbracht haben, Daten in die Datenbank einzugeben. Und je länger wir warten, desto unwahrscheinlicher ist es, dass wir bemerken, dass etwas nicht stimmt. Nehmen wir jedoch einmal an, dass wir uns aus irgendeinem Grund unmittelbar nach dem Hinzufügen von BBDC4 zur Tabelle „tb_Symbols“ entschließen, die Datenbank abzufragen, wie in der folgenden Abbildung dargestellt.

Abbildung 06
„Warten Sie einen Moment. Warum ist das so? Ich habe gerade BBDC4 zur Datenbank hinzugefügt – gibt es dazu bereits Informationen?“ Ja, liebe Leser, das ist möglich. Das ist die Gefahr, wenn man etwas tut und dabei glaubt, man wüsste, was man tut. Naivität oder übermäßiges Selbstvertrauen führen oft zu solchen Problemen, die sich möglicherweise erst viel später zeigen. Somit haben wir keine Möglichkeit mehr, die Zuverlässigkeit der in der Datenbank gespeicherten Daten zu überprüfen. Um dieses Problem zu lösen, müssten wir die Struktur der Datenbank so ändern, dass eine Beziehung zwischen dem Primärschlüssel und dem Fremdschlüssel besteht. Daher sollte derselbe Code 01 wie unten gezeigt ausgeführt werden:
01. PRAGMA FOREIGN_KEYS = ON; 02. 03. DROP TABLE IF EXISTS tb_Quotes; 04. DROP TABLE IF EXISTS tb_Symbols; 05. 06. CREATE TABLE IF NOT EXISTS tb_Symbols 07. ( 08. id PRIMARY KEY, 09. symbol NOT NULL UNIQUE 10. ); 11. 12. CREATE TABLE IF NOT EXISTS tb_Quotes 13. ( 14. of_day NOT NULL, 15. price NOT NULL, 16. fk_id NOT NULL, 17. FOREIGN KEY (fk_id) REFERENCES tb_Symbols(id) 18. ); 19. 20. INSERT INTO tb_Symbols (id, symbol) VALUES 21. (2, 'PETR4'), 22. (1, 'ITUB3'), 23. (3, 'VALE3'); 24. 25. INSERT INTO tb_Quotes (of_day, price, fk_id) VALUES 26. ('2023-07-10', '22.00', 1), 27. ('2023-07-11', '22.20', 1), 28. ('2023-07-12', '22.40', 1), 29. ('2023-07-13', '22.30', 1), 30. ('2023-07-14', '22.60', 1), 31. ('2023-07-10', '26.00', 2), 32. ('2023-07-11', '26.20', 2), 33. ('2023-07-12', '26.40', 2), 34. ('2023-07-13', '26.30', 2), 35. ('2023-07-14', '26.60', 2), 36. ('2023-07-10', '62.00', 3), 37. ('2023-07-11', '62.20', 3), 38. ('2023-07-12', '62.40', 3), 39. ('2023-07-13', '62.30', 3), 40. ('2023-07-14', '62.60', 3); 41. 42. SELECT tq.of_day AS 'Data da cotação', 43. tq.price AS 'Preço Atual', 44. ts.symbol AS 'Nome do Ativo' 45. FROM tb_Quotes AS tq, tb_Symbols AS ts 46. WHERE tq.fk_id = ts.id 47. ORDER BY price DESC;
Code 03
Bitte beachten Sie, dass der einzige Unterschied zwischen den Skripten in Zeile 17 liegt. Genau hier stellen wir die tatsächliche Beziehung zwischen den Tabellen sicher. „Hmm, aber wenn die Lösung so einfach ist, warum erstellen wir dann nicht alles so, wie in Code 03 gezeigt?“ Nun, lieber Leser, das Problem ist, dass es das Löschen von Datensätzen aus der Datenbank erschwert, wenn wir Elemente wie die in Code 03 gezeigten erstellen. Um diese Themen voneinander zu trennen, wenden wir uns nun einem neuen Thema zu.
Datensätze in verknüpften Tabellen löschen
Um zu verstehen, warum eine beträchtliche Anzahl von Benutzern beim Erstellen von Datenbanken Code 01 anstelle von Code 03 bevorzugt, versuchen wir, dasselbe wie zuvor zu tun. Mit anderen Worten: Wir werden das PETR4-Symbol löschen und durch das BBDC4-Symbol ersetzen. Wir können dies anhand des unten gezeigten Beispiels versuchen.

Abbildung 07
Bitte beachten Sie, dass der markierte Bereich in Abbildung 07 den Versuch zeigt, PETR4 aus der Tabelle tb_Symbols zu löschen. SQL sagt uns jedoch, dass dies nicht möglich sein wird. Das liegt daran, dass die Datenbank mit Code 03 erstellt wurde. Viele Leute ärgern sich irgendwann über SQL und suchen nach einer Lösung; die einfachste ist die Verwendung von Code 01, was jedoch früher oder später trotzdem zu Problemen führen wird. Sehr gut. Wie in Abbildung 07 zu sehen ist, handelt es sich bei der Antwort von SQL auf die Abfrage keineswegs um einen Fehler. Dies zeigt, dass tatsächlich eine Beziehung zwischen den Datensätzen in den Tabellen „tb_Symbols“ und „tb_Quotes“ besteht, wodurch ein versehentliches Löschen von Daten verhindert wird.
Um zu verstehen, wo die Beziehung hergestellt wird – was in vielen Fällen notwendig ist –, müssen wir uns auf das Datenbankdiagramm beziehen, das wir im vorherigen Artikel betrachtet haben. Da wir hier jedoch nur zwei Tabellen haben, ist das leicht zu verstehen. Kommen wir also gleich zur Sache.

Abbildung 08
Zur Klarstellung: Der Code für Abbildung 08 ist unten aufgeführt.
01. DELETE FROM tb_Quotes 02. WHERE fk_id = (SELECT ts.id FROM tb_Symbols AS ts WHERE ts.symbol = 'PETR4'); 03. DELETE FROM tb_Symbols WHERE symbol = 'PETR4'; 04. 05. SELECT tq.of_day AS 'Data da cotação', 06. tq.price AS 'Preço Atual', 07. ts.symbol AS 'Nome do Ativo' 08. FROM tb_Quotes AS tq, tb_Symbols AS ts 09. WHERE tq.fk_id = ts.id 10. ORDER BY price DESC;
Code 04
Aus dieser Perspektive betrachtet scheint es, als würden wir die Dinge nur komplizierter machen, doch in Wirklichkeit sorgen wir dafür, dass sie sicherer werden. Beachten Sie, dass wir in Zeile 01 von Code 04 SQL anweisen, etwas aus der Tabelle „tb_Quotes“ zu löschen. In Zeile 02 legen wir fest, was gelöscht werden soll. Bitte beachten Sie, dass dies wie folgt zu verstehen ist: SQL muss in der Tabelle tb_Quotes alle Datensätze für das Symbol PETR4 löschen und anschließend den entsprechenden Eintrag in tb_Symbols entfernen. Aus dieser Perspektive erscheint es übertrieben und es sieht so aus, als würde nichts dabei herauskommen, aber schauen Sie sich das Ergebnis an, wenn wir die Tabelle „tb_Quotes“ abfragen.

Abbildung 09
„Wow. Ist alles so gelaufen, wie erwartet?“ Versuchen wir nun, BBDC4 hinzuzufügen, und schauen wir uns gleich danach an, was sich in der Datenbank befindet. Dies ist in der folgenden Abbildung zu sehen.

Abbildung 10
Es ist leicht zu erkennen, dass der fk_id-Wert im markierten Bereich von Abbildung 10 nicht mit der ID von BBDC4 übereinstimmt. Die Analyse solcher Daten in einer großen Datenbank kann jedoch ziemlich verwirrend sein. Es geht jedoch nicht darum, sich auf die Abfrage selbst zu konzentrieren, sondern zu verstehen, was sich in der Datenbank befindet. Wenn wir eine relationale Abfrage ausführen – was wir in der Praxis tun –, sieht das Ergebnis wie folgt aus:

Abbildung 11
Anscheinend wird hier nichts angezeigt, was eigentlich das Ziel wäre. Der blaue Bereich zeigt das Ergebnis der Ausführung der SQL-Abfrage. Im GRÜNEN Bereich sehen wir den Befehl, der ausgeführt wurde. SQL hat also keinen Verweis auf die ID von BBDC4 an der Stelle der früheren PETR4-ID gefunden. Mit anderen Worten: Die Datenbank bleibt konsistent.
Gut, das ist erfreulich, aber lass uns jetzt über etwas Interessanteres reden. Dazu wenden wir uns nun einem neuen Thema zu.
Verwendung von Triggern
Was wir gleich sehen werden, sollte nur angewendet werden, wenn wir genau wissen, was wir tun. Versuchen Sie nicht, es zu verwenden, ohne sich zuvor mit dem Stoff aus den vorangegangenen Abschnitten vertraut gemacht zu haben. Sonst geraten Sie bei SQL in eine schreckliche Lage. Lassen Sie uns also nun verstehen, worum es hier geht. Nehmen wir an, es gibt eine Datenbank, deren Tabellenstruktur sehr komplex ist. Und diese Datenbank nutzt eine ganz bestimmte Beziehung zwischen diesen Tabellen. Wie bereits im vorigen Abschnitt gezeigt wurde: Wenn wir versuchen, einen Datensatz anhand seines Primärschlüssels zu löschen, auf den eine andere Tabelle über einen Fremdschlüssel verweist, ist dies erst möglich, wenn wir zuvor alle vorhandenen Verweise auf diesen Primärschlüssel gelöscht haben.
Wie diese Aufgabe zu lösen ist, wurde im vorigen Abschnitt beschrieben. Wenn wir nur wenige Tabellen haben, ist so etwas recht einfach und übersichtlich, aber bei vielen Tabellen wird die Sache komplizierter, da für jede Tabelle ein eigener DELETE-Befehl erstellt werden muss.
Der Sinn eines Triggers besteht darin, solche Aufgaben zu vereinfachen, sodass statt mehrerer DELETE-Befehle nur ein einziger verwendet wird. Das klingt einfach, und es ist tatsächlich leicht zu bewerkstelligen, liebe Leser, aber es gibt ein Problem. Jede SQL-Implementierung behandelt Trigger auf unterschiedliche Weise. Beispielsweise kann SQL-Code, der für die Verwendung mit SQLite vorgesehen ist, einen anderen Trigger-Mechanismus haben als vergleichbarer Code in SQL Server oder sogar MySQL. Obwohl alles auf SQL basiert, kann die Handhabung von Triggern je nach Implementierung erheblich variieren. Selbst zwei SQLite-Implementierungen können Trigger unterschiedlich handhaben, da SQLite Open Source ist und daher für bestimmte Zwecke angepasst werden kann.
Im Folgenden wird eine Möglichkeit zur Implementierung von Triggern in SQLite gezeigt.

Abbildung 12
Ein ähnliches Diagramm finden Sie unter lang_createtrigger. Dort finden Sie neben dieser Abbildung auch eine kurze Erklärung, wie das Ganze funktioniert. Dieses schematische Modell zu verstehen, ist nicht schwer. Außerdem ist dies eine große Hilfe beim Verständnis der Befehlsfolge, die zum Erstellen eines Triggers erforderlich ist.
„Na gut, aber was soll dieses Gewirr aus Pfeilen und Text eigentlich darstellen? Ich verstehe fast gar nichts.“ Keine Sorge – mit etwas Übung wird das immer leichter. Lassen Sie uns das ohne unnötigen Stress verstehen. Bitte beachten Sie, dass sich oben in der Abbildung ein Kreis befindet. Dort beginnt der Code. Jeder Befehl ist mit einem Kreis markiert, und die Pfeile zeigen an, welcher Befehl als Nächstes folgen soll. Um zu vermeiden, dass dies langweilig und rein theoretisch wird, schauen wir uns an, wie man einen Trigger erstellt. Erinnern Sie sich an Code 03? Um diesem Code einen Trigger hinzuzufügen, müssen wir lediglich etwas Ähnliches wie in Abbildung 12 erstellen. Das Ergebnis ist folgender Code:
01. PRAGMA FOREIGN_KEYS = ON; 02. 03. DROP TABLE IF EXISTS tb_Quotes; 04. DROP TABLE IF EXISTS tb_Symbols; 05. 06. CREATE TABLE IF NOT EXISTS tb_Symbols 07. ( 08. id PRIMARY KEY, 09. symbol NOT NULL UNIQUE 10. ); 11. 12. CREATE TRIGGER tr_DeleteSymbol BEFORE DELETE ON tb_Symbols 13. BEGIN 14. DELETE FROM tb_Quotes WHERE fk_id = OLD.id; 15. END; 16. 17. CREATE TABLE IF NOT EXISTS tb_Quotes 18. ( 19. of_day NOT NULL, 20. price NOT NULL, 21. fk_id NOT NULL, 22. FOREIGN KEY (fk_id) REFERENCES tb_Symbols(id) 23. ); 24. 25. INSERT INTO tb_Symbols (id, symbol) VALUES 26. (2, 'PETR4'), 27. (1, 'ITUB3'), 28. (3, 'VALE3'); 29. 30. INSERT INTO tb_Quotes (of_day, price, fk_id) VALUES 31. ('2023-07-10', '22.00', 1), 32. ('2023-07-11', '22.20', 1), 33. ('2023-07-12', '22.40', 1), 34. ('2023-07-13', '22.30', 1), 35. ('2023-07-14', '22.60', 1), 36. ('2023-07-10', '26.00', 2), 37. ('2023-07-11', '26.20', 2), 38. ('2023-07-12', '26.40', 2), 39. ('2023-07-13', '26.30', 2), 40. ('2023-07-14', '26.60', 2), 41. ('2023-07-10', '62.00', 3), 42. ('2023-07-11', '62.20', 3), 43. ('2023-07-12', '62.40', 3), 44. ('2023-07-13', '62.30', 3), 45. ('2023-07-14', '62.60', 3); 46. 47. SELECT tq.of_day AS 'Data da cotação', 48. tq.price AS 'Preço Atual', 49. ts.symbol AS 'Nome do Ativo' 50. FROM tb_Quotes AS tq, tb_Symbols AS ts 51. WHERE tq.fk_id = ts.id 52. ORDER BY price DESC; 53.
Code 05
Beachten Sie nun den Unterschied zwischen den Codes 03 und 05. Ihnen ist wahrscheinlich aufgefallen, dass der Unterschied genau in dem Trigger liegt, den wir erstellen. Im Prinzip ist alles in Code 05 verständlich, bis auf eine Sache. Woher stammt dieser Wert „OLD“? Und wie funktioniert dieser Trigger eigentlich? Na gut, versuchen wir es zu verstehen. Dieser OLD steht in direktem Zusammenhang mit SQLite. Mit anderen Worten: Es hängt ganz von der jeweiligen SQLite-Implementierung ab. Andere Implementierungen verwenden möglicherweise einen anderen Namen; für genauere Informationen sollten Sie daher die Dokumentation konsultieren.
Aber OLD bedeutet alt. „In diesem Fall handelt es sich um einen Bezeichner. Aber welcher Bezeichner genau? Wir übergeben keine Parameter an den Trigger. Wie können wir auf etwas Bezug nehmen, das wir nicht näher bezeichnen?“ Bleiben Sie ruhig. Wir haben bereits erwähnt, dass dieses ganze Trigger-System etwas verwirrend ist. Daher müssen Sie zunächst verstehen, was wir über die Arbeit mit einfacheren Tabellen und Systemen erklärt haben. Trigger sind der fortgeschrittenste Teil der SQL-Programmierung. Also überstürze nichts.
Um zu verstehen, woher dieser Wert „OLD“ stammt, müssen wir zunächst verstehen, wie der Code funktioniert. Nachdem wir also Code 05 ausgeführt haben, werden wir das PETR4-Symbol aus der Datenbank löschen und durch das BBDC4-Symbol ersetzen. Aber wir werden das anders angehen als je zuvor, gerade weil wir jetzt einen Trigger haben. Um diese Änderung vorzunehmen, verwenden wir den folgenden Code.
1. DELETE FROM tb_Symbols WHERE symbol = 'PETR4'; 2. INSERT INTO tb_Symbols(id, symbol) VALUES(2, 'BBDC4'); 3. 4. SELECT tq.of_day AS 'Data da cotação', 5. tq.price AS 'Preço Atual', 6. ts.symbol AS 'Nome do Ativo' 7. FROM tb_Quotes AS tq, tb_Symbols AS ts 8. WHERE tq.fk_id = ts.id 9. ORDER BY price DESC;
Code 06
Wenn wir also Code 06 ausführen, erhalten wir folgendes Ergebnis:

Abbildung 13
„Was? Warum? Ich weiß, dass Sie mich austricksen. Das ist ein Witz.“ Wie sehr wünschte ich mir, das wäre so, liebe Leser, aber es ist tatsächlich wahr, denn es gibt einen Trigger. Wenn Zeile 01 von Code 06 ausgeführt wird, werden alle Datensätze in der Datenbank gelöscht, die in irgendeiner Weise mit dem Symbol PETR4 in Verbindung stehen. Und da die ID mit dem Wert zwei nun frei ist, können wir die zweite Zeile von Code 06 verwenden, um das BBDC4-Symbol mit diesem ID-Wert hinzuzufügen. Deshalb betone ich noch einmal: Bevor Sie versuchen, Trigger zu verwenden, sollten Sie zunächst versuchen zu verstehen, wie grundlegendes SQL funktioniert. Andernfalls werden Sie von dem Code, der ausgeführt wird, völlig verwirrt sein.
Einfügen von Daten mithilfe von Triggern
Wenn Ihnen also das, was wir im vorherigen Abschnitt gesehen haben, schon etwas schwer verständlich erschien, machen Sie sich bereit, denn jetzt kommt etwas, das wirklich schwer zu verstehen ist, nämlich das Einfügen von Daten mithilfe von Triggern. Bevor wir beginnen, möchten wir Sie, liebe Leserinnen und Leser, noch einmal daran erinnern, dass Wissen nach und nach, Schritt für Schritt, reift. Es ist sehr wichtig, zunächst die Grundlagen zu verstehen, bevor man sich komplexeren Themen zuwendet, für die ausreichende Vorkenntnisse wirklich unerlässlich sind. Versuchen Sie nicht, etwas zu benutzen, bevor Sie ganz verstanden haben, wie alles funktioniert.
Um zu verstehen, wie man Daten mithilfe von Triggern einfügt, sollten wir mit etwas Einfacherem beginnen. Lassen Sie uns einen Schritt zurücktreten und uns zunächst mit einigen grundlegenderen Konzepten befassen. Das dient dazu, dass Sie sich nicht zu sehr in etwas vertiefen, das vielleicht noch nicht richtig erklärt wurde. Wir möchten noch einmal darauf hinweisen, dass sich alles, was wir hier behandeln, auf die Standardimplementierung von SQLite bezieht. Im Zweifelsfall lesen Sie bitte die Dokumentation der von Ihnen verwendeten Implementierung, um zu erfahren, wie Sie die hier vorgestellten Einstellungen konfigurieren können. Beginnen wir also mit dem folgenden Code:
01. PRAGMA FOREIGN_KEYS = ON; 02. 03. DROP TABLE IF EXISTS tb_Quotes; 04. DROP TABLE IF EXISTS tb_Symbols; 05. 06. CREATE TABLE IF NOT EXISTS tb_Symbols 07. ( 08. id PRIMARY KEY, 09. symbol NOT NULL UNIQUE 10. ); 11. 12. CREATE TABLE IF NOT EXISTS tb_Quotes 13. ( 14. of_day NOT NULL, 15. price NOT NULL, 16. fk_id NOT NULL, 17. FOREIGN KEY (fk_id) REFERENCES tb_Symbols(id) 18. );
Code 07
Ich denke, mittlerweile ist allen hier klar, was Code 07 bewirkt und welche Datenbank erstellt wird, sobald wir damit beginnen, Datensätze in die mit Code 07 erstellten Tabellen einzufügen. Aber vielleicht gibt es noch einige Punkte, die im vorigen Thema nicht ausreichend geklärt wurden, nämlich:
Bevor wir die Datenbank erstellen, müssen wir festlegen, welche Trigger sie enthalten soll.
Nach den vorliegenden Informationen könnte man den Eindruck gewinnen, dass Trigger jederzeit zu einer Datenbank hinzugefügt oder sogar daraus entfernt werden können. In Wirklichkeit läuft das aber nicht ganz so ab. Wenn SQL die Datei erstellt, die die Datenbank enthalten wird, muss alles Notwendige ordnungsgemäß definiert sein. In gewisser Weise können wir, sofern dies von der Implementierung nicht bereitgestellt wird, keine prozedurale Logik zu einer bereits erstellten Datenbank hinzufügen oder daraus entfernen. Ein Beispiel für eine Implementierung, die das Hinzufügen oder Entfernen von Elementen ermöglicht, ist SQL Server. Darin können Sie prozedurale Datenbanklogik jederzeit hinzufügen oder entfernen, da Skripte in der Regel nicht an die Datenbank gebunden sind.
Was jedoch Trigger betrifft, wird in der Regel anders vorgegangen. Bevor Sie also wahllos Datenbanken erstellen, sollten Sie versuchen, sich gründlich mit der Materie auseinanderzusetzen. So können Sie spätere Überraschungen vermeiden. Daher besteht der nächste Schritt darin, einen Trigger hinzuzufügen, der beim Einfügen von Datensätzen in die Datenbank ausgelöst wird. Somit wird Code 07 zu Code 08, der im Folgenden vollständig zu sehen ist:
01. PRAGMA FOREIGN_KEYS = ON; 02. 03. DROP TABLE IF EXISTS tb_Quotes; 04. DROP TABLE IF EXISTS tb_Symbols; 05. 06. CREATE TABLE IF NOT EXISTS tb_Symbols 07. ( 08. id PRIMARY KEY, 09. symbol NOT NULL UNIQUE 10. ); 11. 12. CREATE TABLE IF NOT EXISTS tb_Quotes 13. ( 14. of_day NOT NULL, 15. price NOT NULL, 16. fk_id NOT NULL, 17. FOREIGN KEY (fk_id) REFERENCES tb_Symbols(id) 18. ); 19. 20. CREATE TRIGGER IF NOT EXISTS tr_InsertSymbol AFTER INSERT ON tb_Symbols 21. BEGIN 22. UPDATE tb_Symbols SET symbol = upper(NEW.symbol) WHERE NEW.id = id; 23. END;
Code 08
Vergessen Sie nicht, dabei auch Abbildung 12 heranzuziehen, um zu sehen, was hier in Code 08 erstellt wird. Mit anderen Worten: Was Trigger betrifft, so werden in dieser Abbildung die Befehle DELETE, INSERT und UPDATE angezeigt. Noch einmal: Sie müssen verstehen, was in dem Code steht.
Der Grund, warum Code 07 zu Code 08 erweitert wird, ist ganz einfach. Wenn der Tabelle „tb_Symbols“ ein neues Symbol hinzugefügt wird, wandelt dieser Trigger den Datensatz automatisch in Großbuchstaben um, sodass die Buchstaben in Großbuchstaben angezeigt werden. „Das soll wohl ein Scherz sein?“ Nein; in einer Datenbank ist es in der Regel erforderlich, dass die Daten in irgendeiner Form überprüft und gegebenenfalls korrigiert werden.
Was wir in Code 08 tun, mag zwar trivial erscheinen, ist aber in bestimmten Situationen durchaus sinnvoll. Um zu prüfen, ob Code 08 dieses Ziel erreichen kann, verwenden wir den folgenden Code. Wir möchten Sie noch einmal daran erinnern, dass Sie Code 08 ausführen und nach einer längeren Zeit auch Code 09 ausführen können, wobei alles einwandfrei funktionieren sollte.
1. INSERT INTO tb_Symbols (id, symbol) 2. VALUES (1, 'vale3'), 3. (2, 'PetR4'), 4. (3, 'ITUB4'); 5. SELECT * FROM tb_Symbols;
Code 09
Bitte beachten Sie, dass wir Werte sowohl in Groß- als auch in Kleinbuchstaben eingeben. In der Datenbank sollten jedoch alle Datensätze, die mit Code 09 eingefügt werden, in Großbuchstaben geschrieben werden. Dies wird bei der Ausführung von Zeile 05 deutlich, die Folgendes ergibt:

Abbildung 14
Wie durch Zauberei können wir im markierten Bereich beobachten, dass alles in Großbuchstaben umgewandelt wurde. Bevor Sie jedoch annehmen, dass dies auch ohne den Einsatz von Triggern geschieht, sollten wir bedenken, dass SQL Zeichenfolgenwerte weder ändert noch korrigiert. SQL behält sie genauso bei, wie sie ursprünglich geschrieben wurden, es sei denn, wir verwenden einen Trigger, wie den in Code 08 erstellten, dessen Zweck darin besteht, bestimmte Datensätze in der Datenbank zu ändern oder sogar zu aktualisieren.
Nachdem wir nun das Ergebnis gesehen haben, möchten wir verstehen, wie der in Code 08 implementierte Trigger funktioniert. Das liegt daran, dass Code 09 dies nicht erwähnt. Situationen wie diese verunsichern oft viele SQL-Einsteiger. Auf den ersten Blick könnte man meinen, dass der Trigger nicht ausgelöst werden sollte, da wir ihn nicht direkt aufrufen.
Kehren wir nun zu Code 08 zurück, um zu verstehen, wie der Trigger funktioniert. Zeile 20 ist genauso zu lesen, wie sie in Code 08 steht. Versuchen Sie noch nicht, irgendetwas zu interpretieren; lesen Sie einfach, was in Zeile 20 steht. Um Ihnen zu helfen, zeigen wir Ihnen, wie das gemacht wird.
Falls noch nicht vorhanden, erstellen Sie einen Trigger mit dem Namen „tr_InsertSymbol“, der NACH dem Einfügen von Daten in die Tabelle „tb_Symbols“ ausgeführt werden muss.
Aktualisieren Sie nun die Tabelle „tb_Symbols“ so, dass „symbol“ den Wert „upper(NEW.symbol)“ annimmt, wenn NEW.ID gleich ID ist.
Und wieder haben wir hier etwas gesehen, das von SQLite abhängt. Das liegt daran, dass das Schlüsselwort „NEW“ nur in SQLite existiert. Wenn wir über das Löschen von Datensätzen sprechen, verwenden wir das Wort „OLD“. Und nun verwenden wir das Wort „NEW“. Auf den ersten Blick erscheint das ziemlich verwirrend. „Und tatsächlich bin ich verwirrt, lieber Autor. Ich verstehe einfach nicht, wann man ‚NEW‘ und wann man ‚OLD‘ verwenden soll. Der Grund dafür ist, dass wir einen Trigger namens „tr_InsertSymbol“ erstellt haben. Wenn wir also Daten zur Datenbank hinzufügen, aktualisieren wir die Datenbank, wodurch die Werte im Prinzip nicht mehr neu, sondern alt sind. Das ist Wahnsinn!“
Ich weiß, liebe Leser. Tatsächlich gibt es einige Details, die die Situation zunächst etwas verwirrend machen können, aber wenn wir sie anwenden und uns damit beschäftigen, werden sie keine Verwirrung mehr stiften und Sinn ergeben. Dennoch werde ich versuchen, die Situation zu vereinfachen, damit klar wird, wann man NEW und wann man OLD verwenden sollte. Zumindest reicht das aus, um den Leser weniger zu verwirren.
Bei der Ausführung eines DELETE-Befehls sind die zu löschenden Daten in der Datenbank bereits vorhanden und werden gelöscht. Deshalb verwenden wir OLD. Bei einer INSERT-Anweisung sind die Daten in der Datenbank NOCH NICHT VORHANDEN und werden erst angelegt; daher verwenden wir das Wort NEW. Die Verwirrung rührt gerade daher, dass der Trigger in Zeile 20 von Code 08 angibt, dass der Trigger NACH dem Einfügen der Informationen in die Datenbank ausgeführt werden soll. Es ist jedoch zu beachten, dass es sich hierbei um neue Daten handelt, die zuvor nicht in der Datenbank vorhanden waren.
Die Situation wird jedoch etwas komplizierter, wenn der Trigger mit dem UPDATE-Befehl verknüpft ist. In diesem Fall müssen wir etwas weiterdenken. Also passen Sie gut auf. Die Informationen, die bereits in der Datenbank vorhandene Informationen ersetzen sollen, sollten als NEU behandelt werden. Die Informationen, die bereits in der Datenbank vorhanden sind und ersetzt werden sollen, sollten jedoch als OLD behandelt werden. Wenn Sie das verstanden haben, werden Sie mit jedem Trigger-Skript zurechtkommen, das mit einer Standard-SQLite-Datenbank funktioniert. Wir sprechen hier von der Standardversion von SQLite, da eine Implementierung dies möglicherweise anders handhabt. Versuchen Sie in diesem Fall, die Dokumentation durchzulesen.
Den richtigen Ansatz wählen
Nachdem wir nun wissen, wie man mit Triggern arbeitet, um Datensätze unter Wahrung der Datenintegrität in die Datenbank einzufügen, müssen wir einen weiteren, ebenso wichtigen Aspekt berücksichtigen. Das liegt daran, dass die Datenbankintegrität nur teilweise gewährleistet ist. „Aber warum sagen Sie ‚nur teilweise‘?“ Der Grund liegt im UPDATE-Befehl, liebe Leser. Beachten Sie, dass diese Sicherheitsmaßnahme auf INSERT basiert.
Es gibt jedoch ein Problem mit dem UPDATE-Befehl, das wir lösen müssen. Betrachten wir folgendes Beispiel: Was passiert, wenn wir mit dem Befehl UPDATE etwas in der Datenbank ändern wollen? Da SQL nicht weiß, welche Regeln anzuwenden sind, werden alle mit dem UPDATE-Befehl eingegebenen Daten von SQL akzeptiert und ersetzen alle zuvor geprüften Daten. Um dies zu veranschaulichen, sehen Sie sich den folgenden Code an:
1. INSERT INTO tb_Symbols (id, symbol) 2. VALUES (1, 'vale3'), 3. (2, 'PetR4'), 4. (3, 'ITUB4'); 5. 6. UPDATE tb_Symbols SET symbol = 'iTub4' WHERE id = 3; 7. 8. SELECT * FROM tb_Symbols;
Code 10
Code 10 muss in einer mit Code 08 erstellten Datenbank ausgeführt werden. Dies ist erforderlich, um das unten gezeigte Ergebnis zu erhalten:

Abbildung 15
Im markierten Bereich der Datenbank fällt uns etwas ziemlich Seltsames auf. Etwas, das dort nicht stehen sollte, gerade weil alle Werte in Großbuchstaben geschrieben werden sollten. SQL hat die Änderung aus Zeile 06 in Code 10 jedoch übernommen, wodurch die Integrität der Datenbank beeinträchtigt wurde. Beachten Sie, dass alle Maßnahmen vom zu erreichenden Ziel abhängen. Es gibt keine allgemeingültige Formel. Zuerst müssen wir das Problem verstehen, und erst dann nach Lösungen suchen. All dies muss jedoch festgelegt und eingerichtet werden, bevor die Datenbank mit dem Empfang sensibler Daten beginnt.
Nehmen wir also Folgendes an: Sobald ein Datensatz angelegt wurde, kann er nicht mehr geändert werden. Dadurch können wir die Probleme lösen, auf die wir zuvor gestoßen sind. Wenn wir jedoch eine Aktualisierung der Datenbank zulassen, müssen die in einen bestehenden Datensatz eingefügten Daten bestimmten Regeln entsprechen. In diesem Fall müssen wir einen anderen Weg einschlagen, um das Problem mit dem UPDATE-Befehl zu lösen.
Wir können dem Benutzer aber auch die Möglichkeit geben, den Namen des Symbols zu ändern. Dadurch entsteht beim Benutzer jedoch der Eindruck, dass der Name geändert wurde. In der Datenbank werden die Daten jedoch bei der Erstellung eines neuen Datensatzes tatsächlich kopiert. Der alte Datensatz bezog sich auf eine Situation, in der der Benutzer keinen direkten Zugriff auf die Daten hatte. Der Datenbankadministrator kann jedoch alle Daten wiederherstellen, da der ursprüngliche Datensatz unverändert bleibt. Dieser Ansatz führt uns zu einer weiteren Lösung, die ebenfalls umgesetzt werden muss. Mit anderen Worten: Jeder Fall ist ein Einzelfall. Es gibt keine Lösung, die für alle Fälle passt.
Um die Situation besser zu veranschaulichen, wollen wir uns nun ansehen, wie der UPDATE-Befehl gehandhabt wird, wenn er es dem Benutzer ermöglicht, den Symbolnamen zu ändern, wobei der Name letztendlich denselben Regeln unterliegt wie bei der Ausführung des INSERT-Befehls. Dazu könnten wir eine Abwandlung des unten gezeigten Originalskripts verwenden:
01. PRAGMA FOREIGN_KEYS = ON; 02. 03. DROP TABLE IF EXISTS tb_Quotes; 04. DROP TABLE IF EXISTS tb_Symbols; 05. 06. CREATE TABLE IF NOT EXISTS tb_Symbols 07. ( 08. id PRIMARY KEY, 09. symbol NOT NULL UNIQUE 10. ); 11. 12. CREATE TABLE IF NOT EXISTS tb_Quotes 13. ( 14. of_day NOT NULL, 15. price NOT NULL, 16. fk_id NOT NULL, 17. FOREIGN KEY (fk_id) REFERENCES tb_Symbols(id) 18. ); 19. 20. CREATE TRIGGER IF NOT EXISTS tr_InsertSymbol AFTER INSERT ON tb_Symbols 21. BEGIN 22. UPDATE tb_Symbols SET symbol = NEW.symbol WHERE NEW.id = id; 23. END; 24. 25. CREATE TRIGGER IF NOT EXISTS tr_UpdateSymbol AFTER UPDATE ON tb_Symbols 26. BEGIN 27. UPDATE tb_Symbols SET symbol = upper(NEW.symbol) WHERE NEW.id = id; 28. END;
Code 11
„Aber warte mal kurz. Jetzt machen Sie doch sicher nur Spaß. Wie kommt es, dass der Trigger-Code für den UPDATE-Befehl fast eine exakte Kopie des Triggers für den INSERT-Befehl ist? Sind Sie sicher, dass das klappt? Ich habe Bedenken, denn wenn wir eine INSERT-Anweisung ausführen, wird der Trigger in Zeile 20 ausgelöst. Da wir jedoch in Zeile 22 den Befehl UPDATE verwenden, gehe ich davon aus, dass auch der in Zeile 25 implementierte Trigger ausgelöst wird. Und wenn der Befehl in Zeile 27 ausgeführt wird, gerät SQL in eine Schleife. Ehrlich gesagt glaube ich nicht, dass das funktionieren wird.“
Nun, wenn Sie diese Ausführungssequenz wirklich genau verfolgt haben, liebe Leser, bedeutet das, dass Sie immer noch nicht verstanden habt, wie SQL mit Triggern funktioniert. Aber das ist in Ordnung. Wenn wir nun mit Code 11 eine neue Datenbank erstellen und dann sofort versuchen, Code 10 erneut in dieser neuen Datenbank auszuführen, sehen Sie, was passiert:

Abbildung 16
Lassen Sie uns nun Folgendes verstehen: Wenn der Benutzer das Einfügen eines Datensatzes in die Tabelle „tb_Symbols“ anfordert, fügt SQL den Datensatz möglicherweise ein – oder auch nicht. Wenn es eingefügt wird, führt SQL den Trigger in Zeile 20 von Code 11 aus. Dies führt wiederum dazu, dass Zeile 22 ausgeführt wird, wodurch die in der Datenbank enthaltenen Daten aktualisiert werden. Zeile 22 bewirkt jedoch, dass SQL den Trigger in Zeile 25 ausführt. Bis hierhin hatten Sie recht; nach der Ausführung von Zeile 27 werden wir SQL erneut anweisen, die Datenbank zu aktualisieren.
Diesmal passen wir den Inhalt des Datensatzes jedoch gezielt an ein bestimmtes Muster an, das genau dem entspricht, was zum Zeitpunkt der Erstellung des Datensatzes mit dem Befehl INSERT vorhanden war. Damit haben wir sowohl den INSERT-Befehl als auch den UPDATE-Befehl behandelt. Auch wenn das nicht die beste Art ist, es zu erklären, war es doch die einfachste Möglichkeit, die ich gefunden habe, um zu verdeutlichen, wie wir das machen können.
Und zum Schluss möchte ich noch zeigen, dass es eine relativ einfache Möglichkeit gibt, zu verhindern, dass bestimmte Datensätze durch den UPDATE-Befehl geändert werden: Man muss beim Anlegen der Datenbank einfach den folgenden Code verwenden.
01. PRAGMA FOREIGN_KEYS = ON; 02. 03. DROP TABLE IF EXISTS tb_Quotes; 04. DROP TABLE IF EXISTS tb_Symbols; 05. 06. CREATE TABLE IF NOT EXISTS tb_Symbols 07. ( 08. id PRIMARY KEY, 09. symbol NOT NULL UNIQUE 10. ); 11. 12. CREATE TABLE IF NOT EXISTS tb_Quotes 13. ( 14. of_day NOT NULL, 15. price NOT NULL, 16. fk_id NOT NULL, 17. FOREIGN KEY (fk_id) REFERENCES tb_Symbols(id) 18. ); 19. 20. CREATE TRIGGER IF NOT EXISTS tr_InsertSymbol AFTER INSERT ON tb_Symbols 21. BEGIN 22. UPDATE tb_Symbols SET symbol = NEW.symbol WHERE NEW.id = id; 23. END; 24. 25. CREATE TRIGGER IF NOT EXISTS tr_UpdateSymbol AFTER UPDATE ON tb_Symbols 26. BEGIN 27. UPDATE tb_Symbols SET symbol = upper(NEW.symbol) WHERE NEW.id = id; 28. END; 29. 30. CREATE TRIGGER IF NOT EXISTS tr_UpdateSymbol_Before BEFORE UPDATE ON tb_Symbols 31. WHEN upper(NEW.symbol) != upper(OLD.symbol) 32. BEGIN 33. SELECT RAISE(ABORT, 'ERROR: Unable to update this record...'); 34. END;
Code 12
Hier ist alles ein bisschen anders. Wenn wir jedoch Code 10 erneut auf eine mit Code 12 erstellte Datenbank anwenden, erhalten wir folgendes Ergebnis:

Abbildung 17
Nun möchte ich, dass Sie sich Abbildung 17 genau ansehen und versuchen zu verstehen, was genau Code 12 erzeugt hat, sodass wir bei der Ausführung von Code 10 dieses Ergebnis erhalten haben. Beachten Sie, dass Zeile 01 von Code 10 erfolgreich ausgeführt wird und dadurch Datensätze in der Datenbank angelegt werden. Wenn jedoch Zeile 06 ausgeführt wird, tritt ein Fehler auf, wie in Abbildung 17 zu sehen ist. An dieser Stelle möchte ich Sie bitten, sich die Daten in Abbildung 17 anzusehen. Schauen Sie sich Zeile 03 der Meldung an. Gehen Sie nun zurück zu Code 12, und Sie werden sehen, dass es sich um genau dieselbe Meldung wie in Zeile 33 handelt. Wie ist das möglich?
Wenn wir versuchen, Daten in die Tabelle einzufügen, wird der Trigger in Zeile 20 unmittelbar nach dem Einfügen des Datensatzes ausgeführt. Dadurch wird in Zeile 22 ein Befehl zur Aktualisierung des Datensatzes ausgegeben. Gut, aber vor der Aktualisierung wird der Trigger in Zeile 30 ausgeführt, und erst danach wird der Trigger in Zeile 25 ausgeführt. Bitte beachten Sie, dass wir die Funktionsweise von Code 11 geändert haben. Genau dafür ist die Überprüfung in Zeile 31 gedacht. Beachten Sie, dass diese Überprüfung feststellt, ob sich der Datensatz, den wir aktualisieren möchten, von dem bereits in der Datenbank vorhandenen unterscheidet oder mit diesem übereinstimmt. Durch diese Überprüfung stellen wir sicher, dass der Datensatz nicht geändert wird. Das liegt daran, dass bei einer Abweichung im Datensatz der Trigger ausgelöst wird, wodurch Zeile 33 ausgeführt wird. Wenn SQL Zeile 33 ausführt, wird an denjenigen, der die Aktualisierung angefordert hat, eine Meldung gesendet, in der erklärt wird, dass der Vorgang aus irgendeinem Grund nicht ausgeführt werden kann.
In manchen Fällen kann diese Art der Schema-Gestaltung nützlich sein. Nehmen Sie sich die Zeit, sich damit auseinanderzusetzen, denn diese Informationen könnten Ihnen später viel Ärger ersparen.
Abschließende Gedanken
Was ich hier vorgestellt habe, ist nur die Grundlage für viele weitere SQL-Konzepte, die Sie noch lernen müssen. Ich weiß, dass viele Leute die Stirn runzeln, wenn von SQL die Rede ist, und sagen, es sei sinnlos, sich mit SQL zu beschäftigen und es zu verstehen, dass es viel einfacher sei, selbst Dateien anzulegen, um Daten zu speichern, die man auch in einer Datenbank ablegen könnte, und so weiter. Ich möchte Sie jedoch daran erinnern, dass SQL eines der benutzerfreundlichsten Werkzeuge ist und Ihnen bei Aufgaben helfen kann, die sonst viel mehr Aufwand erfordern würden.
Viele Aufgaben, die ich bei anderen mit Python oder sogar Excel erledigt sehe, ließen sich viel einfacher lösen, wenn man SQL verwenden würde. Da SQL jedoch etwas ist, das Lernen und Aufmerksamkeit erfordert, ziehen es die meisten Menschen vor, sich den Kopf zu zerbrechen, wenn sie versuchen, Daten und Informationen mithilfe klassischer Programmierung zu kombinieren. Aber ich versichere Ihnen: SQL zu lernen lohnt sich wirklich. Vor allem, wenn man beginnt zu verstehen, wie Daten miteinander in Beziehung stehen können.
Damit schließen wir unsere Erörterung zum Thema SQL ab. Und Sie müssen den hier begonnenen Lernprozess fortsetzen. Im nächsten Artikel werden wir sehen, wie das Gezeigte in der Praxis angewendet wird, wenn SQL mit MQL5 kombiniert wird. Sie werden feststellen, dass wir bisher nur an der Oberfläche dessen gekratzt haben, was wir in MetaTrader 5 tatsächlich alles tun können.| Datei | Beschreibung |
|---|---|
| Experts\Expert Advisor.mq5 | Zeigt die Interaktion zwischen Chart Trade und dem Expert Advisor (für die Interaktion ist „Mouse Study“ erforderlich). |
| Indicators\Chart Trade.mq5 | Erstellt ein Fenster zur Konfiguration des zu versendenden Auftrags (für die Interaktion ist „Mouse Study“ erforderlich). |
| Indicators\Market Replay.mq5 | Erstellt Steuerelemente für die Interaktion mit dem Markt-Replay-/Simulationsdienst (für die Interaktion ist „Mouse Study“ erforderlich). |
| Indicators\Mouse Study.mq5 | Ermöglicht die Interaktion zwischen grafischen Bedienelementen und dem Benutzer (sowohl für das Markt-Replay-System als auch für den realen Markt erforderlich). |
| Services\Market Replay.mq5 | Erstellt und verwaltet den Markt-Replay-/Simulationsdienst (die Hauptdatei des gesamten Systems). |
| Server.cpp (VS C++) | Erstellt und verwaltet einen in C++ entwickelten Socket-Server (Mini-Chat-Version). |
| Code in Python\Server.py | Stellt eine Socket-Verbindung in Python zwischen MetaTrader 5 und Excel her und hält diese aufrecht. |
| Indicators\Mini Chat.mq5 | Implementiert einen Mini-Chat über einen Indikator (der Betrieb setzt einen laufenden Server voraus). |
| Experts\Mini Chat.mq5 | Implementiert einen Mini-Chat in einem Expert Advisor (für den Betrieb ist die Nutzung des Servers erforderlich). |
| Scripts\SQLite.mq5 | Veranschaulicht die Ausführung eines SQL-Skripts über MQL5. |
| Files\Script 01.sql | Demonstriert die Erstellung einer einfachen Tabelle mit einem Fremdschlüssel. |
| Files\Script 02.sql | Zeigt, wie man Werte in eine Tabelle einträgt. |
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/13059
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
Analyse der Bilanzdaten von Zentralbanken zur Einschätzung der globalen Liquidität
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Biogeografisch basierte Optimierung (BBO)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.