Documentation Index
Fetch the complete documentation index at: https://help.treble.ai/llms.txt
Use this file to discover all available pages before exploring further.
Optimización de Consultas
El Data Warehouse está construido sobre ClickHouse, una base de datos columnar. Esto significa que las consultas se comportan diferente a una base de datos relacional tradicional. Aquí te explicamos cómo aprovechar la estructura para obtener resultados rápidos.
Principios Clave
1. Siempre filtra por fecha
Todas las tablas de hechos están particionadas por mes (toYYYYMM(created_at)). Filtrar por fecha permite a ClickHouse saltar particiones enteras sin leerlas.
-- Bueno: ClickHouse lee solo 1 partición
SELECT * FROM fact_conversations
WHERE created_at >= '2026-04-01' AND created_at < '2026-05-01'
-- Malo: ClickHouse lee todas las particiones
SELECT * FROM fact_conversations
WHERE agent_name = 'Juan'
2. No necesitas filtrar por company_id
Tu usuario tiene una política de fila que filtra automáticamente por tu empresa. No necesitas agregar WHERE company_id = ... — se aplica de forma transparente.
3. Selecciona solo las columnas que necesitas
ClickHouse es columnar: solo lee las columnas que mencionas en tu SELECT. Menos columnas = menos datos leídos = más rápido.
-- Bueno: lee solo 3 columnas
SELECT conversation_id, created_at, status
FROM fact_conversations
WHERE created_at >= now() - INTERVAL 7 DAY
-- Evita: lee todas las columnas
SELECT *
FROM fact_conversations
WHERE created_at >= now() - INTERVAL 7 DAY
4. Usa LIMIT para explorar
Cuando estés explorando datos, usa LIMIT para evitar traer millones de filas:
SELECT * FROM fact_agent_messages
WHERE created_at >= now() - INTERVAL 1 DAY
ORDER BY created_at DESC
LIMIT 100
Estructura Interna de las Tablas
Cada tabla tiene un orden de clasificación (sort key) que determina cómo se organizan los datos en disco. Las consultas que filtran por las primeras columnas del sort key son mucho más eficientes.
Sort Keys por Tabla
| Tabla | Sort Key | Filtra eficientemente por |
|---|
fact_conversations | (company_id, created_at, conversation_id) | fecha, conversation_id |
fact_agent_messages | (company_id, created_at, message_id) | fecha, message_id |
fact_redirections | (company_id, created_at, redirection_id) | fecha |
fact_agent_status_changes | (company_id, agent_id, created_at) | agent_id + fecha |
fact_agent_daily | (company_id, day, agent_id) | día, agent_id |
fact_sessions | (company_id, created_at, session_id) | fecha, session_id |
fact_deployment_status | (company_id, timestamps_eta, deployment_id) | fecha |
fact_deployment_daily | (company_id, day, poll_id) | día, poll_id |
fact_inbound_messages | (company_id, created_at, session_id) | fecha |
fact_whatsapp_links | (company_id, created_at, event_id) | fecha |
fact_hsm_responses | (company_id, response_date, interaction_answer_id) | fecha |
company_id es siempre la primera columna del sort key. Como tu usuario tiene un filtro automático por empresa, todas tus consultas aprovechan esta optimización sin que tengas que hacer nada.
Índices Secundarios
Algunas tablas tienen índices adicionales que ayudan a filtrar por columnas que no están en el sort key:
| Tabla | Índice | Columna | Tipo | Útil para |
|---|
fact_agent_messages | idx_conversation_id | conversation_id | minmax | Buscar mensajes de una conversación específica |
fact_agent_messages | idx_sender | sender | bloom_filter | Filtrar por sender = 'AGENT' o sender = 'USER' |
fact_sessions | idx_poll_id | poll_id | minmax | Filtrar por campaña |
fact_sessions | idx_inbound_outbound | inbound_outbound | bloom_filter | Filtrar por tipo INBOUND/OUTBOUND |
fact_deployment_status | idx_poll_id | poll_id | minmax | Filtrar por campaña |
fact_deployment_status | idx_status | status | bloom_filter | Filtrar por estado de envío |
fact_inbound_messages | idx_poll_id | poll_id | minmax | Filtrar por campaña |
fact_hsm_responses | idx_hsm_id | hsm_id | minmax | Filtrar por plantilla HSM |
fact_hsm_responses | idx_poll_id | poll_id | minmax | Filtrar por campaña |
Límites del Sistema
Tu usuario tiene los siguientes límites para proteger la estabilidad del sistema:
| Límite | Valor |
|---|
| Tiempo máximo de ejecución | 30 segundos |
| Máximo de filas leídas | 50 millones |
| Máximo de bytes leídos | 5 GB |
| Máximo de filas en resultado | 500,000 |
| Máximo de memoria | 2 GB |
Si tu consulta excede alguno de estos límites, será cancelada automáticamente. Para evitarlo:
- Agrega filtros de fecha más estrechos
- Selecciona menos columnas
- Usa
LIMIT
- Pre-agrega con
GROUP BY en lugar de traer filas individuales
Patrones Comunes
JOIN entre tablas de hechos
Puedes cruzar tablas usando conversation_id o survey_user_id:
-- Mensajes de una conversación con datos de la conversación
SELECT
fc.conversation_id,
fc.agent_name,
fm.created_at AS mensaje_fecha,
fm.sender,
fm.content
FROM client_analytics.fact_conversations fc
INNER JOIN client_analytics.fact_agent_messages fm
ON fm.conversation_id = fc.conversation_id
WHERE fc.created_at >= now() - INTERVAL 7 DAY
ORDER BY fm.created_at
LIMIT 1000
JOIN con dimensiones
-- Productividad por equipo (team_name viene de la dimensión)
SELECT
at.team_name,
sum(ad.chats_handled) AS chats,
round(avg(ad.avg_first_response_sec), 0) AS respuesta_promedio_seg
FROM client_analytics.fact_agent_daily ad
INNER JOIN client_analytics.dim_agent_tags at ON at.agent_id = ad.agent_id
WHERE ad.day >= today() - 30
GROUP BY at.team_name
ORDER BY chats DESC
Nivel de servicio personalizado
-- Define tu propio umbral de SLA
SELECT
toDate(created_at) AS dia,
count() AS conversaciones,
countIf(first_response_sec <= 60) AS dentro_60s,
countIf(first_response_sec <= 120) AS dentro_120s,
countIf(first_response_sec <= 300) AS dentro_5min,
round(countIf(first_response_sec <= 120) * 100.0 / count(), 1) AS sla_pct
FROM client_analytics.fact_conversations
WHERE created_at >= now() - INTERVAL 30 DAY
AND first_response_sec IS NOT NULL
GROUP BY dia
ORDER BY dia DESC