PostgreSQL Query Editor
Free online PostgreSQL 16+ query editor via PGlite with syntax highlighting, query templates, and database design examples. Perfect for learning SQL, database development, and query optimization.
Loading PostgreSQL environment...
Initializing PGlite WASM database
Features
SQL Syntax Highlighting
Advanced PostgreSQL syntax highlighting with intelligent code completion
Query Templates
Pre-built query templates for common PostgreSQL operations and patterns
Database Schema Design
Comprehensive examples for designing efficient PostgreSQL database schemas
Advanced Features
Examples of PostgreSQL advanced features: CTEs, window functions, JSON operations
Performance Optimization
Query optimization techniques, indexing strategies, and performance tuning
Code Sharing
Share PostgreSQL queries and database schemas with team members
Frequently Asked Questions
How to get started with PostgreSQL queries?
Learn basic PostgreSQL syntax and operations:
-- Basic SELECT query
SELECT name, email, created_at
FROM users
WHERE active = true
ORDER BY created_at DESC
LIMIT 10;
-- INSERT data
INSERT INTO users (name, email, password_hash)
VALUES ('John Doe', 'john@example.com', 'hashed_password');
-- UPDATE records
UPDATE users
SET last_login = NOW()
WHERE email = 'john@example.com';
-- DELETE records
DELETE FROM users
WHERE active = false AND created_at < NOW() - INTERVAL '1 year';
Our editor provides syntax highlighting and intelligent code completion.
How to create and manage PostgreSQL tables?
Learn table creation and schema management:
-- Create table with constraints
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
active BOOLEAN DEFAULT true
);
-- Add indexes for performance
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_created_at ON users(created_at);
-- Add foreign key relationships
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
content TEXT,
published_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW()
);
-- Alter table structure
ALTER TABLE users ADD COLUMN profile_image_url VARCHAR(500);
ALTER TABLE posts ADD CONSTRAINT check_title_length CHECK (LENGTH(title) > 0);
Practice database design with proper constraints and relationships.
How to use PostgreSQL JOIN operations?
Master different types of JOIN operations:
-- INNER JOIN - get users with posts
SELECT u.username, u.email, p.title, p.created_at
FROM users u
INNER JOIN posts p ON u.id = p.user_id
WHERE p.published_at IS NOT NULL
ORDER BY p.created_at DESC;
-- LEFT JOIN - get all users with their post count
SELECT u.username, u.email, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id, u.username, u.email
ORDER BY post_count DESC;
-- Multiple JOINs with aggregation
SELECT
u.username,
COUNT(DISTINCT p.id) as post_count,
COUNT(DISTINCT c.id) as comment_count,
MAX(p.created_at) as latest_post
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
LEFT JOIN comments c ON p.id = c.post_id
GROUP BY u.id, u.username
HAVING COUNT(DISTINCT p.id) > 0;
Understanding JOINs is essential for relational database queries.
How to use PostgreSQL advanced features?
Explore powerful PostgreSQL-specific features:
-- Common Table Expressions (CTEs)
WITH user_stats AS (
SELECT
user_id,
COUNT(*) as post_count,
AVG(LENGTH(content)) as avg_content_length
FROM posts
GROUP BY user_id
),
top_users AS (
SELECT user_id, post_count
FROM user_stats
WHERE post_count >= 5
)
SELECT u.username, us.post_count, us.avg_content_length
FROM users u
JOIN user_stats us ON u.id = us.user_id
JOIN top_users tu ON u.id = tu.user_id
ORDER BY us.post_count DESC;
-- Window Functions
SELECT
username,
title,
created_at,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) as post_rank,
COUNT(*) OVER (PARTITION BY user_id) as user_total_posts,
LAG(created_at) OVER (PARTITION BY user_id ORDER BY created_at) as prev_post_date
FROM posts p
JOIN users u ON p.user_id = u.id
WHERE p.published_at IS NOT NULL;
-- JSON Operations
SELECT
id,
metadata->>'category' as category,
metadata->'tags' as tags,
jsonb_array_length(metadata->'tags') as tag_count
FROM posts
WHERE metadata ? 'featured'
AND (metadata->>'featured')::boolean = true;
These advanced features make PostgreSQL incredibly powerful.
How to optimize PostgreSQL query performance?
Learn performance optimization techniques:
-- Create indexes for better performance
CREATE INDEX CONCURRENTLY idx_posts_user_published
ON posts(user_id, published_at)
WHERE published_at IS NOT NULL;
-- Partial index for specific conditions
CREATE INDEX idx_active_users ON users(created_at) WHERE active = true;
-- Composite index for multiple columns
CREATE INDEX idx_posts_search ON posts(title, content) USING gin(to_tsvector('english', title || ' ' || content));
-- Use EXPLAIN to analyze query performance
EXPLAIN (ANALYZE, BUFFERS)
SELECT u.username, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
WHERE u.created_at > NOW() - INTERVAL '1 month'
GROUP BY u.id, u.username
ORDER BY post_count DESC
LIMIT 10;
-- Efficient pagination with cursors
SELECT id, title, created_at
FROM posts
WHERE created_at < '2023-01-01'::timestamp
ORDER BY created_at DESC
LIMIT 20;
-- Use EXISTS instead of IN for better performance
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM posts p
WHERE p.user_id = u.id
AND p.published_at > NOW() - INTERVAL '1 week'
);
Always analyze query plans and create appropriate indexes.
How to work with PostgreSQL data types and functions?
Explore PostgreSQL's rich data types and built-in functions:
-- Working with dates and times
SELECT
NOW() as current_timestamp,
CURRENT_DATE as today,
EXTRACT(YEAR FROM created_at) as year_created,
AGE(created_at) as account_age,
DATE_TRUNC('month', created_at) as month_created
FROM users
WHERE created_at > NOW() - INTERVAL '1 year';
-- String operations
SELECT
username,
UPPER(username) as username_upper,
LENGTH(username) as username_length,
SUBSTRING(email FROM '@(.*)') as domain,
REGEXP_REPLACE(title, '[^a-zA-Z0-9 ]', '', 'g') as clean_title
FROM users u
JOIN posts p ON u.id = p.user_id;
-- Array operations
SELECT
title,
tags,
array_length(tags, 1) as tag_count,
'postgresql' = ANY(tags) as has_postgresql_tag,
array_to_string(tags, ', ') as tags_string
FROM posts
WHERE tags IS NOT NULL
AND array_length(tags, 1) > 0;
-- Conditional logic
SELECT
username,
CASE
WHEN created_at > NOW() - INTERVAL '1 month' THEN 'New'
WHEN created_at > NOW() - INTERVAL '1 year' THEN 'Regular'
ELSE 'Veteran'
END as user_type,
COALESCE(profile_image_url, '/default-avatar.png') as avatar
FROM users;
PostgreSQL offers extensive built-in functions for data manipulation.
What PostgreSQL version and limitations apply?
Our PostgreSQL editor runs PostgreSQL 16+ via PGlite with specific constraints:
Version & Environment:
- PostgreSQL 16+ via PGlite (single-user mode)
- Embedded WASM database running in browser
- Memory limit: ~1GB database size
Key Limitations:
- No client-server architecture (embedded only)
- No connections/sessions (single connection)
- No file-based operations or COPY commands
- Extensions: Most extensions unavailable (no uuid-ossp, pgcrypto, PostGIS)
- Full-text search: No text search configurations (use LIKE patterns instead)
- Functions: No
uuid_generate_v4()
,crypt()
,gen_salt()
,to_tsvector()
- No background processes or autovacuum
- No replication or clustering features
- No custom user-defined functions or procedures
Available Features:
-- Core PostgreSQL features work perfectly:
SELECT, INSERT, UPDATE, DELETE, CTEs, Window Functions
JSONB operations, Arrays, Recursive queries
Advanced aggregation (FILTER, GROUPING SETS)
Indexes (B-tree, GIN for JSONB/Arrays)
Constraints, Foreign Keys, Check Constraints
-- Alternative approaches for missing features:
SERIAL PRIMARY KEY -- instead of UUID
md5(random()::text) -- instead of uuid_generate_v4()
LIKE patterns -- instead of full-text search
Perfect for learning SQL, database design, query optimization, and most PostgreSQL features!
Where can I learn more about PostgreSQL?
Explore these official resources and learning materials:
Official Documentation:
- PostgreSQL 16 Documentation - Complete reference
- PostgreSQL Tutorial - Official getting started guide
- PostgreSQL SQL Reference - SQL command reference
- PGlite Documentation - WASM PostgreSQL used in this tool
Learning Resources:
- Postgres Guide - Comprehensive PostgreSQL guide
- PostgreSQL Performance Tips - Optimization techniques
- PostgreSQL Best Practices - What not to do
- SQL Style Guide - Writing clean SQL code
Academic Resources:
- PostgreSQL research papers on JSONB indexing and performance
- Database design and normalization theory
These resources will help you master PostgreSQL development, performance tuning, and best practices!
PostgreSQL Syntax Guide
Quick reference for PostgreSQL-specific features, JSONB operations, CTEs, window functions, and performance optimization.
PostgreSQL-Specific Data Types
CREATE TABLE example ( id SERIAL PRIMARY KEY, uuid_id UUID DEFAULT gen_random_uuid(), data JSONB, tags TEXT[], coordinates POINT, price NUMERIC(10,2), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() );
PostgreSQL types: SERIAL, UUID, JSONB, arrays, geometric types, precise numeric, timestamptz
SELECT data->>'name' as name, data->'address'->>'city' as city, data #> '{contact,phones,0}' as first_phone FROM users WHERE data ? 'email' AND data @> '{"active": true}';
JSONB operators: ->> (text), -> (jsonb), #> (path), ? (key exists), @> (contains)
CREATE TYPE order_status AS ENUM ('pending', 'processing', 'shipped', 'delivered'); CREATE TABLE orders ( id SERIAL PRIMARY KEY, status order_status DEFAULT 'pending', items TEXT[] DEFAULT '{}', metadata HSTORE );
Custom ENUM types, array defaults, and HSTORE for key-value pairs
Advanced Queries & CTEs
WITH RECURSIVE employee_hierarchy AS ( SELECT id, name, manager_id, 0 as level FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.id, e.name, e.manager_id, eh.level + 1 FROM employees e JOIN employee_hierarchy eh ON e.manager_id = eh.id ) SELECT * FROM employee_hierarchy ORDER BY level, name;
Recursive CTEs for hierarchical data like org charts or category trees
WITH sales_summary AS ( SELECT user_id, SUM(amount) as total, COUNT(*) as order_count FROM orders WHERE created_at >= CURRENT_DATE - INTERVAL '30 days' GROUP BY user_id ) SELECT u.name, s.total, s.order_count, CASE WHEN s.total > 1000 THEN 'VIP' ELSE 'Regular' END as tier FROM users u JOIN sales_summary s ON u.id = s.user_id ORDER BY s.total DESC;
CTEs with date intervals, aggregations, and conditional logic
Window Functions & Analytics
SELECT name, department, salary, ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) as dept_rank, RANK() OVER (ORDER BY salary DESC) as overall_rank, LAG(salary, 1) OVER (ORDER BY hire_date) as prev_salary, AVG(salary) OVER (PARTITION BY department) as dept_avg FROM employees;
Window functions: ROW_NUMBER, RANK, LAG/LEAD, and window aggregations
SELECT date_trunc('month', order_date) as month, SUM(amount) as monthly_total, SUM(SUM(amount)) OVER (ORDER BY date_trunc('month', order_date)) as running_total, LAG(SUM(amount), 1) OVER (ORDER BY date_trunc('month', order_date)) as prev_month FROM orders GROUP BY date_trunc('month', order_date) ORDER BY month;
Time-based analytics with running totals and period-over-period comparisons
SELECT product_id, sale_date, amount, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) OVER (PARTITION BY product_id) as median_price, NTILE(4) OVER (ORDER BY amount) as quartile FROM sales;
Statistical window functions: percentiles, ntiles, and distribution analysis
Indexing & Performance
CREATE INDEX CONCURRENTLY idx_users_email ON users (email); CREATE INDEX idx_orders_user_date ON orders (user_id, created_at DESC); CREATE INDEX idx_products_jsonb_gin ON products USING GIN (metadata); CREATE INDEX idx_locations_gist ON locations USING GIST (coordinates);
Index types: B-tree (default), GIN for JSONB/arrays, GIST for geometric data
EXPLAIN (ANALYZE, BUFFERS) SELECT u.name, 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' GROUP BY u.id, u.name HAVING COUNT(o.id) > 5;
Query analysis with EXPLAIN ANALYZE to identify performance bottlenecks
CREATE INDEX idx_orders_partial ON orders (user_id) WHERE status = 'active'; CREATE INDEX idx_products_expression ON products (LOWER(name)); CREATE UNIQUE INDEX idx_users_email_unique ON users (email) WHERE deleted_at IS NULL;
Advanced indexing: partial indexes, expression indexes, and conditional unique indexes
Full-Text Search
ALTER TABLE articles ADD COLUMN search_vector tsvector; UPDATE articles SET search_vector = to_tsvector('english', title || ' ' || content); CREATE INDEX idx_articles_fts ON articles USING GIN (search_vector);
Set up full-text search with tsvector and GIN index for fast text searching
SELECT title, content, ts_rank(search_vector, query) as rank FROM articles, to_tsquery('english', 'postgresql & database') query WHERE search_vector @@ query ORDER BY rank DESC;
Full-text search queries with ranking and boolean operators
SELECT title, ts_headline('english', content, to_tsquery('database')) as snippet FROM articles WHERE to_tsvector('english', title || ' ' || content) @@ to_tsquery('database');
Generate highlighted search snippets with ts_headline
JSON/JSONB Operations
SELECT id, data->'profile'->>'name' as name, jsonb_array_length(data->'hobbies') as hobby_count, data->'profile'->'age' as age FROM users WHERE data->'profile'->'age' > '25'::jsonb;
Navigate JSON structures and extract values with proper type casting
UPDATE users SET data = jsonb_set(data, '{profile,last_login}', to_jsonb(NOW())) WHERE id = 1; UPDATE users SET data = data || '{"verified": true}'::jsonb WHERE email_verified = true;
Update JSONB data: jsonb_set for nested updates, || for merging objects
SELECT data->'tags' as tags FROM posts WHERE data->'tags' ? 'postgresql' OR data->'tags' ?| array['database', 'sql'] AND data @> '{"published": true}';
JSONB containment and existence operators: ?, ?|, ?&, @>, <@
Array Operations
SELECT name, tags, array_length(tags, 1) as tag_count, 'postgresql' = ANY(tags) as has_postgresql_tag FROM articles WHERE tags && ARRAY['database', 'tutorial'];
Array functions and operators: array_length, ANY, && (overlap)
UPDATE articles SET tags = array_append(tags, 'updated') WHERE id = 1; UPDATE articles SET tags = array_remove(tags, 'deprecated') WHERE 'deprecated' = ANY(tags);
Modify arrays: array_append, array_prepend, array_remove, array_replace
SELECT unnest(tags) as tag, COUNT(*) as usage_count FROM articles GROUP BY unnest(tags) ORDER BY usage_count DESC;
Convert arrays to rows with unnest() for aggregation and analysis
Stored Procedures & Functions
CREATE OR REPLACE FUNCTION calculate_age(birth_date DATE) RETURNS INTEGER AS $$ BEGIN RETURN EXTRACT(YEAR FROM AGE(birth_date)); END; $$ LANGUAGE plpgsql; SELECT name, calculate_age(birth_date) as age FROM users;
Create custom functions with PL/pgSQL for reusable business logic
CREATE OR REPLACE FUNCTION update_user_stats() RETURNS TRIGGER AS $$ BEGIN UPDATE user_stats SET order_count = order_count + 1, last_order = NEW.created_at WHERE user_id = NEW.user_id; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tr_update_stats AFTER INSERT ON orders FOR EACH ROW EXECUTE FUNCTION update_user_stats();
Triggers for automatic data updates and maintaining denormalized statistics
Advanced PostgreSQL Features
CREATE MATERIALIZED VIEW monthly_sales AS SELECT date_trunc('month', order_date) as month, SUM(amount) as total_sales, COUNT(*) as order_count FROM orders GROUP BY date_trunc('month', order_date); REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_sales;
Materialized views for pre-computed results and better query performance
CREATE TABLE orders_partitioned ( id SERIAL, order_date DATE, amount DECIMAL ) PARTITION BY RANGE (order_date); CREATE TABLE orders_2024 PARTITION OF orders_partitioned FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
Table partitioning for better performance on large datasets
SELECT pg_size_pretty(pg_total_relation_size('orders')) as table_size; SELECT schemaname, tablename, attname, n_distinct, correlation FROM pg_stats WHERE tablename = 'orders'; ANALYZE orders;
Database maintenance: check table sizes, statistics, and update query planner stats
Transactions & Concurrency
BEGIN; SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; COMMIT;
Transaction control with isolation levels for data consistency
SELECT * FROM orders WHERE id = 123 FOR UPDATE; SELECT * FROM products WHERE id IN (1,2,3) FOR SHARE; LOCK TABLE inventory IN EXCLUSIVE MODE;
Row-level and table-level locking for concurrent access control
Resources
Complete PostgreSQL reference
Official getting started guide
Comprehensive PostgreSQL guide
WASM PostgreSQL used in this tool
Related PostgreSQL Query Editor Articles
Discover more insights about postgresql query editor and related development topics