Initial commit
This commit is contained in:
475
skills/databases/references/postgresql-queries.md
Normal file
475
skills/databases/references/postgresql-queries.md
Normal file
@@ -0,0 +1,475 @@
|
||||
# PostgreSQL SQL Queries
|
||||
|
||||
SQL queries in PostgreSQL: SELECT, JOINs, subqueries, CTEs, window functions, and advanced patterns.
|
||||
|
||||
## Basic SELECT
|
||||
|
||||
### Simple Queries
|
||||
```sql
|
||||
-- Select all columns
|
||||
SELECT * FROM users;
|
||||
|
||||
-- Select specific columns
|
||||
SELECT id, name, email FROM users;
|
||||
|
||||
-- With alias
|
||||
SELECT name AS full_name, email AS contact_email FROM users;
|
||||
|
||||
-- Distinct values
|
||||
SELECT DISTINCT status FROM orders;
|
||||
|
||||
-- Count rows
|
||||
SELECT COUNT(*) FROM users;
|
||||
SELECT COUNT(DISTINCT status) FROM orders;
|
||||
```
|
||||
|
||||
### WHERE Clause
|
||||
```sql
|
||||
-- Equality
|
||||
SELECT * FROM users WHERE status = 'active';
|
||||
|
||||
-- Comparison
|
||||
SELECT * FROM products WHERE price > 100;
|
||||
SELECT * FROM orders WHERE total BETWEEN 100 AND 500;
|
||||
|
||||
-- Pattern matching
|
||||
SELECT * FROM users WHERE email LIKE '%@example.com';
|
||||
SELECT * FROM users WHERE name ILIKE 'john%'; -- case-insensitive
|
||||
|
||||
-- IN operator
|
||||
SELECT * FROM orders WHERE status IN ('pending', 'processing');
|
||||
|
||||
-- NULL checks
|
||||
SELECT * FROM users WHERE deleted_at IS NULL;
|
||||
SELECT * FROM users WHERE phone_number IS NOT NULL;
|
||||
|
||||
-- Logical operators
|
||||
SELECT * FROM products WHERE price > 100 AND stock > 0;
|
||||
SELECT * FROM users WHERE status = 'active' OR verified = true;
|
||||
SELECT * FROM products WHERE NOT (price > 1000);
|
||||
```
|
||||
|
||||
### ORDER BY
|
||||
```sql
|
||||
-- Ascending (default)
|
||||
SELECT * FROM users ORDER BY created_at;
|
||||
|
||||
-- Descending
|
||||
SELECT * FROM users ORDER BY created_at DESC;
|
||||
|
||||
-- Multiple columns
|
||||
SELECT * FROM orders ORDER BY status ASC, created_at DESC;
|
||||
|
||||
-- NULL handling
|
||||
SELECT * FROM users ORDER BY last_login NULLS FIRST;
|
||||
SELECT * FROM users ORDER BY last_login NULLS LAST;
|
||||
```
|
||||
|
||||
### LIMIT and OFFSET
|
||||
```sql
|
||||
-- Limit results
|
||||
SELECT * FROM users LIMIT 10;
|
||||
|
||||
-- Pagination
|
||||
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 20;
|
||||
|
||||
-- Alternative: FETCH
|
||||
SELECT * FROM users OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
|
||||
```
|
||||
|
||||
## JOINs
|
||||
|
||||
### INNER JOIN
|
||||
```sql
|
||||
-- Match rows from both tables
|
||||
SELECT orders.id, orders.total, customers.name
|
||||
FROM orders
|
||||
INNER JOIN customers ON orders.customer_id = customers.id;
|
||||
|
||||
-- Short syntax
|
||||
SELECT o.id, o.total, c.name
|
||||
FROM orders o
|
||||
JOIN customers c ON o.customer_id = c.id;
|
||||
|
||||
-- Multiple joins
|
||||
SELECT o.id, c.name, p.name AS product
|
||||
FROM orders o
|
||||
JOIN customers c ON o.customer_id = c.id
|
||||
JOIN order_items oi ON oi.order_id = o.id
|
||||
JOIN products p ON oi.product_id = p.id;
|
||||
```
|
||||
|
||||
### LEFT JOIN (LEFT OUTER JOIN)
|
||||
```sql
|
||||
-- All rows from left table, matching rows from right
|
||||
SELECT c.name, o.id AS order_id
|
||||
FROM customers c
|
||||
LEFT JOIN orders o ON c.id = o.customer_id;
|
||||
|
||||
-- Find customers without orders
|
||||
SELECT c.name
|
||||
FROM customers c
|
||||
LEFT JOIN orders o ON c.id = o.customer_id
|
||||
WHERE o.id IS NULL;
|
||||
```
|
||||
|
||||
### RIGHT JOIN (RIGHT OUTER JOIN)
|
||||
```sql
|
||||
-- All rows from right table, matching rows from left
|
||||
SELECT c.name, o.id AS order_id
|
||||
FROM orders o
|
||||
RIGHT JOIN customers c ON o.customer_id = c.id;
|
||||
```
|
||||
|
||||
### FULL OUTER JOIN
|
||||
```sql
|
||||
-- All rows from both tables
|
||||
SELECT c.name, o.id AS order_id
|
||||
FROM customers c
|
||||
FULL OUTER JOIN orders o ON c.id = o.customer_id;
|
||||
```
|
||||
|
||||
### CROSS JOIN
|
||||
```sql
|
||||
-- Cartesian product (all combinations)
|
||||
SELECT c.name, p.name
|
||||
FROM colors c
|
||||
CROSS JOIN products p;
|
||||
```
|
||||
|
||||
### Self Join
|
||||
```sql
|
||||
-- Join table to itself
|
||||
SELECT e1.name AS employee, e2.name AS manager
|
||||
FROM employees e1
|
||||
LEFT JOIN employees e2 ON e1.manager_id = e2.id;
|
||||
```
|
||||
|
||||
## Subqueries
|
||||
|
||||
### Scalar Subquery
|
||||
```sql
|
||||
-- Return single value
|
||||
SELECT name, salary,
|
||||
(SELECT AVG(salary) FROM employees) AS avg_salary
|
||||
FROM employees;
|
||||
```
|
||||
|
||||
### IN Subquery
|
||||
```sql
|
||||
-- Match against set of values
|
||||
SELECT name FROM customers
|
||||
WHERE id IN (
|
||||
SELECT customer_id FROM orders WHERE total > 1000
|
||||
);
|
||||
```
|
||||
|
||||
### EXISTS Subquery
|
||||
```sql
|
||||
-- Check if subquery returns any rows
|
||||
SELECT name FROM customers c
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM orders o WHERE o.customer_id = c.id
|
||||
);
|
||||
|
||||
-- NOT EXISTS
|
||||
SELECT name FROM customers c
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM orders o WHERE o.customer_id = c.id
|
||||
);
|
||||
```
|
||||
|
||||
### Correlated Subquery
|
||||
```sql
|
||||
-- Subquery references outer query
|
||||
SELECT name, salary FROM employees e1
|
||||
WHERE salary > (
|
||||
SELECT AVG(salary) FROM employees e2
|
||||
WHERE e2.department_id = e1.department_id
|
||||
);
|
||||
```
|
||||
|
||||
## Common Table Expressions (CTEs)
|
||||
|
||||
### Simple CTE
|
||||
```sql
|
||||
-- Named temporary result set
|
||||
WITH active_users AS (
|
||||
SELECT id, name, email FROM users WHERE status = 'active'
|
||||
)
|
||||
SELECT * FROM active_users WHERE created_at > '2024-01-01';
|
||||
```
|
||||
|
||||
### Multiple CTEs
|
||||
```sql
|
||||
WITH
|
||||
active_customers AS (
|
||||
SELECT id, name FROM customers WHERE active = true
|
||||
),
|
||||
recent_orders AS (
|
||||
SELECT customer_id, SUM(total) AS total_spent
|
||||
FROM orders
|
||||
WHERE order_date > CURRENT_DATE - INTERVAL '30 days'
|
||||
GROUP BY customer_id
|
||||
)
|
||||
SELECT c.name, COALESCE(o.total_spent, 0) AS spent
|
||||
FROM active_customers c
|
||||
LEFT JOIN recent_orders o ON c.id = o.customer_id;
|
||||
```
|
||||
|
||||
### Recursive CTE
|
||||
```sql
|
||||
-- Tree traversal, hierarchical data
|
||||
WITH RECURSIVE category_tree AS (
|
||||
-- Base case: root categories
|
||||
SELECT id, name, parent_id, 0 AS level
|
||||
FROM categories
|
||||
WHERE parent_id IS NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Recursive case: child categories
|
||||
SELECT c.id, c.name, c.parent_id, ct.level + 1
|
||||
FROM categories c
|
||||
JOIN category_tree ct ON c.parent_id = ct.id
|
||||
)
|
||||
SELECT * FROM category_tree ORDER BY level, name;
|
||||
|
||||
-- Employee hierarchy
|
||||
WITH RECURSIVE org_chart AS (
|
||||
SELECT id, name, manager_id, 1 AS level
|
||||
FROM employees
|
||||
WHERE manager_id IS NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT e.id, e.name, e.manager_id, oc.level + 1
|
||||
FROM employees e
|
||||
JOIN org_chart oc ON e.manager_id = oc.id
|
||||
)
|
||||
SELECT * FROM org_chart;
|
||||
```
|
||||
|
||||
## Aggregate Functions
|
||||
|
||||
### Basic Aggregates
|
||||
```sql
|
||||
-- COUNT, SUM, AVG, MIN, MAX
|
||||
SELECT
|
||||
COUNT(*) AS total_orders,
|
||||
SUM(total) AS total_revenue,
|
||||
AVG(total) AS avg_order_value,
|
||||
MIN(total) AS min_order,
|
||||
MAX(total) AS max_order
|
||||
FROM orders;
|
||||
|
||||
-- COUNT variations
|
||||
SELECT COUNT(*) FROM users; -- All rows
|
||||
SELECT COUNT(phone_number) FROM users; -- Non-NULL values
|
||||
SELECT COUNT(DISTINCT status) FROM orders; -- Unique values
|
||||
```
|
||||
|
||||
### GROUP BY
|
||||
```sql
|
||||
-- Aggregate by groups
|
||||
SELECT status, COUNT(*) AS count
|
||||
FROM orders
|
||||
GROUP BY status;
|
||||
|
||||
-- Multiple grouping columns
|
||||
SELECT customer_id, status, COUNT(*) AS count
|
||||
FROM orders
|
||||
GROUP BY customer_id, status;
|
||||
|
||||
-- With aggregate functions
|
||||
SELECT customer_id,
|
||||
COUNT(*) AS order_count,
|
||||
SUM(total) AS total_spent,
|
||||
AVG(total) AS avg_order
|
||||
FROM orders
|
||||
GROUP BY customer_id;
|
||||
```
|
||||
|
||||
### HAVING
|
||||
```sql
|
||||
-- Filter after aggregation
|
||||
SELECT customer_id, SUM(total) AS total_spent
|
||||
FROM orders
|
||||
GROUP BY customer_id
|
||||
HAVING SUM(total) > 1000;
|
||||
|
||||
-- Multiple conditions
|
||||
SELECT status, COUNT(*) AS count
|
||||
FROM orders
|
||||
GROUP BY status
|
||||
HAVING COUNT(*) > 10;
|
||||
```
|
||||
|
||||
## Window Functions
|
||||
|
||||
### ROW_NUMBER
|
||||
```sql
|
||||
-- Assign unique number to each row
|
||||
SELECT id, name, salary,
|
||||
ROW_NUMBER() OVER (ORDER BY salary DESC) AS rank
|
||||
FROM employees;
|
||||
|
||||
-- Partition by group
|
||||
SELECT id, department, salary,
|
||||
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank
|
||||
FROM employees;
|
||||
```
|
||||
|
||||
### RANK / DENSE_RANK
|
||||
```sql
|
||||
-- RANK: gaps in ranking for ties
|
||||
-- DENSE_RANK: no gaps
|
||||
SELECT id, name, salary,
|
||||
RANK() OVER (ORDER BY salary DESC) AS rank,
|
||||
DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
|
||||
FROM employees;
|
||||
```
|
||||
|
||||
### LAG / LEAD
|
||||
```sql
|
||||
-- Access previous/next row
|
||||
SELECT date, revenue,
|
||||
LAG(revenue) OVER (ORDER BY date) AS prev_revenue,
|
||||
LEAD(revenue) OVER (ORDER BY date) AS next_revenue,
|
||||
revenue - LAG(revenue) OVER (ORDER BY date) AS change
|
||||
FROM daily_sales;
|
||||
```
|
||||
|
||||
### Running Totals
|
||||
```sql
|
||||
-- Cumulative sum
|
||||
SELECT date, amount,
|
||||
SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
|
||||
FROM transactions;
|
||||
|
||||
-- Simpler syntax
|
||||
SELECT date, amount,
|
||||
SUM(amount) OVER (ORDER BY date) AS running_total
|
||||
FROM transactions;
|
||||
```
|
||||
|
||||
### Moving Averages
|
||||
```sql
|
||||
-- 7-day moving average
|
||||
SELECT date, value,
|
||||
AVG(value) OVER (
|
||||
ORDER BY date
|
||||
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
|
||||
) AS moving_avg_7d
|
||||
FROM metrics;
|
||||
```
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### CASE Expressions
|
||||
```sql
|
||||
-- Simple CASE
|
||||
SELECT name,
|
||||
CASE status
|
||||
WHEN 'active' THEN 'Active User'
|
||||
WHEN 'pending' THEN 'Pending Verification'
|
||||
ELSE 'Inactive'
|
||||
END AS status_label
|
||||
FROM users;
|
||||
|
||||
-- Searched CASE
|
||||
SELECT name, age,
|
||||
CASE
|
||||
WHEN age < 18 THEN 'Minor'
|
||||
WHEN age BETWEEN 18 AND 65 THEN 'Adult'
|
||||
ELSE 'Senior'
|
||||
END AS age_group
|
||||
FROM users;
|
||||
```
|
||||
|
||||
### COALESCE
|
||||
```sql
|
||||
-- Return first non-NULL value
|
||||
SELECT name, COALESCE(phone_number, email, 'No contact') AS contact
|
||||
FROM users;
|
||||
```
|
||||
|
||||
### NULLIF
|
||||
```sql
|
||||
-- Return NULL if values equal
|
||||
SELECT name, NULLIF(status, 'deleted') AS active_status
|
||||
FROM users;
|
||||
```
|
||||
|
||||
### Array Operations
|
||||
```sql
|
||||
-- Array aggregate
|
||||
SELECT customer_id, ARRAY_AGG(product_id) AS products
|
||||
FROM order_items
|
||||
GROUP BY customer_id;
|
||||
|
||||
-- Unnest array
|
||||
SELECT unnest(ARRAY[1, 2, 3, 4, 5]);
|
||||
|
||||
-- Array contains
|
||||
SELECT * FROM products WHERE tags @> ARRAY['featured'];
|
||||
```
|
||||
|
||||
### JSON Operations
|
||||
```sql
|
||||
-- Query JSON/JSONB
|
||||
SELECT data->>'name' AS name FROM documents;
|
||||
SELECT data->'address'->>'city' AS city FROM documents;
|
||||
|
||||
-- Check key exists
|
||||
SELECT * FROM documents WHERE data ? 'email';
|
||||
|
||||
-- JSONB operators
|
||||
SELECT * FROM documents WHERE data @> '{"status": "active"}';
|
||||
|
||||
-- JSON aggregation
|
||||
SELECT json_agg(name) FROM users;
|
||||
SELECT json_object_agg(id, name) FROM users;
|
||||
```
|
||||
|
||||
## Set Operations
|
||||
|
||||
### UNION
|
||||
```sql
|
||||
-- Combine results (removes duplicates)
|
||||
SELECT name FROM customers
|
||||
UNION
|
||||
SELECT name FROM suppliers;
|
||||
|
||||
-- Keep duplicates
|
||||
SELECT name FROM customers
|
||||
UNION ALL
|
||||
SELECT name FROM suppliers;
|
||||
```
|
||||
|
||||
### INTERSECT
|
||||
```sql
|
||||
-- Common rows
|
||||
SELECT email FROM users
|
||||
INTERSECT
|
||||
SELECT email FROM subscribers;
|
||||
```
|
||||
|
||||
### EXCEPT
|
||||
```sql
|
||||
-- Rows in first query but not second
|
||||
SELECT email FROM users
|
||||
EXCEPT
|
||||
SELECT email FROM unsubscribed;
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use indexes** on WHERE, JOIN, ORDER BY columns
|
||||
2. **Avoid SELECT *** - specify needed columns
|
||||
3. **Use EXISTS** instead of IN for large subqueries
|
||||
4. **Filter early** - WHERE before JOIN when possible
|
||||
5. **Use CTEs** for readability over nested subqueries
|
||||
6. **Parameterize queries** - prevent SQL injection
|
||||
7. **Use window functions** instead of self-joins
|
||||
8. **Test with EXPLAIN** - analyze query plans
|
||||
Reference in New Issue
Block a user