Une approche intuitive pour résoudre ce problème est la suivante:
- Trouvez le résultat le plus récent pour chaque équipe
- Vérifiez la correspondance précédente et ajoutez-en une au nombre de séquences si le type de résultat correspond
- Répétez l'étape 2 mais arrêtez dès que le premier résultat différent est rencontré
Cette stratégie pourrait l'emporter sur la solution de fonction de fenêtre (qui effectue une analyse complète des données) à mesure que la table s'agrandit, en supposant que la stratégie récursive est mise en œuvre efficacement. La clé du succès est de fournir des index efficaces pour localiser rapidement les lignes (à l'aide de recherches) et éviter les tris. Les index nécessaires sont:
-- New index #1
CREATE UNIQUE INDEX uq1 ON dbo.FantasyMatches
(home_fantasy_team_id, match_id)
INCLUDE (winning_team_id);
-- New index #2
CREATE UNIQUE INDEX uq2 ON dbo.FantasyMatches
(away_fantasy_team_id, match_id)
INCLUDE (winning_team_id);
Pour aider à l'optimisation des requêtes, j'utiliserai une table temporaire pour contenir les lignes identifiées comme faisant partie d'une séquence en cours. Si les séquences sont généralement courtes (comme c'est malheureusement le cas pour les équipes que je suis), ce tableau devrait être assez petit:
-- Table to hold just the rows that form streaks
CREATE TABLE #StreakData
(
team_id bigint NOT NULL,
match_id bigint NOT NULL,
streak_type char(1) NOT NULL,
streak_length integer NOT NULL,
);
-- Temporary table unique clustered index
CREATE UNIQUE CLUSTERED INDEX cuq ON #StreakData (team_id, match_id);
Ma solution de requête récursive est la suivante ( SQL Fiddle ici ):
-- Solution query
WITH Streaks AS
(
-- Anchor: most recent match for each team
SELECT
FT.team_id,
CA.match_id,
CA.streak_type,
streak_length = 1
FROM dbo.FantasyTeams AS FT
CROSS APPLY
(
-- Most recent match
SELECT
T.match_id,
T.streak_type
FROM
(
SELECT
FM.match_id,
streak_type =
CASE
WHEN FM.winning_team_id = FM.home_fantasy_team_id
THEN CONVERT(char(1), 'W')
WHEN FM.winning_team_id IS NULL
THEN CONVERT(char(1), 'T')
ELSE CONVERT(char(1), 'L')
END
FROM dbo.FantasyMatches AS FM
WHERE
FT.team_id = FM.home_fantasy_team_id
UNION ALL
SELECT
FM.match_id,
streak_type =
CASE
WHEN FM.winning_team_id = FM.away_fantasy_team_id
THEN CONVERT(char(1), 'W')
WHEN FM.winning_team_id IS NULL
THEN CONVERT(char(1), 'T')
ELSE CONVERT(char(1), 'L')
END
FROM dbo.FantasyMatches AS FM
WHERE
FT.team_id = FM.away_fantasy_team_id
) AS T
ORDER BY
T.match_id DESC
OFFSET 0 ROWS
FETCH FIRST 1 ROW ONLY
) AS CA
UNION ALL
-- Recursive part: prior match with the same streak type
SELECT
Streaks.team_id,
LastMatch.match_id,
Streaks.streak_type,
Streaks.streak_length + 1
FROM Streaks
CROSS APPLY
(
-- Most recent prior match
SELECT
Numbered.match_id,
Numbered.winning_team_id,
Numbered.team_id
FROM
(
-- Assign a row number
SELECT
PreviousMatches.match_id,
PreviousMatches.winning_team_id,
PreviousMatches.team_id,
rn = ROW_NUMBER() OVER (
ORDER BY PreviousMatches.match_id DESC)
FROM
(
-- Prior match as home or away team
SELECT
FM.match_id,
FM.winning_team_id,
team_id = FM.home_fantasy_team_id
FROM dbo.FantasyMatches AS FM
WHERE
FM.home_fantasy_team_id = Streaks.team_id
AND FM.match_id < Streaks.match_id
UNION ALL
SELECT
FM.match_id,
FM.winning_team_id,
team_id = FM.away_fantasy_team_id
FROM dbo.FantasyMatches AS FM
WHERE
FM.away_fantasy_team_id = Streaks.team_id
AND FM.match_id < Streaks.match_id
) AS PreviousMatches
) AS Numbered
-- Most recent
WHERE
Numbered.rn = 1
) AS LastMatch
-- Check the streak type matches
WHERE EXISTS
(
SELECT
Streaks.streak_type
INTERSECT
SELECT
CASE
WHEN LastMatch.winning_team_id IS NULL THEN 'T'
WHEN LastMatch.winning_team_id = LastMatch.team_id THEN 'W'
ELSE 'L'
END
)
)
INSERT #StreakData
(team_id, match_id, streak_type, streak_length)
SELECT
team_id,
match_id,
streak_type,
streak_length
FROM Streaks
OPTION (MAXRECURSION 0);
Le texte T-SQL est assez long, mais chaque section de la requête correspond étroitement au schéma général du processus donné au début de cette réponse. La requête est allongée par la nécessité d’utiliser certaines astuces pour éviter les tris et produire unTOP
dans la partie récursive de la requête (ce qui n'est normalement pas autorisé).
Le plan d'exécution est relativement petit et simple par rapport à la requête. J'ai ombré la région d'ancrage en jaune et la partie récursive en vert dans la capture d'écran ci-dessous:
Avec les lignes de séquence capturées dans un tableau temporaire, il est facile d'obtenir les résultats récapitulatifs dont vous avez besoin. (L'utilisation d'une table temporaire évite également un déversement de tri qui pourrait se produire si la requête ci-dessous était combinée avec la requête récursive principale)
-- Basic results
SELECT
SD.team_id,
StreakType = MAX(SD.streak_type),
StreakLength = MAX(SD.streak_length)
FROM #StreakData AS SD
GROUP BY
SD.team_id
ORDER BY
SD.team_id;
La même requête peut être utilisée comme base pour la mise à jour de la FantasyTeams
table:
-- Update team summary
WITH StreakData AS
(
SELECT
SD.team_id,
StreakType = MAX(SD.streak_type),
StreakLength = MAX(SD.streak_length)
FROM #StreakData AS SD
GROUP BY
SD.team_id
)
UPDATE FT
SET streak_type = SD.StreakType,
streak_count = SD.StreakLength
FROM StreakData AS SD
JOIN dbo.FantasyTeams AS FT
ON FT.team_id = SD.team_id;
Ou, si vous préférez MERGE
:
MERGE dbo.FantasyTeams AS FT
USING
(
SELECT
SD.team_id,
StreakType = MAX(SD.streak_type),
StreakLength = MAX(SD.streak_length)
FROM #StreakData AS SD
GROUP BY
SD.team_id
) AS StreakData
ON StreakData.team_id = FT.team_id
WHEN MATCHED THEN UPDATE SET
FT.streak_type = StreakData.StreakType,
FT.streak_count = StreakData.StreakLength;
L'une ou l'autre approche produit un plan d'exécution efficace (basé sur le nombre connu de lignes dans la table temporaire):
Enfin, parce que la méthode récursive inclut naturellement le match_id
dans son traitement, il est facile d'ajouter une liste des match_id
s qui forment chaque séquence à la sortie:
SELECT
S.team_id,
streak_type = MAX(S.streak_type),
match_id_list =
STUFF(
(
SELECT ',' + CONVERT(varchar(11), S2.match_id)
FROM #StreakData AS S2
WHERE S2.team_id = S.team_id
ORDER BY S2.match_id DESC
FOR XML PATH ('')
), 1, 1, ''),
streak_length = MAX(S.streak_length)
FROM #StreakData AS S
GROUP BY
S.team_id
ORDER BY
S.team_id;
Production:
Plan d'exécution: