SQL
The declarative language for querying and changing relational data — the statement-semantics side every data interview starts from. Children cover SELECT and filtering, joins, aggregation and grouping, subqueries and CTEs, DDL/DML and transaction statements, query-author performance habits, and window functions.
on this pageshowhide
guide
overview
~2 minSQL interviews test whether you can predict what a query returns before you run it. The opening questions look like filtering and are really about NULL: a comparison that is neither true nor false quietly removes rows, and candidates who have not absorbed that lose points on questions that look trivial. Joins come next, because they show fastest whether someone reasons about row counts: which rows survive, which come back padded with NULLs, which are multiplied. Then grouping and aggregation, where a total taken over a one-to-many join is the classic wrong report. From the middle level up, interviewers expect window functions for rankings, running totals and deduplication, subqueries and CTEs to split a hard problem into named steps, and enough performance sense to explain why one spelling of a predicate is fast and another is not. The write side is checked too: constraints, `DELETE` versus `TRUNCATE`, and what a transaction boundary actually does. The hub follows those seams. [SELECT and Filtering](/topics/lang-sql-select-filtering) is the single-table query: predicates, NULL logic, `DISTINCT`, `CASE`, sorting, row limits and set operations. [Joins](/topics/lang-sql-joins) combine tables, [Aggregation and Grouping](/topics/lang-sql-aggregation) collapses rows into groups, and [Window Functions](/topics/lang-sql-window-functions) compute across rows without collapsing them. [Subqueries and CTEs](/topics/lang-sql-subqueries-ctes) cover how queries nest and compose. [DDL and DML](/topics/lang-sql-ddl-dml) is schema, constraints, modification statements and transaction control. [Indexing and Query Performance](/topics/lang-sql-indexing-query-perf) is the query author's half of performance: writing a statement so an index can help. Learn it roughly in that order. Filtering and NULL come first, since every later section reuses three-valued logic and the logical order of clauses. Joins and aggregation next; together they are most of a typical SQL screen. Pick up the write statements alongside them, because they are short and transaction questions arrive early. Take subqueries and CTEs before window functions, since filtering on a window result means wrapping the query that computes it. Performance comes last: it assumes you can already read the query you are tuning.
primer
### You describe the result; the engine picks the method SQL is declarative. A query states which rows and columns you want, and the optimizer chooses access paths, join algorithms and the order of work. Two consequences run through the hub. The written order of clauses is not the logical one: `FROM` and its joins take effect first and the `SELECT` list late, which is why, in standard SQL, an alias defined in `SELECT` is not visible in `WHERE` and a window function cannot filter rows. And performance questions are about leaving the optimizer good options, not about dictating a plan. ### NULL means unknown, and filters keep only TRUE SQL logic has three values: TRUE, FALSE and UNKNOWN. An ordinary comparison involving NULL yields UNKNOWN, and `WHERE`, `HAVING` and a join's `ON` keep a row only when the condition is TRUE. `CHECK` constraints work the other way round: they reject only FALSE, so UNKNOWN passes. Many surprising results in this hub trace back here: rows that vanish from joins, `NOT IN` returning nothing, averages that skip gaps, and `GROUP BY` or `DISTINCT` putting all NULLs in one group even though NULL never equals NULL. ### Results are bags, and cardinality is the question behind the question A query result is a multiset: rows repeat unless something removes them. A join produces one row per matching pair, so a one-to-many relationship multiplies rows; `DISTINCT`, `UNION` and `GROUP BY` remove or collapse them; a semi-join keeps an outer row once however many matches it has. Strong candidates say, before writing a join, how many rows each side has per key and how many the output should have. Inflated totals, duplicated customers and missing ones are cardinality mistakes, and no error message reports them. ### Collapse or annotate `GROUP BY` collapses each group into a single output row, so afterwards only grouping columns and aggregates remain. Window functions compute over a set of related rows but keep every row and attach the result to each one. Choosing between the two, or combining them, since a window can run over grouped output, is the central modelling decision in analytics questions: rankings, running totals, change from the previous period, keeping one row per key. ### Queries compose A query's result is itself a table, so it can sit in `FROM`, feed a predicate, or be named with `WITH` and referenced later. Composition is how a hard problem becomes readable: each step names one transformation. Interviewers listen for two distinctions: whether an inner query depends on the current outer row, and whether a construct lives for one statement (derived table, CTE) or in the schema (view). Recursive CTEs carry the same idea to hierarchies and graphs. ### The schema enforces correctness; transactions group changes Types, `NOT NULL`, primary and foreign keys, `UNIQUE` and `CHECK` are where the database refuses bad data whichever application sends it. Exact types for exact quantities, keys that make a join provably one-to-one, and explicit target columns in writes are habits interviewers read as experience. A transaction makes a group of statements succeed or fail together; what autocommit does when you open none, and how DDL and bulk deletes behave inside one, are early questions, and the second answer differs by engine. ### Performance starts with how the query is written The author does not choose the plan but decides whether a good plan is possible. Wrapping an indexed column in a function or a type conversion, sorting in an order no index supplies, fetching columns nobody reads, and paging by skipping rows all remove options the optimizer would otherwise have. Reading the plan the engine actually chose, rather than guessing at it, is what senior rounds expect.
- Three-valued logic
- SQL's truth system of TRUE, FALSE and UNKNOWN; comparisons involving NULL yield UNKNOWN, and most filters discard anything that is not TRUE.
- Logical processing order
- The order in which clauses take effect regardless of how they are written: FROM and joins first, the SELECT list late, sorting and row limits last.
- Outer join
- A join that also keeps the unmatched rows of one or both inputs, filling the other side's columns with NULL.
- Semi-join
- A filter that keeps each outer row having at least one match, once, without adding columns from the other side; usually written with EXISTS or IN.
- Anti-join
- A filter that keeps only the outer rows with no match on the other side; NOT EXISTS is the form that stays correct when NULLs are present.
- Join fan-out
- The multiplication of rows when a join key matches several rows on the other side, which silently inflates any sum or count taken afterwards.
- Aggregate function
- A function such as COUNT, SUM or MAX that reduces a set of rows to one value, per group when GROUP BY is present.
- Grouping sets
- A GROUP BY extension that computes several groupings in one query; ROLLUP and CUBE are shorthands for common families of them, used for subtotals and totals.
- Correlated subquery
- A subquery that references a column of the outer query, so logically it is evaluated once for each outer row.
- Common table expression
- A named subquery introduced by WITH and visible only to the statement that follows; the recursive form may reference its own name.
- Window function
- A function computed over a set of rows related to the current one, defined by an OVER clause, returning a value for every row instead of collapsing them.
- Window frame
- The slice of a partition, positioned relative to the current row, that a windowed aggregate reads; set with ROWS, RANGE or GROUPS.
- Constraint
- A declared rule, such as NOT NULL, PRIMARY KEY, UNIQUE, FOREIGN KEY or CHECK, that the database enforces on every write.
- Transaction
- A unit of work whose statements are made permanent together by COMMIT or discarded together by ROLLBACK.
- Sargable predicate
- A condition written so the engine can use an index to find matching rows, typically a bare column compared with a value of the same type.
- Keyset pagination
- Paging that resumes after the last row already shown by filtering on its sort keys, rather than skipping a count of rows with OFFSET.
- Execution plan
- The strategy the engine chose for running a query, including access paths, join methods, sorts and aggregation steps; EXPLAIN displays it.
The sections follow the logical processing order. [SELECT and Filtering](/topics/lang-sql-select-filtering) is one table flowing through `WHERE`, the `SELECT` list, sorting and limits. [Joins](/topics/lang-sql-joins) replace that one table with a combined row source, which is why the placement of a condition, in `ON` or in `WHERE`, changes an outer join's result but not an inner join's. [Aggregation and Grouping](/topics/lang-sql-aggregation) works on whatever the joins produced, so fan-out created in `FROM` turns into a wrong `SUM`. [Window Functions](/topics/lang-sql-window-functions) take effect after grouping and before the final sort, which lets them rank aggregates but keeps them out of `WHERE` and `HAVING`. [Subqueries and CTEs](/topics/lang-sql-subqueries-ctes) cut across the rest: any stage can consume another query's result, and wrapping a query is how you filter on a value computed late. The semi-join shows up three times, as `EXISTS` among subqueries, as a join pattern, and as a cost choice in [EXISTS vs IN vs JOIN](/topics/lang-sql-indexing-query-perf-exists-in-join); learn it once and recognise it each time. [DDL and DML](/topics/lang-sql-ddl-dml) supplies the guarantees the read side leans on: a declared unique key is what lets you say a join cannot fan out, and `NOT NULL` is what lets you set aside many of the NULL traps. [Indexing and Query Performance](/topics/lang-sql-indexing-query-perf) comes back to the same statements from the cost side. A query where several of these meet: ```sql WITH ranked AS ( SELECT c.id, c.region, COALESCE(SUM(o.amount), 0) AS paid_total, RANK() OVER (PARTITION BY c.region ORDER BY COALESCE(SUM(o.amount), 0) DESC) AS pos FROM customers c LEFT JOIN orders o ON o.customer_id = c.id AND o.status = 'PAID' GROUP BY c.id, c.region ) SELECT id, region, paid_total FROM ranked WHERE pos <= 3; ``` The status test sits in `ON`, so a customer with no paid orders stays in the result with NULL order columns; moved to `WHERE`, the same test would remove that customer. With nothing but NULL to add up, `SUM` returns NULL, hence the `COALESCE`. `RANK` is evaluated over the grouped rows, so it can order by an aggregate, and because it cannot be filtered at the level where it is computed, the query names it in a CTE and filters outside. And `RANK`, unlike `ROW_NUMBER`, can return more than three customers for a region when totals tie: a choice interviewers expect you to make on purpose.
- SELECT and Filtering →
Predicates, NULL logic and the logical clause order are assumed by every other section, and most junior questions start here.
- Joins →
The main SQL screen: which rows survive a join, which come back padded with NULLs, and which are multiplied.
- Aggregation and Grouping →
GROUP BY, HAVING and how aggregates treat NULLs; with joins, this is most of everyday reporting SQL.
- Subqueries and CTEs →
Nesting and naming steps, EXISTS versus NOT IN, and recursion; window-function answers usually filter through an outer query.
- Window Functions →
Rankings, running totals and deduplication without collapsing rows; the tool interviewers expect from the middle level up.
- Indexing and Query Performance →
Last, once queries read fluently: rewriting predicates, pagination and batch writes so the engine can use its indexes.
Writing
= NULLor<> NULLin a filter: the comparison is UNKNOWN for every row, so the query returns nothing and raises no error; see NULL and Three-Valued Logic.Using
NOT INagainst a subquery whose column can hold NULL: a single NULL makes the whole filter match nothing. Reach forNOT EXISTSwhen writing an anti-join.Filtering the optional side of a LEFT JOIN in
WHERE: unmatched rows fail the test, and the outer join quietly behaves like an inner one.Summing a parent table's column after joining a child table: each parent row repeats once per child. Aggregate the child first, or join to pre-aggregated rows.
Answering an 'at least one' question with a plain join and patching duplicates with
DISTINCT: it hides the fan-out instead of avoiding it, and costs a sort or hash.Assuming rows come back in insertion or key order without
ORDER BY: the order is unspecified, and a row limit over an unordered result picks arbitrary rows.Picking ROW_NUMBER, RANK or DENSE_RANK without asking how ties should count: each gives a different top-N, and interviewers treat the choice as part of the answer.
Trusting the default frame for a running total ordered by a non-unique column: rows tied on the sort key are summed together and share one total.
Wrapping an indexed column in a function or cast inside
WHERE: the predicate stops being sargable. Move the transformation to the constant side of the comparison.Stating how TRUNCATE behaves inside a transaction as if it were universal: rollback, triggers and identity reset differ by engine, so name the engine you assume.
SQL is an ISO/IEC standard revised every few years, and no engine implements all of it: each dialect adds its own syntax and skips or postpones parts of the standard. This guide assumes standard SQL as current engines broadly support it and points out where a common dialect spelling differs. The revisions interviewers still refer to: - **SQL-92**: explicit `JOIN ... ON` syntax, outer joins included, next to the older comma-separated `FROM` list with join conditions in `WHERE`; also `CASE` expressions. - **SQL:1999**: recursive common table expressions, and `ROLLUP`, `CUBE` and `GROUPING SETS`. - **SQL:2003**: window functions in the core standard, `MERGE`, identity columns and sequence generators. - **SQL:2008**: `TRUNCATE TABLE`, and the standard row-limiting clause, `OFFSET` with `FETCH FIRST`. - **SQL:2016**: JSON functions and row pattern recognition. - **SQL:2023**: a JSON data type, and property graph queries as a new part of the standard. Row limiting is where older material and the standard disagree most visibly: ```sql -- widespread dialect extension (PostgreSQL, MySQL, SQLite) SELECT id, title FROM posts ORDER BY id LIMIT 10 OFFSET 20; -- standard form since SQL:2008 SELECT id, title FROM posts ORDER BY id OFFSET 20 ROWS FETCH FIRST 10 ROWS ONLY; ``` Both return the same ten rows. `LIMIT` is not in the standard at all, SQL Server writes a simple limit with `TOP`, and some engines accept both forms, so say which one you are writing.
SQL is the query language of relational databases, with PostgreSQL, MySQL, SQL Server, Oracle and SQLite among the common ones, and of analytical warehouses and query engines that read files from object storage. Interviewers expect you to name the dialect you know and treat the others as translation: row limiting, upserts, identity generation, string and date functions and some NULL helpers differ between engines, while joins, grouping and window functions read almost the same everywhere. Three neighbours come up. **ORMs and query builders** generate SQL from application code; they remove boilerplate, not the need to read what they emit, and a standard senior question is spotting the one-query-per-row pattern an ORM produced. **Document and key-value stores** trade joins and a fixed schema for flexible records and access built around a known key; SQL's strength is answering questions nobody planned for when the schema was designed. **Dataframe libraries** express the same filter, join, group and window operations step by step in a host language, where SQL states the whole result at once and leaves the plan to the engine.
explore
- SELECT and Filtering60 questions
- Logical Clause Evaluation Order4 questions
- SELECT List and Column Expressions6 questions
- Comparison and Boolean Predicates5 questions
- IN and BETWEEN Predicates5 questions
- LIKE and Pattern Matching4 questions
- NULL and Three-Valued Logic6 questions
- DISTINCT Semantics4 questions
- CASE Expressions6 questions
- ORDER BY and Sorting Semantics5 questions
- LIMIT, FETCH FIRST and OFFSET5 questions
- Set Operations: UNION, INTERSECT, EXCEPT6 questions
- Aliases and Scoping Rules4 questions
- Joins47 questions
- Join Types33 questions
- Join Semantics and Pitfalls14 questions
- Aggregation and Grouping56 questions
- GROUP BY Semantics6 questions
- The Every-Selected-Column Rule4 questions
- Core Aggregate Functions6 questions
- Aggregates and NULLs5 questions
- COUNT(*) vs COUNT(col) vs COUNT(DISTINCT)5 questions
- HAVING vs WHERE5 questions
- GROUPING SETS, ROLLUP, and CUBE5 questions
- The FILTER Clause5 questions
- Conditional Aggregation with CASE5 questions
- String and Array Aggregation5 questions
- Aggregating over Joins (Fan-out)5 questions
- Subqueries and CTEs37 questions
- Subqueries18 questions
- Common Table Expressions19 questions
- DDL and DML66 questions
- Schema Definition (DDL)27 questions
- Data Modification (DML)30 questions
- Transaction Statements9 questions
- Indexing and Query Performance41 questions
- Sargable Predicates4 questions
- Implicit Casts and Type Mismatches4 questions
- SELECT * and Column Pruning5 questions
- EXISTS vs IN vs JOIN4 questions
- OFFSET vs Keyset Pagination5 questions
- Batch DML Patterns5 questions
- Reading EXPLAIN as a Developer4 questions
- Predicate Anti-Patterns5 questions
- Index-Friendly ORDER BY and LIMIT5 questions
- Window Functions49 questions
- OVER Clause Anatomy4 questions
- Ranking Functions5 questions
- Frame Specification: ROWS, RANGE, GROUPS6 questions
- Named Windows: the WINDOW Clause4 questions
- Pattern: Top-N per Group5 questions
- Pattern: Deduplication with ROW_NUMBER5 questions
- Pattern: Gaps and Islands5 questions
- Window Functions vs GROUP BY5 questions
- AI & Data Scientistroleanchors this topic
- AI Engineerroleanchors this topic
- BI Analystroleanchors this topic
- Backend Developerroleanchors this topic
- Cyber Security Expertroleanchors this topic
- Data Analystroleanchors this topic
- Data Engineerroleanchors this topic
- Full Stack Developerroleanchors this topic
- Java Backend Developerroleanchors this topic
- Java SDETroleanchors this topic
- Kotlin Backend Developerroleanchors this topic
- MLOps Engineerroleanchors this topic
- Machine Learning Engineerroleanchors this topic
- PostgreSQL DBAroleanchors this topic
- QA Engineerroleanchors this topic
- SQLskillanchors this topic
questions
356 · 7 sectionsWhy does SELECT id FROM orders JOIN customers ON customers.id = orders.customer_id fail?
basics
~20 sBoth tables in scope expose a column named id, so the unqualified reference is ambiguous and the statement is rejected before it runs. Qualify it as orders.id or customers.id, or give each table an alias and select o.id.
How does SQL's simple CASE form differ from the searched CASE form?
basics
~20 sSimple CASE compares one expression for equality against a list of values (CASE status WHEN 'N' THEN ...). Searched CASE takes an independent boolean predicate per WHEN, so it can test ranges, several columns, or IS NULL.
Does AND bind tighter than OR in a WHERE clause, and what bug does that cause?
basics
~20 sYes. Standard SQL precedence is NOT, then AND, then OR, so a OR b AND c means a OR (b AND c). The classic bug is an OR list of values silently escaping a filter that was meant to apply to all of them.
In SELECT DISTINCT city, state FROM addresses, what exactly does DISTINCT deduplicate?
basics
~10 sDISTINCT applies to the whole select list, not to one column. SELECT DISTINCT city, state returns every unique city-and-state combination, so one city appears repeatedly if it occurs with several states.
What does the predicate x BETWEEN 10 AND 20 mean, and are both endpoints included?
basics
~20 sBETWEEN is inclusive at both ends: x BETWEEN 10 AND 20 means x >= 10 AND x <= 20, so 10 and 20 both match. The lower bound must be written first, or the predicate matches nothing.
In an INNER JOIN on a.dept_id = b.dept_id, what happens to rows where dept_id is NULL?
basics
~20 sThey disappear from the result. NULL = NULL evaluates to UNKNOWN rather than TRUE, and a join keeps only pairs whose predicate is TRUE, so a NULL key matches nothing — not even a NULL key on the other side.
For an INNER JOIN, does it matter whether a filter goes in the ON clause or in WHERE?
basics
~20 sFor an INNER JOIN, ON and WHERE are logically equivalent: a row must satisfy both predicates to survive either way. For outer joins they are not equivalent, because ON is evaluated before NULL-extension and WHERE after it.
How do you list customers that have at least one order without repeating any customer?
basics
~20 sFilter with a semi-join instead of joining: WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id). An inner join emits one output row per matching order, so a customer with five orders appears five times.
How many rows does a CROSS JOIN of a 100-row table and a 20-row table return, and why?
basics
~20 s2,000 rows. A CROSS JOIN returns the Cartesian product: every row of the left table paired with every row of the right, with no join predicate. The output size is N times M, never N plus M.
What rows does a FULL OUTER JOIN return that an INNER JOIN of the same tables does not?
basics
~20 sFULL OUTER JOIN returns every matched pair plus every unmatched row from both tables. An unmatched left row gets NULLs in all right-side columns, and an unmatched right row gets NULLs in all left-side columns.
COUNT, SUM, AVG, MIN and MAX: what argument types does each accept and return?
basics
~20 sCOUNT accepts any expression, or *, and returns an integer count. SUM and AVG require numeric arguments and return numeric results. MIN and MAX accept any orderable type — numbers, text, dates — and return a value of that same type.
Using SUM(CASE WHEN …), how do you count shipped and cancelled orders per customer?
basics
~20 sPut a CASE inside the aggregate. SUM(CASE WHEN status = 'shipped' THEN 1 ELSE 0 END) contributes 1 for each matching row and 0 for the rest, so one GROUP BY query returns a separate count per condition.
What do COUNT(*) and COUNT(city) return for a 10-row table where 3 rows have NULL city?
basics
~10 sCOUNT() returns 10 and COUNT(city) returns 7. COUNT() counts rows; COUNT(expression) counts only the rows where that expression is not NULL, so the three NULL cities are skipped.
What does GROUP BY do to a query's rows, and what determines the result row count?
basics
~20 sGROUP BY partitions the rows that survive FROM and WHERE into groups sharing the same grouping-key values, then emits exactly one row per group. The result has as many rows as there are distinct key combinations.
What is the difference between the WHERE clause and the HAVING clause in SQL?
basics
~20 sWHERE filters individual rows before grouping; HAVING filters whole groups after aggregation. Only HAVING may reference aggregate results such as COUNT(*) or SUM(amount), because those values exist only once rows have been collapsed into groups.
What is the difference between a CTE, a derived table, and a view?
basics
~20 sAll three name a subresult. A derived table is a subquery written inline in FROM and used once there; a CTE is named in a WITH clause and visible only to that one statement; a view is a stored schema object any statement can query.
What does an EXISTS subquery predicate evaluate to, and can it ever be UNKNOWN?
basics
~20 sEXISTS is TRUE when its subquery returns at least one row and FALSE when it returns none. It is two-valued and never UNKNOWN. The subquery's select list is irrelevant; only whether rows come back matters.
What must a scalar subquery return, and what happens if it returns more than one row?
basics
~20 sA scalar subquery must produce exactly one column and at most one row, so it can stand where a single value is expected. Returning two or more rows is a cardinality error raised while the statement runs, not a syntax error caught beforehand.
Where can a subquery appear in a SELECT statement, and what does each position contribute?
basics
~20 sA subquery can sit in the SELECT list as one computed value per row, in FROM as a derived table that must be aliased, and in WHERE or HAVING as an operand of a predicate.
What value do existing rows get when you run ALTER TABLE ... ADD COLUMN on a populated table?
basics
~20 sExisting rows get NULL unless the new column declares a DEFAULT, in which case every existing row is populated with that default value. Adding a NOT NULL column with no DEFAULT to a table that already holds rows is rejected.
In CREATE TABLE, when must a constraint be written at table level instead of inline on a column?
basics
~20 sAny rule covering more than one column — a composite PRIMARY KEY, UNIQUE or FOREIGN KEY, or a CHECK comparing two columns — must be written as a table constraint after the column list. Single-column rules may be written either way.
What does DEFAULT CURRENT_TIMESTAMP in a CREATE TABLE column definition mean, and when is it evaluated?
basics
~20 sDEFAULT names the value the engine stores when an INSERT supplies none for that column. The expression is evaluated at insert time, so each row records its own insertion moment rather than one value frozen when the table was created.
Why is FLOAT the wrong type for a money column, and what should you use instead?
basics
~20 sFLOAT and REAL store binary approximations, so a value such as 0.10 is never held exactly and the error accumulates across sums and multiplications. Money needs an exact type: DECIMAL/NUMERIC with a declared precision and scale.
How do you declare an auto-numbered surrogate key column in standard SQL?
basics
~20 sStandard SQL uses an identity column: id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY. The engine supplies each value from an attached sequence generator, whose START WITH, INCREMENT BY, CYCLE and MINVALUE/MAXVALUE options you set in parentheses.
Why compare a VARCHAR account_no column to '12345' rather than to the number 12345?
basics
~20 sBecause the types must match. Comparing a character column to a numeric literal is not a valid comparison in standard SQL: engines either reject it or silently convert every row's stored value, which throws away the index on that column.
Why does LIMIT 20 OFFSET 200000 get slower as the offset grows?
basics
~20 sOFFSET is scan-and-discard. The engine still produces the first 200,000 rows in ORDER BY sequence and throws them away before emitting 20, so the work grows with the offset and deep pages get steadily slower.
On a table indexed only on id, why is ORDER BY id LIMIT 10 instant but ORDER BY last_name LIMIT 10 slow?
basics
~20 sThe index on id already holds rows in id order, so the engine walks it and stops after ten entries. Nothing indexes last_name, so every row must be read and sorted before the first ten are known.
Beyond typing convenience, what does SELECT * cost compared with listing the columns you need?
basics
~20 sSELECT * projects every column, so the engine reads and ships bytes the caller discards, gives up access paths a narrower projection would allow, and returns a result whose shape silently changes when the table changes.
Why does one set-based UPDATE beat a loop that issues one UPDATE per row?
basics
~20 sA loop pays a network round trip, a parse and a statement execution per row, and often a commit per row. One set-based UPDATE pays all of that once and lets the engine work through the whole row set in bulk.
Which rows survive filtering on ROW_NUMBER() OVER (PARTITION BY email ORDER BY created_at DESC) = 1?
basics
~20 sExactly one row per distinct email value: the one with the newest created_at. ROW_NUMBER numbers rows 1, 2, 3 inside each email partition in the given order, so number 1 is that partition's first row.
What does LAG(amount, 1, 0) OVER (ORDER BY month) return for the first row?
basics
~20 sIt returns 0. LAG reads the value one row back in the window's ordering; the first row has no predecessor, so the third argument (the default) is substituted. Omit that argument and you get NULL.
What do PARTITION BY and ORDER BY do inside a window function's OVER clause?
basics
~20 sPARTITION BY splits the rows into independent groups so the function restarts in each one; ORDER BY sequences the rows inside a partition so position-dependent functions and frames have a defined order. Both parts are optional.
How do ROW_NUMBER(), RANK(), and DENSE_RANK() differ when the ORDER BY values tie?
basics
~20 sROW_NUMBER() numbers every row 1, 2, 3 with no repeats. RANK() gives tied rows the same number and then skips ahead, leaving a gap. DENSE_RANK() also gives ties the same number but continues with the next integer, no gap.
How do you return each customer's most recent order row, with all of its columns?
basics
~20 sRank each customer's orders in a CTE with ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC), then select rows WHERE rn = 1 in an outer query. That keeps the whole row, not just the maximum date.