Complete guide to installing the Countries States Cities database in PostgreSQL with advanced features and spatial queries
PostgreSQL is a powerful, advanced open-source relational database with excellent support for complex queries, JSON data, and spatial operations. This guide covers the complete installation process for the Countries States Cities database.
PostgreSQL installation typically takes 3-6 minutes and requires approximately 220MB of disk space.
psql -U postgres -c "CREATE DATABASE world WITH ENCODING='UTF8';"
PostgreSQL uses UTF-8 encoding by default, which properly handles all international characters.
2
Import Database
Copy
psql -U postgres -d world -f pgsql/world.sql
PostgreSQL import includes automatic index creation for optimal performance.
3
Verify Installation
Copy
psql -U postgres -d world -c "SELECT 'countries' as table_name, COUNT(*) as count FROM countriesUNION ALLSELECT 'states', COUNT(*) FROM statesUNION ALL SELECT 'cities', COUNT(*) FROM cities;"
Expected output:
Copy
table_name | count ------------|-------- countries | 250 states | 5038 cities | 151024
-- Enable PostGIS for advanced spatial queries (optional)CREATE EXTENSION IF NOT EXISTS postgis;
2
Create Spatial Indexes
Copy
-- Add geometry columns for spatial operationsALTER TABLE cities ADD COLUMN geom geometry(POINT, 4326);UPDATE cities SET geom = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326);-- Create spatial indexCREATE INDEX idx_cities_geom ON cities USING GIST(geom);
3
Example Spatial Queries
Copy
-- Find cities within 100km of New York CitySELECT name, country_name, latitude, longitude FROM cities WHERE ST_DWithin( geom, ST_SetSRID(ST_MakePoint(-74.006, 40.7128), 4326)::geography, 100000 -- 100km in meters);-- Find nearest cities to a pointSELECT name, country_name, ST_Distance(geom, ST_SetSRID(ST_MakePoint(-74.006, 40.7128), 4326)::geography) as distanceFROM cities ORDER BY geom <-> ST_SetSRID(ST_MakePoint(-74.006, 40.7128), 4326)LIMIT 10;
-- Create full-text search indexesCREATE INDEX idx_countries_name_fts ON countries USING gin(to_tsvector('english', name));CREATE INDEX idx_cities_name_fts ON cities USING gin(to_tsvector('english', name));CREATE INDEX idx_states_name_fts ON states USING gin(to_tsvector('english', name));
CREATE ROLE csc_user WITH LOGIN PASSWORD 'secure_password_here';
Replace ‘secure_password_here’ with a strong, unique password.
2
Grant Permissions
Copy
-- Grant database accessGRANT CONNECT ON DATABASE world TO csc_user;-- Grant schema usageGRANT USAGE ON SCHEMA public TO csc_user;-- Grant table permissionsGRANT SELECT ON ALL TABLES IN SCHEMA public TO csc_user;-- Grant permissions on future tablesALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO csc_user;
-- Performance indexes for common queriesCREATE INDEX idx_countries_iso2 ON countries(iso2);CREATE INDEX idx_countries_iso3 ON countries(iso3);CREATE INDEX idx_countries_region ON countries(region);CREATE INDEX idx_countries_subregion ON countries(subregion);CREATE INDEX idx_states_country_id ON states(country_id);CREATE INDEX idx_states_name ON states(name);CREATE INDEX idx_states_country_name ON states(country_name);CREATE INDEX idx_cities_country_id ON cities(country_id);CREATE INDEX idx_cities_state_id ON cities(state_id);CREATE INDEX idx_cities_name ON cities(name);CREATE INDEX idx_cities_coordinates ON cities(latitude, longitude);CREATE INDEX idx_cities_country_name ON cities(country_name);
-- List all tables\dt-- Describe table structures\d countries\d states \d cities-- Check table sizesSELECT schemaname as schema, tablename as table, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size, pg_size_pretty(pg_relation_size(schemaname||'.'||tablename)) as table_size, pg_size_pretty(pg_indexes_size(schemaname||'.'||tablename)) as index_sizeFROM pg_tables WHERE schemaname = 'public'ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
PostgreSQL has excellent JSON support. Here are some examples:
Copy
-- Aggregate cities by country as JSONSELECT country_name, json_agg( json_build_object( 'name', name, 'latitude', latitude, 'longitude', longitude )) as citiesFROM cities WHERE country_name = 'Switzerland'GROUP BY country_name;