← Todos los artículos

12 segundos para escanear 3000 filas, vaya

Esta semana me salvaron dos cosas: EXPLAIN ANALYZE y SET LOCAL enable_nestloop = off. La primera es la forma que tiene Postgres de mostrarte lo que el planificador de consultas hizo de verdad, no lo que cree que hará, sino tiempos de ejecución reales, recuentos de filas reales, estrategias de join reales. La segunda es una anulación limitada a la transacción que le dice al planificador “no tienes permiso para usar joins de bucles anidados en esta consulta”. La envuelves en un bloque BEGIN / COMMIT, se aplica a esa única ejecución y no se filtra a nada más de la conexión. Es fea. Es un instrumento tosco. Me dio una aceleración de 16× en una consulta donde tres reescrituras de SQL cada vez más sofisticadas empeoraron las cosas.

El problema: el nodo WindowAgg de Postgres, el que ejecuta tus ROW_NUMBER(), RANK() y SUM() OVER (...), no propaga las estimaciones de cardinalidad. El planificador evalúa la salida de la función de ventana, entra en pánico y estima rows=1. Si esa función de ventana está dentro de una vista que alimenta joins posteriores, esa única mala estimación se propaga a todas las decisiones de join siguientes.

El planificador elige bucles anidados porque los bucles anidados son óptimos para una fila. Tú no tienes una fila. Tienes miles. Cada iteración vuelve a escanear una vista de varios miles de filas. Se generan millones de filas intermedias y se descartan al instante. Tu consulta tarda 12 segundos con 3000 filas de origen y pierdes una hora de tu vida probando reescrituras que fallan todas, porque el error de estimación de fondo envenena cualquier forma de consulta que le eches. SET LOCAL enable_nestloop = off fuerza hash joins, que devoran los datos reales en menos de un segundo. Es una limitación conocida, sin una solución limpia en Postgres gestionado. A veces la solución pragmática es la correcta.