Salah satu pendekatan intuitif untuk menyelesaikan masalah ini adalah:
- Temukan hasil terbaru untuk setiap tim
- Periksa kecocokan sebelumnya dan tambahkan satu ke jumlah coretan jika jenis hasil cocok
- Ulangi langkah 2 tetapi hentikan segera setelah hasil berbeda yang pertama ditemukan
Strategi ini mungkin menang atas solusi fungsi jendela (yang melakukan pemindaian penuh data) karena tabel tumbuh lebih besar, dengan asumsi strategi rekursif diterapkan secara efisien. Kunci keberhasilan adalah menyediakan indeks yang efisien untuk menemukan baris dengan cepat (menggunakan pencarian) dan menghindari pengurutan. Indeks yang dibutuhkan adalah:
-- 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);
Untuk membantu dalam optimasi kueri, saya akan menggunakan tabel sementara untuk menahan baris yang diidentifikasi sebagai bagian dari rangkaian beruntun saat ini. Jika goresan biasanya pendek (seperti halnya untuk tim yang saya ikuti, sayangnya) tabel ini harus cukup kecil:
-- 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);
Solusi kueri rekursif saya adalah sebagai berikut ( SQL Fiddle di sini ):
-- 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);
Teks T-SQL cukup panjang, tetapi setiap bagian dari kueri terkait erat dengan garis besar proses yang diberikan pada awal jawaban ini. Permintaan dibuat lebih lama dengan kebutuhan untuk menggunakan trik tertentu untuk menghindari jenis dan untuk menghasilkan TOP
bagian rekursif dari permintaan (yang biasanya tidak diizinkan).
Paket eksekusi relatif kecil dan sederhana jika dibandingkan dengan kueri. Saya telah memberi warna kuning pada area jangkar, dan bagian rekursif berwarna hijau pada tangkapan layar di bawah ini:
Dengan deretan baris yang ditangkap dalam tabel sementara, mudah untuk mendapatkan hasil ringkasan yang Anda butuhkan. (Menggunakan tabel sementara juga menghindari tumpahan pengurutan yang mungkin terjadi jika kueri di bawah ini dikombinasikan dengan permintaan rekursif utama)
-- 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;
Kueri yang sama dapat digunakan sebagai dasar untuk memperbarui FantasyTeams
tabel:
-- 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;
Atau, jika Anda lebih suka 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;
Salah satu pendekatan menghasilkan rencana eksekusi yang efisien (berdasarkan jumlah baris yang diketahui dalam tabel sementara):
Akhirnya, karena metode rekursif secara alami menyertakan match_id
dalam pemrosesan, mudah untuk menambahkan daftar match_id
s yang membentuk setiap goresan ke output:
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;
Keluaran:
Rencana eksekusi: