[英]Is it possible to simplify this SQL statement with multiple subqueries and JOINs
[英]SQL multiple JOINs or subqueries but avoid cartesian product
我想为游戏实现一个 SQL 数据库。 有许多玩家参加不同的锦标赛。 对于每场比赛,玩家都有一个单独的帐户。 所有游戏都列在一张大表中,其中锦标赛帐户用于描述赢家、输家以及游戏得分。
架构在http://sqlfiddle.com/#!9/55378a或这里再次给出
CREATE TABLE `players` (
`id` int NOT NULL,
`name` varchar(5),
PRIMARY KEY (`id`)
);
CREATE TABLE `tournamentAccounts` (
`tId` int NOT NULL,
`playerId` int NOT NULL,
`handicap` int NOT NULL DEFAULT 10,
PRIMARY KEY (`tId`)
);
CREATE TABLE `games` (
`gameId` int NOT NULL,
`winnerTId` int NOT NULL,
`loserTId` int NOT NULL,
`score` int NOT NULL DEFAULT 0,
PRIMARY KEY (`gameId`)
);
INSERT INTO `players` (`id`, `name`) VALUES
(1, 'a'), (2, 'b'), (3, 'c');
INSERT INTO `tournamentAccounts` (`tId`, `playerId`, `handicap`) VALUES
(1, 1, 10), (2, 1, 2), (3, 2, 0);
INSERT INTO `games` (`gameId`, `winnerTId`, `loserTId`, `score`) VALUES
(1, 1, 3, 3), (2, 1, 3, 2), (3, 3, 1, 6);
我想要实现的目标:列出特定玩家的所有锦标赛得分,即让分handicap + scorepoints of won games - scorepoints of lost games
得分handicap + scorepoints of won games - scorepoints of lost games
。 对于给定的输入,结果集应包含总分分别为 9(tId=1)和 2(tId=2)的两行。 这里的例子是简化的,因为在我的例子中,在锦标赛账户和游戏表之间有更多的条件匹配(例如时间段等),但我想一旦我理解了基本方法,我就可以自己扩展它:-)
我的方法直到现在都失败了,因为我无法获得一个很好的 JOIN 或子查询来工作(我想避免存储过程)。
尝试 1:直接加入
SELECT t.*, (t.handicap +COALESCE(SUM(w.score),0) -COALESCE(SUM(l.score),0)) AS score
FROM tournamentAccounts t
LEFT JOIN games w ON w.winnerTId = t.tId
LEFT JOIN games l ON l.loserTId = t.tId
WHERE playerId = 1
GROUP BY t.tId
虽然这返回了正确的行数,但双 LEFT JOIN 导致了一个笛卡尔积,就像看起来一样:两场赢的比赛与输的比赛合并成两个数据集,因此 10 + 3 - 6 + 2 - 6。这种效果显然变成更糟糕的是我在游戏表中的匹配行越多。
尝试 2 :UNION 与 JOIN(类似于sql 避免笛卡尔积)
SELECT SUM(COALESCE(x.aa,0))
FROM
((SELECT -l.score AS aa FROM games l LEFT JOIN tournamentAccounts t ON l.loserTId = t.tId WHERE t.playerId = 1)
UNION
(SELECT w.score AS aa FROM games w LEFT JOIN tournamentAccounts t ON w.winnerTId = t.tId WHERE t.playerId = 1)) x
有了这个,我得到了适当的得分值,但是它还没有与相应的让球值相结合,而且我不知道如何从这里扩展到该玩家的所有锦标赛账户(这里,我只是拿了一个数据的小快照)以 SQL 方式。
我只会将您查询的游戏部分变成一个联合,而不是整个事情:
SELECT t.*, (t.handicap +COALESCE(SUM(win_score),0) -COALESCE(SUM(loss_score),0)) AS score
FROM tournamentAccounts t
LEFT JOIN (
SELECT w.winnerTId AS tId, w.score AS win_score, 0 AS loss_score FROM games w
UNION ALL
SELECT l.loserTId, 0, l.score FROM games l
) games_won_or_lost ON games_won_or_lost.tId=t.tId
WHERE playerId = 1
GROUP BY t.tId
另一种选择是取消笛卡尔积的影响。 由于输掉比赛的次数,您知道获胜分数过高,因此将SUM(w.score)
替换为ROUND(SUM(w.score)/GREATEST(COUNT(DISTINCT l.gameId),1))
。 同样, SUM(l.score)
变成ROUND(SUM(l.score)/GREATEST(COUNT(DISTINCT w.gameId),1))
。
下面怎么样:-
SELECT t.*, (t.handicap + coalesce(wscore,0) - coalesce(lscore,0)) AS score
FROM tournamentAccounts t
LEFT JOIN (
select sum(score) wscore, winnerTId wid
from games
group by winnerTid
) as w ON w.wid = t.tid
left join (
select sum(score) lscore, loserTid lid
from games
group by loserTid
) as l ON l.lid = t.tid
where playerId = 1
我得到的结果是
tId playerId handicap score
1 1 10 9
2 1 2 2
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.