Hook: MIN is the SQL version of asking, “What is the smallest ticket in the pile?” — and interviewers love it because the tiny details around NULL and grouping reveal whether you really understand aggregates.
Question: What does MIN do in SQL?
Answer: MIN is an aggregate function that returns the smallest non-NULL value from a column or expression. It works with numbers, dates, and other comparable types, and it ignores NULL values. If there are no matching rows, or every value is NULL, the result is NULL.
Interview-Ready Answer: I use MIN when I want the lowest non-NULL value in a result set, like the cheapest price or earliest date. It’s an aggregate, so it can work on the whole table or one value per group with GROUP BY. One important detail is that MIN ignores NULLs, and if there are no qualifying rows, it returns NULL rather than zero or an error.
MIN isDetailed Explanation: Think of MIN as a scanner that walks through values and keeps the smallest one it has seen so far. In SQL, an aggregate is a function that reduces many rows into one value, such as MIN, MAX, SUM, or AVG.
FROM and WHERE clauses.salary or order_date.NULL, it is skipped.GROUP BY, the database repeats the same process separately for each group and returns one result per group.HAVING to keep only groups that meet a threshold, such as departments whose minimum salary is above 50,000.| Approach | What it does | Typical cost | Good for |
|---|---|---|---|
MIN(col) | Returns one smallest value | Usually O(n) | Simple aggregation |
ORDER BY col ASC LIMIT 1 | Sorts or selects top row | Often more work | Need the full row |
MAX(col) | Returns largest value | Usually O(n) | Largest value |
Important nuance: many optimizers can make MIN very fast with a B-tree index on the column, because they may jump straight to the first relevant index entry instead of scanning every row. Without a helpful index or with extra filters, assume a full scan and linear work.
NULL handling: MIN ignores NULLs. If all rows are NULL, the result is NULL.WHERE clause matches no rows, the aggregate still returns one row with NULL.MIN gives one result per group, not one result per table.Memory Hook: “MIN is the smallest apple in the basket; NULL apples don’t count, and an empty basket gives you no apple at all.”
Real-World Example: In a checkout service, product managers often want the lowest price ever seen for each item to detect accidental pricing bugs. A query with MIN(price) can quickly show the cheapest recorded price per SKU, which helps catch a bad import before customers see it.
What goes wrong when someone misunderstands MIN? A developer assumes NULL means “zero” and writes logic that treats “no data” as “free.” In production, the dashboard suddenly reports a minimum price of 0 or a missing minimum is displayed as a real value, triggering false alerts, confusing logs, and support tickets from merchants who think their catalog was corrupted.
Symptom-wise, you might see query results with blank minimums, mismatched totals between reports, or logs showing that a filtered dataset had no rows while the application displayed a numeric default. The fix is to remember that MIN returns NULL when there is nothing non-NULL to compare, and the application should handle that explicitly.
-- Self-contained demo: MIN ignores NULLs, works with GROUP BY, and returns NULL for empty/all-NULL cases.
CREATE TABLE employees (
employee_id INTEGER,
department VARCHAR(20),
salary INTEGER,
hire_date DATE
);
INSERT INTO employees (employee_id, department, salary, hire_date) VALUES
(1, 'Engineering', 120000, DATE '2022-01-10'),
(2, 'Engineering', 110000, DATE '2023-03-01'),
(3, 'Sales', 90000, DATE '2021-11-15'),
(4, 'Sales', NULL, DATE '2024-02-20'),
(5, 'HR', 70000, DATE '2020-06-05');
-- 1) Whole-table minimum: NULL salary is ignored, so 70000 is the lowest valid salary.
SELECT MIN(salary) AS min_salary_all
FROM employees;
-- 2) Per-group minimum: one smallest salary per department.
SELECT department, MIN(salary) AS min_salary_by_department
FROM employees
GROUP BY department
ORDER BY department;
-- 3) MIN also works on dates: this returns the earliest hire date.
SELECT MIN(hire_date) AS earliest_hire_date
FROM employees;
-- 4) Edge case: no matching rows -> aggregate result is NULL, not an error.
SELECT MIN(salary) AS min_salary_no_match
FROM employees
WHERE salary > 1000000;
-- 5) Edge case: all values NULL -> result is NULL.
CREATE TABLE bonus_pool (
bonus_amount INTEGER
);
INSERT INTO bonus_pool (bonus_amount) VALUES
(NULL),
(NULL);
SELECT MIN(bonus_amount) AS min_bonus_all_null
FROM bonus_pool;Follow-up & Tricky Questions:
MIN different from ORDER BY ... LIMIT 1? MIN returns only the smallest value and is an aggregate; ORDER BY ... LIMIT 1 returns the smallest row after sorting or a top-N optimization. If you need the whole row tied to the minimum value, sorting or a window function is often more appropriate.MIN ignore NULL? Yes, it skips NULL values entirely. If every value is NULL or the filtered set is empty, the result is NULL.MIN be used with GROUP BY? Yes, and that is one of its most common uses. It gives one minimum per group, such as the lowest salary per department.MIN in HAVING? Yes. HAVING filters groups after aggregation, so you can write conditions like HAVING MIN(salary) > 50000.MIN work on text? Often yes, but the result depends on the database’s text ordering rules and collation. That means alphabetical order is not always identical across systems.MIN return 0? No. Aggregate functions with no matching rows return NULL, not zero.NULL, does MIN return NULL? No. MIN skips NULLs, so it returns the smallest non-NULL value if one exists.MIN always slower than ORDER BY ... LIMIT 1? Not always. A good index can make MIN extremely fast, and some optimizers can answer it without scanning the whole table.Common Mistakes:
NULL counts as the smallest value. Correction: MIN ignores NULL; it only compares real values.0 for “no data.” Correction: an empty result set returns NULL, so handle that explicitly in SQL or application code.GROUP BY changes the meaning. Correction: without grouping, you get one minimum for the whole filtered set; with grouping, you get one per group.Memory Hook: “Find the smallest, skip the blanks, and remember that empty means NULL.”
Cheat Sheet:
MIN(expr) returns the smallest non-NULL value.GROUP BY.NULL; all-NULL or empty input returns NULL.ORDER BY ... LIMIT 1 is for getting the whole row, not just the value.Practice Tasks:
created_at date in a table.price per category, then filter to categories whose minimum price is above 10 using HAVING.NULL values and verify that MIN returns NULL.