Home/Coding & Tech Skills

5 SQL Basics Every Developer Should Know (2026 Edition)

coding-tech-skills · Coding & Tech Skills

Last week I spent four hours debugging a Node.js microservice that kept timing out on a simple user lookup. The ORM was generating a query with seven nested subqueries, each one hitting the database sequentially. I rewrote it as a single CTE with a covering index, and the response time dropped from 12 seconds to 80 milliseconds. That moment reminded me why SQL basics every developer should know are not just academic—they're the difference between shipping on time and sleeping at your desk.

In 2026, the landscape has shifted. NoSQL databases like MongoDB have matured, but the rise of multi-model systems (PostgreSQL with JSONB, MySQL with document store) means SQL is no longer just for rigid schemas. Cloud warehouses like BigQuery and Snowflake have introduced modern clauses like QUALIFY and LATERAL, making SQL more expressive than ever. Yet the fundamentals—the five pillars I'll cover here—remain the bedrock. If you can't write a clean SELECT, understand JOIN semantics, or spot a missing index, you're leaving performance and maintainability on the table.

1. SELECT, WHERE, and ORDER BY — The Core Query You'll Write Daily

Every query starts with SELECT. But in 2026, the simple SELECT * FROM users WHERE signup_date > '2025-01-01' ORDER BY created_at DESC can hide performance traps. Modern SQL engines optimize column projection aggressively—always list specific columns instead of using *. It saves I/O, reduces memory pressure, and makes your intent explicit.

Here's a concrete example. I was working on an analytics dashboard that needed the top 10 users by last login. The naive query:

SELECT * FROM users ORDER BY last_login DESC LIMIT 10;

This worked fine on a test set of 1,000 rows. In production with 2 million users, it became a full table scan. Adding a WHERE clause to filter on active users and an explicit index (more on that later) turned it into a near-instantaneous index range scan.

Modern twist for 2026: Many databases now support the QUALIFY clause (common in BigQuery and Snowflake) for filtering window functions without subqueries. For example, to get the latest order per user:

SELECT user_id, order_total, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM orders
QUALIFY rn = 1;

This is cleaner than the old WHERE rn = 1 approach in a subquery. It's one of those SQL basics every developer should know because it reduces query nesting and improves readability.

2. JOINs — The Difference Between a Beginner and a Pragmatic Developer

If I had a dollar for every time I've seen a junior developer write a comma join without a WHERE clause, I'd have enough for a decent mechanical keyboard. In 2026, explicit JOIN syntax is the standard. The old-style implicit join (e.g., FROM users, orders WHERE users.id = orders.user_id) is considered legacy and error-prone—it's too easy to accidentally produce a Cartesian product.

Let's walk through a real scenario. You have a users table and an orders table. You want all users and their orders, including users who haven't placed any orders yet. That's a LEFT JOIN:

SELECT u.name, o.order_id, o.total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

If you use INNER JOIN, users without orders disappear. That seems obvious, but I've debugged production bugs where a dev used INNER JOIN thinking it was equivalent, only to lose orphan data that was critical for reporting.

Pragmatic tip: Always start with an INNER JOIN and only switch to LEFT JOIN when you have a specific reason to keep non-matching rows. This forces you to think about your data model. Also, avoid RIGHT JOIN in most cases—it's rarely needed and makes queries harder to read. Write the join direction that matches the logical flow (left table = main entity).

3. GROUP BY and Aggregate Functions — Turning Raw Data into Insights

Aggregations are where SQL starts feeling powerful. But they're also a common source of confusion, especially around HAVING vs WHERE. The rule is simple: WHERE filters rows before grouping; HAVING filters groups after aggregation. They are not interchangeable.

Here's a scenario I faced last month. I needed to find customers who had placed more than 5 orders in 2025, with a total spend over $1,000. The query:

SELECT customer_id, COUNT(*) AS order_count, SUM(total) AS total_spend
FROM orders
WHERE order_date BETWEEN '2025-01-01' AND '2025-12-31'
GROUP BY customer_id
HAVING COUNT(*) > 5 AND SUM(total) > 1000;

Notice the WHERE filters by date first, then GROUP BY aggregates, then HAVING applies the count and sum conditions. If I mistakenly used WHERE for the count condition, it wouldn't work because WHERE can't reference aggregated columns.

2026 insight: Modern SQL engines like DuckDB and Trino support GROUP BY ALL (shorthand for grouping by all non-aggregated columns in the SELECT list). It's a small ergonomic improvement but reduces typos. Also, use ROW_NUMBER() with PARTITION BY for deduplication—it's more reliable than DISTINCT ON (which is PostgreSQL-specific) or GROUP BY with MIN/MAX hacks.

4. Indexing Basics — Why Slow Queries Are a Developer Problem, Not a DBA Problem

In 2026, the line between developer and DBA is blurry. Most cloud databases are managed, but you still need to choose indexes wisely. A missing index can turn a 10ms query into a 10-second scan. An over-indexed table can slow down writes and bloat storage.

Here's what I've learned from painful experience. B-tree indexes are the default and work for equality and range queries (e.g., WHERE status = 'active' or WHERE created_at > '2025-01-01'). Covering indexes (which include all columns needed by a query) are a superpower: they allow the database to answer the query entirely from the index, avoiding a table lookup.

How to check if you need an index: Run EXPLAIN ANALYZE (or EXPLAIN in MySQL) on your slow query. Look for Seq Scan (sequential scan) on large tables. If you see it, adding an index on the columns used in WHERE and JOIN is your first step. For example, in PostgreSQL:

EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;

If it says Seq Scan on orders (cost=... rows=...) with a high row estimate, create an index:

CREATE INDEX idx_orders_user_id ON orders(user_id);

Then re-run the EXPLAIN. You should see Index Scan or Bitmap Heap Scan. This is one of those SQL basics every developer should know because it directly impacts user experience.

Counter-intuitive insight: Sometimes a full table scan is faster than an index scan—if the table is small (under a few thousand rows) or the query returns a large percentage of rows (say, 30%+). Indexes are not free. Always measure before and after.

5. Subqueries and CTEs — Writing Cleaner, More Maintainable SQL

Subqueries are like that one friend who always insists on doing things the hard way. Common Table Expressions (CTEs), introduced with the WITH clause, are the evolved version. They let you define named temporary result sets that you can reference multiple times in the same query. This makes complex logic readable, testable, and debuggable.

Before (subquery hell):

SELECT name, total
FROM (
  SELECT user_id, SUM(amount) AS total
  FROM payments
  GROUP BY user_id
) AS aggregated
JOIN users ON aggregated.user_id = users.id
WHERE total > 1000;

After (CTE):

WITH aggregated AS (
  SELECT user_id, SUM(amount) AS total
  FROM payments
  GROUP BY user_id
)
SELECT u.name, a.total
FROM aggregated a
JOIN users u ON a.user_id = u.id
WHERE a.total > 1000;

The CTE version is flatter, easier to modify, and you can reference aggregated multiple times if needed (e.g., for a second join). In 2026, recursive CTEs are also more common for hierarchical data (org charts, category trees).

Trade-off: Some databases (like PostgreSQL) materialize CTEs by default, meaning the CTE is executed once and its result is stored. This can be good for repeated references but bad if the CTE is only used once and a subquery would inline better. Check your database docs—or use WITH ... AS NOT MATERIALIZED in PostgreSQL if you want the optimizer to inline it.

FAQ: Common Questions About SQL in 2026

Is SQL still worth learning in 2026 with so many NoSQL databases?

Yes—SQL remains the universal language for data querying across relational, document, and even some NoSQL systems (e.g., PostgreSQL JSONB). The skills transfer directly.

What's the single most common mistake beginners make with JOINs?

Forgetting to specify JOIN type, leading to unintended Cartesian products. Always use explicit INNER/LEFT/RIGHT/FULL.

How do I know if my query needs an index?

Run EXPLAIN or EXPLAIN ANALYZE—if you see sequential scans on large tables, an index might help.

What's the difference between HAVING and WHERE in SQL?

WHERE filters rows before grouping, HAVING filters groups after aggregation. They are not interchangeable.

Should I use subqueries or CTEs?

CTEs (WITH clause) are usually easier to read, debug, and can be referenced multiple times—prefer them for complex queries.

Practical Takeaway

The five SQL basics every developer should know in 2026 are: SELECT (with explicit columns and QUALIFY), JOIN (with explicit LEFT/INNER), GROUP BY (with HAVING and window functions), indexing (with EXPLAIN analysis), and CTEs (for readable, modular queries). Master these, and you'll write queries that are fast, maintainable, and easy to debug. The next time an ORM gives you a slow query, you'll know exactly how to fix it—and that's a superpower worth having.