이 문제를 해결하기위한 직관적 인 접근 방식은 다음과 같습니다.
- 각 팀의 최신 결과 찾기
- 결과 유형이 일치하면 이전 일치 항목을 확인하고 연속 수에 하나를 추가하십시오.
- 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 here ).
-- 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
각 행진을 형성 하는 의 목록을 출력에 쉽게 추가 할 수 있습니다 .
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;
산출:
실행 계획 :