SQL Server循环赛

时间:2018-01-04 16:27:15

标签: sql sql-server tsql generator

我正在尝试创建一个循环风格的足球锦标赛,每支球队每周比赛一次(也只有一次),联盟包含12支球队。

使用下面的代码作为示例,您应该能够看到我有12个团队。我已经使用CTE来生成macth灯具,这样每个队伍都可以在主场和对方之间互相比赛一次(这会产生132场比赛)。

由于共有12支球队共132场比赛,因此每周应该有6场比赛,为期22周。

我如何生成每个夹具发生的周数?

DECLARE @Temp TABLE(
    TeamName VARCHAR(100),
    MatchWeek INT
)

INSERT INTO @Temp(TeamName) VALUES
('Team1'),
('Team2'),
('Team3'),
('Team4'),
('Team5'),
('Team6'),
('Team7'),
('Team8'),
('Team9'),
('Team10'),
('Team11'),
('Team12');

SELECT t1.Teamname, t2.Teamname
FROM @Temp t1, @Temp t2
WHERE t1.TeamName <> t2.TeamName

2 个答案:

答案 0 :(得分:10)

这样做。 Demo

the Wikipedia entry the algorithm was taken from中所述,它使用以下位置。

enter image description here

最后一个团队(词典排序)将固定在绿色位置。所有其他球队将围绕1-11位置轮换。使用每对的顶部和底部成员作为主队或客场球队之间的固定装置之间交替,以避免为队伍提供长序列。

如果出现奇数队,一支球队每周都没有比赛。下面通过在这种情况下创建一个虚拟空行然后使用相同的算法来处理这个问题。

WITH Teams
     AS (SELECT TeamName,
                TeamNum = ROW_NUMBER() OVER (ORDER BY TeamName),
                TeamCount = COUNT(*) OVER()
         FROM   @Temp
         /*Purpose of below is to add an extra dummy team if odd number 
           of teams. This null team name will be matched up against competitors 
           having no game that week */
         GROUP  BY TeamName WITH ROLLUP
         HAVING GROUPING(TeamName) = 0
                 OR COUNT(*) %2 = 1),
     Weeks
     AS ( /*We need numbers 1- 11 for a 12 team league etc. 
           Can use the row numbers calculated above for this*/
         SELECT TeamNum AS Week
         FROM   Teams
         WHERE  TeamNum < TeamCount),
     Positioned
     AS (SELECT TeamName,
                TeamNum,
                Week,
                position,
                TeamCount
         FROM   Teams
                CROSS JOIN Weeks
                /*Uses scheduling algorithm from Wikipedia with the last team in fixed position
                  and all other teams rotating around (between positions 1 and 11 in 12 team example)*/
                CROSS APPLY (SELECT CASE
                                      WHEN TeamNum = TeamCount THEN TeamCount
                                      ELSE 1 + ( ( TeamNum + Week - 1 ) % ( TeamCount - 1 ) )
                                    END) CA(position))
SELECT V.*
FROM   Positioned P1
       JOIN Positioned P2
         ON P1.Week = P2.Week
            /*Sum of positions should add up to TeamCount + 1*/
            AND P1.position = 1 + P2.TeamCount - P2.position
            /*Choose Home and Away from alternating Top and Bottom of pair to 
              avoid long runs of either for a team*/
            AND (P2.Week %2 = 0 AND P1.position < P2.position
                 OR P2.Week %2 = 1 AND P1.position > P2.position)
            /*For second half of the season just reversing the "Home" and "Away" teams */
       CROSS APPLY ( VALUES(P1.TeamName, P2.TeamName, P1.Week),
                           (P2.TeamName, P1.TeamName, P1.Week + P1.TeamCount - 1) ) V(HomeTeam, AwayTeam, Week)
/*Exclude any dummy matches if odd number of teams*/
WHERE  V.AwayTeam IS NOT NULL
       AND V.HomeTeam IS NOT NULL
ORDER  BY V.Week 

或者,通过使用此方法交换上述查询的最后一部分,可以通过一些聚合避免Positioned的自联接。 demo

     Positioned
     AS (SELECT TeamName,
                TeamNum,
                Week,
                position,
                TeamCount,
                /*Sum of opposing positions should add up to TeamCount + 1 so can calculate slot for grouping*/
                Slot = CASE WHEN position <= TeamCount / 2 THEN position ELSE TeamCount + 1 - position END
         FROM   Teams
                CROSS JOIN Weeks
                /*Uses scheduling algorithm from Wikipedia with the last team in fixed position
                  and all other teams rotating around (between positions 1 and 11 in 12 team example)*/
                CROSS APPLY (SELECT CASE
                                      WHEN TeamNum = TeamCount
                                        THEN TeamCount
                                      ELSE 1 + ( ( TeamNum + Week ) % ( TeamCount - 1 ) )
                                    END) CA(position)),
     Matches
     AS (SELECT Week,
                Slot,
                TeamCount,
                TopTeam = MAX(CASE WHEN position = slot THEN TeamName END),
                BottomTeam = MAX(CASE WHEN position <> slot THEN TeamName END)
         FROM   Positioned
         GROUP  BY Week,
                   Slot,
                   TeamCount)
SELECT CA.*
FROM   Matches
       CROSS APPLY (
                   /*Choose Home and Away from alternating Top and Bottom of pair to 
                     avoid long runs of either for a team*/
                   /*First two rows are for alternate weeks in the 1st half of the season */
                   SELECT TopTeam, BottomTeam, Week
                   WHERE  Week %2 = 0
                   UNION ALL
                   SELECT BottomTeam, TopTeam, Week
                   WHERE  Week %2 > 0
                   UNION ALL
                   /*For second half of the season just reversing the "Home" and "Away" teams */
                   SELECT BottomTeam, TopTeam, Week + TeamCount - 1
                   WHERE  Week %2 = 0
                   UNION ALL
                   SELECT TopTeam, BottomTeam, Week + TeamCount - 1
                   WHERE  Week %2 > 0) CA(HomeTeam, AwayTeam, Week)
/*Exclude any dummy matches if odd number of teams*/
WHERE  CA.AwayTeam IS NOT NULL
       AND CA.HomeTeam IS NOT NULL
ORDER  BY CA.Week; 

答案 1 :(得分:3)

所以这里的理论是:https://nrich.maths.org/1443

要在SQL中实现,首先我们需要一个团队列表

DECLARE @teams TABLE(
    TeamId int identity(0,1),
    TeamName VARCHAR(100)    
)
INSERT @teams(TeamName) VALUES
('Team01'),
('Team02'),
('Team03'),
('Team04'),
('Team05'),
('Team06'),
('Team07'),
('Team08'),
('Team09'),
('Team10'),
('Team11'),
('Team12');

现在我们创建循环夹具套装。我确定可以使用几何对象并旋转它们,但是......

declare @roundRobin table (fixtureId int, week int, homeTeam int, awayTeam int)
insert @roundRobin 
      select 1, 1, 0, 1
union select 2, 1, 2, 3
union select 3, 1, 4, 5
union select 4, 1, 6, 7
union select 5, 1, 8, 9
union select 6, 1, 10, 11
declare @week int = 2
while @week <= 11
begin
    insert @roundRobin 
    select 1, @week, 0, awayTeam from @roundRobin where week = @week - 1 and fixtureId=2
    union all
    select 2, @week,(select awayTeam from @roundRobin where week = @week - 1 and fixtureId=1), (select awayTeam from @roundRobin where week = @week - 1 and fixtureId=3) 
    union all
    select 3, @week,(select homeTeam from @roundRobin where week = @week - 1 and fixtureId=2), (select awayTeam from @roundRobin where week = @week - 1 and fixtureId=4) 
    union all
    select 4, @week,(select homeTeam from @roundRobin where week = @week - 1 and fixtureId=3), (select awayTeam from @roundRobin where week = @week - 1 and fixtureId=5) 
    union all
    select 5, @week,(select homeTeam from @roundRobin where week = @week - 1 and fixtureId=4), (select awayTeam from @roundRobin where week = @week - 1 and fixtureId=6) 
    union all
    select 6,@week,(select homeTeam from @roundRobin where week = @week - 1 and fixtureId=5), (select homeTeam from @roundRobin where week = @week - 1 and fixtureId=6) 
    select @week = @week + 1
end

现在创建一组这样的赛程,主场和客队都会逆转。

insert @roundRobin  
select fixtureId, week+11, awayTeam, homeTeam from @roundRobin

以随机顺序创建一个周列表以停止回家/远程灯具的运行

declare @weeks table (Week int, WeekOrder int)
insert @weeks 
select number, row_number() over (order by randomorder)
from
(   select number, newid() randomorder from master..spt_values where type='p' and number between 1 and 22 ) v
order by number

现在,每个星期的赛程。多田!

select 
    weekorder,
    ht.TeamName homeTeam,
    at.TeamName awayTeam
from @weeks w
    inner join @roundRobin rr on w.Week = rr.week
    inner join @teams ht on rr.homeTeam = ht.TeamId
    inner join @teams at on rr.awayTeam = at.TeamId
order by weekorder, hometeam

我现在对高级联赛夹具电脑有了新的尊重。