ST_Contains(polygon_geom, point_geom)
- Returns TRUE if point is completely inside polygon
- Most common for point-in-polygon analysis
- Usage:
WHERE ST_Contains(polygons.geom, points.geom)
ST_Within(point_geom, polygon_geom)
- Inverse of ST_Contains
- Returns TRUE if point is completely inside polygon
- Usage:
WHERE ST_Within(points.geom, polygons.geom)
ST_Intersects(geom1, geom2)
- More general - returns TRUE if geometries share any space
- Includes points on boundaries
- Usage:
WHERE ST_Intersects(polygons.geom, points.geom)
- ST_Contains: Standard point-in-polygon (point must be inside, not on boundary)
- ST_Within: Same as ST_Contains but reversed arguments
- ST_Intersects: When boundary points should be included
Without spatial indexes, PostGIS performs full table scans for spatial queries - checking every geometry pair. With indexes, performance improves dramatically (often 100-1000x faster).
-- Create GIST index on geometry column
CREATE INDEX idx_table_geom ON schema.table USING GIST (geom);
-- For large tables, create index concurrently (allows reads during creation)
CREATE INDEX CONCURRENTLY idx_table_geom ON schema.table USING GIST (geom);-- Check if spatial index exists
SELECT schemaname, tablename, indexname, indexdef
FROM pg_indexes
WHERE tablename = 'your_table'
AND indexdef LIKE '%GIST%';- Always index geometry columns used in spatial queries
- Index both tables in a spatial join for optimal performance
- Rebuild indexes periodically on frequently updated tables:
REINDEX INDEX idx_table_geom;
SRID (Spatial Reference System Identifier) defines the coordinate system. Common SRIDs:
- 4326: WGS84 (latitude/longitude) - GPS coordinates
- 3857: Web Mercator - web mapping (Google Maps, OpenStreetMap)
- 2163: US National Atlas Equal Area - US analysis
- Regional UTM zones for accurate distance measurements
-- Find SRID of a geometry column
SELECT Find_SRID('schema_name', 'table_name', 'geom_column');
-- Or query directly
SELECT ST_SRID(geom) FROM table_name LIMIT 1;-- Transform geometry from one SRID to another
ST_Transform(geom, target_srid)
-- Example: Transform from 4326 to 3857
SELECT ST_Transform(geom, 3857) FROM points WHERE ST_SRID(geom) = 4326;- Always transform to matching SRID before spatial operations
- Transform the smaller table in joins (usually points, not polygons)
- Store in consistent SRID when possible to avoid repeated transformations
- Use appropriate SRID for your analysis:
- 4326 for global data, lat/lng coordinates
- Local projected systems for accurate distance/area calculations
SELECT
p.name as polygon_name,
COUNT(pt.id) as point_count
FROM polygons p
LEFT JOIN points pt
ON ST_Contains(p.geom, pt.geom)
GROUP BY p.name;SELECT
p.name as polygon_name,
COUNT(pt.id) as point_count
FROM polygons p
LEFT JOIN points pt
ON ST_Contains(p.geom, ST_Transform(pt.geom, ST_SRID(p.geom)))
GROUP BY p.name;-- Use bounding box pre-filter (automatic with GIST index)
SELECT
p.name,
COUNT(pt.id)
FROM polygons p
LEFT JOIN points pt
ON p.geom && pt.geom -- Bounding box operator (index-optimized)
AND ST_Contains(p.geom, pt.geom) -- Exact spatial test
GROUP BY p.name;-- Points not in any polygon
SELECT pt.*
FROM points pt
LEFT JOIN polygons p
ON ST_Contains(p.geom, pt.geom)
WHERE p.id IS NULL;-
Use EXPLAIN ANALYZE to check query plans:
EXPLAIN ANALYZE SELECT COUNT(*) FROM points pt JOIN polygons p ON ST_Contains(p.geom, pt.geom);
-
Look for "Index Scan using idx_" - confirms index usage
-
Avoid ST_Transform in WHERE clause - transform beforehand if possible
-
Batch large operations - process in chunks for very large datasets
-
Use appropriate geometry types:
- POINT for points
- POLYGON/MULTIPOLYGON for polygons
- Don't use GEOMETRY when specific type is known
- Missing spatial indexes - Most common performance issue
- SRID mismatches - Queries fail or return unexpected results
- NULL geometries - Always handle with WHERE geom IS NOT NULL
- Incorrect function - Using ST_Intersects when ST_Contains is needed
- Not using LEFT JOIN - Missing polygons with zero points in summary
- Check for spatial indexes with
\diin psql - Run EXPLAIN ANALYZE to see query plan
- Ensure SRIDs match
- Check SRIDs match or transform correctly
- Verify geometry validity:
SELECT ST_IsValid(geom) FROM table; - Check for NULL geometries
- Verify correct spatial function (Contains vs Intersects vs Within)
- Check SRID matches geographic region
- Validate geometry topology