Ein intuitiver Ansatz zur Lösung dieses Problems ist:
- Finden Sie das aktuellste Ergebnis für jedes Team
- Überprüfen Sie die vorherige Übereinstimmung und addieren Sie eine zur Streifenanzahl, wenn der Ergebnistyp übereinstimmt
- Wiederholen Sie Schritt 2, hören Sie jedoch auf, sobald das erste unterschiedliche Ergebnis festgestellt wird
Diese Strategie könnte sich gegenüber der Fensterfunktionslösung (die einen vollständigen Scan der Daten durchführt) durchsetzen, wenn die Tabelle größer wird, vorausgesetzt, die rekursive Strategie wird effizient implementiert. Der Schlüssel zum Erfolg besteht darin, effiziente Indizes bereitzustellen, um Zeilen schnell zu finden (mithilfe von Suchen) und Sortierungen zu vermeiden. Die benötigten Indizes sind:
-- 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);
Um die Abfrageoptimierung zu unterstützen, verwende ich eine temporäre Tabelle, um Zeilen zu speichern, die als Teil eines aktuellen Strips identifiziert wurden. Wenn die Streifen normalerweise kurz sind (was leider für die Mannschaften gilt, denen ich folge), sollte diese Tabelle recht klein sein:
-- 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);
Meine Lösung für rekursive Abfragen lautet wie folgt ( SQL Fiddle hier ):
-- 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);
Der T-SQL-Text ist ziemlich lang, aber jeder Abschnitt der Abfrage entspricht genau dem allgemeinen Ablauf, der zu Beginn dieser Antwort angegeben wurde. Die Abfrage wird länger, da bestimmte Tricks verwendet werden müssen, um Sortierungen zu vermeiden und einen TOP
rekursiven Teil der Abfrage zu erzeugen (was normalerweise nicht zulässig ist).
Der Ausführungsplan ist im Vergleich zur Abfrage relativ klein und einfach. Ich habe den Ankerbereich gelb und den rekursiven Teil grün im folgenden Screenshot schattiert:
Mit den in einer temporären Tabelle erfassten Streifenzeilen ist es einfach, die gewünschten Zusammenfassungsergebnisse zu erhalten. (Durch die Verwendung einer temporären Tabelle wird auch ein Sortierungsverlust vermieden, der auftreten kann, wenn die folgende Abfrage mit der rekursiven Hauptabfrage kombiniert wird.)
-- 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;
Dieselbe Abfrage kann als Grundlage für die Aktualisierung der FantasyTeams
Tabelle verwendet werden:
-- 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;
Oder wenn Sie es vorziehen 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;
Bei beiden Ansätzen wird ein effizienter Ausführungsplan erstellt (basierend auf der bekannten Anzahl von Zeilen in der temporären Tabelle):
Schließlich match_id
ist es einfach, eine Liste der match_id
s, die jeden Streifen bilden, zur Ausgabe hinzuzufügen , da die rekursive Methode natürlich die in ihrer Verarbeitung enthält :
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;
Ausgabe:
Ausführungsplan: