MySQL 最近点的空间连接
MySQL Spatial Join on Closest Point
我环顾四周,发现很多人都在寻求按到设定点的距离来订购 table 个点,但我很好奇如何有效地加入两个 tables 两点之间的最小距离。在我的例子中,考虑 table nodes
和 centroids
.
CREATE TABLE nodes (
node_id VARCHAR(255),
pt POINT
);
CREATE TABLE centroids (
centroid_id MEDIUMINT UNSIGNED,
temperature FLOAT,
pt POINT
);
我有大约 300k 个节点和 15k 个质心,我想获得离每个节点最近的质心,这样我就可以为每个节点分配一个温度。到目前为止,我已经在 table 上的 pt
上创建了空间索引,并尝试了 运行 以下查询:
SELECT
nodes.node_id,
MIN(ST_DISTANCE(nodes.pt, centroids.pt))
FROM nodes
INNER JOIN centroids
ON ST_DISTANCE(nodes.pt, centroids.pt) <= 4810
GROUP BY
nodes.node_id
LIMIT 10;
很明显,这个查询不能解决我的问题;它不检索温度,假设最近的质心在 4810 以内,并且只评估 10 个节点。然而,即使有这些简化,这个查询优化得非常差,并且在我键入它时仍然是 运行。当我 MySQL 提供有关查询的详细信息时,它说没有索引被使用并且空间索引的 none 被列为可能的键。
我如何构建一个实际上可以return我想要利用空间索引有效连接的数据的查询?
有很多方法可以解决这个每组最少 n 的问题。
一种方法使用自左连接反模式(这允许联系):
select
n.node_id,
c.centroid_id,
st_distance(n.pt, c.pt) dist,
c.temperature
from nodes n
cross join centroids c
left join centroids c1
on c1.centroid_id <> c.centroid_id
and st_distance(n.pt, c1.pt) < st_distance(n.pt, c.pt)
where c1.centroid_id is null
同样的逻辑可以用not exists
条件表达。
另一种选择是使用相关子查询进行过滤(这不允许联系):
select
n.node_id,
n.node_id,
c.centroid_id,
st_distance(n.pt, c.pt) dist,
c.temperature
from nodes n
inner join centroids c
on c.centroid_id = (
select c1.centroid_id
from centroids c1
order by st_distance(n.pt, c1.pt)
limit 1
)
最后:如果你想要的只是最近质心的temperature
,那么一个简单的子查询应该是一个不错的选择:
select
n.node_id,
(
select c1.temperature
from centroids c1
order by st_distance(n.pt, c1.pt)
limit 1
) temperature
from nodes n
我认为一个好的方法是将数据分区(数字上不是数据库分区)到单元格中。我不知道空间索引在这里应用得有多好,但高级逻辑是说将每个节点和质心点放入正方形区域并找到同一正方形中所有节点质心之间的匹配项,然后确保有' 在 8 个相邻的正方形中进行更接近的匹配(例如,在原始正方形中使用相同的节点)。然后可以使用最接近的匹配来计算和保存温度。所有后续查询都应忽略设置了温度的节点。
仍然会有质心不在相同或 8 个相邻正方形内的节点,然后您可以扩大搜索范围,也许使用宽度和高度加倍的正方形。我可以看到这只在点的 x 和 y 坐标上使用普通索引。我不知道空间索引如何进一步改善这一点。
我环顾四周,发现很多人都在寻求按到设定点的距离来订购 table 个点,但我很好奇如何有效地加入两个 tables 两点之间的最小距离。在我的例子中,考虑 table nodes
和 centroids
.
CREATE TABLE nodes (
node_id VARCHAR(255),
pt POINT
);
CREATE TABLE centroids (
centroid_id MEDIUMINT UNSIGNED,
temperature FLOAT,
pt POINT
);
我有大约 300k 个节点和 15k 个质心,我想获得离每个节点最近的质心,这样我就可以为每个节点分配一个温度。到目前为止,我已经在 table 上的 pt
上创建了空间索引,并尝试了 运行 以下查询:
SELECT
nodes.node_id,
MIN(ST_DISTANCE(nodes.pt, centroids.pt))
FROM nodes
INNER JOIN centroids
ON ST_DISTANCE(nodes.pt, centroids.pt) <= 4810
GROUP BY
nodes.node_id
LIMIT 10;
很明显,这个查询不能解决我的问题;它不检索温度,假设最近的质心在 4810 以内,并且只评估 10 个节点。然而,即使有这些简化,这个查询优化得非常差,并且在我键入它时仍然是 运行。当我 MySQL 提供有关查询的详细信息时,它说没有索引被使用并且空间索引的 none 被列为可能的键。
我如何构建一个实际上可以return我想要利用空间索引有效连接的数据的查询?
有很多方法可以解决这个每组最少 n 的问题。
一种方法使用自左连接反模式(这允许联系):
select
n.node_id,
c.centroid_id,
st_distance(n.pt, c.pt) dist,
c.temperature
from nodes n
cross join centroids c
left join centroids c1
on c1.centroid_id <> c.centroid_id
and st_distance(n.pt, c1.pt) < st_distance(n.pt, c.pt)
where c1.centroid_id is null
同样的逻辑可以用not exists
条件表达。
另一种选择是使用相关子查询进行过滤(这不允许联系):
select
n.node_id,
n.node_id,
c.centroid_id,
st_distance(n.pt, c.pt) dist,
c.temperature
from nodes n
inner join centroids c
on c.centroid_id = (
select c1.centroid_id
from centroids c1
order by st_distance(n.pt, c1.pt)
limit 1
)
最后:如果你想要的只是最近质心的temperature
,那么一个简单的子查询应该是一个不错的选择:
select
n.node_id,
(
select c1.temperature
from centroids c1
order by st_distance(n.pt, c1.pt)
limit 1
) temperature
from nodes n
我认为一个好的方法是将数据分区(数字上不是数据库分区)到单元格中。我不知道空间索引在这里应用得有多好,但高级逻辑是说将每个节点和质心点放入正方形区域并找到同一正方形中所有节点质心之间的匹配项,然后确保有' 在 8 个相邻的正方形中进行更接近的匹配(例如,在原始正方形中使用相同的节点)。然后可以使用最接近的匹配来计算和保存温度。所有后续查询都应忽略设置了温度的节点。
仍然会有质心不在相同或 8 个相邻正方形内的节点,然后您可以扩大搜索范围,也许使用宽度和高度加倍的正方形。我可以看到这只在点的 x 和 y 坐标上使用普通索引。我不知道空间索引如何进一步改善这一点。