[{"data":1,"prerenderedAt":139},["ShallowReactive",2],{"qa-\u002Fsql\u002Fsubqueries\u002Fctes":3},{"page":4,"siblings":130,"blog":136},{"id":5,"title":6,"body":7,"description":30,"difficulty":34,"extension":35,"framework":36,"frameworkSlug":37,"meta":38,"navigation":39,"order":31,"path":40,"questions":41,"questionsCount":120,"related":121,"seo":122,"seoDescription":123,"stem":124,"subtopic":125,"topic":126,"topicSlug":127,"updated":128,"__hash__":129},"qa\u002Fsql\u002Fsubqueries\u002Fctes.md","Ctes",{"type":8,"value":9,"toc":29},"minimark",[10,15],[11,12,14],"h2",{"id":13},"about-common-table-expressions","About Common Table Expressions",[16,17,18,19,23,24,28],"p",{},"CTEs (the ",[20,21,22],"code",{},"WITH"," clause) turn deeply nested subqueries into readable, top-to-bottom\npipelines, and — uniquely — enable ",[25,26,27],"strong",{},"recursion"," for hierarchical data like org charts\nand category trees. Interviewers probe whether you understand a CTE's single-statement\nscope, how it differs from views and temp tables, the anchor\u002Frecursive structure of a\nrecursive CTE, and the reality that CTEs are mainly a readability tool, not a magic\nperformance boost.",{"title":30,"searchDepth":31,"depth":31,"links":32},"",2,[33],{"id":13,"depth":31,"text":14},"medium","md","SQL","sql",{},true,"\u002Fsql\u002Fsubqueries\u002Fctes",[42,47,51,55,59,63,67,72,76,80,84,88,92,96,100,104,108,112,116],{"id":43,"difficulty":44,"q":45,"a":46},"what-is-a-cte","easy","What is a Common Table Expression (CTE)?","A **CTE** is a named temporary result set, defined with the `WITH` clause, that\nexists only for the duration of a single statement. You define it once, then\nreference it by name in the query that follows — like a disposable, inline view.\n\n```sql\nWITH recent_orders AS (\n    SELECT * FROM orders WHERE created_at > '2026-01-01'\n)\nSELECT customer_id, COUNT(*)\nFROM   recent_orders          -- reference the CTE by name\nGROUP  BY customer_id;\n```\n\nCTEs make complex queries **readable** by breaking them into named, top-to-bottom\nsteps instead of deeply nested subqueries.\n\nRule of thumb: a CTE is a named subquery you define up front with `WITH` to make a\nquery read like sequential steps.\n",{"id":48,"difficulty":44,"q":49,"a":50},"cte-syntax","What is the syntax of a CTE?","A CTE starts with `WITH`, names the result set, and defines it in parentheses;\nthe main query follows and references the name.\n\n```sql\nWITH cte_name (optional, col, list) AS (\n    SELECT ...               -- the CTE body\n)\nSELECT * FROM cte_name;      -- the main query MUST follow immediately\n```\n\nKey rules: the main statement must come **right after** the CTE definition; the\noptional column list renames the output columns; and the CTE is only visible to\nthe statement it's attached to.\n\nRule of thumb: `WITH name AS (...)` then the query — the CTE and its consumer are\none statement.\n",{"id":52,"difficulty":34,"q":53,"a":54},"cte-vs-subquery","What is the difference between a CTE and a subquery?","Functionally a CTE is similar to a derived-table subquery, but it offers things a\nplain subquery can't:\n\n- **Reusability** — reference the same CTE multiple times in one query; a derived\n  table must be repeated.\n- **Readability** — named, top-to-bottom steps instead of inside-out nesting.\n- **Recursion** — only CTEs can be recursive.\n\n```sql\n-- a subquery repeated twice...\nSELECT * FROM (SELECT ...) a JOIN (SELECT ...) b ON ...;\n\n-- ...vs a CTE defined once, used twice\nWITH t AS (SELECT ...)\nSELECT * FROM t a JOIN t b ON ...;\n```\n\nPerformance is usually comparable — many engines treat a CTE as syntactic sugar\nfor a subquery.\n\nRule of thumb: prefer a CTE when logic is reused, recursive, or complex enough\nthat naming the steps helps.\n",{"id":56,"difficulty":34,"q":57,"a":58},"cte-vs-view","What is the difference between a CTE and a view?","Both name a query, but their **scope and persistence** differ:\n\n- A **view** is a permanent schema object, stored in the catalog and reusable by\n  **any** query and user, until dropped.\n- A **CTE** is temporary and **scoped to a single statement** — it vanishes when\n  the query finishes and isn't stored anywhere.\n\n```sql\n-- view: created once, reused forever\nCREATE VIEW active_customers AS SELECT * FROM customers WHERE active = true;\n\n-- CTE: lives only inside this one statement\nWITH active_customers AS (SELECT * FROM customers WHERE active = true)\nSELECT * FROM active_customers;\n```\n\nRule of thumb: reuse across many queries → create a view; one-off, query-local\nlogic → use a CTE.\n",{"id":60,"difficulty":34,"q":61,"a":62},"multiple-ctes","Can you define multiple CTEs in one query?","Yes. Write a single `WITH`, then separate each CTE definition with a **comma**.\nThey're listed top-to-bottom and any CTE can reference the ones **defined before\nit**.\n\n```sql\nWITH dept_avg AS (\n    SELECT dept_id, AVG(salary) AS avg_sal\n    FROM   employees GROUP BY dept_id\n),\nhigh_paying AS (\n    SELECT dept_id FROM dept_avg WHERE avg_sal > 80000  -- uses dept_avg\n)\nSELECT e.name\nFROM   employees e\nJOIN   high_paying h ON e.dept_id = h.dept_id;\n```\n\nOnly one `WITH` keyword is needed regardless of how many CTEs follow.\n\nRule of thumb: chain CTEs with commas to build a pipeline of named steps, each\nable to use the ones above it.\n",{"id":64,"difficulty":34,"q":65,"a":66},"cte-chaining","How do you chain CTEs to build a multi-step pipeline?","Because a CTE can reference earlier CTEs, you can express a **data pipeline** as a\nsequence of transformations — each step reads cleanly off the previous one.\n\n```sql\nWITH raw AS (\n    SELECT customer_id, total FROM orders WHERE status = 'paid'\n),\nper_customer AS (\n    SELECT customer_id, SUM(total) AS spend FROM raw GROUP BY customer_id\n),\nranked AS (\n    SELECT customer_id, spend,\n           RANK() OVER (ORDER BY spend DESC) AS rnk\n    FROM per_customer\n)\nSELECT * FROM ranked WHERE rnk \u003C= 10;   -- top 10 spenders\n```\n\nThis is far more readable than three levels of nested subqueries.\n\nRule of thumb: model ETL-style logic as chained CTEs — filter, aggregate, rank,\nthen select.\n",{"id":68,"difficulty":69,"q":70,"a":71},"recursive-cte","hard","What is a recursive CTE?","A **recursive CTE** references **itself** to process hierarchical or iterative\ndata — org charts, category trees, graph traversal, or number\u002Fdate series. It's\ndeclared with `WITH RECURSIVE` (the keyword is optional in SQL Server).\n\n```sql\nWITH RECURSIVE subordinates AS (\n    SELECT id, name, manager_id          -- anchor: the starting row(s)\n    FROM   employees WHERE id = 1\n    UNION ALL\n    SELECT e.id, e.name, e.manager_id    -- recursive: joins back to the CTE\n    FROM   employees e\n    JOIN   subordinates s ON e.manager_id = s.id\n)\nSELECT * FROM subordinates;\n```\n\nIt runs the anchor once, then repeatedly runs the recursive part against the\nprevious result until no new rows appear.\n\nRule of thumb: use a recursive CTE to walk hierarchies and trees that a flat\n`JOIN` can't express.\n",{"id":73,"difficulty":69,"q":74,"a":75},"recursive-cte-parts","What are the two parts of a recursive CTE?","A recursive CTE has two members joined by `UNION ALL`:\n\n1. **Anchor member** — the non-recursive base case; runs **once** to seed the\n   result (e.g. the root of a tree).\n2. **Recursive member** — references the CTE itself; runs **repeatedly**, each\n   iteration operating on the rows the previous iteration produced.\n\n```sql\nWITH RECURSIVE nums AS (\n    SELECT 1 AS n                     -- anchor\n    UNION ALL\n    SELECT n + 1 FROM nums WHERE n \u003C 5 -- recursive, with a stop condition\n)\nSELECT n FROM nums;   -- 1,2,3,4,5\n```\n\nThe recursive member **must** have a terminating condition or the query loops\nuntil it hits the recursion limit.\n\nRule of thumb: anchor seeds, recursive member iterates, `UNION ALL` glues them —\nalways include a stopping condition.\n",{"id":77,"difficulty":69,"q":78,"a":79},"recursive-cte-infinite-loop","How do you prevent an infinite loop in a recursive CTE?","A recursive CTE loops forever if the recursive member never stops producing new\nrows — common with **cyclic data** (A reports to B, B reports to A).\n\nDefenses:\n- A **terminating predicate** in the recursive member (`WHERE n \u003C 100`,\n  `WHERE level \u003C 10`).\n- Track a **path \u002F visited set** and exclude already-seen nodes to break cycles.\n- Rely on the engine's **recursion limit** as a safety net (Postgres has no hard\n  default but you can `LIMIT`; SQL Server defaults to `MAXRECURSION 100`).\n\n```sql\n-- SQL Server: cap iterations explicitly\nSELECT * FROM subordinates OPTION (MAXRECURSION 50);\n```\n\nRule of thumb: every recursive CTE needs a stop condition; for graphs that may\ncontain cycles, also track visited nodes.\n",{"id":81,"difficulty":34,"q":82,"a":83},"cte-multiple-references","Can a CTE be referenced multiple times in the same query?","Yes — a key advantage over a derived table. Define the CTE once and use its name\nas many times as needed, including **self-joins**.\n\n```sql\n-- compare each employee's salary to their manager's, using one CTE twice\nWITH emp AS (\n    SELECT id, name, salary, manager_id FROM employees\n)\nSELECT e.name, e.salary, m.salary AS manager_salary\nFROM   emp e\nJOIN   emp m ON e.manager_id = m.id;\n```\n\nWhether the engine computes the CTE once and caches it or re-evaluates per\nreference depends on the database and whether it's materialized.\n\nRule of thumb: reuse a CTE by name instead of copy-pasting the same subquery —\ndefine once, reference freely.\n",{"id":85,"difficulty":69,"q":86,"a":87},"cte-materialization","Are CTEs materialized or inlined?","It depends on the database. **Materialized** means the CTE is computed once into a\ntemporary work table; **inlined** means it's folded into the main query and\noptimized together (often faster, since predicates can push down).\n\n- **PostgreSQL** — inlined since v12 when referenced once and not recursive; before\n  v12, CTEs were an **optimization fence** (always materialized). You can force\n  either with `MATERIALIZED` \u002F `NOT MATERIALIZED`.\n- **SQL Server \u002F MySQL 8+** — generally inline CTEs into the plan.\n\n```sql\n-- Postgres: force materialization (an optimization barrier)\nWITH t AS MATERIALIZED (SELECT * FROM big_table WHERE active)\nSELECT * FROM t WHERE id = 5;\n```\n\nRule of thumb: don't assume a CTE is materialized — check `EXPLAIN`; in modern\nPostgres use `MATERIALIZED`\u002F`NOT MATERIALIZED` to control it.\n",{"id":89,"difficulty":69,"q":90,"a":91},"cte-performance","Do CTEs improve query performance?","Not inherently — CTEs are primarily a **readability** tool. In most engines a CTE\nproduces the same plan as the equivalent subquery or derived table.\n\nCaveats that can hurt or help:\n- An **older Postgres** materializing a CTE could **block predicate pushdown**,\n  making it slower than an inline subquery.\n- A CTE **referenced many times** that the engine recomputes each time can be\n  slower than expected — materializing it once may help.\n- Recursive CTEs over deep hierarchies can be expensive regardless.\n\nRule of thumb: choose CTEs for clarity, not speed; verify with `EXPLAIN` and\nconsider explicit materialization only when the plan shows a problem.\n",{"id":93,"difficulty":34,"q":94,"a":95},"cte-in-update-delete","Can CTEs be used with INSERT, UPDATE, and DELETE?","Yes. Put the `WITH` clause in front of the DML statement and reference the CTE in\nit — handy for staging the rows to modify.\n\n```sql\n-- delete all but the latest order per customer\nWITH ranked AS (\n    SELECT id, ROW_NUMBER() OVER (PARTITION BY customer_id\n                                  ORDER BY created_at DESC) AS rn\n    FROM orders\n)\nDELETE FROM orders\nWHERE id IN (SELECT id FROM ranked WHERE rn > 1);\n```\n\nPostgres even allows **data-modifying CTEs** (`INSERT\u002FUPDATE\u002FDELETE ... RETURNING`\ninside a `WITH`), letting one statement write to several tables.\n\nRule of thumb: use a CTE to compute exactly which rows a DML statement should\ntouch — especially with window functions for \"keep the latest\" logic.\n",{"id":97,"difficulty":34,"q":98,"a":99},"cte-window-functions","Why are CTEs often paired with window functions?","Window functions like `ROW_NUMBER()` and `RANK()` **can't be used directly in\n`WHERE`** (they're computed after filtering). A CTE (or derived table) lets you\ncompute the window value first, then filter on it in the outer query.\n\n```sql\n-- top 3 earners per department\nWITH ranked AS (\n    SELECT name, dept_id, salary,\n           ROW_NUMBER() OVER (PARTITION BY dept_id\n                              ORDER BY salary DESC) AS rn\n    FROM employees\n)\nSELECT name, dept_id, salary\nFROM   ranked\nWHERE  rn \u003C= 3;       -- filter on the window result\n```\n\nRule of thumb: compute the window function in a CTE, then filter its alias in the\nouter query — the standard \"top-N-per-group\" pattern.\n",{"id":101,"difficulty":44,"q":102,"a":103},"cte-scope","What is the scope of a CTE?","A CTE is visible **only within the single statement** it's attached to. Once that\nstatement finishes, the CTE is gone — you can't reference it from a later\nstatement, and it isn't stored in the schema.\n\n```sql\nWITH t AS (SELECT 1 AS x)\nSELECT * FROM t;     -- works\n\nSELECT * FROM t;     -- ERROR: t no longer exists\n```\n\nAlso, later CTEs in the same `WITH` can see earlier ones, but not vice versa\n(except a recursive CTE referencing itself).\n\nRule of thumb: a CTE lives and dies with one statement — for cross-statement reuse\nuse a view or temp table.\n",{"id":105,"difficulty":44,"q":106,"a":107},"cte-column-aliasing","How do you rename a CTE's output columns?","Provide an explicit **column list** in parentheses after the CTE name. The CTE\nbody's columns are mapped positionally to those names — useful when the body uses\nexpressions or you want clearer names.\n\n```sql\nWITH sales (region, total) AS (\n    SELECT region_id, SUM(amount)\n    FROM   orders\n    GROUP  BY region_id\n)\nSELECT region, total FROM sales ORDER BY total DESC;\n```\n\nThe number of names must match the number of output columns. This is also the\nstandard way to name the columns of a **recursive** CTE.\n\nRule of thumb: add `(col1, col2, ...)` after the CTE name to give its columns\nexplicit names — required when the body's columns are unnamed expressions.\n",{"id":109,"difficulty":34,"q":110,"a":111},"cte-vs-temp-table","What is the difference between a CTE and a temporary table?","Both hold an intermediate result, but their lifetime and storage differ:\n\n- A **CTE** is logical and scoped to one statement; it isn't physically stored\n  (unless the engine materializes it internally) and can't be indexed.\n- A **temp table** is a real table in `tempdb`\u002Fsession scope, persists for the\n  **session\u002Ftransaction**, can be **indexed**, and is reusable across multiple\n  statements.\n\n```sql\n-- temp table: reusable across statements, can index\nCREATE TEMP TABLE big_spenders AS\n    SELECT customer_id, SUM(total) s FROM orders GROUP BY customer_id;\nCREATE INDEX ON big_spenders (s);\nSELECT * FROM big_spenders WHERE s > 1000;\n```\n\nRule of thumb: one-statement, no-index logic → CTE; reuse across statements or\nneed an index on the intermediate → temp table.\n",{"id":113,"difficulty":34,"q":114,"a":115},"cte-recursive-use-cases","What are common use cases for recursive CTEs?","Recursive CTEs shine wherever data is **hierarchical or generated iteratively**:\n\n- **Org charts \u002F management chains** — all reports under a manager.\n- **Category \u002F comment trees** — nested parent-child structures.\n- **Bill of materials** — parts made of sub-parts.\n- **Graph traversal** — shortest path, connected nodes.\n- **Generating series** — number ranges or contiguous date sequences.\n\n```sql\n-- generate every date in a month\nWITH RECURSIVE d AS (\n    SELECT DATE '2026-06-01' AS day\n    UNION ALL\n    SELECT day + 1 FROM d WHERE day \u003C DATE '2026-06-30'\n)\nSELECT day FROM d;\n```\n\nRule of thumb: reach for a recursive CTE whenever you'd otherwise need a loop to\nwalk a tree, graph, or generated series.\n",{"id":117,"difficulty":34,"q":118,"a":119},"when-not-to-use-cte","When should you avoid using a CTE?","CTEs are great for clarity, but they aren't always the right tool:\n\n- When you need the intermediate **reused across multiple statements** — a view or\n  temp table fits better.\n- When you need an **index** on the intermediate result — use a temp table.\n- On **older Postgres**, when materialization would block predicate pushdown and\n  slow the query — an inline subquery may be faster.\n- For a trivial one-liner where a simple subquery is just as clear.\n\nRule of thumb: use CTEs for readable, single-statement logic; switch to views or\ntemp tables when you need persistence, reuse, or indexing.\n",19,null,{"description":30},"SQL CTE interview questions — the WITH clause, recursive CTEs, CTE vs subquery vs view, materialization, multiple CTEs and chaining, and performance.","sql\u002Fsubqueries\u002Fctes","Common Table Expressions (CTEs)","Subqueries & CTEs","subqueries","2026-06-20","b36Ff8SVYX5Lj0B-Q5_So2hKhCvupG7vokR_m7ylLp4",[131,135],{"subtopic":132,"path":133,"order":134},"Subqueries","\u002Fsql\u002Fsubqueries\u002Fsubqueries",1,{"subtopic":125,"path":40,"order":31},{"path":137,"title":138},"\u002Fblog\u002Fsql-ctes-common-table-expressions","SQL CTEs Explained — WITH Clauses, Recursive Queries, and Best Practices",1782244106961]