【发布时间】:2021-03-04 17:29:00
【问题描述】:
我有以下三个表(多对多):
位置
+====+==============+===+===+=============+
| id | coord_system | x | y | last_update |
+====+==============+===+===+=============+
| | | | | |
+----+--------------+---+---+-------------+
映射
+=============+============+
| location_id | history_id |
+=============+============+
| | |
+-------------+------------+
历史
+====+=======+======+
| id | speed | date |
+====+=======+======+
| | | |
+----+-------+------+
位置表表示特定坐标系内的物理 x、y 位置。对于每个 x、y 位置,历史表中至少存在一行。历史表中的每一行都可以指向位置表中的多行。
需要注意的是 (coord_system, x, y) 是索引的并且是唯一的。我认为这没有什么区别,但所有 id 和 coord_system 都是 UUID。在下面的代码示例中,我将使用字母使其更易于阅读。 location 和 history 有额外的列,但不改变问题的范围。 location 表上的 last_update 列应该与 History 表上的 date 列相匹配(我稍后会在帖子中提到这一点)。
目标是获取 (coor_system, x, y) 范围内最近的历史记录行。目前这是通过 CROSS JOIN LATERAl 完成的,例如
SELECT *
FROM location loc
CROSS JOIN LATERAL
(SELECT *
FROM history hist
LEFT JOIN mapping map ON hist.id = map.history_id
WHERE map.location_id = loc.id
ORDER BY date DESC limit(1)) AS records
WHERE loc.coord_system = '43330ccc-3f42-4f05-8ec5-18cb659bfd2d'
AND (x >= 403047
AND x <= 404047)
AND (y >= 16451337
AND y <= 16452337);
对于这个 x、y 和 coord_system 的特定范围,查询需要大约 25 秒才能运行并返回 182 351 行。
我在 SQL 方面不是很有经验,但认为这个查询的目标也可以使用常规连接来实现。如果我对三个表进行连接,使用相同的 x、y 和 coord_system“过滤器”,则大约需要 2 秒并返回约 300 万行。我试图变得聪明并使用日期来修剪结果:
SELECT *
FROM history hist
RIGHT JOIN mapping map ON hist.id = map.history_id
RIGHT JOIN location loc ON loc.id = map.location_id
WHERE loc.coord_system = '43330ccc-3f42-4f05-8ec5-18cb659bfd2d'
AND (x >= 403047
AND x <= 404047)
AND (y >= 16451337
AND y <= 16452337)
AND location.last_update = hist.date
这与原始查询的结果非常接近。结果是约 3 秒内 182 485 行。不幸的是,结果需要完全相同。我猜我在查询中犯了一个逻辑错误,并来到这里希望有人能指出它。
我的问题是:有没有一种聪明的方法可以让连接只从 history.date 列中获取具有“最新”日期的行?正如预期的那样,我试图在保持正确结果集的同时使查询尽可能快地运行。
在下表中,我展示了一个玩具连接示例以及我期望的结果(标记在“return_row”列中)。
+=============+==============+===+===+=============+============+============+=======+============+============+
| location.id | coord_system | x | y | location_id | history_id | history.id | speed | date | return_row |
+=============+==============+===+===+=============+============+============+=======+============+============+
| 0 | a | 1 | 1 | 0 | 0 | 0 | 3.0 | 2020/10/31 | * |
+-------------+--------------+---+---+-------------+------------+------------+-------+------------+------------+
| 0 | a | 1 | 1 | 0 | 1 | 1 | 3.1 | 2020/10/30 | |
+-------------+--------------+---+---+-------------+------------+------------+-------+------------+------------+
| 0 | a | 1 | 1 | 0 | 2 | 2 | 3.2 | 2020/10/29 | |
+-------------+--------------+---+---+-------------+------------+------------+-------+------------+------------+
| 1 | a | 1 | 2 | 1 | 3 | 3 | 3.1 | 2020/10/31 | * |
+-------------+--------------+---+---+-------------+------------+------------+-------+------------+------------+
| 1 | a | 1 | 2 | 1 | 4 | 4 | 3.0 | 2020/10/30 | |
+-------------+--------------+---+---+-------------+------------+------------+-------+------------+------------+
| 2 | a | 2 | 2 | 2 | 5 | 5 | 4 | 2020/10/31 | * |
+-------------+--------------+---+---+-------------+------------+------------+-------+------------+------------+
| 3 | b | 1 | 1 | 3 | 6 | 6 | 5 | 2020/10/1 | * |
+-------------+--------------+---+---+-------------+------------+------------+-------+------------+------------+
【问题讨论】:
标签: sql postgresql where-clause greatest-n-per-group lateral-join