我已经很好地给了这个算法来帮助我在SQL中创建一个fixture列表,但是将它作为SQL代码应用,我不知道该怎么做。是否有人可以指导我如何将其应用于代码?
下面是我的表架构,下面是算法:
联赛:
[LeagueID] TINYINT IDENTITY(1,1) NOT NULL PRIMARY KEY,
[LeagueName] VARCHAR(30) UNIQUE
小组:
[TeamID] TINYINT IDENTITY(1,1) NOT NULL PRIMARY KEY,
[TeamAbbreviation] CHAR(3) UNIQUE,
[TeamName] VARCHAR(50) UNIQUE,
[LeagueID] TINYINT CONSTRAINT FK_Team_League FOREIGN KEY REFERENCES League(LeagueID)
夹具:
[FixtureID] INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
[WeekNumber] INT NOT NULL,
[FixtureDate] DATE NULL,
[HomeTeamID] TINYINT NULL,
[AwayTeamID] TINYINT NULL,
[LeagueID] TINYINT CONSTRAINT FK_Fixture_League FOREIGN KEY REFERENCES League(LeagueID)
算法:
让我们根据N队的有序列表l
(对应于N-1多边形顶点+多边形中心)翻译名为round robin scheduling的算法:
l
通过从列表中对抗最后一个队列来定义赛程,第二个队伍对阵前一个队伍,等等。
也就是说,对于0 ≤ x < N
,您可以扮演团队l[x]
与团队l[N-1-x]
。
要生成下一组灯具,请旋转列表中的N-1
个第一个元素。
那是l = l[1] + l[2] + ... + l[N-2] + l[0] + l[N-1]
完成全套N-1
轮换后,请再次进行,但交换主队和客队:与队l[N-1-x]
对阵队l[x]
而不是相反
如果您从数字排序列表0..N-1
开始,那么在第i
轮,列表为:l = [(i + 0) % (N-1)] + [(i + 1) % (N-1)] + ... + [(i + N-2) % (N-1)] + [N-1]
也就是说,灯具位于i
轮
i
vs N-1
0 < x < (N-1) / 2
, (x + i) % (N-1)
vs (N-1 - x + i) % (N-1)
现在有一个技巧,因为这个仅适用于偶数。否则最后一支球队总是在第i
轮比赛中对阵球队i
,而自然每一轮有一支球队无法上场。这意味着第4队比其他球队多出一场比赛。
要解决此问题,我们添加虚拟团队,以便我们拥有N = 6
和第i
轮的5个团队:
i
vs 5
(虚拟团队)(i + 1) % 4
vs (4 + i) % 4
(i + 2) % 4
vs (3 + i) % 4
现在你已经知道了,你可以生成一个函数,它会根据整数为你提供灯具。它应该输出以下内容:
第0轮比赛0:0比1,比4比2比2比3 1:1轮比赛,2比0比3比3比4 第2轮:2轮比赛,3比1比4比0比0 第3轮:3轮比赛,4比2比0比1比赛 4:4轮比赛,0比3比1比2比赛2比赛
请注意,公式i
和x + i
代替N-1 - x + i
,您可以使用任意多个m * i
(因此x + m * i
和N-1 - x + m * i
)只要m
和N-1
以及relatively prime。此处N - 1 = 5
是素数,因此您可以使用任何您想要的m
。
更新
如下所示,首先是联盟表的测试数据,第二个是team表(按顺序匹配表模式列)
联赛:
1, 'English Premiership'
2, 'English Division 1'
队:
1, 'BCN', 'FC Barcelona', 1
2, 'MAD', 'Real Madrid', 1
3, 'ATH', 'Athletico Madrid', 1
4, 'ESP', 'Espanyol', 1
5, 'MAN', 'Manchester United', 2
6, 'BOL', 'Bolton', 2
7, 'CHE', 'Chelsea', 2
8, 'ARS', 'Arsenal', 2
球队在主客场比赛中互相比赛,只能对阵同一联赛的球队(因此不同的联盟ID)
每轮比赛应该是这样的:
League 1:
Round 1- 01/05/2016:
1v4, 2v3
Round 2- 08/05/2016:
1v2, 3v4
Round 3- 15/05/2016:
3v1, 4v2
Round 4- 22/05/2016:
4v1, 3v2
Round 5- 29/05/2016:
2v1, 4v3
Round 6- 05/06/2016:
1v3, 2v4
League 2:
Round 1- 01/05/2016:
5v8, 6v7
Round 2- 08/05/2016:
5v6, 7v8
Round 3- 15/05/2016:
3v1, 4v2
Round 4- 22/05/2016:
8v5, 7v6
Round 5- 29/05/2016:
6v5, 8v7
Round 6- 05/06/2016:
5v7, 6v8
这应该全部插入'Fixture'表中。
答案 0 :(得分:1)
以下似乎得到了你想要的东西 - 但这是Oracle SQL(我目前无法访问SQL服务器数据库)。但我相信你需要改变的是MOD函数,文本连接以及对DUAL的引用,以使它在SQL Server上运行:
WITH TEAMS(TEAMID,TEAMNAME) AS (
SELECT 0,'TEAM A' FROM DUAL UNION
SELECT 1,'TEAM B' FROM DUAL UNION
SELECT 2,'TEAM C' FROM DUAL UNION
SELECT 3,'TEAM D' FROM DUAL UNION
SELECT 4,'TEAM E' FROM DUAL
),
TEAMSX(TEAMID,TEAMNAME) AS (
SELECT * FROM TEAMS
UNION
SELECT (SELECT COUNT(*) FROM TEAMS) TEAMID,'DUMMY' FROM DUAL WHERE MOD((SELECT COUNT(*) FROM TEAMS),2)<>0),
TEAM_FIX(ROUND,TEAMID,FIXID) AS (
SELECT 0,TEAMID,TEAMID FROM TEAMSX
UNION ALL
SELECT ROUND+1,TEAMID,
CASE WHEN TEAMID=(SELECT COUNT(*)-1 FROM TEAMSX) THEN (SELECT COUNT(*)-1 FROM TEAMSX) ELSE
MOD(TEAMID+(ROUND+1),(SELECT COUNT(*)-1 FROM TEAMSX)) END FROM TEAM_FIX
WHERE ROUND < (SELECT COUNT(*)-2 FROM TEAMSX))
SELECT ROUND, TXT FROM (
SELECT TEAM1.ROUND,TEAM1.FIXID TM1,TEAM2.FIXID TM2,
CASE WHEN TX.TEAMNAME='DUMMY' THEN TEAM1.FIXID || ' rests' ELSE TEAM1.FIXID || ' vs ' || TEAM2.FIXID END TXT FROM
TEAM_FIX TEAM1
JOIN TEAM_FIX TEAM2 ON (TEAM1.ROUND=TEAM2.ROUND AND TEAM1.TEAMID+TEAM2.TEAMID=(SELECT COUNT(*)-1 FROM TEAMSX) AND TEAM1.TEAMID < TEAM2.TEAMID)
JOIN TEAMSX TX ON (TEAM2.TEAMID=TX.TEAMID)
ORDER BY TEAM1.ROUND,TEAM1.TEAMID)
第一个WITH表(TEAMS)就是创建一些样本数据。
如果队员数量奇数,第二张桌子(TEAMSX)将创建一个虚拟团队。
第三个表(TEAM_FIX)是一个递归CTE,它使用你给出的函数为每一轮生成正确的列表排序。
然后在主查询中,我们为每一轮加入TEAM_FIX,以创建团队对决。
答案 1 :(得分:1)
另一个Oracle解决方案。
<强>设置强>:
CREATE TABLE League (
LeagueID INT PRIMARY KEY,
LeagueName VARCHAR(30) UNIQUE
);
CREATE TABLE Team (
TeamID INT PRIMARY KEY,
TeamAbbreviation CHAR(3) UNIQUE,
TeamName VARCHAR(50) UNIQUE,
LeagueID INT CONSTRAINT FK_Team_League REFERENCES League(LeagueID)
);
CREATE TABLE Fixture (
FixtureID INT PRIMARY KEY,
WeekNumber INT NOT NULL,
FixtureDate DATE NULL,
HomeTeamID INT NULL,
AwayTeamID INT NULL,
LeagueID INT CONSTRAINT FK_Fixture_League REFERENCES League(LeagueID)
);
INSERT INTO League VALUES ( 1, 'League 1' );
INSERT INTO League VALUES ( 2, 'League 2' );
INSERT INTO Team VALUES ( 1, 'AAA', 'Team A', 1 );
INSERT INTO Team VALUES ( 2, 'BBB', 'Team B', 1 );
INSERT INTO Team VALUES ( 3, 'CCC', 'Team C', 1 );
INSERT INTO Team VALUES ( 4, 'DDD', 'Team D', 1 );
INSERT INTO Team VALUES ( 5, 'EEE', 'Team E', 2 );
INSERT INTO Team VALUES ( 6, 'FFF', 'Team F', 2 );
INSERT INTO Team VALUES ( 7, 'GGG', 'Team G', 2 );
INSERT INTO Team VALUES ( 8, 'HHH', 'Team H', 2 );
INSERT INTO Team VALUES ( 9, 'III', 'Team I', 2 );
插入 - 灯具:
INSERT INTO Fixture
WITH league_teams ( id, leagueid, idx, is_fake, num_teams, num_fake ) AS (
-- Generate a unique-per-league index for each team that is between 0
-- and the (number of teams - 1) and calculate the number of teams
-- and if this is an odd number then generate a fake team as well.
SELECT TeamID,
LeagueID,
ROW_NUMBER() OVER ( PARTITION BY LeagueID ORDER BY TeamID ) - 1,
0,
COUNT(1) OVER ( PARTITION BY LeagueID ),
MOD( COUNT(1) OVER ( PARTITION BY LeagueID ), 2 )
FROM Team
UNION ALL
SELECT NULL,
LeagueID,
COUNT(1),
1,
COUNT(1),
1
FROM Team
GROUP BY LeagueID
HAVING MOD( COUNT(1), 2 ) > 0
),
cte ( home_idx, away_idx, week_number, leagueID, num_teams, num_fake ) AS (
-- Start by calculating the round 1 games
SELECT idx,
num_teams + num_fake - 1 - idx,
1,
LeagueID,
num_teams,
num_fake
FROM league_teams
WHERE 2 * idx < num_teams
UNION ALL
-- Then generate the successive rounds with the two cases when the
-- away team has the maximum index or otherwise.
SELECT CASE away_idx
WHEN num_teams + num_fake - 1
THEN home_idx + 1
ELSE MOD( home_idx + 1, num_teams + num_fake -1 )
END,
CASE away_idx
WHEN num_teams + num_fake - 1
THEN away_idx
ELSE MOD( away_idx + 1, num_teams + num_fake - 1 )
END,
week_number + 1,
LeagueID,
num_teams,
num_fake
FROM cte
WHERE week_number < num_teams + num_fake - 1
)
-- Finally join the cte results back to the League_Teams table to convert
-- the indexes used in calculation back to the actual team ids.
SELECT rn,
week_number,
NULL,
h.id,
a.id,
c.leagueid
FROM (
-- This step isn't necessary but it keeps the results in a nice order.
SELECT ROWNUM AS rn,
t.*
FROM (
-- Duplicate the results swapping home and away.
SELECT week_number,
home_idx,
away_idx,
LeagueId
FROM cte
UNION ALL
SELECT week_number + num_teams + num_fake - 1,
away_idx,
home_idx,
LeagueId
FROM cte
) t
) c
INNER JOIN League_Teams h
ON ( c.home_idx = h.idx AND c.leagueId = h.leagueID )
INNER JOIN League_Teams a
ON ( c.away_idx = a.idx AND c.leagueId = a.leagueID )
ORDER BY rn;
<强>输出强>:
SELECT * FROM fixture;
FIXTUREID WEEKNUMBER FIXTUREDATE HOMETEAMID AWAYTEAMID LEAGUEID
---------- ---------- ------------------- ---------- ---------- ----------
1 1 1 4 1
2 1 2 3 1
3 1 5 2
4 1 6 9 2
5 1 7 8 2
6 2 2 4 1
7 2 3 1 1
8 2 6 2
9 2 7 5 2
10 2 8 9 2
11 3 3 4 1
12 3 1 2 1
13 3 7 2
14 3 8 6 2
15 3 9 5 2
16 4 8 2
17 4 9 7 2
18 4 5 6 2
19 5 9 2
20 5 5 8 2
21 5 6 7 2
22 4 4 1 1
23 4 3 2 1
24 6 5 2
25 6 9 6 2
26 6 8 7 2
27 5 4 2 1
28 5 1 3 1
29 7 6 2
30 7 5 7 2
31 7 9 8 2
32 6 4 3 1
33 6 2 1 1
34 8 7 2
35 8 6 8 2
36 8 5 9 2
37 9 8 2
38 9 7 9 2
39 9 6 5 2
40 10 9 2
41 10 8 5 2
42 10 7 6 2
(注意:FixtureDate
是NULL
,因为不清楚您希望如何生成这个,但您应该能够获取周数并将其用作从开头的偏移量生成日期的季节