2011-03-25 26 views
0

我有一个模式,看起来像这样:SQL算术和加入三列

+----------+ 
| tour  | 
+----------+ 
| id  | 
| name  | 
+----------+ 

+----------+ 
| golfer | 
+----------+ 
| id  | 
| name  | 
| tour_id | 
+----------+ 

+-----------+ 
| stat  | 
+-----------+ 
| id  | 
| round  | 
| score  | 
| golfer_id | 
+-----------+ 

所以基本上一个高尔夫巡回赛在它的高尔夫球手的X号。高尔夫球手将拥有X个统计数据。统计表中的圆形列仅包含数字(1,2,3,4 ...等等)。它们不一定是一个接一个,但它们是独一无二的。

我现在想要找到属于“PGA”巡回赛和所有高尔夫球手的所有高尔夫球手,从最后2轮开始计算他们的分数。最后两轮实质上是两个数字最大的高尔夫球手统计表中的行数。因此,假设高尔夫球手“老虎伍兹”已经在第1,3,6和10轮比赛中进行了比赛,那么我只想从第6轮和第10轮开始计分他的成绩。另一个要求是我不想向高尔夫球手展示尚未出场的球员至少参加过两轮比赛。

我已经尝试了几种方法来解决这个问题,但总是让自己陷入纠结。

回答

2

如果你只是想要最后两轮(强调“两”)有一个简单的技巧。这个技巧不会扩展到超过两个,或不是最后两个记录。要获取分区中的任意记录,您必须使用窗口函数,这些函数涉及更多,并且仅在新版本的主流数据库引擎中受支持。

诀窍是在高尔夫球员身份证上自我平等地加入“stat”表。这样,你得到任何两轮高尔夫球员的所有组合,包括与同一轮组合:

SELECT s1.round as s1_round, s2.round AS s2_round 
FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id) 

然后你排除(通过WHERE子句)具有相同的回合,也使组合确定这些组合总是第一轮>第二轮。这意味着现在您有任何两轮高尔夫球手的所有组合的不重复:

SELECT s1.round as s1_round, s2.round AS s2_round 
FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id) 
WHERE s1.round > s2.round 

请注意,如果你只选择特定的高尔夫球手的记录和排序DESC上的两个圆形柱,顶部行会是最后两轮的是高尔夫球手:

SELECT TOP 1 s1.round as s1_round, s2.round AS s2_round 
FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id) 
WHERE s1.round > s2.round 
ORDER BY s1.round DESC, s2.round DESC 

TOP 1是SQL Server的行话让高层一行。对于MySQL,您需要使用LIMIT 1。对于其他数据库,请使用数据库引擎的特定方式。

但是,在这种情况下,你不能这么做,因为你需要最后两轮所有的高尔夫球手。你必须做更多的联接:

SELECT id, 
    (SELECT MAX(s1.round) FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id) 
    WHERE s1.round > s2.round AND s1.golfer_id = golfer.id) AS last_round, 
    (SELECT MAX(s2.round) FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id) 
    WHERE s1.round > s2.round AND s1.golfer_id = golfer.id) AS second_to_last_round 
FROM golfer 

这会给你最后两轮(两列)为每个高尔夫球手。

或用两柱温度设定在加入高尔夫球手表也应该工作:

SELECT golfer.id, MAX(r.s1_round) AS last_round, MAX(r.s2_round) AS second_to_last_round 
FROM golfer INNER JOIN 
(
SELECT s1.golfer_id AS golfer_id, s1.round AS s1_round, s2.round AS s2_round 
FROM stat s1 INNER JOIN stat s2 ON (s1.golfer_id = s2.golfer_id) 
WHERE s1.round > s2.round 
) r ON (r.golfer_id = golfer.id) 
GROUP BY golfer.id 

我把它作为一个简单的练习来此查询加入到巡演表,以获得PGA巡回赛的球手,并将此查询返回到统计表以获得最后两轮的分数。

+0

+1这是一个非常性感的答案。 – Hogan 2011-03-25 02:52:44

+0

@霍根,谢谢。这是我在使用窗口函数之前多年使用的一个技巧。尝试查找最后两条记录时非常高效。尝试查找分组中每个记录的最后两个时效率不高。 – 2011-03-25 02:58:25

2

HSQLDB 2.1支持LATERAL连接,它允许使用任意条件进行这种选择。

一个简单的加入将列出在PGA巡回赛的所有球手:

select golfer.name from tour join golfer on (tour.id = tour_id and tour.name = 'PGA') 

你,因为你需要特定的分数,然后横向加入这个表多次。接下来的例子包括最后一轮成绩(仅当剧中起到了一轮)

select golfer.name, firststat.score from tour join golfer on (tour.id = tour_id and tour.name = 'PGA'), 
lateral(select * from stat where golfer_id = golfer.id order by round desc limit 1) firststat 

在下面的例子中,你使用一个以上横向加入到包括倒数第二个回合。如果玩家没有palyed两轮,会有玩家没有行:

select golfer.name, secondstat.score score1, firststat.score score2 from tour join golfer on (tour.id = tour_id and tour.name = 'PGA'), 
lateral(select * from stat where golfer_id = golfer.id order by round desc limit 1 offset 1) secondstat, 
lateral(select * from stat where golfer_id = golfer.id order by round desc limit 1) firststat 

横向连接不需要一个WHERE子句,因为“那里条件”从表中采取从列表出现在当前表格之前。因此,LATERAL表的子查询中的SELECT语句可以使用第一个连接表中的golfer.id。