Interview Prep
SQL Interview Questions — Basics
This lesson covers the fundamental SQL concepts frequently tested in technical interviews, focusing on data retrieval and filtering logic. Understanding these core mechanics is essential for translating business requirements into accurate analytical queries. You should master these foundational topics to ensure data integrity and query efficiency when performing day-to-day database operations.
The Anatomy of a SELECT Statement
The SELECT statement is the primary vehicle for data retrieval in a relational database. Understanding how it operates requires recognizing the logical order of operations, which begins with the FROM clause to identify the data source, followed by the WHERE clause to narrow down the dataset based on specific predicates. When we select columns, we are essentially projecting a subset of our table onto a result set. The reason we specify columns explicitly rather than using the wildcard asterisk is for performance optimization and code maintainability. If the underlying table structure changes, explicitly defined columns prevent your application from breaking or returning unexpected data formats. By mastering the sequence in which SQL processes clauses, you can predict exactly how filtering happens before the final projection is returned to the user, allowing you to write highly predictable and clean code.
-- Select specific columns to reduce I/O overhead
SELECT
customer_name,
email_address
FROM customers
-- WHERE filters rows before the final column projection;
WHERE account_status = 'active';Filtering Data with WHERE Clauses
The WHERE clause acts as a gatekeeper, determining which rows from the source tables satisfy your requirements. It uses logical operators such as AND, OR, and NOT to refine criteria. A critical nuance to remember during interviews is how SQL handles NULL values. Because a NULL represents the absence of data, it is not equal to any value, including zero or an empty string; therefore, you must use IS NULL or IS NOT NULL operators. When you chain multiple conditions, the database engine uses operator precedence. Parentheses are your best tool for grouping logic, ensuring the database evaluates your intended hierarchy of filters. Reasoning through complex conditions by mentally isolating each part of the boolean expression is the key to writing queries that avoid logical errors and ensure your final dataset includes exactly what the business logic demands.
-- Using AND/OR with parentheses to control logic
SELECT product_id, price
FROM products
-- Parentheses ensure the OR logic is isolated from the AND check
WHERE (category = 'Electronics' OR category = 'Office')
AND price > 100
AND discount_code IS NOT NULL;Ordering and Limiting Results
After retrieving and filtering, you often need to organize the data for reporting. The ORDER BY clause is used to sort the result set, and it works by sorting based on the final, computed result set just before it is returned. You can sort by multiple columns, where the second column acts as a tie-breaker for the first. Meanwhile, the LIMIT clause serves to restrict the number of returned rows, which is vital for preventing memory overflow when dealing with millions of records. It is important to note that without an ORDER BY clause, the database does not guarantee any specific sequence for the output. If your requirements demand the top N rows, you must pair LIMIT with ORDER BY to ensure that the rows you retrieve are the most relevant based on the sorting criteria provided.
-- Get the 5 most expensive products
SELECT product_name, price
FROM products
-- Sort first to determine which rows reach the top
ORDER BY price DESC
-- Limit returns only the requested subset
LIMIT 5;Aggregating Data with GROUP BY
Aggregation functions allow you to perform calculations across a set of rows, such as counting items or summing sales totals. The GROUP BY clause is the mechanism used to define the buckets for these calculations. When you use GROUP BY, every column in your SELECT statement must either be an aggregate function or appear within the GROUP BY clause itself; otherwise, the database engine cannot map the aggregate result back to the individual rows. This is a common point of failure for beginners. Think of GROUP BY as a way to collapse multiple source rows into a single summary row for every unique combination of values in the grouping columns. This is the foundation for almost every analytical metric, turning raw event logs into meaningful trends, totals, and counts that business stakeholders rely on for decision-making.
-- Calculate total sales per category
SELECT
category,
SUM(sales_amount) as total_revenue
FROM transactions
-- Grouping collapses all rows into category buckets
GROUP BY category
-- HAVING filters the results AFTER the aggregation
HAVING SUM(sales_amount) > 1000;The Difference Between WHERE and HAVING
A frequent interview trap is the confusion between WHERE and HAVING. The rule of thumb is simple: WHERE filters the raw, individual rows before any grouping or calculation happens, whereas HAVING filters the summarized groups after the aggregation has taken place. If you attempt to filter by an aggregate total in a WHERE clause, the query will fail because the aggregate values do not exist until the grouping process is complete. By understanding this execution sequence, you can place your filtering logic in the correct spot. Always ask yourself: Does this constraint apply to a single row, or does it apply to the result of a calculation? If it is the former, use WHERE. If it is the latter, such as finding categories with high total sales, use the HAVING clause.
-- Filtering rows BEFORE grouping (WHERE)
SELECT status, COUNT(*)
FROM orders
WHERE order_date >= '2023-01-01'
GROUP BY status
-- Filtering groups AFTER aggregation (HAVING)
HAVING COUNT(*) > 50;Key points
- The SELECT statement follows a strict logical order that dictates how data is processed.
- NULL values require specialized operators because they do not equate to standard data types.
- Parentheses in complex WHERE clauses are essential for maintaining logical integrity.
- ORDER BY and LIMIT work together to allow for top-N ranking of records.
- Aggregation functions rely on the GROUP BY clause to categorize individual rows into buckets.
- Every column in an aggregate query must either be in an aggregate function or the GROUP BY clause.
- The WHERE clause filters source data, whereas the HAVING clause filters result groups.
- Predicting the order of execution is the most reliable way to debug complex SQL statements.
Common mistakes
- Mistake: Using double quotes for string literals. Why it's wrong: Standard SQL uses single quotes for strings; double quotes are often reserved for identifiers like table or column names. Fix: Use single quotes for all string values.
- Mistake: Expecting NULL to behave like zero or an empty string. Why it's wrong: NULL represents the absence of a value, so comparisons like 'column = NULL' will fail to return results. Fix: Always use 'IS NULL' or 'IS NOT NULL' to check for missing data.
- Mistake: Forgetting that aggregate functions ignore NULL values. Why it's wrong: Functions like COUNT(*) count rows, but COUNT(column_name) counts only non-null values, leading to unexpected totals. Fix: Use COALESCE(column, 0) if you need to treat missing values as zero.
- Mistake: Using WHERE to filter after an aggregation. Why it's wrong: WHERE is processed before the grouping occurs, whereas HAVING is designed to filter groups after they have been aggregated. Fix: Use HAVING for any condition involving aggregate functions.
- Mistake: Assuming ORDER BY is guaranteed without an explicit clause. Why it's wrong: SQL tables are sets without inherent order; relying on database internal storage order leads to inconsistent results. Fix: Always include an ORDER BY clause if you require a specific sequence.
Interview questions
What is the fundamental purpose of the SELECT statement in SQL and how does it function?
The SELECT statement is the foundational command in SQL used to retrieve data from a database. Its primary purpose is to define which columns you want to view from a specific table. By specifying the column names after the SELECT keyword and the source table after the FROM clause, the database engine executes a query to filter and return the requested records. It is the starting point for almost all data analysis tasks because it allows for both simple data extraction and complex data manipulation. For example, 'SELECT name, email FROM users;' effectively pulls two specific fields for all rows within the users table, enabling developers to isolate only the necessary information for their application logic.
How does the WHERE clause differ from the HAVING clause when filtering data?
The WHERE clause is used to filter records before any groupings are performed, acting as a gatekeeper for raw rows. In contrast, the HAVING clause is used to filter results after an aggregation has occurred, specifically targeting the outcomes of functions like SUM or COUNT. You use WHERE to restrict the input set, and HAVING to restrict the summary set. For instance, 'SELECT department, SUM(salary) FROM employees GROUP BY department HAVING SUM(salary) > 50000;' demonstrates how HAVING evaluates the aggregated total for each group, whereas a WHERE clause would have processed each individual employee record before the summation even began.
Can you explain the difference between a LEFT JOIN and an INNER JOIN?
An INNER JOIN returns only the rows that have matching values in both tables being joined, effectively excluding any records that lack a corresponding pair. A LEFT JOIN, however, returns all records from the left table, plus the matched records from the right table. If there is no match, the result set will contain NULL values for the right side. You choose an INNER JOIN when you need a strict relationship between entities, such as ensuring an order must have a customer. You choose a LEFT JOIN when you want to preserve all records from your primary source, such as listing all customers even if they have never placed a single order.
What is the purpose of the GROUP BY clause and when must it be used?
The GROUP BY clause is used to arrange identical data into groups, which is essential when you need to perform calculations on subsets of your table rather than the whole dataset. Whenever you use aggregate functions like COUNT, MAX, MIN, SUM, or AVG in a query along with non-aggregated columns, those non-aggregated columns must appear in the GROUP BY clause. For example, to find the number of products in each category, you would write 'SELECT category, COUNT(*) FROM products GROUP BY category;'. This forces the database to bucket all individual product rows into their respective categories before calculating the count for each group.
Compare using the DISTINCT keyword versus using GROUP BY to remove duplicate records from a result set.
Both DISTINCT and GROUP BY can eliminate duplicates, but they serve different architectural goals. DISTINCT is a simple, concise way to return unique rows by evaluating the entire tuple of returned columns, making it ideal for straightforward deduplication. GROUP BY is significantly more powerful because it organizes data into categories, allowing you to perform calculations on those unique groups. While you can use GROUP BY without an aggregate function to achieve a distinct result, it is conceptually meant for summarization. Use DISTINCT for simple unique lists, but use GROUP BY whenever you need to compute metrics for those unique entities.
What is the role of a primary key in a relational database, and what constraints does it enforce?
A primary key serves as the unique identifier for every record in a table, ensuring that no two rows are identical or ambiguous. It enforces two strict constraints: the NOT NULL constraint, meaning every record must have an ID, and the UNIQUE constraint, meaning no two records can share the same value. This is critical for data integrity because it allows the database to reliably locate, update, or delete specific records without the risk of affecting the wrong data. Without a primary key, establishing relationships between tables—such as referencing a user in an orders table via a foreign key—would be impossible to perform safely and accurately.
Check yourself
1. Which of the following best describes the difference between the WHERE and HAVING clauses?
- A.WHERE filters rows before grouping, while HAVING filters groups after aggregation.
- B.HAVING is used for string comparisons, while WHERE is used for numeric comparisons.
- C.WHERE requires an index, whereas HAVING works on any column.
- D.HAVING is used to order the final output, while WHERE is used to select columns.
Show answer
A. WHERE filters rows before grouping, while HAVING filters groups after aggregation.
WHERE filters individual rows before any grouping occurs. HAVING acts on the result of a GROUP BY clause to filter aggregate values. Other options incorrectly describe their functional purposes.
2. If you want to count every row in a table including those with NULL values in specific columns, which syntax should you use?
- A.COUNT(column_name)
- B.COUNT(*)
- C.SUM(1)
- D.COUNT(DISTINCT column_name)
Show answer
B. COUNT(*)
COUNT(*) counts the total number of rows regardless of NULL values. COUNT(column_name) skips rows where the column is NULL. The other options either require a specific column or perform unnecessary logic.
3. What is the result of 'SELECT 10 + NULL' in a standard SQL environment?
- A.10
- B.0
- C.NULL
- D.Error
Show answer
C. NULL
In SQL, any arithmetic operation performed on a NULL value results in NULL because the missing value prevents a definitive calculation. It does not default to 0 or throw an error.
4. When performing a LEFT JOIN, what happens when there is no matching record in the right-hand table?
- A.The row from the left table is excluded from the result set.
- B.The entire query returns an error.
- C.The query returns NULL for all columns selected from the right-hand table.
- D.The query creates a duplicate row with default values like 0 or empty strings.
Show answer
C. The query returns NULL for all columns selected from the right-hand table.
A LEFT JOIN preserves all rows from the left table; if no match is found, the columns from the right table are filled with NULL. It does not exclude the left row nor default to zero.
5. Why is the use of 'SELECT *' generally discouraged in production environments?
- A.It is syntactically invalid in most modern SQL databases.
- B.It prevents the use of WHERE clauses.
- C.It causes the database to return unnecessary data, increasing network traffic and potentially breaking code if the schema changes.
- D.It forces the database to perform an automatic ORDER BY operation, slowing down performance.
Show answer
C. It causes the database to return unnecessary data, increasing network traffic and potentially breaking code if the schema changes.
SELECT * fetches every column, which can be inefficient and creates a brittle dependency on the table's exact column order or structure. The other options are false as it is valid, compatible with WHERE, and does not trigger automatic ordering.