Racha de victorias
| | | | |

Racha de un equipo

Cómo nació un ejercicio de rachas de un equipo mientras veía la tabla de posiciones

Hoy los Phillies le ganaron a los Nationals 6-3. Al revisar la tabla de posiciones de las Grandes Ligas, pasó algo que me ocurre con frecuencia: una idea técnica apareció donde no la esperaba.

No estaba pensando en SQL ni en funciones de ventana, tampoco en resolver un problema de análisis de datos. Simplemente estaba viendo las victorias y las derrotas de cada equipo cuando me surgió una pregunta muy natural:

¿Cuál es la racha actual de este equipo?

Es de esas preguntas que parecen sencillas, pero que, cuando intenta responderse con datos, esconden un reto interesante. Ahí dejé de ver una tabla de posiciones y empecé a imaginar cómo resolverlo con SQL.

La respuesta llegó casi de inmediato. No había que inventar nada raro sino mirar la secuencia como lo haría cualquier persona:

  1. Tomar el juego más reciente.
  2. Ver si fue victoria o derrota.
  3. Contar cuántos resultados iguales vienen después. En este contexto un “resultado igual” significa que todos los resultados son derrotas o todos son victorias.
  4. Detenerse cuando cambia esa racha de resultados iguales.

Ese cambio se convirtió en una bandera (flag) y la posición del cambio se convirtió en pos. Y la racha se convirtió en:

flag - 1

A partir de ese resultado nació este ejercicio, ahora convertido en una solución clara, reproducible y útil para cualquier análisis de rendimiento deportivo.

La última racha de un equipo usando SQL (sin complicaciones)

Planteamiento

Usaremos una tabla que indica las victorias (W) y derrotas (L). En este contexto no nos interesa el marcador exacto, sino si el equipo ganó (W) o perdió (L):

Games(
    team,
    game_number,   -- higher = more recent
    result         -- 'W' or 'L'
)

Objetivo:

  • Determinar la racha actual del equipo.
  • Identificar si la racha es de victorias o derrotas.
  • Evitar segmentaciones complejas.
  • Mantener la solución simple, legible y didáctica.

Simulación de datos (CTE reproducible)

Para que el lector pueda ejecutar el ejemplo sin depender de una tabla externa, incluí un CTE con datos simulados. Por simplicidad nos centraremos en la racha de los Phillies.

-- Sample data: simulated sequence of games for the team.
-- Higher game_number means a more recent game.
WITH Games AS (
    SELECT 'Phillies' AS team, 17 AS game_number, 'W' AS result UNION ALL   
    SELECT 'Phillies', 16, 'W' UNION ALL                                   
    SELECT 'Phillies', 15, 'W' UNION ALL                                   
    SELECT 'Phillies', 14, 'W' UNION ALL                                   
    SELECT 'Phillies', 13, 'W' UNION ALL                                   
    SELECT 'Phillies', 12, 'L' UNION ALL
    SELECT 'Phillies', 11, 'L' UNION ALL
    SELECT 'Phillies', 10, 'W' UNION ALL
    SELECT 'Phillies', 9,  'W' UNION ALL
    SELECT 'Phillies', 8,  'W' UNION ALL
    SELECT 'Phillies', 7,  'L' UNION ALL
    SELECT 'Phillies', 6,  'L' UNION ALL
    SELECT 'Phillies', 5,  'W' UNION ALL
    SELECT 'Mets', 16, 'L' UNION ALL                                   
    SELECT 'Mets', 15, 'L' UNION ALL                                   
    SELECT 'Mets', 14, 'W' 
),

-- CTE x:
-- Orders games from most recent to oldest.
-- pos = position in that order (1 = most recent).
-- prev = result of the previous game in the ordered sequence.
x AS (
    SELECT
        team,
        game_number,
        result,
        ROW_NUMBER() OVER (PARTITION BY team ORDER BY game_number DESC) AS pos,
        LAG(result) OVER (PARTITION BY team ORDER BY game_number DESC) AS prev
    FROM Games
),

-- CTE flags:
-- flag = 1 when the result changes compared to the previous game.
-- flag = 0 when the result is the same (streak continues).
-- pos = 1 always has flag = 0 because there is no previous game.
flags AS (
    SELECT
        team,
        pos,
        result,
        CASE 
            WHEN pos = 1 THEN 0          -- first game: no previous result
            WHEN result = prev THEN 0    -- same result: streak continues
            ELSE 1                       -- different result: streak breaks
        END AS flag
    FROM x
)

-- Final calculation:
-- streak_type = result of the most recent game (pos = 1).
-- streak_length:
--   If a flag = 1 exists: first flag position minus 1.
--   If no flag = 1 exists: entire sequence is a streak.
SELECT 
    team,

    -- Type of streak = result of the most recent game
    MAX(CASE WHEN pos = 1 THEN result END) AS streak_type,

    -- Length of the streak
    COALESCE(
        MIN(CASE WHEN flag = 1 THEN pos END) - 1,   -- first break in the streak
        MAX(pos)                                    -- if no break, full sequence is the streak
    ) AS streak_length
FROM flags
GROUP BY team;

Explicación del resultado

Secuencia de los Phillies (más reciente → más viejo):

W, W, W, W, W, L, L, W, W, W, L, L, W
  • La racha actual es:
  • tipo: W
  • longitud: 5
  • Esto es así ya que los cinco juegos más recientes son victorias, y el primer cambio ocurre en pos = 6.
  • Para los Mets, la secuencia más reciente es: L, L, W. Su racha actual es de dos derrotas (streak_type = 'L', streak_length = 2).

Reflexión final

Este ejercicio demuestra algo esencial en la enseñanza de SQL:

Las mejores soluciones no siempre son las más complejas. A veces basta con traducir la intuición humana a SQL.

La combinación de flag + pos es una buena herramienta para resolver problemas de secuencias sin recurrir a estructuras complicadas. Es un ejemplo de cómo pensar primero en el problema y luego en el lenguaje.

Extensiones posibles

  • Versión para un solo equipo
  • Versión sin CTE
  • Explicación visual paso a paso

Si desea revisar el código completo, puede descargar la solución de este ejercicio desde el repositorio de Github.

Imagen de Anne and Saturnino MirandaPixabay

Similar Posts