DEV Community

Alex Spinov
Alex Spinov

Posted on

SQL Queries You'll Use Every Day (But Nobody Teaches in Tutorials)

SQL tutorials teach you SELECT, INSERT, UPDATE, DELETE. Then they stop.

But production SQL is 80% window functions, CTEs, and conditional aggregation. Here are the patterns I use daily.

1. CTEs (Common Table Expressions)

The single most useful SQL feature:

-- Find users who signed up last month and made a purchase
WITH recent_users AS (
    SELECT id, email, created_at
    FROM users
    WHERE created_at > CURRENT_DATE - INTERVAL '30 days'
),
purchasers AS (
    SELECT DISTINCT user_id
    FROM orders
    WHERE created_at > CURRENT_DATE - INTERVAL '30 days'
)
SELECT r.email, r.created_at
FROM recent_users r
JOIN purchasers p ON r.id = p.user_id;
Enter fullscreen mode Exit fullscreen mode

CTEs make complex queries readable. Each WITH block is a named subquery you can reference.

2. Window Functions

Running Total

SELECT 
    date,
    revenue,
    SUM(revenue) OVER (ORDER BY date) as running_total
FROM daily_sales;
Enter fullscreen mode Exit fullscreen mode

Rank Within Groups

-- Top 3 products per category
SELECT * FROM (
    SELECT 
        name, category, sales,
        ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) as rank
    FROM products
) ranked
WHERE rank <= 3;
Enter fullscreen mode Exit fullscreen mode

Previous/Next Row

SELECT 
    date,
    revenue,
    LAG(revenue) OVER (ORDER BY date) as prev_day,
    revenue - LAG(revenue) OVER (ORDER BY date) as change
FROM daily_sales;
Enter fullscreen mode Exit fullscreen mode

3. CASE Expressions for Conditional Logic

-- Categorize users by activity
SELECT 
    email,
    CASE 
        WHEN last_login > CURRENT_DATE - INTERVAL '7 days' THEN 'active'
        WHEN last_login > CURRENT_DATE - INTERVAL '30 days' THEN 'at_risk'
        ELSE 'churned'
    END as status,
    COUNT(*) OVER () as total_users
FROM users;
Enter fullscreen mode Exit fullscreen mode

Conditional Aggregation (Pivot)

-- Count orders by status in one query
SELECT 
    DATE_TRUNC('month', created_at) as month,
    COUNT(*) FILTER (WHERE status = 'completed') as completed,
    COUNT(*) FILTER (WHERE status = 'pending') as pending,
    COUNT(*) FILTER (WHERE status = 'cancelled') as cancelled
FROM orders
GROUP BY 1
ORDER BY 1;
Enter fullscreen mode Exit fullscreen mode

4. COALESCE for Default Values

-- Handle NULL values gracefully
SELECT 
    name,
    COALESCE(phone, email, 'No contact info') as contact,
    COALESCE(discount, 0) as discount
FROM customers;
Enter fullscreen mode Exit fullscreen mode

5. EXISTS vs IN

-- FASTER: EXISTS (stops at first match)
SELECT * FROM users u
WHERE EXISTS (
    SELECT 1 FROM orders o WHERE o.user_id = u.id
);

-- SLOWER: IN (loads all results first)
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders);
Enter fullscreen mode Exit fullscreen mode

EXISTS is almost always faster for subqueries. IN is fine for small static lists.

6. LATERAL JOIN

-- Get latest 3 orders per user
SELECT u.name, o.id, o.total, o.created_at
FROM users u
CROSS JOIN LATERAL (
    SELECT id, total, created_at
    FROM orders
    WHERE user_id = u.id
    ORDER BY created_at DESC
    LIMIT 3
) o;
Enter fullscreen mode Exit fullscreen mode

7. UPSERT (INSERT ON CONFLICT)

-- Insert or update in one statement
INSERT INTO metrics (user_id, date, page_views)
VALUES (123, CURRENT_DATE, 1)
ON CONFLICT (user_id, date) 
DO UPDATE SET page_views = metrics.page_views + 1;
Enter fullscreen mode Exit fullscreen mode

8. Generate Series (for filling gaps)

-- Revenue for every day, even days with no sales
WITH dates AS (
    SELECT generate_series(
        '2026-01-01'::date,
        '2026-03-25'::date,
        '1 day'
    )::date as date
)
SELECT 
    d.date,
    COALESCE(SUM(o.total), 0) as revenue
FROM dates d
LEFT JOIN orders o ON DATE(o.created_at) = d.date
GROUP BY d.date
ORDER BY d.date;
Enter fullscreen mode Exit fullscreen mode

Cheatsheet

Pattern Use When
CTE Breaking complex queries into steps
Window functions Rankings, running totals, row comparison
CASE Conditional logic in queries
COALESCE Default values for NULLs
EXISTS Checking if related rows exist (faster than IN)
LATERAL Correlated subqueries with LIMIT
UPSERT Insert-or-update
generate_series Filling date/time gaps

More database tools: Docker Compose Templates (PostgreSQL, MongoDB, Redis configs) | FastAPI Starter


What SQL pattern do you use the most? Any favorites I missed? 👇

SQL & backend articles at dev.to/0012303

Top comments (0)