Pular para conteúdo

Microsoft SQL Server - Banco de Dados Enterprise

Visão Geral

Microsoft SQL Server (MSSQL) é um sistema de gerenciamento de banco de dados relacional enterprise desenvolvido pela Microsoft. É amplamente utilizado em ambientes corporativos Windows e Azure. O Lumina Flow Builder oferece integração completa com MSSQL 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 (mssql.executor.ts:97-105):

private async executeQuery(pool: sql.ConnectionPool, query: string, context: ExecutionContext): Promise<any> {
  const replacedQuery = this.replaceVariables(query, context.variables);
  const result = await pool.request().query(replacedQuery);

  return {
    rows: result.recordset,
    rowCount: result.rowsAffected[0],
  };
}

Exemplo:

{
  "operation": "executeQuery",
  "config": {
    "server": "localhost",
    "port": 1433,
    "database": "AdventureWorks",
    "user": "sa",
    "password": "Senha@123",
    "encrypt": true,
    "trustServerCertificate": true
  },
  "query": "SELECT TOP 10 * FROM Customers WHERE City = '{{cidade}}' ORDER BY Name",
  "responseVariable": "clientes"
}

2. insert - Inserir Registro

Insere novo registro na tabela usando parâmetros nomeados @campo.

Código interno (mssql.executor.ts:107-131):

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

  const columns = Object.keys(replacedValues).join(', ');
  const valuePlaceholders = Object.keys(replacedValues).map(key => `@${key}`).join(', ');

  const query = `INSERT INTO ${table} (${columns}) VALUES (${valuePlaceholders}); SELECT SCOPE_IDENTITY() AS id`;

  const request = pool.request();
  Object.entries(replacedValues).forEach(([key, value]) => {
    request.input(key, value);
  });

  const result = await request.query(query);

  return {
    insertId: result.recordset[0].id,
    affectedRows: result.rowsAffected[0],
  };
}

Diferença do PostgreSQL/MySQL: - MSSQL usa @campo como placeholder (não $1 ou ?) - Usa SCOPE_IDENTITY() para obter ID inserido - Usa request.input(nome, valor) para parametrizar

Exemplo:

{
  "operation": "insert",
  "table": "Customers",
  "values": {
    "Name": "{{nome}}",
    "Email": "{{email}}",
    "Phone": "{{telefone}}",
    "CreatedDate": "GETDATE()"
  },
  "responseVariable": "resultado"
}

3. update - Atualizar Registro

Atualiza registros existentes baseado em condições WHERE.

Código interno (mssql.executor.ts:133-161):

private async updateRow(
  pool: sql.ConnectionPool,
  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} = @${key}`).join(', ');
  const whereClause = Object.keys(replacedConditions).map(key => `${key} = @cond_${key}`).join(' AND ');

  const query = `UPDATE ${table} SET ${setClause} WHERE ${whereClause}`;

  const request = pool.request();
  Object.entries(replacedValues).forEach(([key, value]) => {
    request.input(key, value);
  });
  Object.entries(replacedConditions).forEach(([key, value]) => {
    request.input(`cond_${key}`, value);
  });

  const result = await request.query(query);

  return {
    affectedRows: result.rowsAffected[0],
  };
}

Exemplo:

{
  "operation": "update",
  "table": "Orders",
  "values": {
    "Status": "Shipped",
    "ShippedDate": "GETDATE()"
  },
  "conditions": {
    "OrderId": "{{pedidoId}}",
    "Status": "Pending"
  },
  "responseVariable": "resultado"
}

4. delete - Deletar Registro

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

Código interno (mssql.executor.ts:163-186):

private async deleteRow(
  pool: sql.ConnectionPool,
  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} = @${key}`).join(' AND ');

  const query = `DELETE FROM ${table} WHERE ${whereClause}`;

  const request = pool.request();
  Object.entries(replacedConditions).forEach(([key, value]) => {
    request.input(key, value);
  });

  const result = await request.query(query);

  return {
    affectedRows: result.rowsAffected[0],
    deleted: result.rowsAffected[0] > 0,
  };
}

Exemplo:

{
  "operation": "delete",
  "table": "Sessions",
  "conditions": {
    "UserId": "{{userId}}",
    "ExpiresAt": "< GETDATE()"
  },
  "responseVariable": "resultado"
}

5. insertOrUpdate (Upsert) - Inserir ou Atualizar

Insere registro se não existe, atualiza se já existe usando MERGE statement.

Código interno (mssql.executor.ts:188-230):

private async upsertRow(
  pool: sql.ConnectionPool,
  table: string,
  values: Record<string, any>,
  conditions: Record<string, any>,
  context: ExecutionContext,
): Promise<any> {
  const replacedValues = {
    ...this.replaceObjectVariables(conditions, context.variables),
    ...this.replaceObjectVariables(values, context.variables)
  };

  const allColumns = Object.keys(replacedValues);
  const conditionColumns = Object.keys(this.replaceObjectVariables(conditions, context.variables));
  const updateColumns = Object.keys(this.replaceObjectVariables(values, context.variables))
    .filter(c => !conditionColumns.includes(c));

  const mergeMatch = conditionColumns.map(key => `target.${key} = source.${key}`).join(' AND ');
  const updateSet = updateColumns.map(key => `target.${key} = source.${key}`).join(', ');
  const insertCols = allColumns.join(', ');
  const insertVals = allColumns.map(key => `source.${key}`).join(', ');

  const sourceColsList = allColumns.join(', ');
  const sourceValsList = allColumns.map(key => `@${key}`).join(', ');

  const query = `
    MERGE ${table} AS target
    USING (VALUES (${sourceValsList})) AS source (${sourceColsList})
    ON (${mergeMatch})
    WHEN MATCHED THEN
      UPDATE SET ${updateSet}
    WHEN NOT MATCHED THEN
      INSERT (${insertCols}) VALUES (${insertVals});
  `;

  const request = pool.request();
  Object.entries(replacedValues).forEach(([key, value]) => {
    request.input(key, value);
  });

  const result = await request.query(query);

  return {
    affectedRows: result.rowsAffected[0],
  };
}

Diferença do PostgreSQL/MySQL: - MSSQL usa MERGE statement (não ON CONFLICT ou ON DUPLICATE KEY) - Requer conditions para identificar quando há match - Mais verboso mas mais flexível (permite diferentes ações em MATCHED/NOT MATCHED)

Exemplo:

{
  "operation": "insertOrUpdate",
  "table": "Products",
  "values": {
    "Name": "{{nome}}",
    "Price": "{{preco}}",
    "UpdatedDate": "GETDATE()"
  },
  "conditions": {
    "SKU": "{{sku}}"
  },
  "responseVariable": "produto"
}

Configuração de Conexão

{
  "server": "localhost",
  "port": 1433,
  "database": "nome_database",
  "user": "usuario",
  "password": "senha",
  "encrypt": true,
  "trustServerCertificate": true
}

Observações: - server ao invés de host - encrypt normalmente true em produção - trustServerCertificate: true para desenvolvimento (produção use certificado válido)

Principais Diferenças do PostgreSQL/MySQL

Aspecto MSSQL PostgreSQL MySQL
Placeholders @campo $1, $2 ?
Parametrização request.input('campo', valor) Array de valores Array de valores
INSERT ID SCOPE_IDENTITY() RETURNING * insertId
UPSERT MERGE ON CONFLICT ON DUPLICATE KEY
TOP SELECT TOP 10 LIMIT 10 LIMIT 10
Data/Hora GETDATE(), GETUTCDATE() NOW(), CURRENT_TIMESTAMP NOW()
String concatenação + ou CONCAT() || ou CONCAT() CONCAT()
IDENTITY IDENTITY(1,1) SERIAL, IDENTITY AUTO_INCREMENT
Schemas dbo padrão public padrão Não usa schemas

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)
NVARCHAR(n) Texto Unicode NVARCHAR(255)
VARCHAR(n) Texto ASCII VARCHAR(255)
NTEXT Texto Unicode longo NTEXT
BIT Boolean 0, 1
DATE Data '2025-01-15'
DATETIME Data e hora '2025-01-15 14:30:00'
DATETIME2 Mais preciso '2025-01-15 14:30:00.1234567'
UNIQUEIDENTIFIER GUID NEWID()
XML Documento XML ''

Exemplo Flow Completo

{
  "name": "MSSQL - 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": "mssql_1",
      "type": "mssql",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Salvar Cliente",
        "parameters": {
          "operation": "insert",
          "config": {
            "server": "localhost",
            "port": 1433,
            "database": "CRM",
            "user": "app_user",
            "password": "Senha@123",
            "encrypt": true,
            "trustServerCertificate": true
          },
          "table": "Customers",
          "values": {
            "Name": "{{nome}}",
            "Email": "{{email}}",
            "CreatedDate": "GETDATE()",
            "Active": 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": "mssql_1" },
    { "source": "mssql_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Recursos Específicos MSSQL

Stored Procedures

-- Chamar com executeQuery
EXEC sp_GetCustomerOrders @CustomerId = 123

Window Functions

-- Ranking com ROW_NUMBER()
SELECT
  Name,
  Sales,
  ROW_NUMBER() OVER (ORDER BY Sales DESC) AS Rank
FROM Employees

Common Table Expressions (CTEs)

-- CTE para queries hierárquicas
WITH EmployeeHierarchy AS (
  SELECT EmployeeId, Name, ManagerId, 0 AS Level
  FROM Employees
  WHERE ManagerId IS NULL
  UNION ALL
  SELECT e.EmployeeId, e.Name, e.ManagerId, eh.Level + 1
  FROM Employees e
  INNER JOIN EmployeeHierarchy eh ON e.ManagerId = eh.EmployeeId
)
SELECT * FROM EmployeeHierarchy

XML Support

-- Query XML data
SELECT
  Data.value('(/root/name)[1]', 'NVARCHAR(100)') AS Name
FROM XmlTable

Boas Práticas MSSQL

SIM: - Use NVARCHAR para suportar Unicode (caracteres internacionais) - Defina índices em colunas de WHERE e JOIN - Use stored procedures para lógica complexa - Configure SQL Server Agent para backups automáticos - Use schemas para organizar objetos (dbo, app, reports) - Monitore com SQL Server Profiler e DMVs

NÃO: - Não use VARCHAR se precisa Unicode (use NVARCHAR) - Não use SELECT * desnecessariamente - Não ignore execution plans (Ctrl+M no SSMS) - Não exponha credenciais sa - Não delete/update sem WHERE - Não ignore índices faltantes

Ferramentas Microsoft

  • SQL Server Management Studio (SSMS): IDE oficial
  • Azure Data Studio: Cliente cross-platform moderno
  • SQL Server Profiler: Monitoramento e debug
  • Database Tuning Advisor: Otimização automática

Recursos de Aprendizado

Troubleshooting

"Login failed for user"

  • Verifique usuário e senha
  • Confirme autenticação SQL (não apenas Windows)
  • Habilite: SQL Server Configuration Manager → SQL Server Network Configuration → TCP/IP

"A connection was successfully established... existing connection was forcibly closed"

  • Verifique encrypt e trustServerCertificate
  • Para desenvolvimento: encrypt: true, trustServerCertificate: true
  • Para produção: use certificado SSL válido

"Invalid object name"

  • Tabela não existe ou está em schema diferente
  • Use nome completo: dbo.Customers ou app.Products
  • Liste tabelas: SELECT * FROM sys.tables

Edições do SQL Server

  • Express: Grátis, limitado (10GB database, 1GB RAM)
  • Standard: Produção médio porte
  • Enterprise: Recursos completos, alta disponibilidade
  • Azure SQL Database: Managed cloud

Próximos Passos

PostgreSQL - Alternativa open-source avançada → MySQL - Alternativa open-source popular → MongoDB - Banco NoSQL para dados não-estruturados