在使用 MySQL 很长时间后,我得出的结论是,几乎任何使用 GROUP BY 对性能都非常不利,所以这里有一个包含几个临时表的解决方案。
CREATE TEMPORARY TABLE VoteCounts (
accountid INT,
contestid INT,
votecount INT DEFAULT 0
);
INSERT INTO VoteCounts (accountid, contestid)
SELECT DISTINCT v2.accountid, v2.contestid
FROM votes v1 JOIN votes v2 USING (contestid)
WHERE v1.accountid = ?; -- the given account
确保您在votes(accountid, contestid) 上有一个索引。
现在您有一个表格,其中包含您的给定用户参加的每个比赛,以及参加相同比赛的所有其他帐户。
UPDATE Votes AS v JOIN VoteCounts AS vc USING (accountid, contestid)
SET vc.votecount = vc.votecount+1;
现在您可以计算每场比赛中每个帐户的票数。
CREATE TEMPORARY TABLE Placings (
accountid INT,
contestid INT,
placing INT
);
SET @prevcontest := 0;
SET @placing := 0;
INSERT INTO Placings (accountid, placing, contestid)
SELECT accountid,
IF(contestid=@prevcontest, @placing:=@placing+1, @placing:=1) AS placing,
@prevcontest:=contestid AS contestid
FROM VoteCounts
ORDER BY contestid, votecount DESC;
现在您有一个表格,其中每个帐户与他们在每场比赛中的各自名次配对。很容易获得给定位置的计数:
SELECT accountid, COUNT(*) AS count_first_place
FROM Placings
WHERE accountid = ? AND placing = 1;
您可以使用 MySQL 技巧来完成所有三合一查询。布尔表达式在 MySQL 中总是返回整数值 0 或 1,因此您可以使用 SUM() 来计算 1。
SELECT accountid,
SUM(placing=1) AS count_first_place,
SUM(placing=2) AS count_second_place,
SUM(placing=3) AS count_third_place
FROM Placings
WHERE accountid = ?; -- the given account
你的评论:
是的,无论从您拥有的标准化数据到您想要的结果如何,这都是一项复杂的任务。您希望它再次聚合(求和)、排名和聚合(计数)。那是一堆工作! :-)
此外,单个查询并不总是完成给定任务的最快方式。程序员普遍认为较短的代码意味着更快的代码。
请注意,我尚未对此进行测试,因此您的里程可能会有所不同。
关于更新的问题:
在不使用 GROUP BY 的情况下,获取每个帐户的 COUNT() 投票是一种棘手的方法。我添加了表别名 v 和 vc,所以现在可能更清楚了。在votes 表中,给定帐户/竞赛有 N 行。在votescount 表中,每个帐户/比赛有一行。当我加入时,UPDATE 会针对 N 行进行评估,因此如果我为这 N 行中的每一行添加 1,我会在与每个相应帐户/比赛对应的行中获得存储在 votescount 中的 N 的计数。