A comprehensive guide to SQL keywords with examples, properties, and relationships. Perfect for developers, database administrators, and data analysts.
- Core SQL Language Keywords
- Logical Operators & Conditions
- JOIN Operations
- Conditional Logic
- Data Definition Language (DDL)
- Constraints & Keys
- Functions
- Window Functions
- Transaction Control
- Advanced SQL Features
- Views and Performance
- PL/pgSQL Special Variables
- Environment Variables
- System Catalogs & Information Schema
- Advanced Data Types
- Text Search and Full-Text Search
- Stored Procedures and Functions
- Triggers and Rules
- Security and Permissions
- Advanced Query Techniques
- Keywords Relationships Summary
Purpose: Retrieves data from one or more tables
Properties:
- Most fundamental SQL command
- Can be combined with almost all other keywords
- Supports subqueries and complex expressions
-- Basic SELECT SELECT first_name, last_name FROM employees; -- SELECT with calculations SELECT product_name, price, price * 0.1 AS tax FROM products; -- SELECT with subquery SELECT name FROM customers WHERE id IN (SELECT customer_id FROM orders);
Relations: Works with FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, JOINS
Purpose: Adds new rows to a table
Properties:
- Creates new data records
- Can insert single or multiple rows
- Can use subqueries for data source
-- Single row insert INSERT INTO employees (first_name, last_name, salary) VALUES ('John', 'Doe', 50000); -- Multiple rows insert INSERT INTO employees (first_name, last_name, salary) VALUES ('Jane', 'Smith', 55000), ('Bob', 'Johnson', 48000); -- Insert from SELECT INSERT INTO archived_orders SELECT * FROM orders WHERE order_date < '2023εΉ΄01ζ01ζ₯';
Relations: Works with VALUES, SELECT (for subqueries), ON CONFLICT
Purpose: Modifies existing data in tables
Properties:
- Changes existing records
- Must use WHERE to avoid updating all rows
- Can update multiple columns simultaneously
-- Basic update UPDATE employees SET salary = 55000 WHERE employee_id = 1; -- Update multiple columns UPDATE products SET price = price * 1.1, last_updated = NOW() WHERE category = 'Electronics'; -- Update with JOIN UPDATE employees e SET salary = salary * 1.05 FROM departments d WHERE e.dept_id = d.id AND d.name = 'Engineering';
Relations: Works with SET, WHERE, FROM, JOIN
Purpose: Removes rows from tables
Properties:
- Permanently removes data
- Can be conditional with WHERE
- Can cascade with foreign key constraints
-- Conditional delete DELETE FROM employees WHERE last_login < '2023εΉ΄01ζ01ζ₯'; -- Delete with subquery DELETE FROM orders WHERE customer_id IN (SELECT id FROM customers WHERE status = 'inactive'); -- Delete all records (dangerous!) DELETE FROM temp_table;
Relations: Works with WHERE, FROM (in some databases for joins)
Purpose: Specifies the source table(s) for data retrieval
Properties:
- Essential for SELECT, UPDATE, DELETE operations
- Can reference tables, views, subqueries
- Supports table aliases
-- Basic FROM SELECT * FROM employees; -- FROM with alias SELECT e.name, d.department_name FROM employees e, departments d; -- FROM with subquery SELECT * FROM ( SELECT name, salary FROM employees WHERE salary > 50000 ) AS high_earners;
Relations: Works with SELECT, JOIN, WHERE, aliases (AS)
Purpose: Combine multiple conditions in WHERE clauses
Properties:
- AND: All conditions must be true
- OR: At least one condition must be true
- NOT: Negates a condition
- Precedence: NOT > AND > OR (use parentheses for clarity)
-- AND operator SELECT * FROM employees WHERE salary > 50000 AND department = 'Engineering'; -- OR operator SELECT * FROM employees WHERE department = 'Sales' OR department = 'Marketing'; -- NOT operator SELECT * FROM customers WHERE NOT country = 'USA'; -- Complex combinations with parentheses SELECT * FROM products WHERE (category = 'Electronics' OR category = 'Books') AND price BETWEEN 10 AND 100 AND NOT discontinued;
Relations: Used with WHERE, HAVING, CASE statements
Purpose: Check if value exists in a list or subquery result
Properties:
- More readable than multiple OR conditions
- Can use with subqueries
- NULL values require special handling
-- IN with list SELECT * FROM employees WHERE department IN ('Sales', 'Marketing', 'HR'); -- IN with subquery SELECT * FROM products WHERE category_id IN ( SELECT id FROM categories WHERE active = true ); -- NOT IN SELECT * FROM customers WHERE country NOT IN ('USA', 'Canada', 'Mexico');
Relations: Alternative to multiple OR conditions, works with subqueries
Purpose: Check if subquery returns any rows
Properties:
- More efficient than IN for large datasets
- Returns TRUE/FALSE based on subquery results
- Handles NULL values better than IN
-- EXISTS SELECT * FROM customers c WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.id ); -- NOT EXISTS SELECT * FROM products p WHERE NOT EXISTS ( SELECT 1 FROM order_items oi WHERE oi.product_id = p.id );
Relations: Used with correlated subqueries, alternative to IN/NOT IN
Purpose: Check if value falls within a range (inclusive)
Properties:
- Inclusive of boundary values
- Works with numbers, dates, strings
- More readable than >= AND <=
-- Numeric range SELECT * FROM employees WHERE salary BETWEEN 40000 AND 80000; -- Date range SELECT * FROM orders WHERE order_date BETWEEN '2024εΉ΄01ζ01ζ₯' AND '2024εΉ΄12ζ31ζ₯'; -- NOT BETWEEN SELECT * FROM products WHERE price NOT BETWEEN 10 AND 50;
Relations: Shorthand for range conditions with AND
Purpose: Pattern matching in text
Properties:
- % matches any sequence of characters
- _ matches any single character
- ILIKE is case-insensitive (PostgreSQL)
- Can use ESCAPE for literal % or _
-- Basic pattern matching SELECT * FROM customers WHERE name LIKE 'John%'; -- Single character wildcard SELECT * FROM products WHERE code LIKE 'A_B_'; -- Case-insensitive (PostgreSQL) SELECT * FROM customers WHERE name ILIKE '%smith%'; -- Escape special characters SELECT * FROM products WHERE description LIKE '%50\% off%' ESCAPE '\';
Relations: Text pattern matching, used in WHERE clauses
Purpose: Combines rows from tables based on related columns
Properties:
- Returns only matching rows from both tables
- Most common join type
- Requires ON or USING clause
-- Basic INNER JOIN SELECT e.name, d.department_name FROM employees e INNER JOIN departments d ON e.dept_id = d.id; -- Multiple JOINs SELECT c.name, o.order_date, p.product_name FROM customers c JOIN orders o ON c.id = o.customer_id JOIN order_items oi ON o.id = oi.order_id JOIN products p ON oi.product_id = p.id;
Purpose: Returns all rows from left table, matching rows from right table
Properties:
- NULL values for non-matching right table columns
- Preserves all left table data
-- All employees with their departments (even if no department) SELECT e.name, d.department_name FROM employees e LEFT JOIN departments d ON e.dept_id = d.id;
Purpose: Simplified JOIN syntax when column names are identical
Properties:
- Only works when join columns have same names
- Eliminates duplicate columns in result
- Cleaner syntax for natural joins
-- Traditional JOIN SELECT * FROM employees e JOIN departments d ON e.department_id = d.department_id; -- Using USING SELECT * FROM employees e JOIN departments d USING (department_id);
Purpose: SQL standard alternative to LIMIT
Properties:
- Part of SQL standard (more portable)
- Used with OFFSET for pagination
- More verbose but standardized syntax
-- Fetch first 10 rows SELECT * FROM employees ORDER BY hire_date FETCH FIRST 10 ROWS ONLY; -- With offset SELECT * FROM employees ORDER BY hire_date OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
Purpose: Handle INSERT conflicts (PostgreSQL specific)
Properties:
- Alternative to INSERT failures
- Can update or ignore conflicts
- Useful for data synchronization
-- Insert or update INSERT INTO products (id, name, price) VALUES (1, 'Widget', 19.99) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, price = EXCLUDED.price, updated_at = NOW(); -- Insert or ignore INSERT INTO categories (name) VALUES ('Electronics') ON CONFLICT (name) DO NOTHING;
Purpose: Return data from INSERT, UPDATE, DELETE operations
Properties:
- Get generated IDs from INSERT
- See changed values from UPDATE
- Confirm deleted records
-- Get generated ID INSERT INTO customers (name, email) VALUES ('John Doe', 'john@example.com') RETURNING id, created_at; -- See updated values UPDATE employees SET salary = salary * 1.1 WHERE department = 'Sales' RETURNING name, salary;
Purpose: Creates new database tables
Properties:
- Defines column names, types, and constraints
- Can specify primary keys, foreign keys
- Various data types available
-- Basic table creation CREATE TABLE employees ( id SERIAL PRIMARY KEY, first_name VARCHAR(50) NOT NULL, last_name VARCHAR(50) NOT NULL, email VARCHAR(100) UNIQUE, hire_date DATE DEFAULT CURRENT_DATE, salary NUMERIC(10,2) CHECK (salary > 0) ); -- Table with foreign key CREATE TABLE orders ( id SERIAL PRIMARY KEY, customer_id INTEGER REFERENCES customers(id), order_date TIMESTAMP DEFAULT NOW(), total DECIMAL(10,2) );
Purpose: Modifies existing table structure
Properties:
- Add, drop, or modify columns
- Add or drop constraints
- Rename table or columns
-- Add column ALTER TABLE employees ADD COLUMN phone VARCHAR(20); -- Modify column ALTER TABLE employees ALTER COLUMN salary TYPE DECIMAL(12,2); -- Add constraint ALTER TABLE employees ADD CONSTRAINT chk_salary CHECK (salary BETWEEN 20000 AND 200000);
Purpose: Create database indexes for faster queries
Properties:
- Improves SELECT performance
- Slows down INSERT/UPDATE/DELETE
- Various index types available
-- Basic index CREATE INDEX idx_employees_department ON employees(department); -- Composite index CREATE INDEX idx_orders_customer_date ON orders(customer_id, order_date); -- Partial index CREATE INDEX idx_active_employees ON employees(name) WHERE status = 'active'; -- Functional index CREATE INDEX idx_lower_email ON customers(LOWER(email));
Purpose: Uniquely identifies each row in a table
Properties:
- Cannot be NULL
- Must be unique
- Only one per table
- Automatically creates index
-- Single column primary key CREATE TABLE customers ( id SERIAL PRIMARY KEY, name VARCHAR(100) ); -- Composite primary key CREATE TABLE order_items ( order_id INTEGER, product_id INTEGER, quantity INTEGER, PRIMARY KEY (order_id, product_id) );
Purpose: Links to primary key in another table
Properties:
- Enforces referential integrity
- Can have cascade options
- Can reference multiple columns
-- Basic foreign key CREATE TABLE orders ( id SERIAL PRIMARY KEY, customer_id INTEGER REFERENCES customers(id) ); -- With cascade options CREATE TABLE order_items ( order_id INTEGER REFERENCES orders(id) ON DELETE CASCADE, product_id INTEGER REFERENCES products(id) ON UPDATE CASCADE );
Purpose: Enforces domain integrity with custom conditions
Properties:
- Boolean expression that must be true
- Can reference multiple columns
- Evaluated on INSERT/UPDATE
-- Simple check constraint CREATE TABLE employees ( salary DECIMAL(10,2) CHECK (salary > 0) ); -- Complex check constraint CREATE TABLE products ( price DECIMAL(10,2), discount_price DECIMAL(10,2), CHECK (discount_price < price) );
SELECT name, LENGTH(name) as name_length FROM customers;
SELECT TRIM(' hello world '); -- Returns 'hello world' SELECT TRIM(BOTH 'x' FROM 'xxxhelloxxx'); -- Returns 'hello'
SELECT UPPER(first_name), LOWER(last_name) FROM employees;
SELECT CONCAT(first_name, ' ', last_name) as full_name FROM employees; -- PostgreSQL also supports: first_name || ' ' || last_name
SELECT NOW(); -- Current timestamp INSERT INTO logs (created_at) VALUES (NOW());
SELECT EXTRACT(YEAR FROM birth_date) as birth_year FROM employees; SELECT DATE_PART('month', order_date) as order_month FROM orders;
SELECT product_name, ROUND(price, 2) FROM products; SELECT ROUND(AVG(salary)) as avg_salary FROM employees;
SELECT ABS(-42); -- Returns 42 SELECT ABS(budget - actual) as variance FROM projects;
SELECT COUNT(*) FROM employees; -- All rows SELECT COUNT(email) FROM customers; -- Non-NULL emails SELECT COUNT(DISTINCT department) FROM employees; -- Unique departments
SELECT department, SUM(salary), AVG(salary) FROM employees GROUP BY department;
Purpose: Handle NULL values elegantly
-- COALESCE for default values SELECT name, COALESCE(phone, email, 'No contact') as contact FROM customers; -- NULLIF to convert empty strings to NULL SELECT NULLIF(TRIM(description), '') as clean_description FROM products;
Purpose: Assigns sequential numbers to rows
SELECT name, salary, ROW_NUMBER() OVER (ORDER BY salary DESC) as salary_rank FROM employees;
Purpose: Assigns ranks with gaps (RANK) or without gaps (DENSE_RANK)
SELECT name, salary, RANK() OVER (ORDER BY salary DESC) as rank, DENSE_RANK() OVER (ORDER BY salary DESC) as dense_rank FROM employees;
Purpose: Access values from following (LEAD) or preceding (LAG) rows
SELECT order_date, total, LAG(total) OVER (ORDER BY order_date) as previous_total, LEAD(total) OVER (ORDER BY order_date) as next_total FROM orders;
Purpose: Divides result set into partitions for window functions
SELECT name, department, salary, AVG(salary) OVER (PARTITION BY department) as dept_avg_salary FROM employees;
Purpose: Control transaction boundaries
-- Transaction example BEGIN; UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; COMMIT; -- With error handling BEGIN; INSERT INTO orders (customer_id, total) VALUES (1, 500); -- If error occurs: ROLLBACK;
Purpose: Create intermediate transaction points
BEGIN; INSERT INTO table1 VALUES (1, 'data'); SAVEPOINT sp1; INSERT INTO table2 VALUES (2, 'more data'); -- If needed: ROLLBACK TO sp1; -- Only rollback to savepoint COMMIT;
Purpose: Create virtual tables based on queries
Properties:
- Stored query definition, not data
- Always shows current data
- Can simplify complex queries
-- Simple view CREATE VIEW active_employees AS SELECT id, name, department, salary FROM employees WHERE status = 'active'; -- Complex view with joins CREATE VIEW employee_details AS SELECT e.name, d.department_name, p.project_name, e.salary FROM employees e JOIN departments d ON e.dept_id = d.id LEFT JOIN projects p ON e.project_id = p.id;
Purpose: Pre-computed and stored view results
Properties:
- Stores actual data (not just query)
- Better performance for complex queries
- Needs periodic refresh
-- Create materialized view CREATE MATERIALIZED VIEW monthly_sales AS SELECT DATE_TRUNC('month', order_date) as month, COUNT(*) as order_count, SUM(total) as total_sales FROM orders GROUP BY DATE_TRUNC('month', order_date); -- Refresh materialized view REFRESH MATERIALIZED VIEW monthly_sales;
Purpose: Show query execution plans
Properties:
- EXPLAIN shows planned execution
- EXPLAIN ANALYZE shows actual execution
- Helps optimize query performance
-- Show execution plan EXPLAIN SELECT * FROM employees WHERE department = 'Sales'; -- Show actual execution statistics EXPLAIN ANALYZE SELECT e.name, d.department_name FROM employees e JOIN departments d ON e.dept_id = d.id WHERE e.salary > 50000;
Purpose: Boolean flag indicating if previous SQL statement affected rows
CREATE OR REPLACE FUNCTION update_employee_salary(emp_id INTEGER, new_salary NUMERIC) RETURNS TEXT AS $$ DECLARE result TEXT; BEGIN UPDATE employees SET salary = new_salary WHERE id = emp_id; IF FOUND THEN result := 'Employee salary updated successfully'; ELSE result := 'Employee not found'; END IF; RETURN result; END; $$ LANGUAGE plpgsql;
Purpose: Number of rows affected by last SQL statement
CREATE OR REPLACE FUNCTION bulk_update_salaries(dept_name TEXT, increase_pct NUMERIC) RETURNS INTEGER AS $$ DECLARE affected_rows INTEGER; BEGIN UPDATE employees SET salary = salary * (1 + increase_pct / 100) WHERE department = dept_name; GET DIAGNOSTICS affected_rows = ROW_COUNT; RETURN affected_rows; END; $$ LANGUAGE plpgsql;
Purpose: Special variables available in trigger functions
CREATE OR REPLACE FUNCTION audit_trigger() RETURNS trigger AS $$ BEGIN IF TG_OP = 'INSERT' THEN INSERT INTO audit_log (table_name, operation, new_data, timestamp) VALUES (TG_TABLE_NAME, TG_OP, row_to_json(NEW), NOW()); RETURN NEW; ELSIF TG_OP = 'UPDATE' THEN INSERT INTO audit_log (table_name, operation, old_data, new_data, timestamp) VALUES (TG_TABLE_NAME, TG_OP, row_to_json(OLD), row_to_json(NEW), NOW()); RETURN NEW; END IF; END; $$ LANGUAGE plpgsql;
Purpose: Configure client connection and behavior
# Connection variables export PGHOST=localhost export PGPORT=5432 export PGUSER=myuser export PGPASSWORD=mypassword export PGDATABASE=mydb # SSL configuration export PGSSLMODE=require export PGSSLCERT=/path/to/client-cert.pem # Now psql uses these settings automatically psql
Purpose: Current database activity and connections
-- Current active connections SELECT pid, usename, application_name, client_addr, state, query_start, LEFT(query, 50) as current_query FROM pg_stat_activity WHERE state = 'active' AND pid <> pg_backend_pid(); -- Long-running queries SELECT pid, now() - query_start as duration, usename, LEFT(query, 100) as query FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '5 minutes';
-- Table information SELECT table_schema, table_name, table_type FROM information_schema.tables WHERE table_schema = 'public'; -- Column information SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = 'employees' ORDER BY ordinal_position;
Purpose: Create custom enumerated types
-- Create enum type CREATE TYPE status_type AS ENUM ('pending', 'active', 'inactive', 'deleted'); -- Use in table CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100), status status_type DEFAULT 'pending' ); -- Query with enum SELECT * FROM users WHERE status = 'active';
Purpose: Store multiple values in single column
-- Create table with array columns CREATE TABLE articles ( id SERIAL PRIMARY KEY, title VARCHAR(200), tags TEXT[], ratings INTEGER[] ); -- Insert array data INSERT INTO articles (title, tags, ratings) VALUES ('PostgreSQL Guide', ARRAY['database', 'sql', 'postgresql'], ARRAY[5, 4, 5, 4]); -- Array queries SELECT * FROM articles WHERE 'postgresql' = ANY(tags); SELECT * FROM articles WHERE tags && ARRAY['database', 'web'];
Purpose: Store universally unique identifiers
-- Enable UUID extension CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- Create table with UUID CREATE TABLE sessions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id INTEGER, created_at TIMESTAMP DEFAULT NOW() );
Purpose: Full-text search capabilities
-- Create table with text search CREATE TABLE documents ( id SERIAL PRIMARY KEY, title VARCHAR(200), content TEXT, search_vector tsvector ); -- Update search vector UPDATE documents SET search_vector = to_tsvector('english', title || ' ' || content); -- Create index for fast searching CREATE INDEX idx_documents_search ON documents USING gin(search_vector); -- Full-text search SELECT *, ts_rank(search_vector, query) as rank FROM documents, to_tsquery('english', 'postgresql & database') query WHERE search_vector @@ query ORDER BY rank DESC;
Purpose: Advanced pattern matching
-- Basic regex matching SELECT * FROM products WHERE code ~ '^[A-Z]{2}[0-9]{4}$'; -- Case-insensitive matching SELECT * FROM customers WHERE name ~* '^john.*smith$'; -- Extract patterns SELECT email, substring(email from '([^@]+)@(.+)') as username FROM users;
-- Simple SQL function CREATE OR REPLACE FUNCTION get_employee_count(dept_name TEXT) RETURNS INTEGER AS $$ SELECT COUNT(*) FROM employees WHERE department = dept_name; $$ LANGUAGE SQL IMMUTABLE; -- PL/pgSQL function with logic CREATE OR REPLACE FUNCTION calculate_bonus(emp_id INTEGER) RETURNS NUMERIC AS $$ DECLARE emp_salary NUMERIC; years_service INTEGER; bonus_amount NUMERIC := 0; BEGIN SELECT salary, EXTRACT(YEAR FROM AGE(NOW(), hire_date)) INTO emp_salary, years_service FROM employees WHERE id = emp_id; IF years_service >= 10 THEN bonus_amount := emp_salary * 0.15; ELSIF years_service >= 5 THEN bonus_amount := emp_salary * 0.10; END IF; RETURN bonus_amount; END; $$ LANGUAGE plpgsql;
Purpose: Automatically execute code in response to events
-- Audit trigger function CREATE OR REPLACE FUNCTION employee_audit_func() RETURNS trigger AS $$ BEGIN IF TG_OP = 'INSERT' THEN INSERT INTO employee_audit (action, employee_id, new_data, timestamp) VALUES ('INSERT', NEW.id, row_to_json(NEW), NOW()); RETURN NEW; ELSIF TG_OP = 'UPDATE' THEN INSERT INTO employee_audit (action, employee_id, old_data, new_data, timestamp) VALUES ('UPDATE', NEW.id, row_to_json(OLD), row_to_json(NEW), NOW()); RETURN NEW; END IF; END; $$ LANGUAGE plpgsql; -- Create triggers CREATE TRIGGER employee_audit_trigger AFTER INSERT OR UPDATE OR DELETE ON employees FOR EACH ROW EXECUTE FUNCTION employee_audit_func();
-- Create roles CREATE ROLE app_user LOGIN PASSWORD 'secure_password'; CREATE ROLE read_only_user; -- Grant permissions GRANT SELECT ON employees TO read_only_user; GRANT SELECT, INSERT, UPDATE ON customers TO app_user; -- Role inheritance CREATE ROLE reporting_team; GRANT read_only_user TO reporting_team;
-- Enable RLS on table ALTER TABLE employees ENABLE ROW LEVEL SECURITY; -- Create policies CREATE POLICY employee_policy ON employees FOR ALL TO app_user USING (department = current_setting('app.user_department'));
WITH department_stats AS ( SELECT department, COUNT(*) as emp_count, AVG(salary) as avg_salary FROM employees GROUP BY department ) SELECT * FROM department_stats WHERE emp_count > 5;
WITH RECURSIVE employee_hierarchy AS ( -- Base case: top-level managers SELECT id, name, manager_id, 1 as level FROM employees WHERE manager_id IS NULL UNION ALL -- Recursive case: employees with managers 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;
-- Pivot sales data by quarter SELECT product_name, SUM(CASE WHEN quarter = 'Q1' THEN sales ELSE 0 END) as q1_sales, SUM(CASE WHEN quarter = 'Q2' THEN sales ELSE 0 END) as q2_sales, SUM(CASE WHEN quarter = 'Q3' THEN sales ELSE 0 END) as q3_sales, SUM(CASE WHEN quarter = 'Q4' THEN sales ELSE 0 END) as q4_sales FROM quarterly_sales GROUP BY product_name;
-- SQL Execution Order 1. FROM / JOIN -- Source tables and relationships 2. WHERE -- Row filtering 3. GROUP BY -- Row grouping 4. HAVING -- Group filtering 5. SELECT -- Column selection and calculations 6. DISTINCT -- Remove duplicates 7. ORDER BY -- Sort results 8. LIMIT/OFFSET -- Limit results
1. Parentheses () -- Highest precedence 2. NOT -- Negation 3. AND -- Logical AND 4. OR -- Logical OR (lowest precedence)
-- Data Query Pattern SELECT columns FROM tables WHERE conditions; -- Aggregation Pattern SELECT columns, AGG_FUNC(column) FROM tables GROUP BY columns HAVING conditions; -- Join Pattern SELECT columns FROM table1 alias1 JOIN table2 alias2 ON join_condition WHERE filter_conditions; -- Subquery Pattern SELECT columns FROM table1 WHERE column IN (SELECT column FROM table2 WHERE conditions); -- Window Function Pattern SELECT columns, WINDOW_FUNC() OVER (PARTITION BY columns ORDER BY columns) FROM tables;
BEGIN; -- Start transaction SQL operations; -- INSERT, UPDATE, DELETE SAVEPOINT name; -- Optional checkpoint More operations; -- Additional work COMMIT; -- Make changes permanent -- OR -- ROLLBACK; -- Undo all changes -- OR -- ROLLBACK TO name; -- Undo to savepoint
-- Index Usage Pattern CREATE INDEX idx_table_columns ON table_name (column1, column2); EXPLAIN ANALYZE SELECT * FROM table_name WHERE column1 = 'value'; -- Materialized View Pattern CREATE MATERIALIZED VIEW summary_view AS SELECT department, COUNT(*), AVG(salary) FROM employees GROUP BY department; REFRESH MATERIALIZED VIEW summary_view; -- Partition Pattern (PostgreSQL 10+) CREATE TABLE sales ( id SERIAL, sale_date DATE, amount DECIMAL ) PARTITION BY RANGE (sale_date); CREATE TABLE sales_2024 PARTITION OF sales FOR VALUES FROM ('2024εΉ΄01ζ01ζ₯') TO ('2025εΉ΄01ζ01ζ₯');
Category | Keywords |
---|---|
Core DML | SELECT, INSERT, UPDATE, DELETE, FROM, WHERE |
Logical Operators | AND, OR, NOT, IN, EXISTS, BETWEEN, LIKE |
Joins | JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN |
Aggregation | GROUP BY, HAVING, COUNT, SUM, AVG, MIN, MAX |
Sorting & Limiting | ORDER BY, LIMIT, OFFSET, FETCH, DISTINCT |
Set Operations | UNION, UNION ALL, INTERSECT, EXCEPT |
Conditional | CASE, WHEN, THEN, ELSE, END, COALESCE, NULLIF |
DDL | CREATE, ALTER, DROP, TABLE, INDEX, VIEW |
Constraints | PRIMARY KEY, FOREIGN KEY, CHECK, UNIQUE, NOT NULL |
Transactions | BEGIN, COMMIT, ROLLBACK, SAVEPOINT |
Window Functions | ROW_NUMBER, RANK, DENSE_RANK, LEAD, LAG, OVER |
PostgreSQL | MySQL | SQL Server | Oracle |
---|---|---|---|
SERIAL | AUTO_INCREMENT | IDENTITY | SEQUENCE |
TEXT | TEXT | NVARCHAR(MAX) | CLOB |
BOOLEAN | BOOLEAN | BIT | NUMBER(1) |
TIMESTAMP | DATETIME | DATETIME2 | TIMESTAMP |
JSONB | JSON | NVARCHAR(MAX) | JSON |
ARRAY[] | JSON | - | VARRAY |
UUID | CHAR(36) | UNIQUEIDENTIFIER | RAW(16) |
Category | PostgreSQL | MySQL | SQL Server |
---|---|---|---|
String | CONCAT, LENGTH, TRIM, UPPER, LOWER | CONCAT, CHAR_LENGTH, TRIM, UPPER, LOWER | CONCAT, LEN, LTRIM/RTRIM, UPPER, LOWER |
Date | NOW(), DATE_PART, AGE | NOW(), DATE_FORMAT, DATEDIFF | GETDATE(), DATEPART, DATEDIFF |
Math | ROUND, CEIL, FLOOR, ABS | ROUND, CEIL, FLOOR, ABS | ROUND, CEILING, FLOOR, ABS |
Aggregate | COUNT, SUM, AVG, STRING_AGG | COUNT, SUM, AVG, GROUP_CONCAT | COUNT, SUM, AVG, STRING_AGG |
-- β Good: Use specific columns SELECT id, name FROM employees WHERE department = 'Sales'; -- β Bad: Select all columns when not needed SELECT * FROM employees WHERE department = 'Sales'; -- β Good: Use indexes effectively CREATE INDEX idx_emp_dept ON employees(department); SELECT * FROM employees WHERE department = 'Sales'; -- β Good: Use EXISTS instead of IN for large datasets SELECT * FROM customers c WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id); -- β Bad: Use IN with large subqueries SELECT * FROM customers WHERE id IN (SELECT customer_id FROM orders);
-- β Good: Use parameterized queries (application code) -- PREPARE stmt FROM 'SELECT * FROM users WHERE id = ?'; -- β Good: Grant minimal necessary permissions GRANT SELECT ON employees TO read_only_user; -- β Good: Use Row Level Security ALTER TABLE sensitive_data ENABLE ROW LEVEL SECURITY; CREATE POLICY user_policy ON sensitive_data USING (user_id = current_setting('app.current_user_id')::INTEGER); -- β Bad: Using string concatenation for queries (SQL injection risk) -- SELECT * FROM users WHERE name = '" + user_input + "'";
-- Monitor query performance EXPLAIN ANALYZE SELECT * FROM large_table WHERE indexed_column = 'value'; -- Check index usage SELECT schemaname, tablename, indexname, idx_tup_read, idx_tup_fetch FROM pg_stat_user_indexes ORDER BY idx_tup_read DESC; -- Monitor slow queries SELECT query, mean_exec_time, calls, total_exec_time FROM pg_stat_statements WHERE mean_exec_time > 1000 ORDER BY mean_exec_time DESC;
-- Running totals SELECT order_date, daily_sales, SUM(daily_sales) OVER (ORDER BY order_date) as running_total FROM daily_sales_summary; -- Top N per group SELECT * FROM ( SELECT product_name, category, sales, ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) as rn FROM product_sales ) ranked WHERE rn <= 3; -- Moving averages SELECT date, value, AVG(value) OVER ( ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW ) as seven_day_avg FROM metrics;
-- Remove duplicates DELETE FROM employees e1 WHERE EXISTS ( SELECT 1 FROM employees e2 WHERE e2.email = e1.email AND e2.id > e1.id ); -- Update with COALESCE for NULL handling UPDATE customers SET phone = COALESCE(NULLIF(TRIM(phone), ''), mobile_phone, 'No phone'); -- Standardize data format UPDATE addresses SET state = UPPER(TRIM(state)) WHERE LENGTH(TRIM(state)) = 2;
-- Bulk insert with conflict resolution INSERT INTO target_table (id, name, updated_at) SELECT id, name, NOW() FROM source_table ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, updated_at = EXCLUDED.updated_at; -- Data transformation with CTE WITH cleaned_data AS ( SELECT id, TRIM(UPPER(name)) as name, CASE WHEN age < 0 THEN NULL WHEN age > 150 THEN NULL ELSE age END as age FROM raw_data WHERE name IS NOT NULL ) INSERT INTO processed_data SELECT * FROM cleaned_data;
This guide is a living document. Contributions are welcome!
- Fork the repository
- Create a feature branch (
git checkout -b feature/new-examples
) - Add your examples or improvements
- Commit your changes (
git commit -am 'Add new SQL examples'
) - Push to the branch (
git push origin feature/new-examples
) - Create a Pull Request
- Provide clear, working examples
- Include both PostgreSQL and MySQL variations when applicable
- Add explanations for complex queries
- Follow the existing formatting style
- Test all code examples before submitting
This project is licensed under the MIT License - see the LICENSE file for details.
- PostgreSQL Global Development Group
- MySQL AB/Oracle Corporation
- Microsoft SQL Server Team
- The SQL standards committee (ISO/IEC)
- All contributors to open-source database projects
If you have questions or need help:
- Open an issue on GitHub
- Check the Discussions section
- Consult the official database documentation
- Join database-specific communities (Reddit, Stack Overflow, Discord)
Made with β€οΈ by the Database Community
Last updated: 2025