Topic 12 of 52
SELECT Columns FROM
Overview
SELECT is the most fundamental SQL statement — it retrieves data from tables. Choosing specific columns instead of SELECT * improves performance and prevents issues when schema changes.
Syntax
sql
-- Select all columns (avoid in production!)
SELECT * FROM users;
-- Select specific columns (always preferred)
SELECT id, name, email, created_at FROM users;
-- Column aliases
SELECT
id AS user_id,
full_name AS name,
email_addr AS email
FROM users;
-- Expressions in SELECT
SELECT
first_name || ' ' || last_name AS full_name,
salary * 12 AS annual_salary,
UPPER(email) AS email_upper,
EXTRACT(YEAR FROM created_at) AS join_year
FROM employees;
-- DISTINCT: remove duplicate rows
SELECT DISTINCT city FROM customers;
SELECT DISTINCT ON (user_id) user_id, amount FROM orders ORDER BY user_id, amount DESC;Common Pitfalls
- SELECT * is an anti-pattern: it fetches unnecessary data, breaks when columns are added/removed, and prevents covering index optimization.
- Column aliases defined in SELECT cannot be referenced in WHERE (use a subquery/CTE) — but they CAN be used in ORDER BY and HAVING.
- Interview tip: COALESCE(column, default) returns the first non-NULL value — essential for handling NULLs in SELECT expressions.
Real-World Example
Building a user profile API query with computed fields:
example
sql
SELECT
u.id,
u.username,
u.email,
u.created_at AS member_since,
EXTRACT(DAYS FROM NOW() - u.created_at)::INT AS days_since_joined,
COALESCE(u.full_name, u.username) AS display_name,
COUNT(DISTINCT o.id) AS total_orders,
COALESCE(SUM(o.total_amount), 0) AS lifetime_value,
CASE
WHEN SUM(o.total_amount) > 100000 THEN 'VIP'
WHEN SUM(o.total_amount) > 10000 THEN 'Premium'
ELSE 'Regular'
END AS customer_tier,
MAX(o.created_at) AS last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'completed'
WHERE u.deleted_at IS NULL
GROUP BY u.id, u.username, u.email, u.created_at, u.full_name
ORDER BY lifetime_value DESC;