sql 273 lines · 2 tabs

Geospatial data with PostGIS

Maria Garcia Feb 2026
2 tabs
-- Install PostGIS extension
CREATE EXTENSION IF NOT EXISTS postgis;

-- Create table with geometry column
CREATE TABLE locations (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  location GEOMETRY(Point, 4326),  -- 4326 = WGS 84 (GPS coordinates)
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Create spatial index
CREATE INDEX idx_locations_geom ON locations USING GIST(location);

-- Insert point (longitude, latitude)
INSERT INTO locations (name, location) VALUES
  ('Statue of Liberty', ST_SetSRID(ST_MakePoint(-74.0445, 40.6892), 4326)),
  ('Empire State Building', ST_SetSRID(ST_MakePoint(-73.9857, 40.7484), 4326)),
  ('Central Park', ST_SetSRID(ST_MakePoint(-73.9654, 40.7829), 4326));

-- Insert from lat/lon columns
INSERT INTO locations (name, location)
SELECT
  name,
  ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)
FROM raw_locations;

-- Find distance between two points (meters)
SELECT
  l1.name AS from_location,
  l2.name AS to_location,
  ST_Distance(
    l1.location::geography,
    l2.location::geography
  ) AS distance_meters
FROM locations l1
CROSS JOIN locations l2
WHERE l1.id != l2.id;

-- Find locations within radius (5km)
SELECT
  name,
  ST_Distance(
    location::geography,
    ST_SetSRID(ST_MakePoint(-74.0060, 40.7128), 4326)::geography
  ) AS distance_meters
FROM locations
WHERE ST_DWithin(
  location::geography,
  ST_SetSRID(ST_MakePoint(-74.0060, 40.7128), 4326)::geography,
  5000  -- 5000 meters = 5km
)
ORDER BY distance_meters;

-- Nearest neighbors (5 closest locations)
SELECT
  name,
  ST_Distance(
    location::geography,
    ST_SetSRID(ST_MakePoint(-74.0060, 40.7128), 4326)::geography
  ) AS distance_meters
FROM locations
ORDER BY location <-> ST_SetSRID(ST_MakePoint(-74.0060, 40.7128), 4326)
LIMIT 5;

-- Extract coordinates
SELECT
  name,
  ST_X(location) AS longitude,
  ST_Y(location) AS latitude
FROM locations;

-- Polygon (service area, delivery zone)
CREATE TABLE delivery_zones (
  id SERIAL PRIMARY KEY,
  zone_name VARCHAR(100),
  boundary GEOMETRY(Polygon, 4326)
);

-- Create polygon
INSERT INTO delivery_zones (zone_name, boundary) VALUES (
  'Downtown',
  ST_SetSRID(
    ST_MakePolygon(
      ST_GeomFromText('LINESTRING(
        -74.02 40.70,
        -74.00 40.70,
        -74.00 40.72,
        -74.02 40.72,
        -74.02 40.70
      )')
    ),
    4326
  )
);

-- Check if point is within polygon
SELECT
  l.name,
  dz.zone_name
FROM locations l
JOIN delivery_zones dz ON ST_Within(l.location, dz.boundary);

-- Find zone for a specific point
SELECT zone_name
FROM delivery_zones
WHERE ST_Contains(
  boundary,
  ST_SetSRID(ST_MakePoint(-74.0060, 40.7128), 4326)
);

-- Bounding box query (faster preliminary filter)
SELECT name
FROM locations
WHERE location && ST_MakeEnvelope(
  -74.05, 40.70,  -- min lon, min lat
  -73.95, 40.80,  -- max lon, max lat
  4326
);
2 files · sql Explain with highlit

Geospatial data represents geographic locations and shapes. I use PostGIS for spatial queries. Point data stores coordinates—latitude, longitude. LineString represents paths. Polygon defines areas. Spatial indexes (GIST) enable fast proximity queries. Distance calculations find nearby points. Intersection queries detect overlaps. Containment checks if point is within polygon. Understanding spatial reference systems ensures accuracy. Bounding box queries filter before precise calculations. Geospatial aggregations compute centroids, unions. Essential for mapping applications, location services, delivery routing, geofencing. PostGIS rivals specialized GIS systems for many use cases.


Related snips

sql
-- Simple function
CREATE OR REPLACE FUNCTION get_full_name(
  first_name VARCHAR,
  last_name VARCHAR
)
RETURNS VARCHAR AS $$

Stored procedures and functions in PostgreSQL

postgresql stored-procedures functions
by Maria Garcia 2 tabs
sql
-- EXPLAIN ANALYZE (actual execution statistics)
EXPLAIN ANALYZE
SELECT u.username, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at >= '2024-01-01'

Advanced query optimization techniques

database optimization query-performance
by Maria Garcia 2 tabs
sql
-- Publisher: Send notification
NOTIFY new_order, 'Order #12345 created';

-- Subscriber: Listen for notifications
LISTEN new_order;

PostgreSQL LISTEN/NOTIFY for pub-sub messaging

postgresql listen-notify pub-sub
by Maria Garcia 2 tabs
sql
-- Logical backup with pg_dump
-- Single database
-- pg_dump -h localhost -U postgres -d mydb -F c -f mydb_backup.dump

-- All databases
-- pg_dumpall -h localhost -U postgres -f all_databases.sql

Database backup and recovery strategies

database backup recovery
by Maria Garcia 2 tabs
sql
-- Prepared statements basics
-- PostgreSQL syntax
PREPARE get_user (INT) AS
SELECT id, username, email
FROM users
WHERE id = $1;

Query plan caching and prepared statements

postgresql performance query-plans
by Maria Garcia 2 tabs
sql
-- Pattern 1: Shared schema with tenant_id column

CREATE TABLE tenants (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  slug VARCHAR(50) UNIQUE NOT NULL,

Multi-tenancy database patterns and strategies

multi-tenancy saas row-level-security
by Maria Garcia 2 tabs

Share this code

Here's the card — post it anywhere.

Geospatial data with PostGIS — share card
Link copied