Um JOIN espacial é uma operação de non-equijoin que usa uma função de predicado espacial para avaliar a relação entre objetos geográficos em duas tabelas. Essa relação serve como condição de junção. Os casos de uso comuns incluem verificar se um ponto de interesse (POI) está dentro de uma área específica ou analisar interseções entre uma trajetória e uma geofence.
Limitações
O MaxCompute implementa JOINs espaciais usando broadcast join. Esse método exige o uso de MAPJOIN HINT para especificar a tabela menor a ser transmitida via broadcast.
As funções de predicado espacial compatíveis incluem ST_CONTAINS, ST_COVERS, ST_WITHIN e ST_INTERSECTS.
Os tipos de junção compatíveis são INNER JOIN, LEFT JOIN (transmite a tabela à direita via broadcast) e RIGHT JOIN (transmite a tabela à esquerda via broadcast).
Exemplos
Exemplo 1
Crie duas áreas sem sobreposição e alguns pontos dentro delas. Os dados geométricos são armazenados como WKT.
CREATE OR REPLACE TABLE areas (
area_id BIGINT,
area_name STRING,
geometry_detail STRING
);
INSERT OVERWRITE TABLE areas VALUES
(1, 'area_1', 'POLYGON((100 30, 110 30, 110 40, 100 40, 100 30))'),
(2, 'area_2', 'POLYGON((120 30, 130 30, 130 40, 120 40, 120 30))');
CREATE OR REPLACE TABLE points (
point_id BIGINT,
point_name STRING,
location STRING
);
INSERT OVERWRITE TABLE points VALUES
(1, 'point_1', 'POINT(105 35)'), -- Falls within area_1
(2, 'point_2', 'POINT(108 38)'), -- Falls within area_1
(3, 'point_3', 'POINT(102 32)'), -- Falls within area_1
(4, 'point_4', 'POINT(125 35)'), -- Falls within area_2
(5, 'point_5', 'POINT(128 39)'); -- Falls within area_2
Consulte os pontos em cada área. Use um MAPJOIN HINT para especificar a tabela menor no JOIN espacial.
Como as computações geoespaciais geralmente são complexas, use SET odps.stage.mapper.split.size=10; para reduzir o tamanho do split e aumentar a concorrência do estágio Map.
-- To support the GEOGRAPHY data type, enable the 2.0 type system.
SET odps.sql.type.system.odps2=true;
SELECT /*+MAPJOIN(a)*/
a.area_name,
a.area_id,
r.point_name,
r.point_id
FROM areas a, points r
WHERE ST_CONTAINS(ST_GEOGFROMTEXT(a.geometry_detail), ST_GEOGFROMTEXT(r.location))
ORDER BY a.area_name, r.point_id LIMIT 10;
-- Result:
+-----------+------------+------------+------------+
| area_name | area_id | point_name | point_id |
+-----------+------------+------------+------------+
| area_1 | 1 | point_1 | 1 |
| area_1 | 1 | point_2 | 2 |
| area_1 | 1 | point_3 | 3 |
| area_2 | 2 | point_4 | 4 |
| area_2 | 2 | point_5 | 5 |
+-----------+------------+------------+------------+
Exemplo 2
Crie duas áreas sem sobreposição e vários pontos. Os polígonos das áreas usam o tipo de dado GEOGRAPHY, enquanto as localizações dos pontos usam valores de longitude e latitude do tipo DOUBLE.
CREATE OR REPLACE TABLE residential_areas (
area_id BIGINT,
area_name STRING,
geog GEOGRAPHY
);
INSERT OVERWRITE TABLE residential_areas VALUES
(1, 'area_1', ST_GEOGFROMTEXT('POLYGON((100 30, 110 30, 110 40, 100 40, 100 30))')),
(2, 'area_2', ST_GEOGFROMTEXT('POLYGON((120 30, 130 30, 130 40, 120 40, 120 30))'));
CREATE OR REPLACE TABLE restaurants (
restaurant_id BIGINT,
restaurant_name STRING,
lon DOUBLE,
lat DOUBLE
);
INSERT OVERWRITE TABLE restaurants VALUES
(1, 'restaurant_1', 105, 35), -- In area_1
(2, 'restaurant_2', 108, 38), -- In area_1
(3, 'restaurant_3', 125, 35), -- In area_2
(4, 'restaurant_4', 150, 50), -- Not in any area
(5, 'restaurant_5', 102, 32); -- In area_1
Consulte a área que contém cada ponto. Use um LEFT JOIN para incluir também os pontos fora de qualquer área.
SELECT /*+MAPJOIN(a)*/
a.area_name,
r.restaurant_id
FROM restaurants r LEFT JOIN residential_areas a
ON ST_CONTAINS(a.geog, ST_GEOGPOINT(r.lon, r.lat))
ORDER BY a.area_name, r.restaurant_id LIMIT 10;
-- Result:
+-----------+---------------+
| area_name | restaurant_id |
+-----------+---------------+
| NULL | 4 |
| area_1 | 1 |
| area_1 | 2 |
| area_1 | 5 |
| area_2 | 3 |
+-----------+---------------+