วิธีการหนึ่งที่ใช้งานง่ายในการแก้ปัญหานี้คือ:
- ค้นหาผลลัพธ์ล่าสุดสำหรับแต่ละทีม
- ตรวจสอบการแข่งขันก่อนหน้าและเพิ่มหนึ่งในการนับแนวถ้าประเภทผลลัพธ์ที่ตรงกัน
- ทำซ้ำขั้นตอนที่ 2 แต่หยุดทันทีที่พบผลลัพธ์ที่แตกต่างแรก
กลยุทธ์นี้อาจชนะเหนือโซลูชันฟังก์ชั่นหน้าต่าง (ซึ่งทำการสแกนแบบเต็มของข้อมูล) เมื่อตารางโตขึ้นโดยสมมติว่ามีการใช้กลยุทธ์แบบเรียกซ้ำ กุญแจสู่ความสำเร็จคือการจัดทำดัชนีที่มีประสิทธิภาพเพื่อค้นหาแถวอย่างรวดเร็ว (ใช้การค้นหา) และเพื่อหลีกเลี่ยงการเรียงลำดับ ดัชนีที่จำเป็นคือ:
-- 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);
เพื่อช่วยในการปรับให้เหมาะสมของแบบสอบถามฉันจะใช้ตารางชั่วคราวเพื่อเก็บแถวที่ระบุว่าเป็นส่วนหนึ่งของแนวปัจจุบัน หากลายเส้นนั้นสั้น (เหมือนจริงสำหรับทีมที่ฉันติดตามเศร้า) ตารางนี้ควรมีขนาดค่อนข้างเล็ก:
-- 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);
โซลูชันคิวรีแบบเรียกซ้ำของฉันมีดังนี้ ( SQL Fiddle ที่นี่ ):
-- 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);
ข้อความ T-SQL นั้นค่อนข้างยาว แต่แต่ละส่วนของการสืบค้นนั้นสอดคล้องกับร่างกระบวนการแบบกว้างที่กำหนดไว้ในตอนต้นของคำตอบนี้ แบบสอบถามใช้เวลานานขึ้นโดยจำเป็นต้องใช้ลูกเล่นบางอย่างเพื่อหลีกเลี่ยงการเรียงลำดับและสร้างTOP
ส่วนในการสอบถามซ้ำ (ซึ่งโดยทั่วไปจะไม่ได้รับอนุญาต)
แผนการดำเนินการมีขนาดค่อนข้างเล็กและเรียบง่ายโดยเปรียบเทียบกับแบบสอบถาม ฉันได้แรเงาพื้นที่สีเหลืองและสีเขียวส่วนซ้ำในภาพด้านล่าง:
ด้วยการจับแถวริ้วในตารางชั่วคราวมันเป็นเรื่องง่ายที่จะได้รับผลลัพธ์สรุปที่คุณต้องการ (การใช้ตารางชั่วคราวยังหลีกเลี่ยงการเรียงลำดับการรั่วไหลที่อาจเกิดขึ้นหากแบบสอบถามด้านล่างรวมกับแบบสอบถามแบบเรียกซ้ำหลัก)
-- 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;
แบบสอบถามเดียวกันสามารถใช้เป็นพื้นฐานสำหรับการปรับปรุงFantasyTeams
ตาราง:
-- 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;
หรือถ้าคุณต้องการ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;
วิธีใดวิธีหนึ่งสร้างแผนการดำเนินการที่มีประสิทธิภาพ (ขึ้นอยู่กับจำนวนแถวที่รู้จักในตารางชั่วคราว):
ท้ายที่สุดเนื่องจากวิธีการแบบเรียกซ้ำรวมmatch_id
อยู่ในการประมวลผลของมันจึงเป็นเรื่องง่ายที่จะเพิ่มรายการของmatch_id
s ที่จัดรูปแบบแต่ละช่วงลงในเอาต์พุต:
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;
เอาท์พุท:
แผนการดำเนินการ: