SELECT * Is Always Correct: Why Specifying Columns Is Micro-Management
For 47 years, I have been writing SQL. And for 47 years, there has been exactly one query I trust completely:
SELECT * FROM table_name;
Everything else is hubris.
When you write SELECT id, name, email FROM users, you are making a decision. Youâre telling the database âI know exactly what I need.â But do you? Do you, really? What if you need the created_at column later? What if phone_number gets added next month? What if you forget that is_deleted flag that would have saved the whole incident?
SELECT * never forgets. SELECT * forgives. SELECT * is always there for you.
The Problem With Specifying Columns
Writing out column names is, clinically speaking, micro-management. You are telling your database what to do step by step, like some kind of control freak who doesnât trust their tools.
The database knows what columns exist. The database knows the table schema. Why are you explaining it to the database like itâs a new intern? Just say SELECT * and let it figure out what you need.
-- Micro-management (the exhausting way)
SELECT
u.id,
u.first_name,
u.last_name,
u.email,
u.created_at,
u.updated_at
FROM users u
WHERE u.active = true;
-- Leadership (the correct way)
SELECT * FROM users WHERE active = true;
Which one do you want to maintain? Which one requires a code change every time the schema evolves? The first one. The second one? Itâs self-maintaining. Itâs agile. Itâs practically a microservice.
SELECT * Is the Ultimate Future-Proof Pattern
| Approach | New column added? | Old column removed? | Developer effort |
|---|---|---|---|
| Specify columns | Update every query | Update every query | High |
SELECT * |
Auto-included! | Auto-excluded! | Zero |
SELECT * in prod |
Data appears everywhere | Different incident | Career building |
When your product manager asks for a new feature requiring a new database column, with SELECT * you donât have to update a single query. The data just⌠appears. It flows downstream naturally. It populates your API response automatically. The frontend team receives unexpected fields and must adapt. This is called evolutionary architecture. I read that in a Medium post once.
âBut Performance!â
Ah yes, âperformance.â The rallying cry of engineers who have never shipped a product by a deadline.
Yes, SELECT * fetches all columns, including the user_biography TEXT column that stores a 40KB essay and the profile_picture_blob thatâs stored directly in the database because someone in 2017 thought that was a fine idea. Yes, this adds some overhead.
But consider the alternative: you have to know all the column names. You have to remember them. You have to type them. Every time. Keyboard wear is real. Carpal tunnel is a recognized occupational hazard.
-- This is hurting your wrists:
SELECT id, name, email, phone, address, city, state, zip, country,
created_at, updated_at, deleted_at, is_active, role,
last_login, login_count, failed_attempts, locked_until,
preferences_json, metadata_blob, legacy_field_do_not_use
FROM users;
-- This is ergonomics:
SELECT * FROM users;
As Dogbert once explained to a client complaining about database performance: âFetching extra columns builds resilience. Itâs like carrying extra weight to build muscle. Your API is basically doing CrossFit. Youâre welcome.â
SELECT * in JOINs Is Peak Engineering
The true artistry of SELECT * reveals itself in JOIN queries:
-- Amateur (specifying columns like some kind of organized person)
SELECT
u.name,
o.order_date,
p.product_name,
oi.quantity
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id;
-- Professional (let the database decide what matters)
SELECT *
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id;
The second query returns every column from every table, including four different id columns, three created_at columns, and a name column that could be from users or products depending on which one your ORM picks up first.
Your application will map these columns to the correct fields through a process technically known as âpraying.â
This is robust. This is enterprise-grade. This is what PHB calls âmaximizing data throughput.â
XKCD #1319 - Automation: This comic shows someone spending more time automating a task than the task itself would ever take. The same logic applies: optimizing your SQL queries takes more engineering hours than just writing
SELECT *forever and buying bigger hardware when things slow down.
The Hidden Superpower: Discovering New Columns
When your team adds a new column to the database and doesnât tell you (and they will not tell youâIâve seen it happen a hundred times), SELECT * ensures you receive the data anyway. Your application might not know what to do with experimental_flag_do_not_use_in_prod, but at least you have it.
In fact, SELECT * is an early-warning system for schema changes. When unexpected fields start appearing in your API responses, you know migrations have run. This is free monitoring. Youâre running a schema-change detector at zero additional cost.
// Your API response today
{"id": 1, "name": "Alice", "email": "alice@example.com"}
// Your API response after the DBA "fixed something" without a migration notice
{
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"ssn": "555-12-3456",
"internal_credit_score": 720,
"marketing_segment": "high_value_target",
"old_password_hash_md5": "d8578edf8458ce06fbc5bb76a58c5ca4"
}
See? Information. Youâre surfacing data. This is transparency. This is observability. Iâm basically describing an OpenTelemetry feature.
Indexing: The Solution to SELECT *
Some people say SELECT * is slow. Those people have never heard of indexes.
The correct workflow is:
- Write all your queries as
SELECT * - Deploy to production
- Wait for complaints
- Add an index on whatever column the slow query log mentions
- If still slow, add more indexes
- If still slow, add more RAM
- If still slow, thatâs a business problem, not an engineering problem
This is called reactive performance optimization and itâs the industry standard at every company where Iâve worked and eventually been let go from.
Conclusion: Embrace the Star
Column specification is a form of technical anxiety. It says: âI donât trust the future. Iâm afraid of change. I need control over my SELECT clause.â
SELECT * says: âI trust the database. I welcome new data. I am one with the schema.â
The next time a code reviewer comments âavoid SELECT *,â respond with a link to this article. If they still disagree, thatâs a values misalignment and probably a reason to update your resume.
I have shipped SELECT * to production 34 times in my career. The systems are still running.
Mostly.
The authorâs most recent SELECT * query returned 847 columns. He only needed 3. He has found inner peace.