Estándares de Revisión de Consultas
Las nuevas consultas SQL en los PRs de la aplicación necesitan el mismo rigor que la revisión de contratos de API. Requiere evidencia: EXPLAIN, estimaciones de filas y justificación de índices antes de fusionar a las rutas de producción.
Receta
Tarjeta de receta de referencia rápida, lista para copiar y pegar.
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT o.id, o.total
FROM orders o
WHERE o.customer_id = $1
AND o.created_at >= now() - interval '90 days'
ORDER BY o.created_at DESC
LIMIT 50;## Lista de Verificación de Revisión SQL de PR
- [ ] EXPLAIN adjuntado desde staging con estadísticas similares a producción
- [ ] Filas estimadas dentro de 10x de las reales (ANALYZE actual)
- [ ] Justificación de índice o prueba de que un escaneo secuencial es aceptable
- [ ] No usar SELECT * en rutas críticas
- [ ] Los tipos de parámetros coinciden con los tipos de columna (sin conversión implícita)Cuándo usar esto: Cualquier PR que toque SQL del repositorio, consultas crudas de ORM, definiciones de informes o funciones generadas por migraciones.
Ejemplo de Trabajo
El desarrollador añade una consulta de panel que filtra orders por status y created_at.
-- Antes de fusionar: el revisor ejecuta en staging
EXPLAIN (ANALYZE, BUFFERS)
SELECT count(*) FROM orders
WHERE status = 'pending'
AND created_at > '2026-01-01';
-- El plan muestra un escaneo secuencial en 12M de filas, 4.2s
-- El revisor solicita evidencia de índice
CREATE INDEX CONCURRENTLY orders_status_created_idx
ON orders (status, created_at DESC);
EXPLAIN (ANALYZE, BUFFERS)
SELECT count(*) FROM orders
WHERE status = 'pending'
AND created_at > '2026-01-01';
-- Escaneo de índice, 45msLo que esto demuestra:
- Adjuntar planes antes y después al añadir índices.
ANALYZEen staging después de cargas de datos grandes antes de confiar en las estimaciones.- El orden de las columnas del índice multicolumna coincide con el patrón de filtro + ordenación.
- La migración para el índice se envía en un paso de despliegue separado cuando el nivel de riesgo lo requiere.
Análisis Profundo
Artefactos Requeridos en el PR
| Artefacto | Propósito |
|---|---|
EXPLAIN (ANALYZE, BUFFERS) | Demostrar la ruta de acceso y la E/S |
Estimación de rows vs. real | Detectar estadísticas desactualizadas |
| Estimación de frecuencia de llamadas | Costo = tiempo x QPS |
| Sensibilidad a bloqueos | FOR UPDATE en bucles de API |
Sanidad de la Estimación de Filas
SELECT relname, last_analyze, last_autoanalyze, n_live_tup
FROM pg_stat_user_tables
WHERE relname = 'orders';Si las estimaciones difieren en 100x, ejecuta ANALYZE orders (o corrige estadísticas extendidas) antes de aprobar el gasto en índices.
Cuándo un Escaneo Secuencial Está Bien
- Tablas de dimensión pequeñas (< 10k filas) consultadas una vez por solicitud.
- Informe de administración único con tiempo de espera de sentencia y horario fuera de horas pico.
- La consulta devuelve una gran fracción de la tabla donde el índice + la obtención del heap cuestan más.
Documenta el "escaneo secuencial aceptado" con prueba de recuento de filas en el comentario del PR.
ORM y N+1
-- Detectar patrón: 1 + N consultas en el rastro APM
-- Corregir: JOIN o WHERE id = ANY($1::bigint[])
SELECT * FROM line_items WHERE order_id = ANY($1::bigint[]);Trampas Comunes
- EXPLAIN sin ANALYZE - Muestra solo estimaciones; oculta el tiempo de ejecución. Corrección: Requerir ANALYZE en staging para rutas críticas.
- Revisión contra tablas vacías - Escaneos de índice instantáneos en 0 filas. Corrección: Rellenar staging al 10-30% de los recuentos de filas de producción.
- Conversión implícita que mata el índice -
WHERE created_at > $1con parámetro de texto. Corrección: Coincidir tipos; convertir el parámetro, no la columna. - Falta de LIMIT en ordenación - Ordena millones de filas para la página 1 de la interfaz de usuario. Corrección: Paginación por clave o filtros más estrictos.
- Nuevo índice por PR - La proliferación de índices ralentiza las escrituras. Corrección: Consolidar índices multicolumna; revisar Manejo de Solicitudes de Índices.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Vista materializada | Agregado costoso reutilizado | Se requiere frescura < 1 minuto |
| Columna desnormalizada | Forma estable con muchas lecturas | Núcleo normalizado con muchas escrituras |
| Solo pg_stat_statements | Auditoría post-despliegue | Puerta de enlace pre-fusión |
| Reescrittura de consulta | Mal orden de unión | Solo falta de estadísticas |
Preguntas Frecuentes
¿Quién realiza la revisión de consultas?
DBA o revisor de SQL designado; el líder técnico establece el estándar, no debe ser un cuello de botella en cada PR.
¿Se permite EXPLAIN de producción?
Réplicas de solo lectura sí para SELECT; nunca DDL experimental en producción para revisión.
¿Qué QPS requiere revisión obligatoria?
Política del equipo; un umbral común es cualquier consulta esperada > 10 QPS o que toque > 1M de filas.
¿Cómo revisar SQL oculto de GraphQL/ORM?
Habilitar el registro de SQL en las pruebas de integración de staging; capturar planes en artefactos de CI.
¿Necesitan las escrituras EXPLAIN?
EXPLAIN funciona para INSERT/UPDATE con cuidado; enfócate en el costo de bloqueo y los disparadores.
¿Qué pasa con las sentencias preparadas?
Los planes pueden diferir; prueba con la misma ruta de preparación/ejecución que usa PgBouncer.
¿Deben los revisores ejecutar VACUUM?
No en revisión; asegúrate de que autovacuum esté saludable para que las estadísticas de ANALYZE sean confiables.
¿Cómo almacenar EXPLAIN en un PR?
Pega el plan de texto o enlaza el artefacto de CI; almacena en el ticket de migración para índices.
¿Son las CTEs siempre vallas de optimización?
PostgreSQL 12+ integra muchas CTEs; aún verifica el plan después de la reescritura.
¿Qué debo leer a continuación?
Ver Manejo de Solicitudes de Índices para el flujo de aprobación.
Relacionado
- Manejo de Solicitudes de Índices - aprobación de índices
- Conceptos Básicos de EXPLAIN - lectura de planes
- Revisión de Código para SQL - flujo de git
- pg_stat_statements - auditoría de producción
Versiones de Stack: Esta página fue escrita para PostgreSQL 18.4 (estable 18, mantenimiento 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, y PostGIS 3.5+.