Một cách tiếp cận trực quan để giải quyết vấn đề này là:
- Tìm kết quả gần đây nhất cho mỗi đội
- Kiểm tra trận đấu trước và thêm một vào số đếm nếu loại kết quả khớp
- Lặp lại bước 2 nhưng dừng ngay khi gặp kết quả khác nhau đầu tiên
Chiến lược này có thể chiến thắng giải pháp chức năng cửa sổ (thực hiện quét toàn bộ dữ liệu) khi bảng phát triển lớn hơn, giả sử chiến lược đệ quy được thực hiện hiệu quả. Chìa khóa thành công là cung cấp các chỉ mục hiệu quả để xác định vị trí các hàng một cách nhanh chóng (sử dụng tìm kiếm) và tránh sắp xếp. Các chỉ số cần thiết là:
-- 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);
Để hỗ trợ tối ưu hóa truy vấn, tôi sẽ sử dụng một bảng tạm thời để giữ các hàng được xác định là một phần của chuỗi hiện tại. Nếu các vệt thường ngắn (như đúng với các đội tôi theo dõi, thật đáng buồn) thì bảng này khá nhỏ:
-- 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);
Giải pháp truy vấn đệ quy của tôi như sau ( SQL Fiddle tại đây ):
-- 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);
Văn bản T-SQL khá dài, nhưng mỗi phần của truy vấn tương ứng chặt chẽ với phác thảo quy trình rộng được đưa ra khi bắt đầu câu trả lời này. Truy vấn được thực hiện lâu hơn bởi nhu cầu sử dụng một số thủ thuật nhất định để tránh sắp xếp và tạo ra một TOP
phần đệ quy của truy vấn (thường không được phép).
Kế hoạch thực hiện tương đối nhỏ và đơn giản bằng cách so sánh với truy vấn. Tôi đã tô màu vùng neo màu vàng và phần đệ quy màu xanh lục trong ảnh chụp màn hình bên dưới:
Với các hàng sọc được ghi trong một bảng tạm thời, thật dễ dàng để có được kết quả tóm tắt mà bạn yêu cầu. (Sử dụng bảng tạm thời cũng tránh được sự cố tràn sắp xếp có thể xảy ra nếu truy vấn bên dưới được kết hợp với truy vấn đệ quy chính)
-- 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;
Truy vấn tương tự có thể được sử dụng làm cơ sở để cập nhật FantasyTeams
bảng:
-- 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;
Hoặc, nếu bạn thích 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;
Cách tiếp cận tạo ra một kế hoạch thực hiện hiệu quả (dựa trên số lượng hàng đã biết trong bảng tạm thời):
Cuối cùng, vì phương thức đệ quy tự nhiên bao gồm match_id
trong quá trình xử lý của nó, nên dễ dàng thêm một danh sách các match_id
s tạo thành từng vệt cho đầu ra:
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;
Đầu ra:
Kế hoạch thực hiện: