SQL算术和连接三列

Posted

技术标签:

【中文标题】SQL算术和连接三列【英文标题】:SQL Arithmetic and joining three columns 【发布时间】:2011-03-25 02:01:48 【问题描述】:

我的架构如下所示:

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

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

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

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

我现在想查找属于“PGA”巡回赛的所有高尔夫球手,并为每个高尔夫球手计算他们在过去 2 轮比赛中的得分。最后两轮基本上是统计表中具有最大两个数字的高尔夫球手的行。因此,假设高尔夫球手“老虎伍兹”参加了第 1、3、6 和 10 轮比赛,那么我只想统计他在第 6 轮和第 10 轮的成绩。另一个要求是我不想显示尚未参加比赛的高尔夫球手至少参加过两轮比赛。

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

【问题讨论】:

【参考方案1】:

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

诀窍是在高尔夫球手 id 上将“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。对于其他数据库,使用数据库引擎的特殊方式。

但是,在这种情况下,您不能仅仅因为需要最后两轮 ALL 高尔夫球手就这样做。你必须做更多的连接:

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 巡回赛的高尔夫球手,并将此查询加入到统计表中以获取最后两轮的分数,这只是一个简单的练习。

【讨论】:

@Hogan,谢谢。这是我多年来使用的一个技巧,在窗口函数出现之前。尝试查找最后两条记录时非常有效。尝试为分组中的每条记录查找最后两条时效率不高。【参考方案2】:

HSQLDB 2.1 支持 LATERAL 连接,它允许这种带有任意标准的选择。

一个简单的加入将列出所有参加 PGA 巡回赛的高尔夫球手:

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

然后,您可以根据需要多次 LATERAL 将此表加入特定分数。下一个示例包括上一轮的得分(仅当游戏已经进行了一轮)

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

在下一个示例中,您再使用一个横向连接来包括最后一轮。如果玩家没有玩过两轮,则该玩家将没有行:

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

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

【讨论】:

以上是关于SQL算术和连接三列的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有算术计数的SQL子查询中使用GROUP BY

如何将算术和与 SQL 中的分析函数结合起来进行运行

Derby SQL 时间戳算术和 JDBC 转义语法

使用算术运算优化 MySQL 嵌套选择

字符串和算术运算的 PHP 连接

Oracle-18-select语句初步&SQL中用算术表达式&别名的使用&连接运算符%distinct&where子句