SQL (ANSI)ProgramaciónDesarrollador SQL

Calcule el número máximo de reservas activas simultáneamente dentro de un sistema de reservas de hotel dado los sellos de tiempo de check-in y check-out, utilizando estrictamente operaciones basadas en conjuntos de ANSI SQL sin recurrir al desglose temporal o bucles procedimentales?

Supere entrevistas con el asistente de IA Hintsage

Respuesta a la pregunta

Historia de la pregunta: El análisis de intervalos temporales ha desafiado a las bases de datos relacionales desde la década de 1970, ya que SQL fue diseñado originalmente sin tipos de intervalos nativos. Las primeras soluciones se basaron en la iteración basada en cursores o en productos cartesianos entre intervalos, resultando en una complejidad cuadrática. La introducción de funciones de ventana en SQL:2003 y la especificación de marco ROWS BETWEEN permitió agregados en ejecución eficientes, estableciendo la base para los cálculos de concurrencia basados en eventos modernos.

El problema: Determinar la máxima concurrencia requiere comprender los momentos precisos en los que ocurren cambios de estado, específicamente cuando comienza o termina una reserva. El enfoque ingenuo expande cada intervalo en unidades de tiempo discretas (desglose temporal), lo cual es computacionalmente prohibitivo para estancias de larga duración. El desafío central radica en calcular cuántos intervalos se superponen en un punto específico sin materializar cada momento de la línea de tiempo.

La solución: Emplear un patrón de simulación de eventos discretos. Transformar la tabla de intervalos en una corriente de eventos utilizando UNION ALL, asignando un peso de +1 a cada check-in (inicio) y -1 a cada check-out (fin). Al ordenar cronológicamente estos eventos y aplicar una suma acumulativa mediante funciones de ventana SUM() OVER (ORDER BY ...), se obtiene el conteo activo en cada punto de transición. El valor máximo de esta suma acumulativa representa la máxima concurrencia.

WITH events AS ( SELECT check_in AS event_time, 1 AS delta FROM reservations UNION ALL SELECT check_out AS event_time, -1 AS delta FROM reservations ), concurrency AS ( SELECT event_time, SUM(delta) OVER ( ORDER BY event_time, delta DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS active_count FROM events ) SELECT MAX(active_count) AS peak_concurrency FROM concurrency;

Situación de la vida real

Descripción del problema: Una cadena de resorts de lujo experimentó incidentes de sobreventa misteriosos durante los fines de semana festivos a pesar de que su sistema de disponibilidad reportaba vacantes. La consulta heredada calculaba la ocupación diaria expandiendo cada reserva en filas nocturnas individuales utilizando un CTE recursivo, generando millones de filas para estancias de un mes. Durante el análisis de la Noche de Año Nuevo, este enfoque requería 12 segundos para ejecutarse y bloqueaba la tabla de transacciones de reservas, impidiendo reservas en tiempo real.

Solución A: Expansión de desglose de tiempo con tablas de conteo. El equipo de operaciones sugirió inicialmente pre-generar una tabla de calendario y unirla contra las reservas usando event_date BETWEEN check_in AND check_out. Este método proporciona agregados diarios intuitivos compatibles con cláusulas estándar de GROUP BY. Pros: Conceptualmente simple para analistas de negocios, fácil de integrar con herramientas de BI existentes. Contras: Genera filas O(N × D) donde D es la duración promedio, causando un crecimiento exponencial; falla catastróficamente con granularidad a nivel de minutos o arrendamientos a largo plazo; consume espacio excesivo en tempdb.

Solución B: Árbol de intervalos con rutas materializadas. Un arquitecto senior propuso implementar una estructura de árbol de segmentos utilizando conjuntos anidados para indexar los límites de intervalos, permitiendo consultas de superposición en tiempo logarítmico. Pros: Complejidad teórica óptima para actualizaciones frecuentes y consultas puntuales. Contras: Requiere disparadores complejos para mantener el equilibrio del árbol; viola la portabilidad de ANSI SQL al depender de extensiones procedimentales; introduce amplificación de escritura que daña la carga de trabajo OLTP durante picos de reservas.

Solución C: Corriente de eventos cronológicos con sumas acumulativas (elegida). El equipo de bases de datos adoptó el enfoque basado en eventos, tratando cada límite de reserva como una operación delta. Esto redujo el conjunto de datos de millones de filas explotadas a exactamente 2N eventos (inicio y fin para cada reserva). Pros: Complejidad O(N log N) dominada por la operación de ordenamiento, huella de memoria constante y pura compatibilidad de ANSI SQL en PostgreSQL, Oracle y SQL Server. Contras: Requiere un manejo cuidadoso de eventos simultáneos y no identifica inherentemente qué reservas específicas contribuyeron al pico sin uniones adicionales.

Resultado: La latencia consulta se redujo de 12 segundos a 45 milisegundos. El análisis reveló que el verdadero cuello de botella no era el inventario de habitaciones (500 unidades) sino la capacidad del elevador, ya que 320 huéspedes intentaron check-ins simultáneos a las 6 PM. Esta percepción llevó a implementar niveles de check-in escalonados en lugar de construir una nueva ala, ahorrando $2M en gastos de capital y eliminando bloqueos.

Lo que los candidatos suelen pasar por alto

¿Por qué la solución requiere ORDER BY event_time, delta DESC específicamente, y qué sucede si omites el ordenamiento secundario en delta?

Los candidatos frecuentemente ignoran la semántica de las condiciones límite en marcas de tiempo compartidas. Cuando un huésped hace el check-out exactamente a las 10:00 AM y otro hace el check-in a las 10:00 AM, el orden de procesamiento determina si la habitación parece ocupada por dos huéspedes simultáneamente. Al ordenar con delta DESC, aseguramos que -1 (salida) se procese antes que +1 (llegada) en marcas de tiempo idénticas. Sin este ordenamiento secundario, la suma acumulativa temporalmente baja y luego salta, registrando potencialmente un pico fantasma cuando el estado anterior era en realidad más alto. Este sutil ordenamiento previene errores off-by-one que llevan a vulnerabilidades de sobreventa en sistemas de producción.

¿Cómo modificarías esta consulta para identificar qué reservas específicas estaban activas durante el momento de máxima concurrencia, no solo el conteo?

La mayoría de los candidatos intentan filtrar dentro del mismo CTE, sin reconocer que el pico puede abarcar un intervalo continuo en lugar de un solo punto. El enfoque robusto requiere una estrategia de dos pasos: primero, aislar la marca de tiempo donde active_count es igual al máximo usando una subconsulta o CTE, luego unirse de nuevo a la tabla original de reservas usando el predicado de superposición r.check_in <= peak.event_time AND r.check_out > peak.event_time. Los candidatos pasan por alto que múltiples marcas de tiempo pueden compartir el mismo valor máximo, lo que necesita una cláusula DISTINCT o EXISTS para evitar listados duplicados de reservas cuando el pico se mantiene a través de varias transiciones de eventos.

¿Qué modificaciones son necesarias si las reglas comerciales cambian de modo que el tiempo de check-out sea inclusivo (el huésped ocupa la habitación hasta las 11:59 PM) en lugar de exclusivo, y cómo afecta esto al peso del evento?

Los candidatos a menudo pasan por alto la semántica de los límites de intervalos. Los puntos finales inclusivos [inicio, fin] crean superposiciones cuando una reserva termina y otra comienza el mismo día. La solución requiere convertir límites inclusivos en exclusivos añadiendo una épsilon infinitesimal (o la siguiente unidad de tiempo discreta) a los tiempos de check-out antes de generar el evento -1. Alternativamente, ajustar la lógica de unión para utilizar check_out >= event_time manteniendo intacta la lógica de suma acumulativa. No ajustar esto transforma el modelo de evento discreto de intervalos semi-abiertos a intervalos cerrados, causando que el algoritmo informe conflictos donde no existen y subestime la verdadera capacidad en exactamente una unidad durante períodos de alta rotación.