数据库设计:
小提琴:http ://sqlfiddle.com/#!2/4f23b3
我有一张桌子players
CREATE TABLE players (
id MEDIUMINT(7) unsigned AUTO_INCREMENT PRIMARY KEY ,
name VARCHAR(30)
) ENGINE = InnoDB
一张桌子objects
CREATE TABLE objects (
id INT(9) unsigned AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(30),
type VARCHAR(20)
) ENGINE = InnoDB
还有一个描述对象和玩家之间关系的表格one-to-many
(一个玩家可以有多个对象),但这并不严格one-to-many
,我们稍后会看到:
CREATE TABLE playerobjects (
objectid INT(9) unsigned NOT NULL,
playerid MEDIUMINT(7) unsigned NOT NULL,
`date` DATE NOT NULL,
PRIMARY KEY(objectid,`date`)
) ENGINE = InnoDB
上表可能难以理解,但这就是它的全部含义:
- 玩家可以在任何给定日期拥有许多对象
- 一个对象在任何给定日期只能属于一个玩家
- 一个玩家可能会从另一个玩家那里窃取一个对象,这意味着该表
playerobjects
将有两个用于被盗对象的条目,具有不同的 playerid 和不同的日期 - 一件物品每天只能被盗一次
因此,我们可以从上述准则推断出一个对象可能有很多所有者,但每天只有一个。也有可能玩家 1 从玩家 2 那里偷了一些物品,第二天,玩家 2 从玩家 1 那里偷了东西。
我想做的事 :
现在,从上面的数据库中,我想找出一个玩家偷了多少物品,目前正在摆姿势。我还想制作一个排行榜,显示按编号排序的顶级玩家。被盗物品。
也就是说,如果 Bob 先拿到了这个对象,但 Emily 从 Bob 那里偷了它,而 Sheldon 从 Emily 那里偷了它,那么查询应该只显示该对象是 Sheldon 在某个日期偷的,而不是其他任何事情,因为 Emily 确实从 Bob 那里偷了这个对象,但是那是很久以前的事了,该对象的当前所有者是 Sheldon。
示例代码
INSERT INTO players (name) VALUES ('Bob') , #id 1
('Emily') , #id 2
('Sheldon'); #id 3
INSERT INTO objects (name,type) VALUES ('Choco vanilla','ice cream'), #1
('Butterscotch','ice cream'), #2
('Nexus 4','Mobile Phone'), #3
('Snoopy','pet'), #4
('minecraft','game'); #5
INSERT INTO playerobjects (playerid,objectid,date) VALUES (1,1,'2013-05-15'),
(2,2,'2013-05-15'),
(3,3,'2013-05-15'),
(1,4,'2013-05-15'),
(2,1,'2013-05-16'),
(1,5,'2013-05-16'),
(3,1,'2013-05-17'),
(1,3,'2013-05-18'),
(3,3,'2013-05-19'),
(3,5,'2013-05-19'),
(2,5,'2013-05-20');
id 为 3 的个人的预期结果,行:
(3,1,'2013-05-17'),
(3,3,'2013-05-19')
id 为 2,行的个人的预期结果:
(2,5,'2013-05-20')
玩家 1 的预期结果:没有,因为他偷的东西是从他身上重新偷走的,只剩下他没有从任何人那里偷走的东西。
排行榜查询的预期结果:
sheldon 2
emily 1
bob 0
我想出什么:
排行榜查询,提供错误信息,而且我认为效率低下:
SELECT tpo.playerid,COUNT(*) as steals
FROM `playerobjects` AS tpo
LEFT JOIN `playerobjects` AS tpo2 ON (tpo.objectid = tpo2.objectid AND tpo.date < tpo2.date)
LEFT JOIN `playerobjects` AS tpo3 ON (tpo.objectid = tpo3.objectid AND tpo.date > tpo3.date)
WHERE tpo2.objectid IS NULL
AND tpo3.objectid IS NOT NULL
GROUP BY (tpo.playerid)
ORDER BY steals DESC
查询单个玩家和关于偷窃的详细信息,给出错误信息:
SELECT tpo.objectid,tpo.date
FROM `playerobjects` AS tpo
LEFT JOIN `playerobjects` AS tpo2 ON (tpo.objectid = tpo2.objectid AND tpo.date < tpo2.date)
LEFT JOIN `playerobjects` AS tpo3 ON (tpo.objectid = tpo3.objectid AND tpo.date > tpo3.date)
WHERE tpo2.objectid IS NULL
AND tpo3.objectid IS NOT NULL
为什么这些查询给出错误的信息?因为它们还计算对象的先前记录。将其与之前的 Bob Emily Sheldon 示例相关联,虽然它应该只返回 Sheldon 从 Emily 窃取对象时的记录,但它还显示 Emily 从 Bob 那里窃取对象的记录。我不知道如何在不使用复杂的子查询的情况下解决这个问题,这会使查询更加低效。我真的希望有更好的方法来做到这一点。