🗃️Developer

SQL Queries Every Developer Should Actually Know

Not the intro tutorial. The queries that make you useful to your team — JOINs that make sense, aggregations, window functions, and when NOT to use SQL.

9 min readFebruary 5, 2026By FreeToolKit TeamFree to read

Most SQL tutorials cover SELECT, WHERE, and simple JOINs. That gets you 60% of the way. Here's the 40% that makes the difference between querying data and actually understanding it.

JOINs: All Four Types and When Each Applies

  • INNER JOIN: Returns rows where the join condition matches in both tables. Most common. SELECT u.name, o.total FROM users u INNER JOIN orders o ON u.id = o.user_id — returns only users who have at least one order.
  • LEFT JOIN: All rows from the left table, matching rows from the right (NULL if no match). SELECT u.name, COUNT(o.id) as order_count FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.name — includes users with zero orders.
  • RIGHT JOIN: All rows from the right table. Rarely needed — you can always restructure to use LEFT JOIN by swapping table order.
  • FULL OUTER JOIN: All rows from both tables, NULLs where no match. Use for finding discrepancies between two datasets.

Aggregation Patterns That Actually Come Up

Top N per group

-- Top 3 orders per customer by total
SELECT *
FROM (
  SELECT *,
    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY total DESC) as rn
  FROM orders
) ranked
WHERE rn <= 3;

Running total

-- Running revenue total by date
SELECT
  date,
  daily_revenue,
  SUM(daily_revenue) OVER (ORDER BY date) as running_total
FROM daily_sales;

Find duplicates

-- Email addresses that appear more than once
SELECT email, COUNT(*) as occurrences
FROM users
GROUP BY email
HAVING COUNT(*) > 1;

CTEs: Making Complex Queries Readable

Common Table Expressions (WITH clauses) let you name intermediate results. Instead of nesting subqueries 4 levels deep, CTEs read like sequential steps:

CTE example

WITH active_users AS (
  SELECT id, name FROM users WHERE status = 'active'
),
recent_orders AS (
  SELECT user_id, SUM(total) as total_spend
  FROM orders
  WHERE created_at > NOW() - INTERVAL '30 days'
  GROUP BY user_id
)
SELECT u.name, COALESCE(o.total_spend, 0) as spend_last_30
FROM active_users u
LEFT JOIN recent_orders o ON u.id = o.user_id
ORDER BY spend_last_30 DESC;

When to Stop Using SQL (And Use Application Code)

Complex string manipulation: SQL string functions are clunky. Extract the data in SQL, manipulate strings in application code.

Business logic with many conditionals: A 200-line SQL query with nested CASE WHEN statements is hard to test and maintain. Move logic that changes frequently to application code.

Recursive operations: SQL has recursive CTEs, but recursive algorithms are generally clearer in application code.

Frequently Asked Questions

What's the difference between WHERE and HAVING?+
WHERE filters rows before grouping, HAVING filters groups after grouping. SELECT category, COUNT(*) as count FROM products WHERE price > 10 GROUP BY category HAVING COUNT(*) > 5 — WHERE removes products priced $10 or less before grouping, then HAVING removes categories with 5 or fewer remaining products. You can't use aggregate functions (COUNT, SUM, AVG) in WHERE clauses because aggregation happens after WHERE. This is the practical rule: if the filter involves an aggregate function, use HAVING.
When should I use a subquery vs a JOIN?+
JOINs are usually more performant because the database optimizer handles them better. Subqueries are useful for readability when the relationship is conceptually separate, for correlated subqueries (where the subquery references the outer query's row), and for EXISTS checks (SELECT 1 WHERE EXISTS (...) often outperforms COUNT). In practice: start with a JOIN, use a subquery when it makes the intent clearer or the JOIN is awkward, and use CTEs (WITH clauses) for complex multi-step queries.
What is an index and when should I add one?+
An index is a data structure that lets the database find rows without scanning the entire table. Without an index on a 10-million-row table, SELECT * FROM orders WHERE user_id = 123 scans all 10 million rows. With an index on user_id, it jumps directly to the matching rows. Add indexes on: columns in WHERE clauses of frequently-run queries, JOIN conditions, ORDER BY columns. Indexes have a cost — they slow INSERT/UPDATE/DELETE and use disk space. Don't add indexes preemptively; add them when you identify slow queries.
How do I find and fix slow SQL queries?+
EXPLAIN ANALYZE (Postgres) or EXPLAIN (MySQL) before your query shows the execution plan — how the database intends to run the query, what indexes it uses, and the estimated cost of each step. Look for 'Seq Scan' on large tables (full table scan, no index) or 'Hash Join' on very large tables. Slow queries in production: use your database's slow query log or an APM tool to identify them, then EXPLAIN the problematic query in a development environment to diagnose.

🔧 Free Tools Used in This Guide

FT

FreeToolKit Team

FreeToolKit Team

We build free browser-based tools and write practical guides without the fluff.

Tags:

sqldatabasedeveloperbackend