preview
Simulação de mercado (Parte 24): Iniciando o SQL (VII)

Simulação de mercado (Parte 24): Iniciando o SQL (VII)

MetaTrader 5Testador |
94 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Simulação de mercado (Parte 23): Iniciando o SQL (VI), foi explicado uma série de pequenas coisas das quais você precisa entender sobre SQL. Isto para que você consiga lidar minimamente com bancos de dados. Pois bem, como temos muita a falar ainda, e o assunto será bem denso, não vamos perder tempo com introduções. Vamos direto ao que interessa.


Entendendo o problema

Bancos de dados nada mais são que um conjunto de registros em um arquivo. Ok, mas para entender o que eu pretendo explicar aqui. Vamos usar o seguinte código:

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;

Código 01

Este código 01, não deve ser novidade para você. Isto por que tudo que está sendo usado nele foi explicado nos artigos anteriores. Assim ao executar o mesmo, teremos como resposta o que é visto na imagem abaixo.

Imagem 01

Ok, esta imagem mostra o que era esperado ser visto. Pelo simples fato de termos criando três ativos, e colocando cinco dados de cotação em cada um. Totalizando assim 15 registros. Mas agora vamos ao problema. E este surge, principalmente quando você tenta remover algo do banco. Mas por que, começaremos a ter problemas a remover registros do banco? Não faz muito sentido. De fato, não faz sentido a princípio. Porém, existe uma questão que acontece quando usamos bancos, ou tabelas relacionadas. E que não acontece quando usamos um outro esquema de montagem do banco. Um destes problemas pode ser visto ao se executar o que é visto logo abaixo.

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;

Código 02

Você pode pensar: Bem, mas o que tem de errado, em se fazer isto? Meu objetivo era mesmo apagar todos os registros de PETR4. De fato, meu caro leitor, ao executar a linha um, o SQL irá remover os registros sobre PETR4. Tanto que, quando o comando SELECT da linha 3 é executado temos o seguinte resultado:

Imagem 02

Esta imagem prova que o comando da linha 1 funcionou. Mas você sabe o que de fato o comando da linha um fez? E mais, você realmente sabe o que ocorreu dentro do banco de dados? Muitos diriam: Mas é claro que eu sei o que aconteceu. Pois bem, meu caro leitor. Então quero que você me responda o seguinte: O código 01 está ou não criando um banco de dados relacional? Se sim, onde está acontecendo esta relação? Se a resposta for não, como podemos garantir esta relação? Eu entendo que muitos, acham que sabe lidar com bancos de dados, apenas por conseguir escrever alguns comandos em SQL. Mas a coisa não é tão simples como parece. E também não é tão complicada como muitos querem fazer parecer. De qualquer forma, quero que você entenda uma coisa:

O código 01 NÃO CRIA UM BANCO DE DADOS RELACIONAL.

Mas é claro que cria. Estou vendo isto quando estou declarando uma chave estrangeira dentro da tabela. E a usando para referenciar dados em outra tabela. Como assim o banco não é relacional? Enlouqueceu de vez? Não, meu caro leitor. Vou provar a você que o banco não é relacional. Veja a imagem abaixo.

Imagem 03

Apesar de não vermos todas as 30 combinações possíveis, você pode notar que não temos nenhuma referência ao ativo PETR4. Porém, temos o fk_id no valor 2 aparecendo, mas no id dos símbolos tal valor não aparece. Isto torna o banco de dados mais propício a ter falhas de consistência ao longo do tempo. E o motivo será explicado daqui a pouco. Mas agora vamos entender o que o comando DELETE de fato fez no banco de dados. Por conta que o banco não é relacional, ambas tabelas são independentes uma da outra. Contendo apenas uma simples ligação para referenciamento de dados.

Este esquema tem lá as suas vantagens, apesar de não ser uma garantia de que estamos de fato criando um banco relacional. E agora vamos entender o real motivo. Como sabemos o id 2 está vago. Isto pelo simples fato de olharmos a tabela tb_Symbols, como você pode ver abaixo.

Imagem 04

Sabendo disto, você decide colocar um outro símbolo neste id vago. Por exemplo BBDC4. E isto é feito usando o que é mostrado abaixo.

Imagem 05

Pergunta: Existe algo de errado em se fazer isto? Bem, a princípio não. Já que o id 2 estava de fato vago. Mas agora vem o problema. Quando você começar a adicionar dados na tabela tb_Quotes, você terá valores não validos dentro do seu banco. E é bem provável que você somente venha a perceber isto depois de um bom tempo colocando dados no banco. E quanto mais o tempo passar, menor será a chance de você perceber que algo está errado. Mas vamos supor, que por algum motivo, que só DEUS saberia explicar, você assim que adicionou BBDC4 na tabela tb_Symbols, decida fazer uma pesquisa no banco, como mostrado na imagem abaixo.

Imagem 06

Parou geral. Como assim? Acabei de adicionar BBDC4 no banco, e ela já contém informações? Sim, meu caro leitor. E este é o perigo em se fazer as coisas imaginando saber o que está sendo feito. Fazer as coisas na inocência, ou confiança de que já sabe, acaba gerando este tipo de problema, que muitas vezes só aparece depois de muito tempo. Aí você não tem como validar o que está presente no banco de dados. Para resolver isto, precisaríamos mudar a forma como o banco está sendo construído. Fazendo de fato que exista uma relação entre uma chave primária e uma chave estrangeira. Assim aquele mesmo código 01, deveria ser feito como mostrado logo abaixo.

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;

Código 03

Note que a única diferença entre os scripts é a linha 17. Justamente ali, estamos garantindo que as tabelas irão de fato estar relacionadas entre si. Hum, mas se a solução é algo tão simples assim, por que não criar as coisas como está sendo feito neste código 03? Bem, meu amigo leitor, o problema é que ao criamos as coisas como você vê neste código 03, acabamos tornando mais difícil deletar coisas no banco de dados. Para separar os assuntos, vamos a um novo tópico.


Deletando as coisas em tabelas correlacionadas

Para de fato entender, por que boa parte prefere usar o código 01 ao código 03 para criar os bancos de dados. Vamos tentar fazer a mesma coisa feita antes. Ou seja, vamos remover o ativo PETR4 e no lugar dele colocar o ativo BBDC4. Então você poderia tentar fazer usando o que é visto abaixo.

Imagem 07

Note que nesta imagem 07, o ponto destacado está tentando remover PETR4 da tabela tb_Symbols. Mas o SQL nos informa que isto não será possível de ser feito. Isto por que, agora o banco foi criado usando o código 03. Muitos acabam ficando irritados com o SQL, e procuram uma solução. Sendo que a mais fácil é justamente usar o código 01. O que em algum momento vai dar problema. Muito bem, o que está sendo mostrado como resposta do SQL ao requerimento, como você pode ver na imagem 07, está longe de ser uma falha. Isto indica que de fato existe uma relação entre os registros da tabela tb_Symbols e tb_Quotes, que impede de algo ser removido de forma arbitrária.

Para saber onde a relação acontece, e em muitos casos você precisa entender isto. Você deverá recorrer ao diagrama do banco de dados. Coisa que foi vista no artigo anterior. Mas como aqui temos apenas duas tabelas, isto fica fácil de ser compreendido. Sendo assim podemos ir direto ao ponto.

Imagem 08

E para que você não fique na dúvida, o código visto nesta imagem 08 é mostrado logo abaixo.

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;

Código 04

Olhando assim, parece que estamos complicando as coisas. Mas na verdade, estamos é as deixando mais seguras. Veja que na linha um deste código 04, estamos dizendo ao SQL para apagar algo da tabela tb_Quotes. Já na linha dois dizemos o que é esta coisa a ser apagada. Note que a forma de interpretar isto é: SQL, vá a tabela tb_Quotes e apague todos os registros do ativo PETR4, e depois vá a tabela tb_Symbols e apague o registro sobre o ativo PETR4. Olhando assim, parece ser sacanagem, e que isto não irá funcionar. Mas veja o resultado quando executamos uma pesquisa na tabela tb_Quotes.

Imagem 09

Opá. Será mesmo que tudo ocorreu como esperado? Vamos tentar agora adicionar BBDC4 e logo depois olhar o que existe no banco de dados. Isto pode ser visto na imagem abaixo.

Imagem 10

Você pode facilmente notar que na área demarcada na imagem 10, não temos o fk_id igual ao id de BBDC4. Mas fazer uma leitura assim, em um banco grande, pode ser bem confuso. Mas a ideia não é de fato focar na pesquisa em si. Mas sim tentar entender o que existe dentro do banco de dados. Mas se fizermos uma pesquisa relacional, que de fato seria o que faríamos na prática, o resultado seria o que é visto na imagem abaixo.

Imagem 11

Ok, aparentemente não temos nada sendo mostrado aqui. Mas este seria de fato o objetivo. Na área em AZUL temos o que o SQL irá colocar como resultado da pesquisa. Já a área VERDE, podemos visualizar o comando que foi executado. Então o SQL não encontrou nenhuma ocorrência do id de BBDC4 no que era o antigo id de PETR4. Ou seja, o banco de dados está integro.

Muito bem, isto foi divertido, mas agora vamos falar de um tema ainda mais divertido. Mas vamos fazer isto em um novo tópico.


Usando Gatilhos

O que iremos ver agora, somente deverá ser utilizado se você tiver certeza do que está fazendo. Não tente usar isto, antes de entender o que foi visto nos tópicos anteriores. Ou você acabará em uma enroscada sem tamanho dentro do SQL. Ok, dado o aviso, vamos ver do que se trata. Suponhamos que exista um banco muito complexo em termos de estrutura de tabelas. E este banco está fazendo uso de uma relação muito específica entre estas tabelas. Como você viu no tópico anterior, se você tentar remover uma registro com base na sua chave primária, e esta esteja sendo referenciada em outra tabela, por meio da chave estrangeira. Você não conseguirá remover o registro. A menos que você primeiro remova todas a referências que existir a esta chave primária.

A forma de se fazer isto, foi explicada no tópico anterior. Quando temos poucas tabelas fazer tal coisa é bem simples e direto. Mas se tivermos muitas tabelas, a coisa começa a ficar complicada. Já que para cada tabela, você precisará de um comando DELETE sendo construído.

A ideia do gatilho é simplesmente o de simplificar este tipo de tarefa. Onde ao invés de usar vários comandos DELETE, você passará a usar apenas um. Parece estranho, ou mesmo fácil fazer isto. E é realmente fácil de se fazer isto, meu caro leitor. Mas existe um problema. Cada implementação do SQL lida de maneira diferente com gatilhos. Por exemplo, um código SQL visando usar o SQLite, pode ter um mecanismo diferente de gatilho que um código similar em SQL Server ou mesmo MySQL. Apesar de tudo ser SQL, a forma de tratar gatilhos pode ser bem diferente, em diferentes implementações. Até mesmo duas implementações SQLite podem lidar de forma diferente com os gatilhos, visto que o SQLite é de código aberto, podendo assim ser modificado para um determinado objetivo.

Uma forma de implementar gatilhos no SQLite, pode ser visto no esquema logo abaixo.

Imagem 12

Este mesmo esquema pode ser visto em lang_createtrigger. Lá além deste esquema, você poderá ver uma breve explicação de como o mesmo funciona. Entender este modelo de esquema é simples. E o mesmo nos ajuda bastante a entender a sequência de comandos que precisamos para criar um gatilho.

Legal, mas o que toda esta complicação de setas e texto está tentando nos dizer? Já que não estou conseguindo entender quase nada. Calma meu caro leitor, com o tempo você se acostuma. Mas vamos entender isto sem stress. Note que no topo da imagem temos uma bolinha. Ali começa o código. Cada comando está sendo circulado e temos as setas indicando o possível próximo comando que deverá ser dado. Para não ficar algo chato e só na teoria. Vamos ver um gatilho sendo criado. Lembra do código 03? Pois bem, para adicionarmos um gatilho aquele código, tudo que precisamos fazer é criar algo parecido com isto que é visto na imagem 12. O resultado é o código visto logo abaixo.

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. 

Código 05

Agora veja qual é a diferença entre o código 03 e este código 05. Você irá notar que a diferença, é exatamente o gatilho que estamos construindo. Basicamente você irá conseguir entender tudo que está sendo colocado neste código 05. Exceto por uma coisa. De onde veio este tal valor OLD? E como este gatilho de fato funciona? Bem, uma coisa de cada vez. Este tal de OLD, é algo que está diretamente ligado ao SQLite. Ou seja, é algo totalmente dependente da implementação do SQLite. Outras implementações podem usar um outro nome, sendo assim necessário consultar a documentação para mais detalhes.

Mas o OLD quer dizer antigo. Isto no caso da id. Mas qual id? Não estamos repassando nenhum parâmetro para dentro do gatilho. Como podemos estar referenciando algo que não estamos informando? Calma. Eu disse que isto de gatilho é um tanto quanto confuso. Por isto você precisa entender antes o que expliquei sobre como trabalhar com tabelas e sistemas mais simples. Gatilhos seria o creme de lá creme da programação SQL. Por isto não tenha pressa, vá com calma.

Para entender de onde vem este valor OLD, temos que entender como o código funciona. Então depois de executar este código 05. Vamos remover o ativo PETR4 do banco e no lugar dele colocar o ativo BBDC4. Mas faremos isto de uma maneira diferente, do que já foi visto. Justamente por conta de que agora temos um gatilho presente. Para fazer esta mudança, usamos o código visto logo abaixo.

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;

Código 06

Assim ao executarmos o código 06, teremos como resultado o que é visto logo abaixo.

Imagem 13

Oque? Como assim? Já sei, você está me enganando. Isto é uma pegadinha. Até queria que fosse este o caso, meu caro leitor. Mas é isto mesmo, por conta da existência do gatilho. Quando a linha um deste código 06 for executada, todo o registro que esteja de alguma forma ligado ao ativo PETR4 será removido do banco de dados. E como agora a id com valor igual a dois está livre. Podemos usando a segunda linha do código 06, adicionar o ativo BBDC4 com este valor de id. Por isto que volto a reforçar, antes de tentar usar gatilhos, procure entender primeiro como o SQL mais básico funciona. Caso contrário, você irá ficar completamente perdido em meio ao código que estará sendo executado.


Inserindo dados com gatilhos

Bem, se você achou o que foi visto no tópico anterior, algo complicado de entender, então prepare-se. Pois agora iremos ver algo realmente complicado de entender, que é justamente inserir dados utilizando para isto gatilhos. Antes de começarmos, volto a lembrar a você, meu amigo leitor, que o conhecimento vai amadurecendo aos poucos. É muito importante, que você procure primeiro entender as base iniciais, antes de começar a tentar se aventurar em questões mais complexas. Onde realmente se necessita ter um conhecimento prévio adequado para se entender o que está acontecendo. Não tente utilizar algo antes de realmente entender como as coisas funcionam.

Para entender como inserir dados utilizando para isto gatilhos. Precisamos começar com algo mais simples. Então vamos dar alguns passos para trás e rever alguns conceitos mais básicos primeiramente. Isto para evitar que você venha a mergulhar em algo que talvez não tenha sido devidamente explicado anteriormente. Lembrando mais uma vez, o que iremos ver aqui se aplica a uma implementação padrão do SQLite. Na dúvida procure na documentação da implementação que você está tentando fazer uso, como estabelecer o funcionamento do que será visto aqui. Assim vamos iniciar pelo código mostrado logo abaixo.

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. );

Código 07

Acredito que nesta altura do campeonato, todos aqui consigam entender o que este código 07 está fazendo. E que tipo de banco de dado estará sendo construído quando começarmos a inserir registros dentro das tabelas que estão sendo criadas por este código 07. Mas talvez uma outra coisa, não tenha ficado devidamente esclarecida no tópico anterior, que é justamente o fato de que:

ANTES DE CRIAR O BANCO DE DADOS, precisamos estabelecer os gatilhos que ele irá conter.

Da maneira como as coisas vem sendo explicadas, talvez você tenha tido a impressão de que você pode adicionar, ou até mesmo remover, gatilhos a qualquer momento em um banco de dados. Na verdade, não é bem assim que as coisas funcionam. No momento em que o arquivo que irá conter o banco de dados é criado pelo SQL. Tudo e qualquer coisa que precisaremos deverá estar devidamente definido. De certa forma, salvo o fato da implementação permitir, você não conseguirá adicionar ou remover procedimentos de trabalho do banco de dados já criado. Um exemplo de implementação que permite adicionar ou remover coisas é o SQL Server. Nele, você pode adicionar ou remover métodos de tratamento a qualquer momento. Já que normalmente os scripts não irão se ligar ao banco de dados.

Mas em se tratando de gatilhos, isto normalmente não é tratado desta maneira. Então procure estudar as coisas antes de sair por aí, criando bancos de dados a esmo. Isto para evitar surpresas no futuro. Sendo assim, a próxima coisa a ser feita, será adicionar um gatilho que irá trabalhar durante o processo de inserir registros no banco de dados. Desta forma o código 07 irá se transformar no código 08, visto logo abaixo na íntegra.

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;

Código 08

Agora, não se esqueça de olhar na imagem 12 o que está sendo criado aqui no código 08. Ou seja, quando o assunto é gatilho, aquela mesma imagem, cobre os comandos DELETE, INSERT e UPDATE. Lembrando mais uma vez que você precisa entender o que está sendo colocado no código.

O objetivo desta atualização feita no código 07, para gerar o código 08, é muito simples. Sempre que inserirmos algum novo ativo na tabela tb_Symbols. Este gatilho que estamos criando, irá atualizar automaticamente o registro para que as letras sejam colocadas em maiúsculo. Cara, você está de brincadeira? Não meu caro leitor. Normalmente em um banco de dados, precisamos que as informações possam ser checadas de alguma forma, e quando necessário, ajustadas de uma certa maneira.

Isto que estamos fazendo no código 08, apesar de parecer banal, faz todo sentido em certos cenários. Para testar se este código 08, consegue ou não cumprir este objetivo, vamos usar o código logo abaixo. Lembrando mais uma vez, de que você pode executar o código 08, e bem depois executar o código 09. Que ainda assim tudo deverá funcionar perfeitamente bem.

1. INSERT INTO tb_Symbols (id, symbol)
2.                 VALUES (1, 'vale3'),
3.                        (2, 'PetR4'),
4.                        (3, 'ITUB4');
5. SELECT * FROM tb_Symbols;

Código 09

Note que estamos adicionando tanto informações já em maiúsculo, como em minúsculo. Mas dentro do banco de dados, estes registros que o código 09 estará adicionando deverão estar todos em maiúsculo. Isto pode ser visto, quando a linha cinco é executada e nos é gerado o seguinte resultado visto abaixo.

Imagem 14

Como se fosse mágica, podemos observar na região demarcada, que de fato os valores foram convertidos. Mas antes que você ache que isto acontece, mesmo sem fazer uso de gatilhos. Devo lembra que o SQL não muda ou ajusta valores do tipo string. Da mesma forma como eles foram escritos originalmente, o SQL os irá manter. Salvo o fato de você utilizar um gatilho, como o que foi feito no código 08, cuja finalidade é modificar, ou até mesmo atualizar algum registro dentro do banco de dados.

Agora que você já viu o resultado. Deve estar um tanto curioso para entender como o gatilho implementando no código 08 funciona. Isto devido justamente ao fato de que não existe nenhuma menção dele no código 09. E este tipo de coisa, não raramente deixa muito iniciante em SQL com a pulga atras da orelha. Já que a princípio, o gatilho não deveria ser executado. Já que não o estamos fazendo nenhuma referência a ele diretamente.

Ok, então vamos voltar nossa atenção ao código 08 para entender como o gatilho funciona. A linha vinte, deverá ser lida exatamente como está sendo escrito no código 08. Por enquanto, não tente interpretar nada, apenas leia o que está escrito na linha vinte. Para lhe ajudar, vou mostrar como você deveria fazer.

Crie um gatilho se este não existir, cujo nome será tr_InsertSymbol, que deverá ser executado DEPOIS de inserirmos algo na tabela tb_Symbols

Note que não é complicado. É tudo uma questão de prestar atenção ao que queremos que seja feito. Muito bem, o conteúdo das linhas 21 e 23 são apenas palavras reservadas do SQL para delimitar o corpo de algum tipo de procedimento que desejamos trabalhar. Novamente volte a imagem 12 para mais detalhes. Já que isto que está sendo mostrado aqui, é apenas e tão somente um mero exemplo ilustrativo. Agora vamos entender a linha vinte e dois. Ela deve ser lida da mesma forma que foi lida a linha vinte. E mais uma vez para lhe ajudar neste processo. Você a deveria ler da seguinte forma:

Atualiza a tabela tb_Symbols, tornando symbol igual a upper de NEW.symbol quando NEW.id for igual a id

Novamente acabamos de ver algo dependendo do SQLite sendo novamente utilizado aqui. Isto por que NEW só existe no SQLite. Quando falamos sobre remover registros usamos a palavra OLD. E agora usamos a palavra NEW. Isto a princípio parece um tanto quanto confuso. E de fato estou ficando confuso, meu amigo autor. E não estou conseguindo entender quando usar NEW e quando usar OLD. E o motivo é que criamos um gatilho, chamado tr_InsertSymbol. Daí adicionando, alguma informação no banco de dados. Mas estamos atualizando o banco, o que a princípio, faria com que os valores não fossem novos, mas sim antigos. Cara isto é muito doido.

Sei disto meu caro leitor. Existem de fato alguns detalhes, que fazem as coisas ficarem um tanto quanto confusas no início. Mas conforme você for utilizando e estudando. Elas deixarão de ser confusas e passarão a fazer sentido. Mas vou tentar simplificar as coisas, a ponto que possa ser possível entender quando usar NEW e quando usar OLD. Pelo menos a ponto de que você fique menos confuso.

Na operação DELETE, a informação que está sendo removida JÁ EXISTE no banco de dados, e estará deixando de existir. Por isto usamos OLD. Na operação INSERT, a informação NÃO EXISTE no banco de dados, e passará a existir. Por isto usamos NEW. A parte que é confusa é justamente o fato de que o gatilho da linha vinte do código 08 está dizendo que o gatilho deverá ser executado DEPOIS que a informação ter sido inserida no banco de dados. Porém, toda via e, entretanto, você deve considerar que a informação inda assim é nova, e ainda não existe no banco de dados.

Agora a coisa de fato fica um pouco mais complicada de entender, é quando o gatilho está ligado ao comando UPDATE. Neste caso, você precisará pensar de uma maneira um pouco mais ampla. Então preste atenção. A informação que estará entrando em substituição a que já existe no banco de dados, deverá ser tratada como NEW. Já a informação que existe no banco de dados, e está sendo substituída deverá ser tratada como OLD. Se você entender isto, conseguirá lidar com qualquer script gatilho que estiver sendo executado em um banco de dados SQLite, padrão. Digo SQLite padrão, pois pode ser que a implementação lide com isto de uma maneira diferente. Neste caso, procure estudar a documentação da mesma.


Escolhendo um caminho

Agora que já sabemos como podemos lidar com um gatilho para inserir registros no banco de dados. Garantindo assim a integridade das informações. Precisamos ver uma outra coisa igualmente importante. Isto por que a integridade do banco de dados, está sendo garantida somente em parte. Mas por que você está dizendo que somente em parte? O motivo é o comando UPDATE, meu caro leitor. Lembre-se de que a garantia que foi criada, se deu em cima do comando INSERT.

Mas existe um problema no comando UPDATE que precisamos resolver. Pense no seguinte: O que acontece se o usuário desejar mudar algo no banco de dados usando um comando UPDATE? Como o SQL não sabe que regras adotar, qualquer coisa que for inserida via comando UPDATE será aceito pelo SQL e substituirá alguma informação previamente aferida. Para demonstrar isto, veja o código logo abaixo.

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;

Código 10

Este código 10 deverá ser executado em um banco de dados construído pelo código 08. Isto para que você tenha o seguinte resultado mostrado abaixo.

Imagem 15

Na região demarcada você pode observar algo bem estranho no banco de dados. Algo que não deveria estar ali, justamente por que todos os valores deveriam estar em caixa alta. Mas por conta da linha seis deste código 10, o SQL mudou o registro, e assim a integridade do banco passou a ficar comprometida. Note que tudo que está sendo feito, depende do objetivo a ser alcançado. Não existe de fato uma receita de bolo. Precisamos entender o problema, para somente depois procurar meios de resolver o mesmo. Porém, tudo isto deve ser definido e determinado antes que o banco comece a receber dados sensíveis.

Então vamos supor o seguinte: Uma vez que um registro tenha sido feito, o mesmo não possa ser modificado. Isto nos dará uma forma de solucionar este tipo de problema visto acima. Mas se você permitir que o banco possa ser atualizado, os dados a serem inseridos no registro já existente deverá seguir alguma regra. Neste caso temos que seguir por outro caminho para resolver a questão do comando UPDATE.

Mas também, você pode permitir que o usuário modifique o nome do ativo. No entanto, ao fazer isto o usuário terá a ilusão de que o nome foi trocado. Porém, dentro do banco, o que será mesmo feito, seria uma cópia dos dados com o surgimento de um novo registro. O antigo registro seria colocado em uma situação que o usuário não poderia acessar os dados diretamente. Mas um administrador do banco, poderia recuperar todas as informações, já que o registro original se manteria intacto. Este tipo de coisa, nos geraria uma outra solução a ser implementada. Ou seja, cada caso é um caso. Não existe de maneira alguma uma solução servirá para todos os casos.

Assim para que possamos exemplificar um pouco as coisas. Vamos ver como seria o tratamento do comando UPDATE, para que o usuário pudesse trocar o nome do ativo. Porém de forma que no final o nome iria se manter dentro das mesmas regras impostas durante um comando INSERT. Para fazer isto, poderíamos fazer uso da seguinte modificação do script original, que pode ser vista logo abaixo.

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;

Código 11

Mas espere um pouco aí. Agora você está querendo é fazer graça. Como assim, o código de gatilho do comando UPDATE ser quase uma cópia perfeita do gatilho do comando INSERT? Você tem certeza de que isto irá funcionar? Tenho lá as minhas dúvidas, já que se estivermos fazendo um INSERT o gatilho da linha vinte será disparado. Mas já que na linha 22 estamos usando o comando UPDATE. Acho que o gatilho implementado na linha 25 também será disparado. E quando o comando da linha 27 executar, o SQL irá entrar em loop. De fato, acho que isto não irá funcionar.

Ok, se você de fato observou esta sequência de execução, meu caro leitor. Significa que você ainda não entendeu como o SQL lida com os gatilhos. Mas tudo bem. Agora, se você criar um novo banco de dados usando este código 11 e logo depois tentar executar novamente o código 10 dentro deste novo banco de dados. Veja o que irá acontecer.

Imagem 16

Agora entenda o seguinte: Quando o usuário pedir para inserir um registro na tabela tb_Symbols. O registro poderá ou não ser inserido pelo SQL. Caso seja, o SQL executará  o gatilho presente na linha vinte do código 11. Este por sua vez fará com que a linha 22 seja executada. Atualizando assim as informações contidas no banco de dados. Mas esta linha 22 irá forçar o SQL a executar o gatilho da linha 25. Até este ponto você estava certo. Uma vez que a linha 27 for executada. Faremos um pedido para que o SQL atualize novamente o banco de dados.

Porém desta vez, estaremos forçando o conteúdo do registro para um certo modelo. Que era justamente o que existia no momento em que o registro foi criado pelo comando INSERT. Assim conseguimos cobrir tanto o comando INSERT, quanto o comando UPDATE. Se bem que esta não é a melhor forma de se fazer isto. Mas para que você consiga entender, esta foi a forma mais simples que encontrei para explicar como poderíamos fazer as coisas.

Uma última coisa que quero mostrar, é o seguinte: Supondo que você queira evitar que algum registro seja modificado. E isto via comando UPDATE. Uma maneira relativamente simples de se fazer isto, é usando o código visto logo abaixo, no momento de criar o banco de dados.

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;

Código 12

Aqui a coisa é um pouco diferente. Mas se você usar novamente o código 10 em um banco criado por este código 12. Irá ver o seguinte resultado mostrado logo abaixo.

Imagem 17

Agora quero que você preste muita atenção a esta imagem 17. E procure entender o que o código 12 criou, para que quando o código 10 fosse executado obtivéssemos este resultado mostrado. Observe que a linha um do código 10 irá ter sucesso criando assim os registros no banco de dados. Porém, quando a linha seis for executada será disparado um erro como está sendo indicado na imagem 17. É neste ponto que quero que você se atente as informações vistas na imagem 17. Observe a terceira linha da mensagem. Agora volte ao código 12 e veja que é exatamente a mesma mensagem vista na linha 33. Como isto é possível?

Ao tentarmos inserir algo na tabela, iremos executar o gatilho da linha 20. Isto logo depois do registro ter sido inserido. Isto fará com que a linha 22, peça para atualizar o registro. Bem, mas antes de que a atualização ocorra, temos a execução do gatilho da linha 30. Somente depois é que iremos executar o gatilho da linha 25. Note que mudamos a forma como o código 11 trabalhava. E o motivo para isto é justamente fazer com que o teste na linha 31 aconteça. Note que este teste, checa se o registro que estamos tentando atualizar é diferente ou igual ao que já existe no banco de dados. Como estamos testando isto, iremos garantir que o registro não venha a ser modificado. Isto por que, caso o registro seja diferente o gatilho será executado. Fazendo assim com que a linha 33 seja posta em execução. Quando o SQL executar esta linha 33, será repassado uma informação para quem chamou a atualização. Explicando que a operação não poderá ser executada por qualquer motivo.

Este tipo de modelagem do banco de dados, pode ser interessante em alguns momento. Procure estudar isto, pois pode ser que ele evite dores de cabeça futuras para você, meu amigo leitor.


Considerações finais

Isto que mostrei aqui, é apenas a base de uma série de coisas das quais você precisa procurar estudar a fim de conseguir trabalhar com SQL. Sei que muita gente torce o nariz, quando o assunto é SQL. Dizendo que estudar e entender de SQL é bobagem, que é muito mais fácil criamos nós mesmos os arquivos para conter informações que poderiam ser colocadas em um banco de dados. Entre diversas outras coisas. Mas quero lembrar a você, meu amigo leitor, que o SQL é uma das ferramentas mais agradáveis que existe. E que pode lhe ajudar a fazer coisas que de outra forma seriam feitas com muito mais trabalho e esforço.

Muitas das coisas que vejo pessoas usando Python, ou mesmo Excel para serem feitas, poderiam ser feitas muito mais facilmente se fosse utilizado SQL. Mas como SQL é algo que precisa de estudo e atenção. Boa parte prefere quebrar a cabeça tentando combinar dados e informações usando para isto programação clássica. Mas garanto a você, estudar SQL irá mesmo valer muito a pena. Ainda mais quando você começar a entender como dados podem ser relacionados entre si.

Assim, aqui encerro o tema sobre SQL. Ficando você responsável por dar continuidade ao aprendizado que foi iniciado aqui. No próximo artigo, iremos ver como tudo isto que foi mostrado, se aplicada quando unimos SQL com MQL5. Você irá ver que até agora você apenas arranhou a superfície do que realmente podemos fazer com o MetaTrader 5.
ArquivoDescrição
Experts\Expert Advisor.mq5
Demonstra a interação entre o Chart Trade e o Expert Advisor ( É necessário o Mouse Study para interação )
 Indicators\Chart Trade.mq5Cria a janela para configuração da ordem a ser enviada   ( É necessário o Mouse Study para interação )
 Indicators\Market Replay.mq5Cria os controles para interação com o serviço de replay / simulador   ( É necessário o Mouse Study para interação )
 Indicators\Mouse Study.mq5Permite interação entre os controles gráficos e o usuário ( Necessário tanto para operar o replay simulador, quanto no mercado real )
 Services\Market Replay.mq5Cria e mantém o serviço de replay e simulação de mercado ( Arquivo principal de todo o sistema )
 Code VS C++\Servidor.cppCria e mantém um soquete servidor criado em C++ ( Versão Mini Chat )
 Code in Python\Server.pyCria e mantém um soquete em python para comunicação entre o MetaTrader 5 e o Excel
 Indicators\Mini Chat.mq5Permite implementar um mini chat via indicador ( Necessário uso de um servidor para funcionar )
 Experts\Mini Chat.mq5Permite implementar um mini chat via Expert Advisor ( Necessário uso de um servidor para funcionar )
 Scripts\SQLite.mq5Demonstra uso de script SQL por meio do MQL5
 Files\Script 01.sqlDemonstra a criação de uma tabela simples, com chave estrangeira
 Files\Script 02.sqlDemonstra a adição de valores em uma tabela
Arquivos anexados |
Anexo.zip (571.71 KB)
Aplicando Seleção de Recursos Localizada em Python e MQL5 Aplicando Seleção de Recursos Localizada em Python e MQL5
Este artigo explora um algoritmo de seleção de recursos introduzido no artigo 'Local Feature Selection for Data Classification' de Narges Armanfard et al. O algoritmo é implementado em Python para construir modelos de classificação binária que podem ser integrados com aplicativos MetaTrader 5 para inferência.
Do básico ao intermediário: Ponteiro para função Do básico ao intermediário: Ponteiro para função
Você provavelmente já deve ter ouvido falar em ponteiro. Isto quando o assunto é programação. Mas você sabia que podemos fazer uso deste tipo de dado aqui no MQL5? Isto claro, de uma forma a não perder o controle ou gerar coisas bizarras durante a execução do código. Porém, sendo um recurso com uso muito específico e voltado para certos tipos de atividade. É difícil ver alguém falando sobre o que seria de fato um ponteiro e como usar eles no MQL5.
Simulação de mercado: Iniciando o SQL no MQL5 (I) Simulação de mercado: Iniciando o SQL no MQL5 (I)
Neste artigo, começaremos a explorar o uso do SQL dentro de um código MQL5. Vemos como podemos cria um banco de dados. Ou melhor dizendo, como podemos criar um arquivo de banco de dados em SQLite, usando para isto dispositivos ou procedimentos contidos dentro da linguagem MQL5. Veremos também, como criar uma tabela e depois como criar uma relação entre tabelas via chave primária e chave estrangeira. Isto tudo, usando novamente o MQL5. Veremos como é simples tornar um código que poderá no futuro ser portado para outras implementações do SQL, usando uma classe para nos ajudar a ocultar a implementação que está sendo criada. E o mais importante de tudo. Veremos que em diversos momentos, podemos correr o risco de fazer algo não dar certo ao usarmos SQL. Isto devido ao fato de que dentro do código MQL5, um código SQL irá ser sempre colocado como sendo uma STRING.
Redes neurais em trading: Modelos com uso de wavelet transform e atenção multitarefa (Conclusão) Redes neurais em trading: Modelos com uso de wavelet transform e atenção multitarefa (Conclusão)
No artigo anterior, exploramos os fundamentos teóricos e começamos a implementar as abordagens do framework Multitask-Stockformer, que combina wavelet transform e o modelo multitarefa Self-Attention. Damos continuidade à implementação dos algoritmos desse framework e avaliamos sua eficácia com dados históricos reais.