空间连接与包含关系
确定哪些点位于哪些区域中。
空间连接与包含关系 是 CoddyKit 上的免费 SQL Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 SQL Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 SQL Academy 课程共包含 4 节课。
什么是空间连接
空间连接根据地理关系而不是匹配键来组合两个表。您不再询问“这个 ID 是否等于那个 ID?”,而是询问“这个点是否落在那个多边形内?”或“这两个形状是否重叠?”
PostGIS 通过几何类型和空间函数扩展 PostgreSQL,使这些连接成为可能。结果与常规 SQL JOIN 相同——组合两个表中的行——但连接条件是几何关系。
设置示例表
我们将创建两个用于操作的表:cities 用于存储点位置,countries 用于存储多边形边界。两者都使用 PostGIS 提供的 GEOMETRY 类型,并采用 SRID 4326(标准 WGS84 经纬度)。
SRID(空间参考 ID)用于告诉 PostGIS 应使用哪个坐标系。SRID 4326 是 GPS 数据中最常用的坐标系。
CREATE TABLE countries (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
border GEOMETRY(POLYGON, 4326)
);
CREATE TABLE cities (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
location GEOMETRY(POINT, 4326)
);插入示例数据
我们将使用 ST_GeomFromText,从熟知文本(WKT)格式插入几何值。WKT 是表示几何体的标准文本格式:点使用 POINT(lon lat),多边形使用 POLYGON((...))。
请注意,在 WKT 中经度位于纬度之前——这与数学中的 X、Y 约定一致。
INSERT INTO countries (name, border) VALUES
('France', ST_GeomFromText('POLYGON((-5 42, 8 42, 8 51, -5 51, -5 42))', 4326)),
('Spain', ST_GeomFromText('POLYGON((-9 36, 3 36, 3 44, -9 44, -9 36))', 4326));
INSERT INTO cities (name, location) VALUES
('Paris', ST_GeomFromText('POINT(2.35 48.85)', 4326)),
('Madrid', ST_GeomFromText('POINT(-3.70 40.42)', 4326)),
('Bordeaux', ST_GeomFromText('POINT(-0.58 44.84)', 4326)),
('Lisbon', ST_GeomFromText('POINT(-9.14 38.72)', 4326));ST_Contains:核心函数
当几何体 A 完全包含几何体 B 时,ST_Contains(geometry A, geometry B) 会返回 TRUE。在当前场景中,当城市点位于国家多边形内部时,ST_Contains(country.border, city.location) 会返回真值。
这是 PostGIS 中包含连接的核心。该函数属于 OGC 标准,适用于任意几何类型组合。
SELECT
ci.name AS city,
co.name AS country
FROM cities ci
JOIN countries co
ON ST_Contains(co.border, ci.location);ST_Within:反向视角
ST_Within(geometry A, geometry B) 与 ST_Contains 正好相反:当几何体 A 完全位于几何体 B 内部时,它会返回 TRUE。ST_Within(city, country) 在逻辑上等同于 ST_Contains(country, city)。
在这里,两个函数会产生相同的结果。选择哪个函数取决于可读性——请选择在您的查询中读起来更自然的写法。
-- These two queries return identical results:
-- Using ST_Contains (country contains city)
SELECT ci.name, co.name
FROM cities ci
JOIN countries co ON ST_Contains(co.border, ci.location);
-- Using ST_Within (city is within country)
SELECT ci.name, co.name
FROM cities ci
JOIN countries co ON ST_Within(ci.location, co.border);使用 LEFT JOIN 查找未匹配的点
普通的 JOIN 会丢弃不在任何国家多边形内的城市。请将 LEFT JOIN 与 WHERE ... IS NULL 检查结合使用,以查找没有包含它们的多边形的点——这对于发现数据质量问题或位于覆盖范围之外的点很有用。
SELECT
ci.name AS city,
co.name AS country
FROM cities ci
LEFT JOIN countries co
ON ST_Contains(co.border, ci.location)
ORDER BY co.name NULLS LAST;统计每个多边形中的点
空间连接与聚合结合使用时非常有用。将城市与国家连接,然后按国家分组,您就可以统计每个多边形内有多少个点。这种模式在地理分析中非常常见,例如统计每个区域的商店数量、每个行政区的事件数量、每个分区的传感器数量等。
SELECT
co.name AS country,
COUNT(ci.id) AS city_count
FROM countries co
LEFT JOIN cities ci
ON ST_Contains(co.border, ci.location)
GROUP BY co.name
ORDER BY city_count DESC;使用空间索引提升性能
在大型数据集中,空间连接可能会很慢,因为每个点都要与每个多边形进行测试。GIST 索引(广义搜索树)允许 PostGIS 使用包围盒预过滤,在运行精确的 ST_Contains 测试之前跳过大多数比较。
请始终为用于连接或筛选的几何列创建 GIST 索引。查询规划器会自动使用该索引。
-- Create GIST indexes on both geometry columns
CREATE INDEX idx_countries_border
ON countries USING GIST (border);
CREATE INDEX idx_cities_location
ON cities USING GIST (location);
-- The same join now benefits from index acceleration
SELECT ci.name, co.name
FROM cities ci
JOIN countries co
ON ST_Contains(co.border, ci.location);ST_Intersects:相交的几何体
如果两个几何体共享任意一个点(包括仅在边界处接触),ST_Intersects(A, B) 就会返回 TRUE。它比 ST_Contains 的条件更宽松:两个相交的多边形即使彼此都没有完全包含对方,也会被判定为相交。
对于点在多边形内的测试,ST_Intersects 和 ST_Contains 是等价的,但 ST_Intersects 对 GIST 索引的利用效率更高,因此在实践中通常更受青睐。
-- ST_Intersects is index-friendly and equivalent
-- to ST_Contains for point-in-polygon tests
SELECT
ci.name AS city,
co.name AS country
FROM cities ci
JOIN countries co
ON ST_Intersects(co.border, ci.location);将点连接到最近的多边形
当一个点恰好位于边界上,或非常接近多个多边形时,您可能希望找到最近的多边形,而不是返回所有匹配项。ST_Distance 用于测量两个几何体之间的距离,从而支持 ORDER BY + LIMIT 模式,或支持使用 LATERAL 连接配合 ORDER BY ... LIMIT 1。
这称为最近邻连接,常用于将 GPS 轨迹贴合到道路路段,或将点分配给最近的区域。
-- For each city, find the single closest country centroid
SELECT DISTINCT ON (ci.name)
ci.name AS city,
co.name AS nearest_country,
ST_Distance(ci.location, ST_Centroid(co.border)) AS dist
FROM cities ci
CROSS JOIN countries co
ORDER BY ci.name, dist;实际示例:行政区中的餐厅
下面是一个完整的实际示例:找出每家餐厅所属的城市行政区,然后统计每个行政区中的餐厅数量。这种模式适用于所有点在多边形内的场景,例如社区中的自动取款机、辖区内的事故、配送区域中的订单。
该查询在空间连接中使用 ST_Within,并对结果进行分组以生成汇总报告。
-- Assume tables: districts(id, name, boundary GEOMETRY)
-- restaurants(id, name, location GEOMETRY)
SELECT
d.name AS district,
COUNT(r.id) AS restaurant_count,
STRING_AGG(r.name, ', ' ORDER BY r.name) AS names
FROM districts d
LEFT JOIN restaurants r
ON ST_Within(r.location, d.boundary)
GROUP BY d.name
ORDER BY restaurant_count DESC;快速检查
检验您对 PostGIS 中空间连接和包含关系的理解。
总结:空间连接与包含关系
在本课中,您学习了如何使用 PostGIS 空间连接来回答“哪些点落在哪些区域中?”这一问题。
主要要点:
- ST_Contains(多边形, 点)——当多边形完全包含点时返回真值。
- ST_Within(点, 多边形)——作用相反;在逻辑上等同于 ST_Contains。
- ST_Intersects——更通用的重叠测试;对于点在多边形内的查询更适合使用索引。
- 在几何列上创建GIST 索引对于大规模数据下的性能至关重要。
- 将空间连接与 GROUP BY 结合,以统计或聚合每个区域中的点。
- 使用 LEFT JOIN 检测落在所有多边形之外的点。
空间连接让您可以直接在结构化查询语言中进行地理分析,无需外部 GIS 工具。
常见问题解答
「空间连接与包含关系」课时是免费的吗?
是的 — 「空间连接与包含关系」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 SQL Academy 课程的其余内容,请升级到 CoddyKit PRO。 SQL Academy 课程共包含 4 节课。
「空间连接与包含关系」这节课中我会学到什么?
确定哪些点位于哪些区域中。 你通过在浏览器中直接运行的动手代码来练习 SQL Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 SQL Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 SQL Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「空间连接与包含关系」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 SQL Academy 课中编写并运行代码吗?
能。每节 SQL Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 空间数据类型
- 距离与最近邻
- 空间连接与包含关系
- 空间索引(GiST)