0Pricing
SQL Academy · Lección

Uniones espaciales y contención

Qué puntos se encuentran en qué áreas.

Uniones espaciales y contención es una lección gratuita de SQL Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de SQL Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de SQL Academy incluye 4 lecciones en total.

¿Qué es una unión espacial?

Una unión espacial combina dos tablas basándose en una relación geográfica en lugar de una clave coincidente. En vez de preguntar «¿este ID es igual a ese ID?», plantea preguntas como «¿este punto está dentro de ese polígono?» o «¿estas dos formas se solapan?»

PostGIS amplía PostgreSQL con tipos de geometría y funciones espaciales que hacen posibles estas uniones. El resultado es el mismo que el de un JOIN SQL normal: las filas de ambas tablas combinadas, pero con una condición geométrica.

Configuración de tablas de ejemplo

Crearemos dos tablas para trabajar: cities, que almacena ubicaciones de puntos, y countries, que almacena límites poligonales. Ambas utilizan el tipo GEOMETRY de PostGIS con SRID 4326 (latitud/longitud estándar de WGS84).

El SRID (identificador de referencia espacial) indica a PostGIS qué sistema de coordenadas debe utilizar. El SRID 4326 es el más común para datos 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)
);

Inserción de datos de ejemplo

Utilizaremos ST_GeomFromText para insertar valores de geometría en formato Well-Known Text (WKT). WKT es una representación de texto estándar para geometrías: POINT(lon lat) para puntos y POLYGON((...)) para polígonos.

Observe que en WKT la longitud aparece antes que la latitud; esto coincide con la convención X, Y de las matemáticas.

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: la función principal

ST_Contains(geometry A, geometry B) devuelve TRUE cuando la geometría A contiene completamente la geometría B. En nuestro caso, ST_Contains(country.border, city.location) devuelve true cuando un punto de ciudad se encuentra dentro de un polígono de país.

Esta es la base de los joins de contención en PostGIS. La función forma parte del estándar OGC y funciona con cualquier combinación de tipos de geometría.

SELECT
  ci.name  AS city,
  co.name  AS country
FROM   cities   ci
JOIN   countries co
  ON   ST_Contains(co.border, ci.location);

ST_Within: la perspectiva inversa

ST_Within(geometry A, geometry B) es exactamente lo contrario de ST_Contains: devuelve TRUE cuando la geometría A está completamente dentro de la geometría B. ST_Within(city, country) es lógicamente idéntico a ST_Contains(country, city).

Aquí ambas funciones producen el mismo resultado. La elección entre ellas depende de la legibilidad: elija la que se lea de forma más natural en su consulta.

-- 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 para encontrar puntos sin coincidencia

Un JOIN normal descarta las ciudades que no se encuentran dentro de ningún polígono de país. Utilice un LEFT JOIN combinado con una comprobación WHERE ... IS NULL para encontrar puntos que no tienen ningún polígono que los contenga; esto resulta útil para detectar problemas de calidad de los datos o puntos fuera de su área de cobertura.

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;

Contar puntos por polígono

Los joins espaciales son especialmente útiles cuando se combinan con la agregación. Al unir las ciudades con los países y agrupar después por país, puede contar cuántos puntos se encuentran dentro de cada polígono. Este patrón es muy común en el análisis geográfico: contar tiendas por región, eventos por distrito, sensores por zona, entre otros casos.

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;

Uso de un índice espacial para mejorar el rendimiento

Los joins espaciales pueden ser lentos con conjuntos de datos grandes porque cada punto se comprueba contra cada polígono. Un índice GIST (árbol de búsqueda generalizado) permite que PostGIS utilice un prefiltro de cajas envolventes para omitir la mayoría de las comparaciones antes de ejecutar la comprobación precisa de ST_Contains.

Cree siempre un índice GIST en las columnas de geometría que utilice en joins o filtros. El planificador de consultas lo utilizará automáticamente.

-- 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: geometrías superpuestas

ST_Intersects(A, B) devuelve TRUE si dos geometrías comparten algún punto, incluso si solo se tocan en un límite. Es menos restrictivo que ST_Contains: dos polígonos superpuestos se intersectan aunque ninguno contenga completamente al otro.

Para comprobar si un punto está dentro de un polígono, ST_Intersects y ST_Contains son equivalentes, pero ST_Intersects utiliza el índice GIST de forma más eficiente y suele preferirse en la práctica.

-- 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);

Unir puntos con el polígono más cercano

Cuando un punto se encuentra exactamente sobre un límite o muy cerca de varios polígonos, es posible que necesite el polígono más cercano en lugar de todas las coincidencias. ST_Distance mide la distancia entre dos geometrías, lo que permite utilizar patrones con ORDER BY + LIMIT o el join especializado LATERAL con ORDER BY ... LIMIT 1.

Esto se denomina join de vecinos más cercanos y es habitual al ajustar trazas GPS a segmentos de carretera o asignar un punto a la región más cercana.

-- 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;

Ejemplo práctico: restaurantes en distritos

Este es un ejemplo realista de principio a fin: determinar a qué distrito de la ciudad pertenece cada restaurante y, después, contar los restaurantes de cada distrito. Este patrón se aplica a cualquier escenario de punto en polígono: cajeros automáticos en barrios, accidentes en distritos policiales, pedidos en zonas de reparto.

La consulta utiliza ST_Within dentro de un join espacial y agrupa los resultados para generar un informe resumido.

-- 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;

Comprobación rápida

Compruebe su comprensión de los joins espaciales y la contención en PostGIS.

Recapitulación: joins espaciales y contención

En esta lección aprendió a responder la pregunta ¿qué puntos se encuentran en qué áreas? mediante joins espaciales de PostGIS.

Conclusiones clave:

  • ST_Contains(polygon, point) — devuelve true cuando el polígono contiene completamente el punto.
  • ST_Within(point, polygon) — es la operación inversa y lógicamente equivalente a ST_Contains.
  • ST_Intersects — comprobación de superposición más general y compatible con índices para puntos dentro de polígonos.
  • Los índices GIST en columnas de geometría son esenciales para obtener un buen rendimiento a gran escala.
  • Combine los joins espaciales con GROUP BY para contar o agregar puntos por región.
  • Utilice LEFT JOIN para detectar puntos que se encuentran fuera de todos los polígonos.

Los joins espaciales permiten realizar análisis geográficos directamente en SQL, sin necesidad de ninguna herramienta GIS externa.

Preguntas frecuentes

¿La lección «Uniones espaciales y contención» es gratis?

Sí — el texto completo de «Uniones espaciales y contención» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de SQL Academy, actualiza a CoddyKit PRO. El curso de SQL Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Uniones espaciales y contención»?

Qué puntos se encuentran en qué áreas. Practicas SQL Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar SQL Academy?

No se requiere experiencia previa. SQL Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Uniones espaciales y contención»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de SQL Academy?

Sí. Cada lección de SQL Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Tipos de datos espaciales
  2. Distancia y vecinos más cercanos
  3. Uniones espaciales y contención
  4. Índices espaciales (GiST)
← Volver a SQL Academy