Why Every Developer Should Learn SQL
ORMs have made it possible to build entire applications without writing a single line of SQL. That convenience has also created a generation of developers who struggle the moment they need to debug a slow query, write a migration, or pull data that does not fit neatly into an ORM method chain.
SQL is not a niche skill for database administrators. It is a fundamental part of the developer toolkit, and learning it will make you better at almost every aspect of backend development. The Stack Overflow Developer Survey ↗ consistently ranks SQL among the most commonly used languages, with over 50% of professional developers using it regularly. A separate JetBrains Developer Ecosystem Survey ↗ found that SQL remains one of the top five languages developers plan to adopt or continue using, underscoring its enduring relevance.
The ORM Comfort Trap
Modern ORMs like Prisma, SQLAlchemy, ActiveRecord, and Entity Framework are genuinely excellent tools. They handle connection pooling, parameterised queries, type safety, and migrations. For straightforward CRUD operations, they are the right choice.
The trap is assuming that the ORM will always generate optimal SQL. It will not. ORMs make trade-offs to provide a general-purpose API, and those trade-offs often result in inefficient queries that only become visible under load. In my experience, the N+1 query problem alone accounts for the majority of performance issues I have seen in ORM-heavy applications. I recall one project where a single page load triggered over 300 database queries because the ORM was lazily loading nested relationships; rewriting it as two raw SQL queries with JOINs reduced the page load from eight seconds to under 200 milliseconds.
Consider a simple example: fetching a list of users with their most recent order. An ORM might execute N+1 queries (one to fetch users, then one per user to fetch their latest order). A developer who knows SQL would write a single query with a JOIN and a window function. The difference between these approaches can be the difference between a 200ms response and a 5-second timeout.
| Approach | Queries Executed | Typical Response Time (10k users) | Scalability |
|---|---|---|---|
| ORM N+1 | 10,001 | 4,000-8,000ms | Poor |
| ORM eager loading | 2 | 400-800ms | Moderate |
| Raw SQL with JOIN | 1 | 50-200ms | Excellent |
| Raw SQL with window function | 1 | 30-150ms | Excellent |
SQL Is the Language of Data
Every relational database speaks SQL. PostgreSQL, MySQL, SQL Server, SQLite, Oracle, and dozens of others all use SQL as their query language. Learning SQL gives you access to all of them.
More importantly, SQL is how you communicate about data. When a product manager asks “how many users signed up last month who also made a purchase?”, the answer is a SQL query. When a support engineer needs to check why a customer’s order failed, the answer is a SQL query. When your monitoring dashboard needs a custom metric, the source is often a SQL query.
Developers who know SQL can answer these questions in minutes. Those who do not must build custom API endpoints, write scripts, or ask someone else. This ability to work directly with data is also invaluable when designing APIs that expose the right data in the right shape.
Essential SQL Concepts Every Developer Needs
JOINs
JOINs are how you combine data from multiple tables. Understanding INNER JOIN, LEFT JOIN, RIGHT JOIN, and CROSS JOIN is non-negotiable. Most real-world queries involve at least one JOIN, and misunderstanding join behaviour is one of the most common sources of bugs in data retrieval.
SELECT u.name, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.name;
The difference between LEFT JOIN and INNER JOIN here determines whether users with zero orders appear in the results. That distinction matters.
Aggregation and Grouping
GROUP BY with aggregate functions (COUNT, SUM, AVG, MIN, MAX) is how you summarise data. This is the foundation of every report, dashboard, and data export you will ever build.
SELECT
DATE_TRUNC('month', created_at) AS month,
category,
COUNT(*) AS total_orders,
SUM(amount) AS revenue
FROM orders
WHERE created_at >= '2025-01-01'
GROUP BY month, category
ORDER BY month, revenue DESC;
Subqueries and CTEs
Common Table Expressions (CTEs) let you break complex queries into readable, logical steps. They are the SQL equivalent of extracting a function: they make your intent clear and your code maintainable.
WITH monthly_revenue AS (
SELECT
user_id,
DATE_TRUNC('month', created_at) AS month,
SUM(amount) AS revenue
FROM orders
GROUP BY user_id, month
)
SELECT
user_id,
AVG(revenue) AS avg_monthly_revenue
FROM monthly_revenue
GROUP BY user_id
HAVING AVG(revenue) > 100;
This query finds users whose average monthly spending exceeds 100. Try expressing that cleanly through an ORM.
Window Functions
Window functions are where SQL becomes genuinely powerful. They let you perform calculations across related rows without collapsing them into groups.
SELECT
name,
department,
salary,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank
FROM employees;
This ranks every employee within their department by salary, all in a single query. Window functions are used extensively in reporting, analytics, and anywhere you need row-level detail alongside aggregate calculations.
Indexing
Understanding indexes is critical for writing performant SQL. An index is a data structure that allows the database to find rows without scanning the entire table. Without appropriate indexes, queries that run in milliseconds on a development database with 100 rows will take minutes on a production database with 10 million rows.
| Index Type | Use Case | Trade-off |
|---|---|---|
| B-tree (default) | Equality and range queries on most data types | Good all-rounder, moderate write overhead |
| Hash | Exact equality lookups | Very fast for exact matches, no range support |
| GIN | Full-text search, JSONB, array columns | Slower to build, excellent query performance |
| GiST | Geospatial data, range types | Specialised, not for general use |
| Partial | Queries filtering on a common condition | Smaller index, faster updates, targeted benefit |
At minimum, ensure you have indexes on columns used in WHERE clauses, JOIN conditions, and ORDER BY clauses. Use EXPLAIN (or EXPLAIN ANALYSE in PostgreSQL) to see how the database executes your query and whether it is using indexes effectively. The PostgreSQL EXPLAIN documentation ↗ is an excellent resource for learning to read query plans.
SQL for Debugging and Incident Response
When a production incident involves data, SQL is your fastest diagnostic tool. Being able to query the database directly lets you verify assumptions, check for data inconsistencies, and understand the scope of a problem in real time.
Compare these two approaches during an incident:
- Without SQL knowledge: Ask a database administrator to run queries for you, wait for results, formulate the next question, and repeat.
- With SQL knowledge: Connect to a read replica, write targeted queries, and diagnose the issue in minutes.
During a time-sensitive incident, the difference between these approaches is significant. The developer who can query the database directly resolves the issue while the other is still waiting for someone to help. I have found that SQL fluency cuts incident resolution time in half for data-related issues. This capability pairs naturally with strong logging practices, which give you the context to know which queries to run.
SQL for Migrations and Schema Design
Even if you use an ORM for migrations, understanding the SQL it generates is essential. Schema changes on large tables can lock the table for extended periods if done carelessly. Adding an index on a table with 50 million rows is not the same as adding one to a table with 500 rows. For more on handling migrations safely, see our guide to database migrations without the fear.
Knowledge of SQL lets you review migration files critically. You can spot potential issues like adding a NOT NULL column without a default value (which requires rewriting the entire table in some databases) or creating an index that will never be used.
How to Start Learning SQL
The best way to learn SQL is to use it with real data. Set up a local PostgreSQL instance (Docker makes this trivial) and import a dataset that interests you. Public datasets from Kaggle ↗, government data portals, or even your own application’s anonymised data all work well.
Start with simple SELECT queries and progressively tackle JOINs, aggregation, subqueries, and window functions. The PostgreSQL documentation is one of the best technical references available and covers every concept with clear examples.
Practise reading EXPLAIN output early. Understanding query plans is what separates a developer who writes correct SQL from one who writes performant SQL. This kind of performance awareness also makes you better at writing tests that actually help, because you learn to test for performance characteristics alongside correctness.
The Return on Investment
SQL has been around since the 1970s and shows no signs of disappearing. Unlike framework-specific knowledge that becomes obsolete every few years, SQL skills compound over your entire career. Every hour you invest in learning SQL will pay dividends for decades.
Whether you are debugging a production incident, building a reporting dashboard, optimising a slow endpoint, or interviewing for your next role, SQL proficiency will serve you well. It is one of the highest-leverage skills a developer can have, sitting alongside fundamentals like good API design and solid testing practices. For a broader look at the skills that compound as your career progresses, see the senior developer mindset.
Frequently asked questions
Can I get by as a developer without learning SQL?
Technically, yes. ORMs abstract away most database interactions, and many developers go years without writing raw SQL. But you will eventually hit performance problems, debugging scenarios, or data analysis tasks where ORM-generated queries fall short. Knowing SQL makes you significantly more effective in those situations and helps you write better ORM code in the first place.
Which SQL dialect should I learn first?
Start with standard ANSI SQL, which works across all major databases. PostgreSQL is an excellent first database to practise with because it closely follows the SQL standard and has outstanding documentation. Once you are comfortable with standard SQL, picking up MySQL, SQL Server, or SQLite-specific syntax is straightforward.
How much SQL does a frontend developer really need?
At minimum, you should understand SELECT queries with WHERE clauses, JOINs, and basic aggregation (COUNT, SUM, AVG, GROUP BY). This is enough to query a database directly when debugging, write simple data exports, and understand the data layer your API sits on top of. Even this baseline knowledge will set you apart from peers who cannot.
Are ORMs bad? Should I write raw SQL everywhere?
ORMs are not bad. They are excellent for standard CRUD operations, migrations, and keeping your codebase consistent. The problem arises when developers use ORMs without understanding the SQL they generate. The best approach is to use your ORM for routine operations and drop down to raw SQL for complex queries, reports, or performance-critical paths.
What is the best way to practise SQL?
Get a local PostgreSQL or SQLite database running and work with real data. Import a public dataset (Kaggle has thousands), then challenge yourself to answer questions about it using only SQL. Sites like SQLZoo, LeetCode (database section), and HackerRank also offer interactive SQL exercises that progress from beginner to advanced.
Enjoyed this article? Get more developer tips straight to your inbox.
Comments
Join the conversation. Share your experience or ask a question below.
No comments yet. Be the first to share your thoughts.