POSTGRESQL EXECUTEQUERY - Executar Consulta SQL
O que é este Node?
O POSTGRESQL EXECUTEQUERY é o node responsável por executar consultas SQL personalizadas no banco de dados PostgreSQL, permitindo SELECT, CREATE TABLE, ALTER TABLE e outras operações SQL.
Por que este Node existe?
Aplicações precisam interagir com bancos de dados relacionais. O POSTGRESQL EXECUTEQUERY existe para:
- Flexibilidade total: Executar qualquer query SQL personalizada
- Queries complexas: Realizar JOINs, subconsultas, CTEs e agregações
- DDL operations: Criar/modificar estruturas de tabelas e índices
- Transações: Executar múltiplas operações em uma transação
- Funções e procedures: Chamar stored procedures e funções do PostgreSQL
- Analytics: Realizar análises complexas com SQL puro
Como funciona internamente?
Quando o POSTGRESQL EXECUTEQUERY é executado, o sistema:
- Valida configuração: Verifica se config com credenciais foi fornecida
- Cria pool de conexões: Estabelece conexão com PostgreSQL usando pg driver
- Substitui variáveis: Troca {{variavel}} por valores do contexto
- Executa query: Envia SQL para PostgreSQL e aguarda resultado
- Retorna resultado: Retorna rows (linhas), rowCount e command executado
- Fecha conexão: Encerra pool após execução
- Se erro: Lança exceção com detalhes do erro SQL
Código interno (postgresql.executor.ts:94-102):
private async executeQuery(pool: Pool, query: string, context: ExecutionContext): Promise<any> {
const replacedQuery = this.replaceVariables(query, context.variables);
const result = await pool.query(replacedQuery);
return {
rows: result.rows,
rowCount: result.rowCount,
command: result.command,
};
}
Quando você DEVE usar este Node?
Use POSTGRESQL EXECUTEQUERY quando precisar de consultas SQL personalizadas:
Casos de uso
- SELECT com JOINs: "Buscar pedidos com dados de cliente e produtos"
- Agregações complexas: "Relatório de vendas por região com totais"
- Subconsultas: "Clientes que compraram acima da média"
- CTEs (Common Table Expressions): "Queries hierárquicas recursivas"
- Criar estruturas: "CREATE TABLE para nova funcionalidade"
- Análises avançadas: "Window functions, GROUPING SETS, ROLLUP"
Quando NÃO usar POSTGRESQL EXECUTEQUERY
- Insert simples: Use POSTGRESQL INSERT (mais seguro e estruturado)
- Update simples: Use POSTGRESQL UPDATE (previne SQL injection)
- Delete simples: Use POSTGRESQL DELETE (mais seguro)
- Upsert: Use POSTGRESQL INSERTORUPDATE (otimizado)
Parâmetros Detalhados
config (object, obrigatório)
O que é: Configuração de conexão com PostgreSQL.
Estrutura:
{
"host": "localhost",
"port": 5432,
"database": "nome_database",
"user": "usuario",
"password": "senha",
"ssl": false
}
Segurança: NUNCA exponha credenciais em código! Use variáveis de ambiente.
query (string, obrigatório)
O que é: Consulta SQL a ser executada.
Formato: SQL válido do PostgreSQL
Suporte a variáveis: Use {{variavel}} para substituir valores do contexto
Flow completo para testar:
{
"name": "Teste PostgreSQL - Execute Query",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "input_1",
"type": "input",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Cidade",
"parameters": {
"message": "Buscar clientes de qual cidade?",
"variable": "cidade"
}
}
},
{
"id": "postgres_1",
"type": "postgresql",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Buscar Clientes",
"parameters": {
"operation": "executeQuery",
"config": {
"host": "localhost",
"port": 5432,
"database": "crm",
"user": "app_user",
"password": "secure_password",
"ssl": false
},
"query": "SELECT id, nome, email, telefone FROM clientes WHERE cidade = '{{cidade}}' ORDER BY nome LIMIT 10",
"responseVariable": "clientes"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Resultado",
"parameters": {
"message": "Encontrados {{clientes.rowCount}} clientes em {{cidade}}:\n\n{{clientes.rows}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 900, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "input_1" },
{ "source": "input_1", "target": "postgres_1" },
{ "source": "postgres_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Teste: Query é executada e retorna clientes da cidade especificada.
responseVariable (string, opcional)
O que é: Nome da variável para armazenar resultado no contexto.
Padrão: Se não especificado, resultado não é salvo em variável
Parâmetros
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| operation | string | Sim | Deve ser "executeQuery" |
| config | object | Sim | Configuração de conexão PostgreSQL |
| config.host | string | Sim | Endereço do servidor PostgreSQL |
| config.port | number | Não | Porta (padrão: 5432) |
| config.database | string | Sim | Nome do database |
| config.user | string | Sim | Usuário PostgreSQL |
| config.password | string | Sim | Senha do usuário |
| config.ssl | boolean | Não | Usar SSL (padrão: false) |
| query | string | Sim | Consulta SQL a executar |
| responseVariable | string | Não | Variável para armazenar resultado |
Exemplo 1: Relatório de Vendas com JOIN
Objetivo: Buscar vendas com dados de cliente e produto usando JOIN
JSON para Importar
{
"name": "PostgreSQL - Relatório Vendas",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "date_1",
"type": "date",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Data Início",
"parameters": {
"message": "Data inicial (DD/MM/YYYY):",
"variable": "dataInicio",
"format": "DD/MM/YYYY"
}
}
},
{
"id": "postgres_1",
"type": "postgresql",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Buscar Vendas",
"parameters": {
"operation": "executeQuery",
"config": {
"host": "localhost",
"port": 5432,
"database": "ecommerce",
"user": "app_user",
"password": "secure_password",
"ssl": false
},
"query": "SELECT v.id, v.data_venda, v.total, c.nome AS cliente, p.nome AS produto FROM vendas v INNER JOIN clientes c ON v.cliente_id = c.id INNER JOIN produtos p ON v.produto_id = p.id WHERE v.data_venda >= '{{dataInicio}}' ORDER BY v.data_venda DESC LIMIT 20",
"responseVariable": "vendas"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Mostrar Resultado",
"parameters": {
"message": "Vendas desde {{dataInicio}}:\n\nTotal: {{vendas.rowCount}} vendas\n\n{{vendas.rows}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 900, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "date_1" },
{ "source": "date_1", "target": "postgres_1" },
{ "source": "postgres_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Saída esperada:
Sistema: Data inicial (DD/MM/YYYY):
Usuário: 01/01/2025
Sistema: Vendas desde 01/01/2025:
Total: 15 vendas
[Array com dados das vendas incluindo cliente e produto]
Exemplo 2: Agregação com GROUP BY
Objetivo: Relatório de total de vendas por categoria de produto
JSON para Importar
{
"name": "PostgreSQL - Vendas por Categoria",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "postgres_1",
"type": "postgresql",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Agregar Vendas",
"parameters": {
"operation": "executeQuery",
"config": {
"host": "localhost",
"port": 5432,
"database": "ecommerce",
"user": "app_user",
"password": "secure_password",
"ssl": false
},
"query": "SELECT p.categoria, COUNT(v.id) AS total_vendas, SUM(v.total) AS receita_total, AVG(v.total) AS ticket_medio FROM vendas v INNER JOIN produtos p ON v.produto_id = p.id WHERE v.data_venda >= CURRENT_DATE - INTERVAL '30 days' GROUP BY p.categoria ORDER BY receita_total DESC",
"responseVariable": "relatorio"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Exibir Relatório",
"parameters": {
"message": "Vendas dos últimos 30 dias por categoria:\n\n{{relatorio.rows}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 700, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "postgres_1" },
{ "source": "postgres_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Saída esperada:
Sistema: Vendas dos últimos 30 dias por categoria:
[
{ categoria: 'Eletrônicos', total_vendas: 45, receita_total: 85000, ticket_medio: 1888.89 },
{ categoria: 'Livros', total_vendas: 78, receita_total: 3240, ticket_medio: 41.54 }
]
Exemplo 3: CTE (Common Table Expression)
Objetivo: Query recursiva usando WITH para hierarquia de categorias
JSON para Importar
{
"name": "PostgreSQL - CTE Recursivo",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "input_1",
"type": "input",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Categoria",
"parameters": {
"message": "ID da categoria raiz:",
"variable": "categoriaId"
}
}
},
{
"id": "postgres_1",
"type": "postgresql",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Buscar Hierarquia",
"parameters": {
"operation": "executeQuery",
"config": {
"host": "localhost",
"port": 5432,
"database": "ecommerce",
"user": "app_user",
"password": "secure_password",
"ssl": false
},
"query": "WITH RECURSIVE categoria_tree AS (SELECT id, nome, parent_id, 0 AS nivel FROM categorias WHERE id = {{categoriaId}} UNION ALL SELECT c.id, c.nome, c.parent_id, ct.nivel + 1 FROM categorias c INNER JOIN categoria_tree ct ON c.parent_id = ct.id) SELECT * FROM categoria_tree ORDER BY nivel, nome",
"responseVariable": "hierarquia"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Mostrar Árvore",
"parameters": {
"message": "Hierarquia de categorias:\n\n{{hierarquia.rows}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 900, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "input_1" },
{ "source": "input_1", "target": "postgres_1" },
{ "source": "postgres_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Saída esperada:
Sistema: ID da categoria raiz:
Usuário: 1
Sistema: Hierarquia de categorias:
[
{ id: 1, nome: 'Eletrônicos', parent_id: null, nivel: 0 },
{ id: 2, nome: 'Computadores', parent_id: 1, nivel: 1 },
{ id: 3, nome: 'Notebooks', parent_id: 2, nivel: 2 }
]
Resposta do Node
{
"rows": [
{
"id": 1,
"nome": "João Silva",
"email": "joao@example.com",
"cidade": "São Paulo"
},
{
"id": 2,
"nome": "Maria Santos",
"email": "maria@example.com",
"cidade": "São Paulo"
}
],
"rowCount": 2,
"command": "SELECT"
}
Boas Práticas
✅ SIM:
- Use prepared statements para prevenir SQL injection
- Limite resultados com LIMIT para evitar sobrecarga
- Use índices em colunas de WHERE e JOIN
- Use EXPLAIN ANALYZE para otimizar queries lentas
- Normalize dados adequadamente (3FN geralmente)
- Use transações para operações múltiplas relacionadas
- Valide input antes de construir queries
❌ NÃO:
- Não concatene variáveis diretamente no SQL (SQL injection!)
- Não use SELECT * em produção (especifique colunas)
- Não faça queries dentro de loops (N+1 problem)
- Não exponha credenciais de banco em código
- Não use DISTINCT quando GROUP BY resolve
- Não ignore índices em queries frequentes
Dicas
💡 Variáveis: Use {{variavel}} para substituição segura de valores
💡 Performance: EXPLAIN mostra plano de execução e custos
💡 Índices: CREATE INDEX acelera queries com WHERE e JOIN
💡 Transações: BEGIN, COMMIT, ROLLBACK para operações atômicas
💡 Window Functions: ROW_NUMBER(), RANK() para análises avançadas
💡 Full-text search: Use tsvector e tsquery para busca textual
💡 JSON: PostgreSQL suporta jsonb para dados semi-estruturados
Próximo Node
→ POSTGRESQL INSERT - Inserir registros → POSTGRESQL UPDATE - Atualizar registros → POSTGRESQL DELETE - Deletar registros → POSTGRESQL INSERTORUPDATE - Upsert (Insert ou Update)