Pular para conteúdo

MySQL - Banco de Dados Relacional Popular

Visão Geral

MySQL é o sistema de gerenciamento de banco de dados relacional open-source mais popular do mundo, utilizado por milhões de aplicações web. O Lumina Flow Builder oferece integração completa com MySQL através de 5 operações principais.

Operações Disponíveis

1. executeQuery - Consultas SQL Personalizadas

Executa qualquer consulta SQL personalizada (SELECT, CREATE, ALTER, etc).

Código interno (mysql.executor.ts:94-103):

private async executeQuery(connection: Connection, query: string, context: ExecutionContext): Promise<any> {
  const replacedQuery = this.replaceVariables(query, context.variables);
  const [rows, fields] = await connection.query(replacedQuery);

  return {
    rows: Array.isArray(rows) ? rows : [rows],
    rowCount: Array.isArray(rows) ? rows.length : 1,
    fields: fields,
  };
}

Exemplo:

{
  "operation": "executeQuery",
  "config": {
    "host": "localhost",
    "port": 3306,
    "database": "ecommerce",
    "user": "app_user",
    "password": "senha123",
    "ssl": false
  },
  "query": "SELECT * FROM clientes WHERE cidade = '{{cidade}}' ORDER BY nome LIMIT 10",
  "responseVariable": "clientes"
}

2. insert - Inserir Registro

Insere novo registro na tabela usando prepared statements com placeholders ?.

Código interno (mysql.executor.ts:105-124):

private async insertRow(
  connection: Connection,
  table: string,
  values: Record<string, any>,
  context: ExecutionContext,
): Promise<any> {
  const replacedValues = this.replaceObjectVariables(values, context.variables);

  const columns = Object.keys(replacedValues).join(', ');
  const placeholders = Object.keys(replacedValues).map(() => '?').join(', ');
  const valueArray = Object.values(replacedValues);

  const query = `INSERT INTO ${table} (${columns}) VALUES (${placeholders})`;
  const [result] = await connection.query(query, valueArray);

  return {
    insertId: (result as any).insertId,
    affectedRows: (result as any).affectedRows,
  };
}

Diferença do PostgreSQL: MySQL retorna insertId (não insertedRow completo)

Exemplo:

{
  "operation": "insert",
  "table": "clientes",
  "values": {
    "nome": "{{nome}}",
    "email": "{{email}}",
    "telefone": "{{telefone}}",
    "data_cadastro": "NOW()"
  },
  "responseVariable": "resultado"
}

3. update - Atualizar Registro

Atualiza registros existentes baseado em condições WHERE.

Código interno (mysql.executor.ts:126-153):

private async updateRow(
  connection: Connection,
  table: string,
  values: Record<string, any>,
  conditions: Record<string, any>,
  context: ExecutionContext,
): Promise<any> {
  const replacedValues = this.replaceObjectVariables(values, context.variables);
  const replacedConditions = this.replaceObjectVariables(conditions, context.variables);

  const setClause = Object.keys(replacedValues)
    .map(key => `${key} = ?`)
    .join(', ');

  const whereClause = Object.keys(replacedConditions)
    .map(key => `${key} = ?`)
    .join(' AND ');

  const valueArray = [...Object.values(replacedValues), ...Object.values(replacedConditions)];

  const query = `UPDATE ${table} SET ${setClause} WHERE ${whereClause}`;
  const [result] = await connection.query(query, valueArray);

  return {
    affectedRows: (result as any).affectedRows,
    changedRows: (result as any).changedRows,
  };
}

Diferença do PostgreSQL: MySQL retorna affectedRows e changedRows (não registros atualizados)

Exemplo:

{
  "operation": "update",
  "table": "pedidos",
  "values": {
    "status": "enviado",
    "data_envio": "NOW()"
  },
  "conditions": {
    "id": "{{pedidoId}}",
    "status": "pendente"
  },
  "responseVariable": "resultado"
}

4. delete - Deletar Registro

Remove registros da tabela baseado em condições WHERE.

Código interno (mysql.executor.ts:155-176):

private async deleteRow(
  connection: Connection,
  table: string,
  conditions: Record<string, any>,
  context: ExecutionContext,
): Promise<any> {
  const replacedConditions = this.replaceObjectVariables(conditions, context.variables);

  const whereClause = Object.keys(replacedConditions)
    .map(key => `${key} = ?`)
    .join(' AND ');

  const valueArray = Object.values(replacedConditions);

  const query = `DELETE FROM ${table} WHERE ${whereClause}`;
  const [result] = await connection.query(query, valueArray);

  return {
    affectedRows: (result as any).affectedRows,
    deleted: (result as any).affectedRows > 0,
  };
}

Exemplo:

{
  "operation": "delete",
  "table": "sessoes",
  "conditions": {
    "user_id": "{{userId}}",
    "expires_at": "< NOW()"
  },
  "responseVariable": "resultado"
}

5. insertOrUpdate (Upsert) - Inserir ou Atualizar

Insere registro se não existe, atualiza se já existe usando ON DUPLICATE KEY UPDATE.

Código interno (mysql.executor.ts:178-205):

private async upsertRow(
  connection: Connection,
  table: string,
  values: Record<string, any>,
  context: ExecutionContext,
): Promise<any> {
  const replacedValues = this.replaceObjectVariables(values, context.variables);

  const columns = Object.keys(replacedValues).join(', ');
  const placeholders = Object.keys(replacedValues).map(() => '?').join(', ');
  const updateClause = Object.keys(replacedValues)
    .map(key => `${key} = VALUES(${key})`)
    .join(', ');
  const valueArray = Object.values(replacedValues);

  const query = `
    INSERT INTO ${table} (${columns})
    VALUES (${placeholders})
    ON DUPLICATE KEY UPDATE ${updateClause}
  `;

  const [result] = await connection.query(query, valueArray);

  return {
    insertId: (result as any).insertId,
    affectedRows: (result as any).affectedRows,
  };
}

Diferença do PostgreSQL: - MySQL usa ON DUPLICATE KEY UPDATE (não ON CONFLICT) - Não precisa especificar conflictKeys (usa chave UNIQUE/PK automaticamente) - Usa VALUES(campo) para referenciar valores inseridos

Exemplo:

{
  "operation": "insertOrUpdate",
  "table": "produtos",
  "values": {
    "sku": "{{sku}}",
    "nome": "{{nome}}",
    "preco": "{{preco}}",
    "atualizado_em": "NOW()"
  },
  "responseVariable": "produto"
}

Configuração de Conexão

{
  "host": "localhost",
  "port": 3306,
  "database": "nome_database",
  "user": "usuario",
  "password": "senha",
  "ssl": false
}

Principais Diferenças do PostgreSQL

Aspecto MySQL PostgreSQL
Placeholders ? $1, $2, $3
INSERT retorno insertId (número) insertedRow (objeto completo)
UPDATE retorno affectedRows, changedRows updatedRows (array), rowCount
UPSERT sintaxe ON DUPLICATE KEY UPDATE ON CONFLICT ... DO UPDATE
UPSERT config Detecta UNIQUE/PK automaticamente Requer conflictKeys
AUTO_INCREMENT AUTO_INCREMENT SERIAL ou IDENTITY
Funções data NOW(), CURDATE() CURRENT_TIMESTAMP, NOW()
Strings Case-insensitive por padrão Case-sensitive
Limite LIMIT 10 LIMIT 10
Tipos JSON JSON JSON, JSONB (binário)

Tipos de Dados Comuns

Tipo Descrição Exemplo
INT Número inteiro 42
BIGINT Inteiro grande 9223372036854775807
DECIMAL(p,s) Decimal preciso DECIMAL(10,2)
VARCHAR(n) Texto variável VARCHAR(255)
TEXT Texto longo TEXT
TINYINT(1) Boolean 0, 1
DATE Data '2025-01-15'
DATETIME Data e hora '2025-01-15 14:30:00'
TIMESTAMP Timestamp '2025-01-15 14:30:00'
JSON Objeto JSON '{"nome": "João"}'
ENUM Lista de valores ENUM('ativo', 'inativo')

Exemplo Flow Completo

{
  "name": "MySQL - Sistema de Cadastro",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "email_1",
      "type": "email",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Email",
        "parameters": {
          "message": "Seu email:",
          "variable": "email"
        }
      }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Nome",
        "parameters": {
          "message": "Seu nome:",
          "variable": "nome"
        }
      }
    },
    {
      "id": "mysql_1",
      "type": "mysql",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Salvar Cliente",
        "parameters": {
          "operation": "insert",
          "config": {
            "host": "localhost",
            "port": 3306,
            "database": "crm",
            "user": "app_user",
            "password": "senha123",
            "ssl": false
          },
          "table": "clientes",
          "values": {
            "nome": "{{nome}}",
            "email": "{{email}}",
            "data_cadastro": "NOW()",
            "ativo": 1
          },
          "responseVariable": "cliente"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Sucesso",
        "parameters": {
          "message": "Cliente cadastrado!\n\nID: {{cliente.insertId}}\nNome: {{nome}}\nEmail: {{email}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1100, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "email_1" },
    { "source": "email_1", "target": "input_1" },
    { "source": "input_1", "target": "mysql_1" },
    { "source": "mysql_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Boas Práticas MySQL

SIM: - Use InnoDB como engine (suporta transações) - Defina índices em colunas de WHERE e JOIN - Use prepared statements (implementado automaticamente) - Configure charset utf8mb4 para emojis - Use LIMIT em queries grandes - Normalize dados (3FN)

NÃO: - Não use MyISAM em produção (sem transações) - Não use SELECT * desnecessariamente - Não faça queries em loops - Não ignore índices - Não exponha credenciais - Não delete/update sem WHERE

Recursos de Aprendizado

Troubleshooting

"Access denied for user"

  • Verifique usuário e senha
  • Confirme permissões do usuário: GRANT ALL ON database.* TO 'user'@'host'
  • Teste com: mysql -u user -p

"Unknown database"

  • Database não existe
  • Crie com: CREATE DATABASE nome_database

"Table doesn't exist"

  • Tabela não existe ou nome incorreto
  • MySQL é case-sensitive em Linux
  • Liste tabelas: SHOW TABLES

Próximos Passos

PostgreSQL - Alternativa mais avançada → Microsoft SQL Server - Solução enterprise → MongoDB - Banco NoSQL