Hice un violín de SQL para esta pregunta si eso facilita las cosas a cualquiera.
Tengo una especie de base de datos de deportes de fantasía y lo que estoy tratando de descubrir es cómo obtener datos de "racha actual" (como 'W2' si el equipo ha ganado sus últimos 2 enfrentamientos, o 'L1' si perdieron su último enfrentamiento después de ganar el enfrentamiento anterior, o 'T1' si empataron su enfrentamiento más reciente).
Aquí está mi esquema básico:
CREATE TABLE FantasyTeams (
team_id BIGINT NOT NULL
)
CREATE TABLE FantasyMatches(
match_id BIGINT NOT NULL,
home_fantasy_team_id BIGINT NOT NULL,
away_fantasy_team_id BIGINT NOT NULL,
fantasy_season_id BIGINT NOT NULL,
fantasy_league_id BIGINT NOT NULL,
fantasy_week_id BIGINT NOT NULL,
winning_team_id BIGINT NULL
)
Un valor de NULL
en la winning_team_id
columna indica un empate para esa coincidencia.
Aquí hay una declaración DML de muestra con algunos datos de muestra para 6 equipos y 3 semanas de enfrentamientos:
INSERT INTO FantasyTeams
SELECT 1
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 4
UNION
SELECT 5
UNION
SELECT 6
INSERT INTO FantasyMatches
SELECT 1, 2, 1, 2, 4, 44, 2
UNION
SELECT 2, 5, 4, 2, 4, 44, 5
UNION
SELECT 3, 6, 3, 2, 4, 44, 3
UNION
SELECT 4, 2, 4, 2, 4, 45, 2
UNION
SELECT 5, 3, 1, 2, 4, 45, 3
UNION
SELECT 6, 6, 5, 2, 4, 45, 6
UNION
SELECT 7, 2, 6, 2, 4, 46, 2
UNION
SELECT 8, 3, 5, 2, 4, 46, 3
UNION
SELECT 9, 4, 1, 2, 4, 46, NULL
GO
Aquí hay un ejemplo de la salida deseada (basada en el DML anterior) que tengo problemas incluso para comenzar a descubrir cómo derivar:
| TEAM_ID | STEAK_TYPE | STREAK_COUNT |
|---------|------------|--------------|
| 1 | T | 1 |
| 2 | W | 3 |
| 3 | W | 3 |
| 4 | T | 1 |
| 5 | L | 2 |
| 6 | L | 1 |
He probado varios métodos usando subconsultas y CTE, pero no puedo armarlo. Me gustaría evitar usar un cursor ya que podría tener un gran conjunto de datos para ejecutar esto en el futuro. Siento que podría haber una forma de involucrar variables de tabla que unan estos datos de alguna manera, pero todavía estoy trabajando en ello.
Información adicional: podría haber un número variable de equipos (cualquier número par entre 6 y 10) y el total de enfrentamientos aumentará en 1 para cada equipo cada semana. ¿Alguna idea sobre cómo debo hacer esto?
fuente
bigint
para tantas columnas dondeint
probablemente harían 3) por qué todos los_
s ?! 4) Prefiero que los nombres de las tablas sean singulares, pero reconozco que no todos están de acuerdo conmigo // pero aparte de eso, lo que nos han mostrado aquí parece coherente, síRespuestas:
Como está en SQL Server 2012, puede usar un par de nuevas funciones de ventanas.
Violín de SQL
C1
calcula elstreak_type
para cada equipo y partido.C2
encuentra el anteriorstreak_type
ordenado pormatch_id desc
.C3
genera una suma acumuladastreak_sum
ordenadamatch_id desc
manteniendo un valor0
siempre questreak_type
sea el mismo que el último valor.La consulta principal resume las rayas donde
streak_sum
está0
.fuente
LEAD()
. No hay suficientes personas que conozcan las nuevas funciones de ventanas en 2012FantasyTeams JOIN FantasyMatches
conFantasyMatches CROSS APPLY (VALUES (home_fantasy_team_id), (away_fantasy_team_id))
y por lo tanto mejorar potencialmente el rendimiento.FantasyTeams
, probablemente sea mejor unirse a la consulta principal.Un enfoque intuitivo para resolver este problema es:
Esta estrategia podría vencer a la solución de función de ventana (que realiza un análisis completo de los datos) a medida que la tabla se hace más grande, suponiendo que la estrategia recursiva se implemente de manera eficiente. La clave del éxito es proporcionar índices eficientes para ubicar filas rápidamente (usando búsquedas) y evitar géneros. Los índices necesarios son:
Para ayudar en la optimización de consultas, usaré una tabla temporal para mantener las filas identificadas como parte de una racha actual. Si las rachas son típicamente cortas (como es cierto para los equipos que sigo, lamentablemente) esta tabla debería ser bastante pequeña:
Mi solución de consulta recursiva es la siguiente ( SQL Fiddle aquí ):
El texto T-SQL es bastante largo, pero cada sección de la consulta se corresponde estrechamente con el esquema general del proceso dado al comienzo de esta respuesta. La consulta se hace más larga por la necesidad de usar ciertos trucos para evitar géneros y producir un
TOP
en la parte recursiva de la consulta (que normalmente no está permitido).El plan de ejecución es relativamente pequeño y simple en comparación con la consulta. He sombreado la región de anclaje en amarillo y la parte recursiva en verde en la captura de pantalla a continuación:
Con las filas seguidas capturadas en una tabla temporal, es fácil obtener los resultados de resumen que necesita. (El uso de una tabla temporal también evita un derrame de clasificación que podría ocurrir si la consulta a continuación se combinara con la consulta recursiva principal)
La misma consulta se puede utilizar como base para actualizar la
FantasyTeams
tabla:O, si lo prefieres
MERGE
:Cualquiera de los dos enfoques produce un plan de ejecución eficiente (basado en el número conocido de filas en la tabla temporal):
Finalmente, debido a que el método recursivo naturalmente incluye el
match_id
en su procesamiento, es fácil agregar una lista de losmatch_id
s que forman cada racha a la salida:Salida:
Plan de ejecución:
fuente
EXISTS (... INTERSECT ...)
lugar de soloStreaks.streak_type = CASE ...
? Sé que el método anterior puede ser útil cuando necesita unir NULL en ambos lados, así como valores, pero no es como si la parte correcta pudiera producir NULL en este caso, así que ...CASE
se utiliza, el optimizador no puede utilizar una concatenación de fusión (que conserva el orden de las claves de unión) y utiliza una concatenación más clases en su lugar.Otra forma de obtener el resultado es mediante un CTE recursivo.
Demostración de SQLFiddle
fuente