Escreve SQL otimizado para o stack Evolution (PostgreSQL primário) com boas práticas. Use quando precisar traduzir uma necessidade de dados em SQL, construir uma query com múltiplas CTEs, joins e agregações, otimizar uma query contra tabelas grandes, ou obter sintaxe específica para consultas no banco do Evo CRM, Evo AI, ou qualquer serviço do stack Evolution. Dialetos secundários disponíveis: Snowflake, BigQuery, MySQL, DuckDB.
Escreve uma query SQL a partir de uma descrição em linguagem natural, otimizada para o dialeto PostgreSQL (stack padrão Evolution) e seguindo boas práticas.
/data-write-query <descrição do que você precisa consultar>
Analisar a descrição do usuário para identificar:
Dialeto primário do workspace:
Dialetos secundários (se explicitamente solicitados):
Se o usuário não especificar, assumir PostgreSQL como padrão.
Se a fonte de dados estiver disponível via MCP ou CLI:
Queries de exploração de schema (PostgreSQL):
-- Listar todas as tabelas no schema
SELECT table_name, table_type
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name;
-- Detalhes das colunas de uma tabela
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'nome_da_tabela'
ORDER BY ordinal_position;
-- Tamanho das tabelas
SELECT relname AS tabela,
pg_size_pretty(pg_total_relation_size(relid)) AS tamanho_total
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
-- Índices de uma tabela
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'nome_da_tabela';
Seguir estas boas práticas:
Estrutura:
novos_clientes_diarios, usuarios_ativos, receita_por_plano)Performance:
SELECT * em queries de produção — especificar apenas as colunas necessáriasEXISTS sobre IN para subconsultas com grandes conjuntos de resultadosLegibilidade:
a, b, c)Otimizações específicas do PostgreSQL:
-- Usar EXPLAIN ANALYZE para entender o plano de execução
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT ...;
-- Usar LIMIT + OFFSET para paginação
SELECT * FROM tabela
ORDER BY criado_em DESC
LIMIT 50 OFFSET 0;
-- Window functions eficientes no PostgreSQL
SELECT
cliente_id,
valor,
SUM(valor) OVER (PARTITION BY cliente_id ORDER BY data_pagamento) AS valor_acumulado,
ROW_NUMBER() OVER (PARTITION BY cliente_id ORDER BY data_pagamento DESC) AS rn
FROM pagamentos;
-- DISTINCT ON (específico do PostgreSQL) — mais eficiente que ROW_NUMBER
SELECT DISTINCT ON (cliente_id)
cliente_id, plano, criado_em
FROM assinaturas
ORDER BY cliente_id, criado_em DESC;
-- Funções de data no PostgreSQL
DATE_TRUNC('month', criado_em) -- Início do mês
EXTRACT(DOW FROM criado_em) -- Dia da semana (0=domingo)
NOW() AT TIME ZONE 'America/Sao_Paulo' -- Hora atual em BRT
criado_em AT TIME ZONE 'UTC' AT TIME ZONE 'America/Sao_Paulo' -- Converter para BRT
INTERVAL '30 days' -- Subtração de intervalo
DATE_PART('epoch', fim - inicio) -- Diferença em segundos
-- JSON/JSONB (comum no stack Evolution)
dados->>'campo' -- Extrair como texto
dados->'campo' -- Extrair como JSON
jsonb_array_elements(dados->'lista') -- Expandir array JSON
dados @> '{"status": "active"}'::jsonb -- Contém (usa índice GIN)
-- Array operations
ANY(ARRAY['active', 'trial']) -- In array
array_agg(campo ORDER BY data) -- Agregar em array
unnest(tags) -- Expandir array em linhas
Padrões de query para as fontes do workspace:
-- Padrão: Análise de MRR mensal (típico para dados exportados do Stripe)
WITH receita_mensal AS (
SELECT
DATE_TRUNC('month', data_pagamento) AS mes,
SUM(valor_cents) / 100.0 AS receita_total,
COUNT(DISTINCT cliente_id) AS clientes_pagantes
FROM pagamentos
WHERE status = 'succeeded'
AND data_pagamento >= NOW() - INTERVAL '12 months'
GROUP BY 1
),
crescimento AS (
SELECT
mes,
receita_total,
clientes_pagantes,
LAG(receita_total) OVER (ORDER BY mes) AS receita_anterior,
ROUND(
(receita_total - LAG(receita_total) OVER (ORDER BY mes)) /
NULLIF(LAG(receita_total) OVER (ORDER BY mes), 0) * 100, 2
) AS crescimento_pct
FROM receita_mensal
)
SELECT * FROM crescimento ORDER BY mes;
-- Padrão: Instâncias ativas com versão (típico para Licensing)
WITH instancias_ativas AS (
SELECT
instancia_id,
versao,
pais,
criado_em,
ultimo_ping
FROM instancias
WHERE ultimo_ping >= NOW() - INTERVAL '24 hours'
AND status = 'active'
),
por_versao AS (
SELECT
versao,
COUNT(*) AS total,
COUNT(DISTINCT pais) AS paises_distintos
FROM instancias_ativas
GROUP BY 1
)
SELECT
versao,
total,
paises_distintos,
ROUND(total * 100.0 / SUM(total) OVER (), 2) AS percentual
FROM por_versao
ORDER BY total DESC;
-- Padrão: Análise de funil (Evo CRM)
WITH funil AS (
SELECT
DATE_TRUNC('week', criado_em) AS semana,
COUNT(*) FILTER (WHERE etapa = 'lead') AS leads,
COUNT(*) FILTER (WHERE etapa = 'qualificado') AS qualificados,
COUNT(*) FILTER (WHERE etapa = 'proposta') AS propostas,
COUNT(*) FILTER (WHERE etapa = 'fechado_ganho') AS fechados
FROM oportunidades
WHERE criado_em >= NOW() - INTERVAL '90 days'
GROUP BY 1
)
SELECT
semana,
leads,
qualificados,
propostas,
fechados,
ROUND(qualificados * 100.0 / NULLIF(leads, 0), 1) AS taxa_qualificacao,
ROUND(fechados * 100.0 / NULLIF(leads, 0), 1) AS taxa_fechamento
FROM funil
ORDER BY semana;
-- Padrão: Análise de cohort de retenção
WITH cohorts AS (
SELECT
cliente_id,
DATE_TRUNC('month', primeira_assinatura) AS cohort_mes
FROM clientes
),
atividade_mensal AS (
SELECT DISTINCT
cliente_id,
DATE_TRUNC('month', data_evento) AS mes_ativo
FROM eventos
),
retencao AS (
SELECT
c.cohort_mes,
DATE_PART('month', AGE(a.mes_ativo, c.cohort_mes)) AS meses_depois,
COUNT(DISTINCT c.cliente_id) AS clientes_retidos
FROM cohorts c
JOIN atividade_mensal a USING (cliente_id)
WHERE a.mes_ativo >= c.cohort_mes
GROUP BY 1, 2
),
tamanho_cohort AS (
SELECT cohort_mes, COUNT(DISTINCT cliente_id) AS tamanho
FROM cohorts
GROUP BY 1
)
SELECT
r.cohort_mes,
r.meses_depois,
r.clientes_retidos,
tc.tamanho AS tamanho_cohort,
ROUND(r.clientes_retidos * 100.0 / tc.tamanho, 1) AS taxa_retencao
FROM retencao r
JOIN tamanho_cohort tc USING (cohort_mes)
ORDER BY r.cohort_mes, r.meses_depois;
Fornecer:
Se a fonte de dados estiver disponível via skill, oferecer executar a query e analisar os resultados. Se o usuário preferir executar manualmente, a query está pronta para copiar e colar.
Agregação simples:
/data-write-query Contagem de assinaturas por plano nos últimos 30 dias
Análise complexa:
/data-write-query Análise de retenção por cohort — agrupar clientes pelo mês de assinatura, depois mostrar qual percentual ainda está ativo aos 1, 3, 6 e 12 meses
Performance crítica:
/data-write-query Temos uma tabela de eventos com 500M de linhas particionada por data. Encontrar os 100 usuários com mais eventos nos últimos 7 dias com o tipo de evento mais recente de cada um.
Com timezone:
/data-write-query Instâncias criadas esta semana, agrupadas por dia em BRT (America/Sao_Paulo)