SELECT vs. Column List
SELECT * vs. Column List
Level 3 — CRUD Operations (The Four Pillars of SQL) The comparison and best practice standard contrasting selecting all table columns (
SELECT *) with explicitly listing target columns (SELECT name, email).
1. Prerequisites
SELECT— The baseline data retrieval statement.
2. Term Category
SQL Command / Clause (Projection Performance Optimization): SELECT * vs Explicit Columns contrasts fetching all columns against selecting specific required attributes to minimize network I/O.
3. Explanation
Environment Context
- Universal Standard (
SELECT *is convenient for local CLI debugging but is considered a dangerous anti-pattern inside production application codebases).
(1) Design Motivation — "Why did we design this?"
When writing SQL, the asterisk (*) acts as a wildcard meaning "all columns."
Writing SELECT * FROM users; is fast, convenient, and requires less typing than writing out ten separate column names.
However, in production application backend code, using SELECT * is a severe performance and security risk:
- Wasted Network Bandwidth & RAM: If your
userstable has abiocolumn storing paragraphs of text or aprofile_picbinary field, fetching them when you only wanted to show a list of usernames wastes massive amounts of database memory, web server RAM, and network bandwidth. - App Fragility (Code Breaks): If your backend code maps database rows directly to local objects, and database administrators add or delete columns from the table, your application's object models will mismatch, causing server crashes.
- Bypassing Index Optimizations: If you only query columns that are indexed (e.g., querying
emailwhich has a B-Tree index), Postgres can read the value directly from the index in memory. This is called an Index-Only Scan and takes microseconds. If you writeSELECT *, Postgres is forced to perform slow physical disk reads to fetch the non-indexed columns.
(2) When to use what
SELECT *: Only use when run manually insidepsqlor pgAdmin to quickly explore what columns exist in a table.- Explicit Column List: Use always in your application code (NodeJS, Python APIs, database migrations).
(3) Reality Metaphor
Imagine ordering groceries:
SELECT *is like calling the supermarket and saying: "Bring me the entire inventory of your store." You only wanted a carton of milk, but now a flatbed truck is dumping thousands of boxes on your lawn, clogging traffic and costing you a fortune.- Column List is like giving the store a precise shopping list: "Bring me 1 carton of milk." It is fast, cheap, and fits in a small bag.
(4) Code Examples
Bloated Query vs. Optimized Query
Assume we have a table with a massive text column:
CREATE TABLE wiki_pages (
id INT PRIMARY KEY,
title VARCHAR(200),
author VARCHAR(100),
body_content TEXT -- Can hold megabytes of text
);
Anti-Pattern (Slow & Wastes Memory):
-- BAD: If we only want to show a index list of titles,
-- this fetches megabytes of body_content for every single row!
SELECT * FROM wiki_pages;
Best Practice (Fast & Secure):
-- GOOD: Fetches only a few bytes per row, running up to 100x faster!
SELECT title, author FROM wiki_pages;
4. Common Mistakes & Pitfalls
Mistake 1: Using SELECT * in production API routes because "it saves development typing time"
The mistake: Writing a database model function in NodeJS that fetches everything:
// BAD: Fetches password hashes, secure tokens, and huge text blocks
// and accidentally exposes them to the public API!
const users = await db.query('SELECT * FROM users');
res.json(users.rows);
Why it's wrong: Besides the memory and network costs, SELECT * is a major security leak. If your table stores password hashes (password_hash) or private session tokens, SELECT * pulls these into memory. If you serialize the database row directly to JSON and send it to the frontend, you will leak credentials to the public web.
Fix: Always explicitly name only the columns that your code actually needs to process.
// GOOD: Safe, secure, and performant
const users = await db.query('SELECT id, username, email FROM users');
res.json(users.rows);
Mistake 2: Using SELECT * in Production API Endpoints Disabling Covered Query Optimizations
The mistake: Executing SELECT * FROM users WHERE email = 'a@ex.com' when index { email, id } exists.
Why it's wrong: SELECT * forces fetching the full row from disk (HEAP read). Specifying SELECT id, email allows the query engine to execute a Covered Index Only Scan.
Incorrect:
SELECT * FROM users WHERE email = 'a@ex.com'; -- ❌ Disk HEAP read!
Fix:
SELECT id, email FROM users WHERE email = 'a@ex.com'; -- Covered Index Only Scan
Mistake 3: Breaking Application Code when Table Columns Are Re-Ordered or Added via SELECT *
The mistake: Binding positional array rows in code after executing SELECT *.
Why it's wrong: If a schema migration inserts a new column in the middle of a table, positional array indices in code shift, causing runtime application bugs.
Incorrect:
const [id, name, email] = row; // ❌ Breaks if new column is added!
Fix:
SELECT id, name, email FROM users; -- Immutable explicit column list
5. Practice Exercises
Exercise 1: Explicit Column Selection Optimization
Scenario:
Refactor a bloated SELECT * query returning 30 columns on a 1,000,000 row table to return ONLY id and email.
Requirements:
- Compare
SELECT *vsSELECT id, email.
Answer
Implementation
-- ❌ Bloated projection (transfers unneeded text/bytea columns over network)
-- SELECT * FROM users;
-- ✅ Optimized explicit column projection
SELECT id, email
FROM users;
Technical Explanation
SELECT *fetches every column, including largeTEXT,JSONB, orBYTEAblobs, consuming excess RAM and network I/O.- Explicit column selection reduces network payload sizes significantly.
- Allows PostgreSQL to execute Index-Only Scans (
Covered Queries).
Exercise 2: Enabling Index-Only Scans via Projection
Scenario:
Demonstrate how explicit column projection enables an Index-Only Scan using index { email: 1, username: 1 }.
Requirements:
- Execute
EXPLAIN ANALYZE SELECT email, username FROM users WHERE email = 'alice@example.com'.
Answer
Implementation
EXPLAIN ANALYZE
SELECT email, username
FROM users
WHERE email = 'alice@example.com';
Technical Explanation
- If query projects ONLY fields stored in the B-tree index, PostgreSQL reads keys directly from the index (
Index-Only Scan). SELECT *forces PostgreSQL to fetch raw table heap pages from disk (Heap Fetches).- Critical performance rule.
Exercise 3: API Schema Safety Trade-Offs
Scenario:
Explain why using SELECT * in production API handlers causes breaking changes when new table columns are added.
Requirements:
- Contrast explicit interface projection vs raw table SELECT *.
Answer
Implementation
API Schema Safety Analysis:
- SELECT * exposes internal/sensitive new columns (e.g. 'password_hash') added during database migrations to client JSON outputs automatically.
- Explicit column lists (SELECT id, username, email) guarantee stable, secure API contracts.
Technical Explanation
- Explicit projections prevent accidental data exposure when new columns are added to tables.
- Hardens backend API contract security.
- Production database practice.
6. Related Terms
SELECT— The parent query command.
7. Key Takeaways
SELECT *retrieves all columns in a table; Column List retrieves specific ones.- Asterisk queries waste network bandwidth, database RAM, and client memory.
SELECT *exposes databases to security leaks (e.g. passwords, tokens).- Explicit column lists allow the database to use fast Index-Only Scans.
- Always use explicit column lists in production backend application scripts.