2 visitas

10 Estrategias para Optimizar Queries SQL en PostgreSQL (sin ORMs, sin excusas)

Optimizar queries SQL no es un arte oscuro reservado para DBAs. Es una responsabilidad compartida, sobre todo cuando trabajamos con bases de datos PostgreSQL en aplicaciones que necesitan escalar o simplemente dejar de arrastrarse. Si estás lidiando con apps legacy llenas de consultas gigantes y poco documentadas, o si estás empezando desde cero con una arquitectura moderna, estos 10 puntos te pueden ahorrar dolores de cabeza y muchos EXPLAIN ANALYZE.

1. Usa índices… pero con cabeza

Crear un índice no es gratis. Sí, puede mejorar la lectura, pero penaliza escritura y ocupa espacio. Asegúrate de que realmente lo necesitas.

-- Útil si siempre filtras por "status"
CREATE INDEX idx_orders_status ON orders(status);

Tip rápido:

No indexes columnas con alta cardinalidad si tu filtro no las usa en WHERE o JOINs.


2. EXPLAIN ANALYZE es tu mejor amigo

Antes de optimizar, entiende el plan de ejecución. EXPLAIN ANALYZE te dice exactamente qué está haciendo PostgreSQL y cuánto tarda.

EXPLAIN ANALYZE
SELECT * FROM orders WHERE status = 'shipped';

¿Ves un "Seq Scan" en una tabla grande? Eso es una bandera roja.


3. **Evita SELECT ***

Obvio, pero aún muy común. Seleccionar todas las columnas cuando solo necesitas dos es desperdiciar ancho de banda y RAM.

-- Malo
SELECT * FROM users;

-- Mejor
SELECT id, email FROM users;

4. Agrega filtros antes de hacer JOINs

Filtrar antes de hacer JOINs puede reducir drásticamente el número de registros intermedios.

-- Peor
SELECT * FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.country = 'AR';

-- Mejor
SELECT * FROM (
  SELECT * FROM users WHERE country = 'AR'
) u
JOIN orders o ON o.user_id = u.id;

5. Cuidado con funciones en WHERE

Usar funciones sobre columnas rompe el uso de índices. Evítalo o reescribe el query.

-- Evita esto
SELECT * FROM orders WHERE date_trunc('day', created_at) = '2025-08-01';

-- Mejor
SELECT * FROM orders
WHERE created_at >= '2025-08-01'
  AND created_at < '2025-08-02';

6. Utiliza CTEs, pero no abuses

Los CTEs (WITH) son útiles para legibilidad, pero antes de PostgreSQL 12 eran tratados como barreras de optimización. Desde la versión 12, ya no lo son por defecto, pero sigue siendo importante medir el impacto.

WITH recent_orders AS (
  SELECT * FROM orders WHERE created_at > NOW() - INTERVAL '30 days'
)
SELECT * FROM recent_orders WHERE status = 'pending';

Usa EXPLAIN para asegurarte de que no te está costando rendimiento.


7. LIMIT + ORDER BY bien usados salvan vidas

Cuando necesitas los primeros n resultados, asegúrate de tener un índice que soporte ese ORDER BY.

-- Index que soporta esto:
CREATE INDEX idx_orders_created_at ON orders(created_at DESC);

-- Consulta eficiente:
SELECT * FROM orders ORDER BY created_at DESC LIMIT 10;

8. Particiona tablas si hay mucho histórico

Si tienes una tabla logs con millones de registros por año, el particionamiento puede hacer que ciertas consultas pasen de segundos a milisegundos.

CREATE TABLE logs_2025 (
  CHECK (created_at >= '2025-01-01' AND created_at < '2026-01-01')
) INHERITS (logs);

Esto se puede automatizar con pg_partman o scripts propios.


9. Precalcula si lo vas a consultar mucho

Si haces el mismo cálculo caro 100 veces por minuto, considera usar una tabla materializada.

CREATE MATERIALIZED VIEW top_users AS
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
ORDER BY order_count DESC;

-- Luego:
REFRESH MATERIALIZED VIEW top_users;

10. Cuida el tipo de datos y las conversiones

Hacer casts implícitos fuerza scans innecesarios. PostgreSQL no puede usar el índice si estás comparando id::text con una cadena.

-- Evita esto
SELECT * FROM users WHERE id::text = '123';

-- Mejor
SELECT * FROM users WHERE id = 123;

Diferencias entre apps legacy y modernas

Característica Apps Legacy Apps Nuevas
Indexación Mal planificada o inexistente Pensada desde el diseño
Uso de EXPLAIN Casi nunca Parte del workflow
Tipos de datos Genéricos (text everywhere) Definidos con precisión
Particionamiento Ausente o hecho a mano Automatizado o planeado
Materialización Evitada por complejidad Usada para acelerar respuestas
Código SQL Incrustado y duplicado Modular y probado
Cultura de performance Reactiva (cuando ya es lento) Proactiva (tests, monitoreo)

Cierre

La diferencia entre una query que tarda 5 segundos y una que tarda 20ms muchas veces es solo cuestión de escribir mejor SQL.

No necesitas ser experto en PostgreSQL, pero sí necesitas medir y entender lo que escribes. A medida que crecen tus datos, también debe crecer tu responsabilidad sobre cómo los consultas.

¿Tienes alguna otra técnica que usás para mejorar tus queries? Comentala acá 👇

10 Estrategias para Optimizar Queries SQL en PostgreSQL (sin ORMs, sin excusas)
03 August 2025

Me dedico a crear soluciones web eficientes y a compartir mi conocimiento con la comunidad de desarrolladores.

Alejandro Leone
Backend Developer