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
encryptetrustServerCertificate - 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.Customersouapp.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