[{"data":1,"prerenderedAt":2995},["ShallowReactive",2],{"page-\u002Fsql\u002F07-common-table-expressions":3},{"id":4,"title":5,"body":6,"description":2988,"extension":2989,"meta":2990,"navigation":769,"path":2991,"seo":2992,"stem":2993,"__hash__":2994},"content\u002Fsql\u002F07-common-table-expressions.md","07 — Common Table Expressions (CTEs)",{"type":7,"value":8,"toc":2970},"minimark",[9,13,36,41,235,239,242,444,448,456,660,664,671,677,837,845,873,880,888,892,895,1084,1088,1100,1274,1278,1821,1825,1830,2047,2051,2320,2324,2429,2433,2526,2530,2675,2679,2689,2798,2960,2963,2966],[10,11,5],"h1",{"id":12},"_07-common-table-expressions-ctes",[14,15,16,17,21,22,25,26,25,29,25,32,35],"p",{},"A Common Table Expression (CTE) is a named temporary result set defined with ",[18,19,20],"code",{},"WITH",", usable within a single ",[18,23,24],{},"SELECT","\u002F",[18,27,28],{},"INSERT",[18,30,31],{},"UPDATE",[18,33,34],{},"DELETE",". CTEs make complex queries readable by breaking them into named stages — and enable recursion (chapter 19).",[37,38,40],"h2",{"id":39},"basic-syntax","Basic Syntax",[42,43,45],"code-wrapper",{"language":44},"sql",[46,47,51],"pre",{"className":48,"code":49,"language":44,"meta":50,"style":50},"language-sql shiki shiki-themes github-light github-dark","-- Single CTE: name a subquery, reference it in the main query.\n-- The CTE 'big_orders' is a named subquery whose result is available in the main SELECT.\n-- PostgreSQL 12+ inlines non-recursive CTEs by default (folds into main query like a macro).\nWITH big_orders AS (\n  SELECT customer_id, SUM(amount) AS total\n  FROM orders\n  GROUP BY customer_id\n  HAVING SUM(amount) > 100\n)\nSELECT c.name, b.total\nFROM big_orders b\nJOIN customers c ON c.id = b.customer_id\nORDER BY b.total DESC;\n","",[18,52,53,62,68,74,90,111,120,129,146,152,177,186,216],{"__ignoreMap":50},[54,55,58],"span",{"class":56,"line":57},"line",1,[54,59,61],{"class":60},"sdCPZ","-- Single CTE: name a subquery, reference it in the main query.\n",[54,63,65],{"class":56,"line":64},2,[54,66,67],{"class":60},"-- The CTE 'big_orders' is a named subquery whose result is available in the main SELECT.\n",[54,69,71],{"class":56,"line":70},3,[54,72,73],{"class":60},"-- PostgreSQL 12+ inlines non-recursive CTEs by default (folds into main query like a macro).\n",[54,75,77,80,84,87],{"class":56,"line":76},4,[54,78,20],{"class":79},"svdQ7",[54,81,83],{"class":82},"ssxIu"," big_orders ",[54,85,86],{"class":79},"AS",[54,88,89],{"class":82}," (\n",[54,91,93,96,99,103,106,108],{"class":56,"line":92},5,[54,94,95],{"class":79},"  SELECT",[54,97,98],{"class":82}," customer_id, ",[54,100,102],{"class":101},"snvgF","SUM",[54,104,105],{"class":82},"(amount) ",[54,107,86],{"class":79},[54,109,110],{"class":82}," total\n",[54,112,114,117],{"class":56,"line":113},6,[54,115,116],{"class":79},"  FROM",[54,118,119],{"class":82}," orders\n",[54,121,123,126],{"class":56,"line":122},7,[54,124,125],{"class":79},"  GROUP BY",[54,127,128],{"class":82}," customer_id\n",[54,130,132,135,138,140,143],{"class":56,"line":131},8,[54,133,134],{"class":79},"  HAVING",[54,136,137],{"class":101}," SUM",[54,139,105],{"class":82},[54,141,142],{"class":79},">",[54,144,145],{"class":101}," 100\n",[54,147,149],{"class":56,"line":148},9,[54,150,151],{"class":82},")\n",[54,153,155,157,160,163,166,169,172,174],{"class":56,"line":154},10,[54,156,24],{"class":79},[54,158,159],{"class":101}," c",[54,161,162],{"class":82},".",[54,164,165],{"class":101},"name",[54,167,168],{"class":82},", ",[54,170,171],{"class":101},"b",[54,173,162],{"class":82},[54,175,176],{"class":101},"total\n",[54,178,180,183],{"class":56,"line":179},11,[54,181,182],{"class":79},"FROM",[54,184,185],{"class":82}," big_orders b\n",[54,187,189,192,195,198,200,202,205,208,211,213],{"class":56,"line":188},12,[54,190,191],{"class":79},"JOIN",[54,193,194],{"class":82}," customers c ",[54,196,197],{"class":79},"ON",[54,199,159],{"class":101},[54,201,162],{"class":82},[54,203,204],{"class":101},"id",[54,206,207],{"class":79}," =",[54,209,210],{"class":101}," b",[54,212,162],{"class":82},[54,214,215],{"class":101},"customer_id\n",[54,217,219,222,224,226,229,232],{"class":56,"line":218},13,[54,220,221],{"class":79},"ORDER BY",[54,223,210],{"class":101},[54,225,162],{"class":82},[54,227,228],{"class":101},"total",[54,230,231],{"class":79}," DESC",[54,233,234],{"class":82},";\n",[37,236,238],{"id":237},"multiple-chained-ctes","Multiple Chained CTEs",[14,240,241],{},"Chain multiple CTEs, separating with commas. Later CTEs can reference earlier ones — top-to-bottom pipeline flow:",[42,243,244],{"language":44},[46,245,247],{"className":48,"code":246,"language":44,"meta":50,"style":50},"-- Pipeline: customer_totals → city_totals → final filter.\n-- Each CTE reads the prior one. This reads like English: \"compute totals, then city averages, then filter.\"\nWITH\n  customer_totals AS (\n    SELECT customer_id, SUM(amount) AS total\n    FROM orders\n    GROUP BY customer_id\n  ),\n  city_totals AS (\n    -- References customer_totals (defined above) — forward references are allowed.\n    SELECT c.city, AVG(ct.total) AS avg_customer_spend\n    FROM customer_totals ct\n    JOIN customers c ON c.id = ct.customer_id\n    GROUP BY c.city\n  )\nSELECT * FROM city_totals\nWHERE avg_customer_spend > 50\nORDER BY avg_customer_spend DESC;\n",[18,248,249,254,259,264,273,288,295,302,307,316,321,355,362,386,398,404,418,432],{"__ignoreMap":50},[54,250,251],{"class":56,"line":57},[54,252,253],{"class":60},"-- Pipeline: customer_totals → city_totals → final filter.\n",[54,255,256],{"class":56,"line":64},[54,257,258],{"class":60},"-- Each CTE reads the prior one. This reads like English: \"compute totals, then city averages, then filter.\"\n",[54,260,261],{"class":56,"line":70},[54,262,263],{"class":79},"WITH\n",[54,265,266,269,271],{"class":56,"line":76},[54,267,268],{"class":82},"  customer_totals ",[54,270,86],{"class":79},[54,272,89],{"class":82},[54,274,275,278,280,282,284,286],{"class":56,"line":92},[54,276,277],{"class":79},"    SELECT",[54,279,98],{"class":82},[54,281,102],{"class":101},[54,283,105],{"class":82},[54,285,86],{"class":79},[54,287,110],{"class":82},[54,289,290,293],{"class":56,"line":113},[54,291,292],{"class":79},"    FROM",[54,294,119],{"class":82},[54,296,297,300],{"class":56,"line":122},[54,298,299],{"class":79},"    GROUP BY",[54,301,128],{"class":82},[54,303,304],{"class":56,"line":131},[54,305,306],{"class":82},"  ),\n",[54,308,309,312,314],{"class":56,"line":148},[54,310,311],{"class":82},"  city_totals ",[54,313,86],{"class":79},[54,315,89],{"class":82},[54,317,318],{"class":56,"line":154},[54,319,320],{"class":60},"    -- References customer_totals (defined above) — forward references are allowed.\n",[54,322,323,325,327,329,332,334,337,340,343,345,347,350,352],{"class":56,"line":179},[54,324,277],{"class":79},[54,326,159],{"class":101},[54,328,162],{"class":82},[54,330,331],{"class":101},"city",[54,333,168],{"class":82},[54,335,336],{"class":101},"AVG",[54,338,339],{"class":82},"(",[54,341,342],{"class":101},"ct",[54,344,162],{"class":82},[54,346,228],{"class":101},[54,348,349],{"class":82},") ",[54,351,86],{"class":79},[54,353,354],{"class":82}," avg_customer_spend\n",[54,356,357,359],{"class":56,"line":188},[54,358,292],{"class":79},[54,360,361],{"class":82}," customer_totals ct\n",[54,363,364,367,369,371,373,375,377,379,382,384],{"class":56,"line":218},[54,365,366],{"class":79},"    JOIN",[54,368,194],{"class":82},[54,370,197],{"class":79},[54,372,159],{"class":101},[54,374,162],{"class":82},[54,376,204],{"class":101},[54,378,207],{"class":79},[54,380,381],{"class":101}," ct",[54,383,162],{"class":82},[54,385,215],{"class":101},[54,387,389,391,393,395],{"class":56,"line":388},14,[54,390,299],{"class":79},[54,392,159],{"class":101},[54,394,162],{"class":82},[54,396,397],{"class":101},"city\n",[54,399,401],{"class":56,"line":400},15,[54,402,403],{"class":82},"  )\n",[54,405,407,409,412,415],{"class":56,"line":406},16,[54,408,24],{"class":79},[54,410,411],{"class":79}," *",[54,413,414],{"class":79}," FROM",[54,416,417],{"class":82}," city_totals\n",[54,419,421,424,427,429],{"class":56,"line":420},17,[54,422,423],{"class":79},"WHERE",[54,425,426],{"class":82}," avg_customer_spend ",[54,428,142],{"class":79},[54,430,431],{"class":101}," 50\n",[54,433,435,437,439,442],{"class":56,"line":434},18,[54,436,221],{"class":79},[54,438,426],{"class":82},[54,440,441],{"class":79},"DESC",[54,443,234],{"class":82},[37,445,447],{"id":446},"ctes-reused-multiple-times","CTEs Reused Multiple Times",[14,449,450,451,455],{},"A derived table can be referenced once; a CTE can be referenced ",[452,453,454],"strong",{},"multiple times"," in the same statement:",[42,457,458],{"language":44},[46,459,461],{"className":48,"code":460,"language":44,"meta":50,"style":50},"-- Compare each customer's spend to the overall mean and median.\n-- 'totals' is referenced THREE times: main query + two scalar subqueries.\n-- With MATERIALIZED (PG12+), it's computed once and reused; without, it may be inlined.\nWITH totals AS (\n  SELECT customer_id, SUM(amount) AS total\n  FROM orders\n  GROUP BY customer_id\n)\nSELECT\n  t.customer_id,\n  t.total,\n  t.total - (SELECT AVG(total) FROM totals) AS diff_from_mean,\n  t.total - (\n    SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY total) FROM totals\n  ) AS diff_from_median\nFROM totals t\nORDER BY t.total DESC;\n",[18,462,463,468,473,478,489,503,509,515,519,524,537,547,579,591,628,638,645],{"__ignoreMap":50},[54,464,465],{"class":56,"line":57},[54,466,467],{"class":60},"-- Compare each customer's spend to the overall mean and median.\n",[54,469,470],{"class":56,"line":64},[54,471,472],{"class":60},"-- 'totals' is referenced THREE times: main query + two scalar subqueries.\n",[54,474,475],{"class":56,"line":70},[54,476,477],{"class":60},"-- With MATERIALIZED (PG12+), it's computed once and reused; without, it may be inlined.\n",[54,479,480,482,485,487],{"class":56,"line":76},[54,481,20],{"class":79},[54,483,484],{"class":82}," totals ",[54,486,86],{"class":79},[54,488,89],{"class":82},[54,490,491,493,495,497,499,501],{"class":56,"line":92},[54,492,95],{"class":79},[54,494,98],{"class":82},[54,496,102],{"class":101},[54,498,105],{"class":82},[54,500,86],{"class":79},[54,502,110],{"class":82},[54,504,505,507],{"class":56,"line":113},[54,506,116],{"class":79},[54,508,119],{"class":82},[54,510,511,513],{"class":56,"line":122},[54,512,125],{"class":79},[54,514,128],{"class":82},[54,516,517],{"class":56,"line":131},[54,518,151],{"class":82},[54,520,521],{"class":56,"line":148},[54,522,523],{"class":79},"SELECT\n",[54,525,526,529,531,534],{"class":56,"line":154},[54,527,528],{"class":101},"  t",[54,530,162],{"class":82},[54,532,533],{"class":101},"customer_id",[54,535,536],{"class":82},",\n",[54,538,539,541,543,545],{"class":56,"line":179},[54,540,528],{"class":101},[54,542,162],{"class":82},[54,544,228],{"class":101},[54,546,536],{"class":82},[54,548,549,551,553,555,558,561,563,566,569,571,574,576],{"class":56,"line":188},[54,550,528],{"class":101},[54,552,162],{"class":82},[54,554,228],{"class":101},[54,556,557],{"class":79}," -",[54,559,560],{"class":82}," (",[54,562,24],{"class":79},[54,564,565],{"class":101}," AVG",[54,567,568],{"class":82},"(total) ",[54,570,182],{"class":79},[54,572,573],{"class":82}," totals) ",[54,575,86],{"class":79},[54,577,578],{"class":82}," diff_from_mean,\n",[54,580,581,583,585,587,589],{"class":56,"line":218},[54,582,528],{"class":101},[54,584,162],{"class":82},[54,586,228],{"class":101},[54,588,557],{"class":79},[54,590,89],{"class":82},[54,592,593,595,598,600,603,605,608,610,613,616,618,620,623,625],{"class":56,"line":388},[54,594,277],{"class":79},[54,596,597],{"class":101}," PERCENTILE_CONT",[54,599,339],{"class":82},[54,601,602],{"class":101},"0",[54,604,162],{"class":82},[54,606,607],{"class":101},"5",[54,609,349],{"class":82},[54,611,612],{"class":79},"WITHIN",[54,614,615],{"class":101}," GROUP",[54,617,560],{"class":82},[54,619,221],{"class":79},[54,621,622],{"class":82}," total) ",[54,624,182],{"class":79},[54,626,627],{"class":82}," totals\n",[54,629,630,633,635],{"class":56,"line":400},[54,631,632],{"class":82},"  ) ",[54,634,86],{"class":79},[54,636,637],{"class":82}," diff_from_median\n",[54,639,640,642],{"class":56,"line":406},[54,641,182],{"class":79},[54,643,644],{"class":82}," totals t\n",[54,646,647,649,652,654,656,658],{"class":56,"line":420},[54,648,221],{"class":79},[54,650,651],{"class":101}," t",[54,653,162],{"class":82},[54,655,228],{"class":101},[54,657,231],{"class":79},[54,659,234],{"class":82},[37,661,663],{"id":662},"materialized-vs-not-materialized-postgresql-12","MATERIALIZED vs NOT MATERIALIZED (PostgreSQL 12+)",[14,665,666,667,670],{},"By default, PostgreSQL 12+ ",[452,668,669],{},"inlines"," non-recursive CTEs — folds them into the main query like a macro, allowing predicate pushdown and join reordering.",[14,672,673,676],{},[18,674,675],{},"MATERIALIZED"," forces the CTE to be computed once and stored (like a temp table):",[42,678,679],{"language":44},[46,680,682],{"className":48,"code":681,"language":44,"meta":50,"style":50},"-- MATERIALIZED: compute once, store result, reuse.\n-- Use when: expensive computation + referenced multiple times.\n-- Warning: prevents predicate pushdown from the outer query into the CTE.\nWITH expensive AS MATERIALIZED (\n  SELECT id, complex_function(data) AS result FROM big_table  -- runs ONCE, full scan\n)\nSELECT * FROM expensive WHERE result > 0.5;\n\n-- NOT MATERIALIZED: force inlining even when planner would materialize\n-- (e.g., recursive CTEs or multiply-referenced CTEs with costly re-evaluation).\nWITH cheap AS NOT MATERIALIZED (\n  SELECT id, name FROM small_table\n)\nSELECT * FROM cheap WHERE name LIKE 'A%';\n",[18,683,684,689,694,699,711,736,740,765,771,776,781,795,809,813],{"__ignoreMap":50},[54,685,686],{"class":56,"line":57},[54,687,688],{"class":60},"-- MATERIALIZED: compute once, store result, reuse.\n",[54,690,691],{"class":56,"line":64},[54,692,693],{"class":60},"-- Use when: expensive computation + referenced multiple times.\n",[54,695,696],{"class":56,"line":70},[54,697,698],{"class":60},"-- Warning: prevents predicate pushdown from the outer query into the CTE.\n",[54,700,701,703,706,708],{"class":56,"line":76},[54,702,20],{"class":79},[54,704,705],{"class":82}," expensive ",[54,707,86],{"class":79},[54,709,710],{"class":82}," MATERIALIZED (\n",[54,712,713,715,718,721,723,725,728,730,733],{"class":56,"line":92},[54,714,95],{"class":79},[54,716,717],{"class":82}," id, complex_function(",[54,719,720],{"class":79},"data",[54,722,349],{"class":82},[54,724,86],{"class":79},[54,726,727],{"class":82}," result ",[54,729,182],{"class":79},[54,731,732],{"class":82}," big_table  ",[54,734,735],{"class":60},"-- runs ONCE, full scan\n",[54,737,738],{"class":56,"line":113},[54,739,151],{"class":82},[54,741,742,744,746,748,750,752,754,756,759,761,763],{"class":56,"line":122},[54,743,24],{"class":79},[54,745,411],{"class":79},[54,747,414],{"class":79},[54,749,705],{"class":82},[54,751,423],{"class":79},[54,753,727],{"class":82},[54,755,142],{"class":79},[54,757,758],{"class":101}," 0",[54,760,162],{"class":82},[54,762,607],{"class":101},[54,764,234],{"class":82},[54,766,767],{"class":56,"line":131},[54,768,770],{"emptyLinePlaceholder":769},true,"\n",[54,772,773],{"class":56,"line":148},[54,774,775],{"class":60},"-- NOT MATERIALIZED: force inlining even when planner would materialize\n",[54,777,778],{"class":56,"line":154},[54,779,780],{"class":60},"-- (e.g., recursive CTEs or multiply-referenced CTEs with costly re-evaluation).\n",[54,782,783,785,788,790,793],{"class":56,"line":179},[54,784,20],{"class":79},[54,786,787],{"class":82}," cheap ",[54,789,86],{"class":79},[54,791,792],{"class":79}," NOT",[54,794,710],{"class":82},[54,796,797,799,802,804,806],{"class":56,"line":188},[54,798,95],{"class":79},[54,800,801],{"class":82}," id, ",[54,803,165],{"class":79},[54,805,414],{"class":79},[54,807,808],{"class":82}," small_table\n",[54,810,811],{"class":56,"line":218},[54,812,151],{"class":82},[54,814,815,817,819,821,823,825,828,831,835],{"class":56,"line":388},[54,816,24],{"class":79},[54,818,411],{"class":79},[54,820,414],{"class":79},[54,822,787],{"class":82},[54,824,423],{"class":79},[54,826,827],{"class":79}," name",[54,829,830],{"class":79}," LIKE",[54,832,834],{"class":833},"sJ6F3"," 'A%'",[54,836,234],{"class":82},[14,838,839,844],{},[452,840,841,842],{},"When to use ",[18,843,675],{},":",[846,847,848,859,866],"ul",{},[849,850,851,852,855,856,858],"li",{},"CTE is ",[452,853,854],{},"expensive"," and referenced ",[452,857,454],{}," → compute once, reuse.",[849,860,861,862,865],{},"You want to ",[452,863,864],{},"fence"," the optimizer out of a transformation that would be slower (rare — measure first).",[849,867,868,869,872],{},"CTE has ",[452,870,871],{},"side effects"," (data-modifying CTEs are always materialized).",[14,874,875,844],{},[452,876,877,878],{},"When NOT to use ",[18,879,675],{},[846,881,882,885],{},[849,883,884],{},"CTE referenced once + outer query filters heavily → inlining lets the filter reach the CTE's tables.",[849,886,887],{},"Before PostgreSQL 12, all CTEs were materialized (CTEs were \"optimization fences\"). Now opt-in.",[37,889,891],{"id":890},"ctes-with-insertupdatedelete","CTEs with INSERT\u002FUPDATE\u002FDELETE",[14,893,894],{},"CTEs work with DML — use them to drive data modifications:",[42,896,897],{"language":44},[46,898,900],{"className":48,"code":899,"language":44,"meta":50,"style":50},"-- Archive old orders: CTE identifies rows, INSERT copies them.\nWITH old_orders AS (\n  SELECT id FROM orders WHERE ordered_on \u003C '2023-01-01'\n)\nINSERT INTO orders_archive\nSELECT * FROM orders WHERE id IN (SELECT id FROM old_orders);\n\n-- Update the top spender's status: CTE finds the top spender, UPDATE applies.\nWITH top AS (\n  SELECT customer_id\n  FROM orders\n  GROUP BY customer_id\n  ORDER BY SUM(amount) DESC\n  LIMIT 1\n)\nUPDATE customers SET status = 'vip'\nWHERE id IN (SELECT customer_id FROM top);\n",[18,901,902,907,918,941,945,953,981,985,990,1002,1008,1014,1020,1032,1040,1044,1062],{"__ignoreMap":50},[54,903,904],{"class":56,"line":57},[54,905,906],{"class":60},"-- Archive old orders: CTE identifies rows, INSERT copies them.\n",[54,908,909,911,914,916],{"class":56,"line":64},[54,910,20],{"class":79},[54,912,913],{"class":82}," old_orders ",[54,915,86],{"class":79},[54,917,89],{"class":82},[54,919,920,922,925,927,930,932,935,938],{"class":56,"line":70},[54,921,95],{"class":79},[54,923,924],{"class":82}," id ",[54,926,182],{"class":79},[54,928,929],{"class":82}," orders ",[54,931,423],{"class":79},[54,933,934],{"class":82}," ordered_on ",[54,936,937],{"class":79},"\u003C",[54,939,940],{"class":833}," '2023-01-01'\n",[54,942,943],{"class":56,"line":76},[54,944,151],{"class":82},[54,946,947,950],{"class":56,"line":92},[54,948,949],{"class":79},"INSERT INTO",[54,951,952],{"class":82}," orders_archive\n",[54,954,955,957,959,961,963,965,967,970,972,974,976,978],{"class":56,"line":113},[54,956,24],{"class":79},[54,958,411],{"class":79},[54,960,414],{"class":79},[54,962,929],{"class":82},[54,964,423],{"class":79},[54,966,924],{"class":82},[54,968,969],{"class":79},"IN",[54,971,560],{"class":82},[54,973,24],{"class":79},[54,975,924],{"class":82},[54,977,182],{"class":79},[54,979,980],{"class":82}," old_orders);\n",[54,982,983],{"class":56,"line":122},[54,984,770],{"emptyLinePlaceholder":769},[54,986,987],{"class":56,"line":131},[54,988,989],{"class":60},"-- Update the top spender's status: CTE finds the top spender, UPDATE applies.\n",[54,991,992,994,997,1000],{"class":56,"line":148},[54,993,20],{"class":79},[54,995,996],{"class":79}," top",[54,998,999],{"class":79}," AS",[54,1001,89],{"class":82},[54,1003,1004,1006],{"class":56,"line":154},[54,1005,95],{"class":79},[54,1007,128],{"class":82},[54,1009,1010,1012],{"class":56,"line":179},[54,1011,116],{"class":79},[54,1013,119],{"class":82},[54,1015,1016,1018],{"class":56,"line":188},[54,1017,125],{"class":79},[54,1019,128],{"class":82},[54,1021,1022,1025,1027,1029],{"class":56,"line":218},[54,1023,1024],{"class":79},"  ORDER BY",[54,1026,137],{"class":101},[54,1028,105],{"class":82},[54,1030,1031],{"class":79},"DESC\n",[54,1033,1034,1037],{"class":56,"line":388},[54,1035,1036],{"class":79},"  LIMIT",[54,1038,1039],{"class":101}," 1\n",[54,1041,1042],{"class":56,"line":400},[54,1043,151],{"class":82},[54,1045,1046,1048,1051,1054,1057,1059],{"class":56,"line":406},[54,1047,31],{"class":79},[54,1049,1050],{"class":82}," customers ",[54,1052,1053],{"class":79},"SET",[54,1055,1056],{"class":79}," status",[54,1058,207],{"class":79},[54,1060,1061],{"class":833}," 'vip'\n",[54,1063,1064,1066,1068,1070,1072,1074,1077,1079,1081],{"class":56,"line":420},[54,1065,423],{"class":79},[54,1067,924],{"class":82},[54,1069,969],{"class":79},[54,1071,560],{"class":82},[54,1073,24],{"class":79},[54,1075,1076],{"class":82}," customer_id ",[54,1078,182],{"class":79},[54,1080,996],{"class":79},[54,1082,1083],{"class":82},");\n",[37,1085,1087],{"id":1086},"data-modifying-ctes-postgresql","Data-Modifying CTEs (PostgreSQL)",[14,1089,1090,1091,1095,1096,1099],{},"PostgreSQL lets DML statements ",[1092,1093,1094],"em",{},"be"," CTEs, with ",[18,1097,1098],{},"RETURNING"," feeding subsequent CTEs — atomic multi-step operations:",[42,1101,1102],{"language":44},[46,1103,1105],{"className":48,"code":1104,"language":44,"meta":50,"style":50},"-- Atomic \"move\": DELETE from orders, INSERT into archive, all in one statement.\n-- The DELETE's RETURNING rows feed the INSERT. Both run in the same transaction.\n-- Note: data-modifying CTEs see the PRE-statement snapshot — they don't see\n-- each other's changes within the same statement (only RETURNING rows are visible).\nWITH moved AS (\n  DELETE FROM orders WHERE ordered_on \u003C '2023-01-01'\n  RETURNING *\n)\nINSERT INTO orders_archive SELECT * FROM moved;\n\n-- Multi-table atomic operation: insert customer, use RETURNING id for their first order.\nWITH new_customer AS (\n  INSERT INTO customers (name, email)\n  VALUES ('Alice', 'alice@example.com')\n  RETURNING id\n)\nINSERT INTO orders (customer_id, amount, ordered_on)\nSELECT id, 100.00, CURRENT_DATE\nFROM new_customer;\n",[18,1106,1107,1112,1117,1122,1127,1138,1155,1163,1167,1183,1187,1192,1203,1216,1233,1238,1242,1249,1266],{"__ignoreMap":50},[54,1108,1109],{"class":56,"line":57},[54,1110,1111],{"class":60},"-- Atomic \"move\": DELETE from orders, INSERT into archive, all in one statement.\n",[54,1113,1114],{"class":56,"line":64},[54,1115,1116],{"class":60},"-- The DELETE's RETURNING rows feed the INSERT. Both run in the same transaction.\n",[54,1118,1119],{"class":56,"line":70},[54,1120,1121],{"class":60},"-- Note: data-modifying CTEs see the PRE-statement snapshot — they don't see\n",[54,1123,1124],{"class":56,"line":76},[54,1125,1126],{"class":60},"-- each other's changes within the same statement (only RETURNING rows are visible).\n",[54,1128,1129,1131,1134,1136],{"class":56,"line":92},[54,1130,20],{"class":79},[54,1132,1133],{"class":82}," moved ",[54,1135,86],{"class":79},[54,1137,89],{"class":82},[54,1139,1140,1143,1145,1147,1149,1151,1153],{"class":56,"line":113},[54,1141,1142],{"class":79},"  DELETE",[54,1144,414],{"class":79},[54,1146,929],{"class":82},[54,1148,423],{"class":79},[54,1150,934],{"class":82},[54,1152,937],{"class":79},[54,1154,940],{"class":833},[54,1156,1157,1160],{"class":56,"line":122},[54,1158,1159],{"class":82},"  RETURNING ",[54,1161,1162],{"class":79},"*\n",[54,1164,1165],{"class":56,"line":131},[54,1166,151],{"class":82},[54,1168,1169,1171,1174,1176,1178,1180],{"class":56,"line":148},[54,1170,949],{"class":79},[54,1172,1173],{"class":82}," orders_archive ",[54,1175,24],{"class":79},[54,1177,411],{"class":79},[54,1179,414],{"class":79},[54,1181,1182],{"class":82}," moved;\n",[54,1184,1185],{"class":56,"line":154},[54,1186,770],{"emptyLinePlaceholder":769},[54,1188,1189],{"class":56,"line":179},[54,1190,1191],{"class":60},"-- Multi-table atomic operation: insert customer, use RETURNING id for their first order.\n",[54,1193,1194,1196,1199,1201],{"class":56,"line":188},[54,1195,20],{"class":79},[54,1197,1198],{"class":82}," new_customer ",[54,1200,86],{"class":79},[54,1202,89],{"class":82},[54,1204,1205,1208,1211,1213],{"class":56,"line":218},[54,1206,1207],{"class":79},"  INSERT INTO",[54,1209,1210],{"class":82}," customers (",[54,1212,165],{"class":79},[54,1214,1215],{"class":82},", email)\n",[54,1217,1218,1221,1223,1226,1228,1231],{"class":56,"line":388},[54,1219,1220],{"class":79},"  VALUES",[54,1222,560],{"class":82},[54,1224,1225],{"class":833},"'Alice'",[54,1227,168],{"class":82},[54,1229,1230],{"class":833},"'alice@example.com'",[54,1232,151],{"class":82},[54,1234,1235],{"class":56,"line":400},[54,1236,1237],{"class":82},"  RETURNING id\n",[54,1239,1240],{"class":56,"line":406},[54,1241,151],{"class":82},[54,1243,1244,1246],{"class":56,"line":420},[54,1245,949],{"class":79},[54,1247,1248],{"class":82}," orders (customer_id, amount, ordered_on)\n",[54,1250,1251,1253,1255,1258,1260,1263],{"class":56,"line":434},[54,1252,24],{"class":79},[54,1254,801],{"class":82},[54,1256,1257],{"class":101},"100",[54,1259,162],{"class":82},[54,1261,1262],{"class":101},"00",[54,1264,1265],{"class":82},", CURRENT_DATE\n",[54,1267,1269,1271],{"class":56,"line":1268},19,[54,1270,182],{"class":79},[54,1272,1273],{"class":82}," new_customer;\n",[37,1275,1277],{"id":1276},"complex-implementation-multi-stage-etl-pipeline","Complex Implementation: Multi-Stage ETL Pipeline",[42,1279,1280],{"language":44},[46,1281,1283],{"className":48,"code":1282,"language":44,"meta":50,"style":50},"-- Production ETL: stage → dedup → aggregate → upsert.\n-- Each CTE is a named stage in the pipeline. All run in one atomic statement.\nWITH\n  -- Stage 1: Read raw events from staging table, filter invalid rows.\n  staged AS (\n    SELECT\n      event_id,\n      customer_id,\n      product_id,\n      amount,\n      event_ts,\n      source                                  -- track provenance for debugging\n    FROM raw_events_staging\n    WHERE event_ts >= '2024-01-01'\n      AND customer_id IS NOT NULL             -- drop rows with NULL FK (would poison later joins)\n      AND amount > 0                          -- drop negative\u002Fzero amounts (data quality)\n  ),\n\n  -- Stage 2: Deduplicate by event_id (keep latest by event_ts).\n  -- ROW_NUMBER partitioned by event_id, ordered by event_ts DESC → rn=1 is the latest.\n  deduped AS (\n    SELECT *\n    FROM (\n      SELECT\n        *,\n        ROW_NUMBER() OVER (PARTITION BY event_id ORDER BY event_ts DESC) AS rn\n      FROM staged\n    ) ranked\n    WHERE rn = 1                               -- only the latest version of each event\n  ),\n\n  -- Stage 3: Aggregate to daily customer-product totals.\n  aggregated AS (\n    SELECT\n      customer_id,\n      product_id,\n      DATE(event_ts) AS event_date,\n      SUM(amount) AS daily_total,\n      COUNT(*) AS event_count\n    FROM deduped\n    GROUP BY customer_id, product_id, DATE(event_ts)\n  ),\n\n  -- Stage 4: Upsert into the fact table (PostgreSQL ON CONFLICT).\n  -- INSERT new rows; on conflict, UPDATE the daily_total and event_count.\n  upserted AS (\n    INSERT INTO fact_daily_sales (customer_id, product_id, event_date, daily_total, event_count)\n    SELECT customer_id, product_id, event_date, daily_total, event_count\n    FROM aggregated\n    ON CONFLICT (customer_id, product_id, event_date)\n    DO UPDATE SET\n      daily_total = EXCLUDED.daily_total,\n      event_count = EXCLUDED.event_count,\n      updated_at = CURRENT_TIMESTAMP\n    RETURNING id, customer_id, product_id, event_date\n  )\n\n-- Final: log what was upserted (for audit\u002Fmonitoring).\nSELECT COUNT(*) AS rows_upserted, COUNT(DISTINCT customer_id) AS customers_affected\nFROM upserted;\n",[18,1284,1285,1290,1295,1299,1304,1313,1318,1323,1328,1333,1338,1343,1351,1358,1372,1385,1399,1403,1407,1412,1418,1428,1436,1443,1449,1457,1493,1502,1508,1525,1530,1535,1541,1551,1556,1561,1566,1580,1593,1611,1619,1633,1638,1643,1649,1655,1665,1674,1682,1690,1699,1710,1728,1745,1756,1762,1767,1772,1778,1813],{"__ignoreMap":50},[54,1286,1287],{"class":56,"line":57},[54,1288,1289],{"class":60},"-- Production ETL: stage → dedup → aggregate → upsert.\n",[54,1291,1292],{"class":56,"line":64},[54,1293,1294],{"class":60},"-- Each CTE is a named stage in the pipeline. All run in one atomic statement.\n",[54,1296,1297],{"class":56,"line":70},[54,1298,263],{"class":79},[54,1300,1301],{"class":56,"line":76},[54,1302,1303],{"class":60},"  -- Stage 1: Read raw events from staging table, filter invalid rows.\n",[54,1305,1306,1309,1311],{"class":56,"line":92},[54,1307,1308],{"class":82},"  staged ",[54,1310,86],{"class":79},[54,1312,89],{"class":82},[54,1314,1315],{"class":56,"line":113},[54,1316,1317],{"class":79},"    SELECT\n",[54,1319,1320],{"class":56,"line":122},[54,1321,1322],{"class":82},"      event_id,\n",[54,1324,1325],{"class":56,"line":131},[54,1326,1327],{"class":82},"      customer_id,\n",[54,1329,1330],{"class":56,"line":148},[54,1331,1332],{"class":82},"      product_id,\n",[54,1334,1335],{"class":56,"line":154},[54,1336,1337],{"class":82},"      amount,\n",[54,1339,1340],{"class":56,"line":179},[54,1341,1342],{"class":82},"      event_ts,\n",[54,1344,1345,1348],{"class":56,"line":188},[54,1346,1347],{"class":82},"      source                                  ",[54,1349,1350],{"class":60},"-- track provenance for debugging\n",[54,1352,1353,1355],{"class":56,"line":218},[54,1354,292],{"class":79},[54,1356,1357],{"class":82}," raw_events_staging\n",[54,1359,1360,1363,1366,1369],{"class":56,"line":388},[54,1361,1362],{"class":79},"    WHERE",[54,1364,1365],{"class":82}," event_ts ",[54,1367,1368],{"class":79},">=",[54,1370,1371],{"class":833}," '2024-01-01'\n",[54,1373,1374,1377,1379,1382],{"class":56,"line":400},[54,1375,1376],{"class":79},"      AND",[54,1378,1076],{"class":82},[54,1380,1381],{"class":79},"IS NOT NULL",[54,1383,1384],{"class":60},"             -- drop rows with NULL FK (would poison later joins)\n",[54,1386,1387,1389,1392,1394,1396],{"class":56,"line":406},[54,1388,1376],{"class":79},[54,1390,1391],{"class":82}," amount ",[54,1393,142],{"class":79},[54,1395,758],{"class":101},[54,1397,1398],{"class":60},"                          -- drop negative\u002Fzero amounts (data quality)\n",[54,1400,1401],{"class":56,"line":420},[54,1402,306],{"class":82},[54,1404,1405],{"class":56,"line":434},[54,1406,770],{"emptyLinePlaceholder":769},[54,1408,1409],{"class":56,"line":1268},[54,1410,1411],{"class":60},"  -- Stage 2: Deduplicate by event_id (keep latest by event_ts).\n",[54,1413,1415],{"class":56,"line":1414},20,[54,1416,1417],{"class":60},"  -- ROW_NUMBER partitioned by event_id, ordered by event_ts DESC → rn=1 is the latest.\n",[54,1419,1421,1424,1426],{"class":56,"line":1420},21,[54,1422,1423],{"class":82},"  deduped ",[54,1425,86],{"class":79},[54,1427,89],{"class":82},[54,1429,1431,1433],{"class":56,"line":1430},22,[54,1432,277],{"class":79},[54,1434,1435],{"class":79}," *\n",[54,1437,1439,1441],{"class":56,"line":1438},23,[54,1440,292],{"class":79},[54,1442,89],{"class":82},[54,1444,1446],{"class":56,"line":1445},24,[54,1447,1448],{"class":79},"      SELECT\n",[54,1450,1452,1455],{"class":56,"line":1451},25,[54,1453,1454],{"class":79},"        *",[54,1456,536],{"class":82},[54,1458,1460,1463,1466,1469,1471,1474,1477,1480,1482,1484,1486,1488,1490],{"class":56,"line":1459},26,[54,1461,1462],{"class":101},"        ROW_NUMBER",[54,1464,1465],{"class":82},"() ",[54,1467,1468],{"class":79},"OVER",[54,1470,560],{"class":82},[54,1472,1473],{"class":79},"PARTITION",[54,1475,1476],{"class":79}," BY",[54,1478,1479],{"class":82}," event_id ",[54,1481,221],{"class":79},[54,1483,1365],{"class":82},[54,1485,441],{"class":79},[54,1487,349],{"class":82},[54,1489,86],{"class":79},[54,1491,1492],{"class":82}," rn\n",[54,1494,1496,1499],{"class":56,"line":1495},27,[54,1497,1498],{"class":79},"      FROM",[54,1500,1501],{"class":82}," staged\n",[54,1503,1505],{"class":56,"line":1504},28,[54,1506,1507],{"class":82},"    ) ranked\n",[54,1509,1511,1513,1516,1519,1522],{"class":56,"line":1510},29,[54,1512,1362],{"class":79},[54,1514,1515],{"class":82}," rn ",[54,1517,1518],{"class":79},"=",[54,1520,1521],{"class":101}," 1",[54,1523,1524],{"class":60},"                               -- only the latest version of each event\n",[54,1526,1528],{"class":56,"line":1527},30,[54,1529,306],{"class":82},[54,1531,1533],{"class":56,"line":1532},31,[54,1534,770],{"emptyLinePlaceholder":769},[54,1536,1538],{"class":56,"line":1537},32,[54,1539,1540],{"class":60},"  -- Stage 3: Aggregate to daily customer-product totals.\n",[54,1542,1544,1547,1549],{"class":56,"line":1543},33,[54,1545,1546],{"class":82},"  aggregated ",[54,1548,86],{"class":79},[54,1550,89],{"class":82},[54,1552,1554],{"class":56,"line":1553},34,[54,1555,1317],{"class":79},[54,1557,1559],{"class":56,"line":1558},35,[54,1560,1327],{"class":82},[54,1562,1564],{"class":56,"line":1563},36,[54,1565,1332],{"class":82},[54,1567,1569,1572,1575,1577],{"class":56,"line":1568},37,[54,1570,1571],{"class":79},"      DATE",[54,1573,1574],{"class":82},"(event_ts) ",[54,1576,86],{"class":79},[54,1578,1579],{"class":82}," event_date,\n",[54,1581,1583,1586,1588,1590],{"class":56,"line":1582},38,[54,1584,1585],{"class":101},"      SUM",[54,1587,105],{"class":82},[54,1589,86],{"class":79},[54,1591,1592],{"class":82}," daily_total,\n",[54,1594,1596,1599,1601,1604,1606,1608],{"class":56,"line":1595},39,[54,1597,1598],{"class":101},"      COUNT",[54,1600,339],{"class":82},[54,1602,1603],{"class":79},"*",[54,1605,349],{"class":82},[54,1607,86],{"class":79},[54,1609,1610],{"class":82}," event_count\n",[54,1612,1614,1616],{"class":56,"line":1613},40,[54,1615,292],{"class":79},[54,1617,1618],{"class":82}," deduped\n",[54,1620,1622,1624,1627,1630],{"class":56,"line":1621},41,[54,1623,299],{"class":79},[54,1625,1626],{"class":82}," customer_id, product_id, ",[54,1628,1629],{"class":79},"DATE",[54,1631,1632],{"class":82},"(event_ts)\n",[54,1634,1636],{"class":56,"line":1635},42,[54,1637,306],{"class":82},[54,1639,1641],{"class":56,"line":1640},43,[54,1642,770],{"emptyLinePlaceholder":769},[54,1644,1646],{"class":56,"line":1645},44,[54,1647,1648],{"class":60},"  -- Stage 4: Upsert into the fact table (PostgreSQL ON CONFLICT).\n",[54,1650,1652],{"class":56,"line":1651},45,[54,1653,1654],{"class":60},"  -- INSERT new rows; on conflict, UPDATE the daily_total and event_count.\n",[54,1656,1658,1661,1663],{"class":56,"line":1657},46,[54,1659,1660],{"class":82},"  upserted ",[54,1662,86],{"class":79},[54,1664,89],{"class":82},[54,1666,1668,1671],{"class":56,"line":1667},47,[54,1669,1670],{"class":79},"    INSERT INTO",[54,1672,1673],{"class":82}," fact_daily_sales (customer_id, product_id, event_date, daily_total, event_count)\n",[54,1675,1677,1679],{"class":56,"line":1676},48,[54,1678,277],{"class":79},[54,1680,1681],{"class":82}," customer_id, product_id, event_date, daily_total, event_count\n",[54,1683,1685,1687],{"class":56,"line":1684},49,[54,1686,292],{"class":79},[54,1688,1689],{"class":82}," aggregated\n",[54,1691,1693,1696],{"class":56,"line":1692},50,[54,1694,1695],{"class":79},"    ON",[54,1697,1698],{"class":82}," CONFLICT (customer_id, product_id, event_date)\n",[54,1700,1702,1705,1707],{"class":56,"line":1701},51,[54,1703,1704],{"class":82},"    DO ",[54,1706,31],{"class":79},[54,1708,1709],{"class":79}," SET\n",[54,1711,1713,1716,1718,1721,1723,1726],{"class":56,"line":1712},52,[54,1714,1715],{"class":82},"      daily_total ",[54,1717,1518],{"class":79},[54,1719,1720],{"class":101}," EXCLUDED",[54,1722,162],{"class":82},[54,1724,1725],{"class":101},"daily_total",[54,1727,536],{"class":82},[54,1729,1731,1734,1736,1738,1740,1743],{"class":56,"line":1730},53,[54,1732,1733],{"class":82},"      event_count ",[54,1735,1518],{"class":79},[54,1737,1720],{"class":101},[54,1739,162],{"class":82},[54,1741,1742],{"class":101},"event_count",[54,1744,536],{"class":82},[54,1746,1748,1751,1753],{"class":56,"line":1747},54,[54,1749,1750],{"class":82},"      updated_at ",[54,1752,1518],{"class":79},[54,1754,1755],{"class":82}," CURRENT_TIMESTAMP\n",[54,1757,1759],{"class":56,"line":1758},55,[54,1760,1761],{"class":82},"    RETURNING id, customer_id, product_id, event_date\n",[54,1763,1765],{"class":56,"line":1764},56,[54,1766,403],{"class":82},[54,1768,1770],{"class":56,"line":1769},57,[54,1771,770],{"emptyLinePlaceholder":769},[54,1773,1775],{"class":56,"line":1774},58,[54,1776,1777],{"class":60},"-- Final: log what was upserted (for audit\u002Fmonitoring).\n",[54,1779,1781,1783,1786,1788,1790,1792,1794,1797,1800,1802,1805,1808,1810],{"class":56,"line":1780},59,[54,1782,24],{"class":79},[54,1784,1785],{"class":101}," COUNT",[54,1787,339],{"class":82},[54,1789,1603],{"class":79},[54,1791,349],{"class":82},[54,1793,86],{"class":79},[54,1795,1796],{"class":82}," rows_upserted, ",[54,1798,1799],{"class":101},"COUNT",[54,1801,339],{"class":82},[54,1803,1804],{"class":79},"DISTINCT",[54,1806,1807],{"class":82}," customer_id) ",[54,1809,86],{"class":79},[54,1811,1812],{"class":82}," customers_affected\n",[54,1814,1816,1818],{"class":56,"line":1815},60,[54,1817,182],{"class":79},[54,1819,1820],{"class":82}," upserted;\n",[37,1822,1824],{"id":1823},"anti-pattern-deeply-nested-subqueries-vs-flat-cte-pipeline","Anti-Pattern: Deeply Nested Subqueries vs Flat CTE Pipeline",[1826,1827,1829],"h3",{"id":1828},"wrong-way-nested-subqueries","❌ Wrong Way — Nested Subqueries",[42,1831,1832],{"language":44},[46,1833,1835],{"className":48,"code":1834,"language":44,"meta":50,"style":50},"-- Three levels of nesting: unreadable, unmaintainable, can't inspect intermediate stages.\nSELECT city, AVG(customer_total) AS avg_spend\nFROM (\n  SELECT c.city, c.id, SUM(o.amount) AS customer_total\n  FROM (\n    SELECT * FROM customers WHERE status = 'active'\n  ) c\n  JOIN orders o ON c.id = o.customer_id\n  GROUP BY c.city, c.id\n) per_customer\nGROUP BY city\nHAVING AVG(customer_total) > (\n  SELECT AVG(total) FROM (\n    SELECT SUM(amount) AS total FROM orders GROUP BY customer_id\n  ) avg_per_customer\n);\n",[18,1836,1837,1842,1859,1865,1905,1911,1930,1935,1960,1979,1984,1992,2005,2017,2038,2043],{"__ignoreMap":50},[54,1838,1839],{"class":56,"line":57},[54,1840,1841],{"class":60},"-- Three levels of nesting: unreadable, unmaintainable, can't inspect intermediate stages.\n",[54,1843,1844,1846,1849,1851,1854,1856],{"class":56,"line":64},[54,1845,24],{"class":79},[54,1847,1848],{"class":82}," city, ",[54,1850,336],{"class":101},[54,1852,1853],{"class":82},"(customer_total) ",[54,1855,86],{"class":79},[54,1857,1858],{"class":82}," avg_spend\n",[54,1860,1861,1863],{"class":56,"line":70},[54,1862,182],{"class":79},[54,1864,89],{"class":82},[54,1866,1867,1869,1871,1873,1875,1877,1880,1882,1884,1886,1888,1890,1893,1895,1898,1900,1902],{"class":56,"line":76},[54,1868,95],{"class":79},[54,1870,159],{"class":101},[54,1872,162],{"class":82},[54,1874,331],{"class":101},[54,1876,168],{"class":82},[54,1878,1879],{"class":101},"c",[54,1881,162],{"class":82},[54,1883,204],{"class":101},[54,1885,168],{"class":82},[54,1887,102],{"class":101},[54,1889,339],{"class":82},[54,1891,1892],{"class":101},"o",[54,1894,162],{"class":82},[54,1896,1897],{"class":101},"amount",[54,1899,349],{"class":82},[54,1901,86],{"class":79},[54,1903,1904],{"class":82}," customer_total\n",[54,1906,1907,1909],{"class":56,"line":92},[54,1908,116],{"class":79},[54,1910,89],{"class":82},[54,1912,1913,1915,1917,1919,1921,1923,1925,1927],{"class":56,"line":113},[54,1914,277],{"class":79},[54,1916,411],{"class":79},[54,1918,414],{"class":79},[54,1920,1050],{"class":82},[54,1922,423],{"class":79},[54,1924,1056],{"class":79},[54,1926,207],{"class":79},[54,1928,1929],{"class":833}," 'active'\n",[54,1931,1932],{"class":56,"line":122},[54,1933,1934],{"class":82},"  ) c\n",[54,1936,1937,1940,1943,1945,1947,1949,1951,1953,1956,1958],{"class":56,"line":131},[54,1938,1939],{"class":79},"  JOIN",[54,1941,1942],{"class":82}," orders o ",[54,1944,197],{"class":79},[54,1946,159],{"class":101},[54,1948,162],{"class":82},[54,1950,204],{"class":101},[54,1952,207],{"class":79},[54,1954,1955],{"class":101}," o",[54,1957,162],{"class":82},[54,1959,215],{"class":101},[54,1961,1962,1964,1966,1968,1970,1972,1974,1976],{"class":56,"line":148},[54,1963,125],{"class":79},[54,1965,159],{"class":101},[54,1967,162],{"class":82},[54,1969,331],{"class":101},[54,1971,168],{"class":82},[54,1973,1879],{"class":101},[54,1975,162],{"class":82},[54,1977,1978],{"class":101},"id\n",[54,1980,1981],{"class":56,"line":154},[54,1982,1983],{"class":82},") per_customer\n",[54,1985,1986,1989],{"class":56,"line":179},[54,1987,1988],{"class":79},"GROUP BY",[54,1990,1991],{"class":82}," city\n",[54,1993,1994,1997,1999,2001,2003],{"class":56,"line":188},[54,1995,1996],{"class":79},"HAVING",[54,1998,565],{"class":101},[54,2000,1853],{"class":82},[54,2002,142],{"class":79},[54,2004,89],{"class":82},[54,2006,2007,2009,2011,2013,2015],{"class":56,"line":218},[54,2008,95],{"class":79},[54,2010,565],{"class":101},[54,2012,568],{"class":82},[54,2014,182],{"class":79},[54,2016,89],{"class":82},[54,2018,2019,2021,2023,2025,2027,2030,2032,2034,2036],{"class":56,"line":388},[54,2020,277],{"class":79},[54,2022,137],{"class":101},[54,2024,105],{"class":82},[54,2026,86],{"class":79},[54,2028,2029],{"class":82}," total ",[54,2031,182],{"class":79},[54,2033,929],{"class":82},[54,2035,1988],{"class":79},[54,2037,128],{"class":82},[54,2039,2040],{"class":56,"line":400},[54,2041,2042],{"class":82},"  ) avg_per_customer\n",[54,2044,2045],{"class":56,"line":406},[54,2046,1083],{"class":82},[1826,2048,2050],{"id":2049},"right-way-flat-cte-pipeline","✅ Right Way — Flat CTE Pipeline",[42,2052,2053],{"language":44},[46,2054,2056],{"className":48,"code":2055,"language":44,"meta":50,"style":50},"-- Each stage is named and inspectable. Comment out the final SELECT and\n-- replace with 'SELECT * FROM aggregated' to debug any intermediate stage.\nWITH active_customers AS (\n  SELECT * FROM customers WHERE status = 'active'\n),\ncustomer_totals AS (\n  SELECT c.city, c.id, SUM(o.amount) AS customer_total\n  FROM active_customers c\n  JOIN orders o ON c.id = o.customer_id\n  GROUP BY c.city, c.id\n),\noverall_avg AS (\n  SELECT AVG(customer_total) AS avg_total FROM customer_totals\n)\nSELECT c.city, AVG(c.customer_total) AS avg_spend\nFROM customer_totals c\nCROSS JOIN overall_avg oa\nGROUP BY c.city\nHAVING AVG(c.customer_total) > oa.avg_total\nORDER BY avg_spend DESC;\n",[18,2057,2058,2063,2068,2079,2097,2102,2111,2147,2154,2176,2194,2198,2207,2225,2229,2258,2265,2273,2283,2309],{"__ignoreMap":50},[54,2059,2060],{"class":56,"line":57},[54,2061,2062],{"class":60},"-- Each stage is named and inspectable. Comment out the final SELECT and\n",[54,2064,2065],{"class":56,"line":64},[54,2066,2067],{"class":60},"-- replace with 'SELECT * FROM aggregated' to debug any intermediate stage.\n",[54,2069,2070,2072,2075,2077],{"class":56,"line":70},[54,2071,20],{"class":79},[54,2073,2074],{"class":82}," active_customers ",[54,2076,86],{"class":79},[54,2078,89],{"class":82},[54,2080,2081,2083,2085,2087,2089,2091,2093,2095],{"class":56,"line":76},[54,2082,95],{"class":79},[54,2084,411],{"class":79},[54,2086,414],{"class":79},[54,2088,1050],{"class":82},[54,2090,423],{"class":79},[54,2092,1056],{"class":79},[54,2094,207],{"class":79},[54,2096,1929],{"class":833},[54,2098,2099],{"class":56,"line":92},[54,2100,2101],{"class":82},"),\n",[54,2103,2104,2107,2109],{"class":56,"line":113},[54,2105,2106],{"class":82},"customer_totals ",[54,2108,86],{"class":79},[54,2110,89],{"class":82},[54,2112,2113,2115,2117,2119,2121,2123,2125,2127,2129,2131,2133,2135,2137,2139,2141,2143,2145],{"class":56,"line":122},[54,2114,95],{"class":79},[54,2116,159],{"class":101},[54,2118,162],{"class":82},[54,2120,331],{"class":101},[54,2122,168],{"class":82},[54,2124,1879],{"class":101},[54,2126,162],{"class":82},[54,2128,204],{"class":101},[54,2130,168],{"class":82},[54,2132,102],{"class":101},[54,2134,339],{"class":82},[54,2136,1892],{"class":101},[54,2138,162],{"class":82},[54,2140,1897],{"class":101},[54,2142,349],{"class":82},[54,2144,86],{"class":79},[54,2146,1904],{"class":82},[54,2148,2149,2151],{"class":56,"line":131},[54,2150,116],{"class":79},[54,2152,2153],{"class":82}," active_customers c\n",[54,2155,2156,2158,2160,2162,2164,2166,2168,2170,2172,2174],{"class":56,"line":148},[54,2157,1939],{"class":79},[54,2159,1942],{"class":82},[54,2161,197],{"class":79},[54,2163,159],{"class":101},[54,2165,162],{"class":82},[54,2167,204],{"class":101},[54,2169,207],{"class":79},[54,2171,1955],{"class":101},[54,2173,162],{"class":82},[54,2175,215],{"class":101},[54,2177,2178,2180,2182,2184,2186,2188,2190,2192],{"class":56,"line":154},[54,2179,125],{"class":79},[54,2181,159],{"class":101},[54,2183,162],{"class":82},[54,2185,331],{"class":101},[54,2187,168],{"class":82},[54,2189,1879],{"class":101},[54,2191,162],{"class":82},[54,2193,1978],{"class":101},[54,2195,2196],{"class":56,"line":179},[54,2197,2101],{"class":82},[54,2199,2200,2203,2205],{"class":56,"line":188},[54,2201,2202],{"class":82},"overall_avg ",[54,2204,86],{"class":79},[54,2206,89],{"class":82},[54,2208,2209,2211,2213,2215,2217,2220,2222],{"class":56,"line":218},[54,2210,95],{"class":79},[54,2212,565],{"class":101},[54,2214,1853],{"class":82},[54,2216,86],{"class":79},[54,2218,2219],{"class":82}," avg_total ",[54,2221,182],{"class":79},[54,2223,2224],{"class":82}," customer_totals\n",[54,2226,2227],{"class":56,"line":388},[54,2228,151],{"class":82},[54,2230,2231,2233,2235,2237,2239,2241,2243,2245,2247,2249,2252,2254,2256],{"class":56,"line":400},[54,2232,24],{"class":79},[54,2234,159],{"class":101},[54,2236,162],{"class":82},[54,2238,331],{"class":101},[54,2240,168],{"class":82},[54,2242,336],{"class":101},[54,2244,339],{"class":82},[54,2246,1879],{"class":101},[54,2248,162],{"class":82},[54,2250,2251],{"class":101},"customer_total",[54,2253,349],{"class":82},[54,2255,86],{"class":79},[54,2257,1858],{"class":82},[54,2259,2260,2262],{"class":56,"line":406},[54,2261,182],{"class":79},[54,2263,2264],{"class":82}," customer_totals c\n",[54,2266,2267,2270],{"class":56,"line":420},[54,2268,2269],{"class":79},"CROSS JOIN",[54,2271,2272],{"class":82}," overall_avg oa\n",[54,2274,2275,2277,2279,2281],{"class":56,"line":434},[54,2276,1988],{"class":79},[54,2278,159],{"class":101},[54,2280,162],{"class":82},[54,2282,397],{"class":101},[54,2284,2285,2287,2289,2291,2293,2295,2297,2299,2301,2304,2306],{"class":56,"line":1268},[54,2286,1996],{"class":79},[54,2288,565],{"class":101},[54,2290,339],{"class":82},[54,2292,1879],{"class":101},[54,2294,162],{"class":82},[54,2296,2251],{"class":101},[54,2298,349],{"class":82},[54,2300,142],{"class":79},[54,2302,2303],{"class":101}," oa",[54,2305,162],{"class":82},[54,2307,2308],{"class":101},"avg_total\n",[54,2310,2311,2313,2316,2318],{"class":56,"line":1414},[54,2312,221],{"class":79},[54,2314,2315],{"class":82}," avg_spend ",[54,2317,441],{"class":79},[54,2319,234],{"class":82},[37,2321,2323],{"id":2322},"ctes-vs-derived-tables-vs-subqueries","CTEs vs Derived Tables vs Subqueries",[2325,2326,2327,2346],"table",{},[2328,2329,2330],"thead",{},[2331,2332,2333,2337,2340,2343],"tr",{},[2334,2335,2336],"th",{},"Feature",[2334,2338,2339],{},"CTE",[2334,2341,2342],{},"Derived table",[2334,2344,2345],{},"Subquery",[2347,2348,2349,2364,2378,2389,2405,2417],"tbody",{},[2331,2350,2351,2355,2358,2361],{},[2352,2353,2354],"td",{},"Named",[2352,2356,2357],{},"✅",[2352,2359,2360],{},"✅ (alias)",[2352,2362,2363],{},"inline",[2331,2365,2366,2369,2372,2375],{},[2352,2367,2368],{},"Reusable in same query",[2352,2370,2371],{},"✅ (multiple refs)",[2352,2373,2374],{},"❌ (one use)",[2352,2376,2377],{},"❌",[2331,2379,2380,2383,2385,2387],{},[2352,2381,2382],{},"Can reference earlier CTEs",[2352,2384,2357],{},[2352,2386,2377],{},[2352,2388,2377],{},[2331,2390,2391,2394,2401,2403],{},[2352,2392,2393],{},"Recursion",[2352,2395,2396,2397,2400],{},"✅ (",[18,2398,2399],{},"WITH RECURSIVE",")",[2352,2402,2377],{},[2352,2404,2377],{},[2331,2406,2407,2410,2413,2415],{},[2352,2408,2409],{},"Readability for multi-stage",[2352,2411,2412],{},"✅✅",[2352,2414,2357],{},[2352,2416,2377],{},[2331,2418,2419,2422,2425,2427],{},[2352,2420,2421],{},"Inlined by optimizer (PG 12+)",[2352,2423,2424],{},"✅ (default)",[2352,2426,2357],{},[2352,2428,2357],{},[37,2430,2432],{"id":2431},"tips-tricks","💡 Tips & Tricks",[846,2434,2435,2464,2480,2485,2494,2507],{},[849,2436,2437,2440,2441,2444,2445,168,2448,168,2451,2454,2455,168,2458,168,2460,2463],{},[452,2438,2439],{},"Idiom"," — name CTEs by ",[452,2442,2443],{},"what they compute",", not by the table they scan. ",[18,2446,2447],{},"big_orders",[18,2449,2450],{},"customer_totals",[18,2452,2453],{},"city_avg"," are good; ",[18,2456,2457],{},"cte1",[18,2459,720],{},[18,2461,2462],{},"temp"," are bad. The name is the documentation.",[849,2465,2466,2469,2470,2472,2473,2476,2477,2479],{},[452,2467,2468],{},"Performance"," — in PostgreSQL 12+, default (inlined) CTEs perform like derived tables — no penalty. But ",[18,2471,675],{}," + single reference may ",[1092,2474,2475],{},"prevent"," predicate pushdown and slow the query. Use ",[18,2478,675],{}," deliberately (expensive + reused), not reflexively.",[849,2481,2482,2484],{},[452,2483,2439],{}," — chain CTEs as a pipeline (each reads the prior). This top-to-bottom flow matches how you'd explain the query in English. Define them flat at the top, in dependency order — don't nest CTEs inside CTEs.",[849,2486,2487,2489,2490,2493],{},[452,2488,2439],{}," — use data-modifying CTEs for atomic multi-table operations. ",[18,2491,2492],{},"WITH ins AS (INSERT ... RETURNING), sel AS (SELECT ... FROM ins)"," keeps everything in one transaction — no partial state on failure.",[849,2495,2496,2499,2500,2502,2503,2506],{},[452,2497,2498],{},"Debug"," — to debug a multi-CTE pipeline, comment out the final ",[18,2501,24],{}," and replace it with ",[18,2504,2505],{},"SELECT * FROM some_cte"," to inspect an intermediate stage — much easier than unwinding nested subqueries.",[849,2508,2509,2512,2513,2515,2516,25,2518,2521,2522,2525],{},[452,2510,2511],{},"Portability"," — ",[18,2514,20],{}," is standard SQL (PostgreSQL, MySQL 8.0+, SQLite 3.8+, SQL Server, Oracle). ",[18,2517,675],{},[18,2519,2520],{},"NOT MATERIALIZED"," hints are PostgreSQL-specific. Data-modifying CTEs are PostgreSQL-specific (SQL Server uses ",[18,2523,2524],{},"OUTPUT"," differently).",[37,2527,2529],{"id":2528},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[846,2531,2532,2541,2558,2571,2580,2588,2613,2634,2650,2663],{},[849,2533,2534,2537,2538,2540],{},[452,2535,2536],{},"CTE materialization in PG12+ (inlining)"," — by default, non-recursive CTEs are inlined (folded into the main query). This is usually good (predicate pushdown), but if you relied on pre-PG12 fence behavior, queries may change performance characteristics. Use ",[18,2539,675],{}," to restore the fence.",[849,2542,2543,2546,2547,2550,2551,2553,2554,2557],{},[452,2544,2545],{},"CTE referencing order"," — a CTE can reference CTEs defined ",[1092,2548,2549],{},"before"," it in the same ",[18,2552,20],{},", but not ",[1092,2555,2556],{},"after"," (no forward references). Define CTEs in dependency order.",[849,2559,2560,2563,2564,2567,2568,2570],{},[452,2561,2562],{},"CTE name shadows tables"," — a CTE named ",[18,2565,2566],{},"orders"," shadows the real ",[18,2569,2566],{}," table in the main query. Legal but confusing — avoid reusing table names as CTE names.",[849,2572,2573,2512,2576,2579],{},[452,2574,2575],{},"A CTE is only valid for one statement",[18,2577,2578],{},"WITH ... SELECT ..."," — the CTE doesn't persist to the next query. For cross-query reuse, use a temp table or a view.",[849,2581,2582,2587],{},[452,2583,2584,2586],{},[18,2585,675],{}," + single reference can hurt"," — materializing prevents predicate pushdown. If the outer query filters heavily, inlining lets the filter reach the CTE's tables; materializing computes the full CTE first. Measure before forcing.",[849,2589,2590,2593,2594,168,2596,2599,2600,2602,2603,2606,2607,2609,2610,2612],{},[452,2591,2592],{},"Data-modifying CTEs see a snapshot"," — in ",[18,2595,2492],{},[18,2597,2598],{},"sel"," sees the ",[18,2601,1098],{}," rows, but a ",[1092,2604,2605],{},"separate"," CTE reading the same table the ",[18,2608,28],{}," targets sees the pre-",[18,2611,28],{}," state. All data-modifying CTEs execute against the same snapshot.",[849,2614,2615,2623,2624,2627,2628,2630,2631,2633],{},[452,2616,2617,2618,25,2620,2622],{},"CTE in ",[18,2619,31],{},[18,2621,34],{}," can't target the CTE's own table"," — you can't ",[18,2625,2626],{},"WITH t AS (SELECT ...) DELETE FROM t",". The CTE is read-only. You can reference a CTE in the ",[18,2629,423],{}," of a ",[18,2632,34],{}," from a real table.",[849,2635,2636,2512,2643,2646,2647,2649],{},[452,2637,2638,2639,2642],{},"Recursive CTEs require the ",[18,2640,2641],{},"RECURSIVE"," keyword",[18,2644,2645],{},"WITH RECURSIVE ..."," — forgetting ",[18,2648,2641],{}," on a self-referencing CTE is a syntax error. See chapter 19.",[849,2651,2652,2655,2656,2658,2659,2662],{},[452,2653,2654],{},"Column names from a CTE"," — a CTE's columns are named by its ",[18,2657,24],{},". If an expression has no alias, the column name is implementation-defined (PostgreSQL uses ",[18,2660,2661],{},"?column?","). Always alias expressions in CTEs you'll reference by name.",[849,2664,2665,2668,2669,2671,2672,2674],{},[452,2666,2667],{},"Scope of CTE names"," — a CTE name is visible only within the ",[18,2670,20],{}," statement that defines it. It's not visible in subqueries outside the ",[18,2673,20],{},", in functions, or in subsequent statements.",[37,2676,2678],{"id":2677},"spot-the-bug","🧠 Spot the Bug",[14,2680,2681,2682,2685,2686,2688],{},"A developer writes this data-modifying CTE and expects to see the newly inserted rows in the ",[18,2683,2684],{},"audit_log"," CTE. But ",[18,2687,2684],{}," returns zero rows. Why?",[42,2690,2691],{"language":44},[46,2692,2694],{"className":48,"code":2693,"language":44,"meta":50,"style":50},"WITH\n  inserted AS (\n    INSERT INTO customers (name, email)\n    VALUES ('Bob', 'bob@example.com')\n    RETURNING id, name\n  ),\n  audit_log AS (\n    -- Intent: log that a new customer was added by reading the customers table.\n    SELECT id, name FROM customers WHERE name = 'Bob'\n  )\nSELECT * FROM audit_log;\n",[18,2695,2696,2700,2709,2719,2736,2744,2748,2757,2762,2783,2787],{"__ignoreMap":50},[54,2697,2698],{"class":56,"line":57},[54,2699,263],{"class":79},[54,2701,2702,2705,2707],{"class":56,"line":64},[54,2703,2704],{"class":82},"  inserted ",[54,2706,86],{"class":79},[54,2708,89],{"class":82},[54,2710,2711,2713,2715,2717],{"class":56,"line":70},[54,2712,1670],{"class":79},[54,2714,1210],{"class":82},[54,2716,165],{"class":79},[54,2718,1215],{"class":82},[54,2720,2721,2724,2726,2729,2731,2734],{"class":56,"line":76},[54,2722,2723],{"class":79},"    VALUES",[54,2725,560],{"class":82},[54,2727,2728],{"class":833},"'Bob'",[54,2730,168],{"class":82},[54,2732,2733],{"class":833},"'bob@example.com'",[54,2735,151],{"class":82},[54,2737,2738,2741],{"class":56,"line":92},[54,2739,2740],{"class":82},"    RETURNING id, ",[54,2742,2743],{"class":79},"name\n",[54,2745,2746],{"class":56,"line":113},[54,2747,306],{"class":82},[54,2749,2750,2753,2755],{"class":56,"line":122},[54,2751,2752],{"class":82},"  audit_log ",[54,2754,86],{"class":79},[54,2756,89],{"class":82},[54,2758,2759],{"class":56,"line":131},[54,2760,2761],{"class":60},"    -- Intent: log that a new customer was added by reading the customers table.\n",[54,2763,2764,2766,2768,2770,2772,2774,2776,2778,2780],{"class":56,"line":148},[54,2765,277],{"class":79},[54,2767,801],{"class":82},[54,2769,165],{"class":79},[54,2771,414],{"class":79},[54,2773,1050],{"class":82},[54,2775,423],{"class":79},[54,2777,827],{"class":79},[54,2779,207],{"class":79},[54,2781,2782],{"class":833}," 'Bob'\n",[54,2784,2785],{"class":56,"line":154},[54,2786,403],{"class":82},[54,2788,2789,2791,2793,2795],{"class":56,"line":179},[54,2790,24],{"class":79},[54,2792,411],{"class":79},[54,2794,414],{"class":79},[54,2796,2797],{"class":82}," audit_log;\n",[2799,2800,2801,2805,2839,2854,2947],"details",{},[2802,2803,2804],"summary",{},"Answer",[14,2806,2807,2809,2810,2813,2814,2816,2817,2820,2821,2824,2825,2599,2827,2829,2830,2832,2833,2835,2836,2838],{},[18,2808,2684],{}," reads the ",[18,2811,2812],{},"customers"," ",[452,2815,2325],{},", not the ",[18,2818,2819],{},"inserted"," CTE. Data-modifying CTEs all execute against the ",[452,2822,2823],{},"same pre-statement snapshot"," — so ",[18,2826,2684],{},[18,2828,2812],{}," table as it was ",[1092,2831,2549],{}," the ",[18,2834,28],{}," ran. The newly inserted row doesn't exist in ",[18,2837,2684],{},"'s snapshot, so it returns zero rows.",[14,2840,2841,2842,2844,2845,2847,2848,2850,2851,2853],{},"To see the inserted rows, ",[18,2843,2684],{}," must reference the ",[18,2846,2819],{}," CTE (which exposes the ",[18,2849,1098],{}," rows), not the ",[18,2852,2812],{}," table:",[42,2855,2856],{"language":44},[46,2857,2859],{"className":48,"code":2858,"language":44,"meta":50,"style":50},"WITH\n  inserted AS (\n    INSERT INTO customers (name, email)\n    VALUES ('Bob', 'bob@example.com')\n    RETURNING id, name\n  ),\n  audit_log AS (\n    -- ✅ Read from the 'inserted' CTE, which exposes RETURNING rows.\n    SELECT id, name FROM inserted\n  )\nSELECT * FROM audit_log;\n",[18,2860,2861,2865,2873,2883,2897,2903,2907,2915,2920,2933,2937],{"__ignoreMap":50},[54,2862,2863],{"class":56,"line":57},[54,2864,263],{"class":79},[54,2866,2867,2869,2871],{"class":56,"line":64},[54,2868,2704],{"class":82},[54,2870,86],{"class":79},[54,2872,89],{"class":82},[54,2874,2875,2877,2879,2881],{"class":56,"line":70},[54,2876,1670],{"class":79},[54,2878,1210],{"class":82},[54,2880,165],{"class":79},[54,2882,1215],{"class":82},[54,2884,2885,2887,2889,2891,2893,2895],{"class":56,"line":76},[54,2886,2723],{"class":79},[54,2888,560],{"class":82},[54,2890,2728],{"class":833},[54,2892,168],{"class":82},[54,2894,2733],{"class":833},[54,2896,151],{"class":82},[54,2898,2899,2901],{"class":56,"line":92},[54,2900,2740],{"class":82},[54,2902,2743],{"class":79},[54,2904,2905],{"class":56,"line":113},[54,2906,306],{"class":82},[54,2908,2909,2911,2913],{"class":56,"line":122},[54,2910,2752],{"class":82},[54,2912,86],{"class":79},[54,2914,89],{"class":82},[54,2916,2917],{"class":56,"line":131},[54,2918,2919],{"class":60},"    -- ✅ Read from the 'inserted' CTE, which exposes RETURNING rows.\n",[54,2921,2922,2924,2926,2928,2930],{"class":56,"line":148},[54,2923,277],{"class":79},[54,2925,801],{"class":82},[54,2927,165],{"class":79},[54,2929,414],{"class":79},[54,2931,2932],{"class":82}," inserted\n",[54,2934,2935],{"class":56,"line":154},[54,2936,403],{"class":82},[54,2938,2939,2941,2943,2945],{"class":56,"line":179},[54,2940,24],{"class":79},[54,2942,411],{"class":79},[54,2944,414],{"class":79},[54,2946,2797],{"class":82},[14,2948,2949,2952,2953,2956,2957,2959],{},[452,2950,2951],{},"The lesson",": within a data-modifying CTE statement, CTEs that read a table see the ",[1092,2954,2955],{},"pre-statement"," snapshot. Only ",[18,2958,1098],{}," rows (accessed via the CTE name) reflect the DML changes. Never read the target table of a data-modifying CTE expecting to see that CTE's writes.",[37,2961,2962],{"id":2802},"Summary",[14,2964,2965],{},"You can now write single and chained CTEs, reuse them multiple times, drive DML from them, and choose between inlining and materialization — turning multi-stage queries into readable, atomic pipelines. Next: set operations for combining result sets.",[2967,2968,2969],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}",{"title":50,"searchDepth":64,"depth":64,"links":2971},[2972,2973,2974,2975,2976,2977,2978,2979,2983,2984,2985,2986,2987],{"id":39,"depth":64,"text":40},{"id":237,"depth":64,"text":238},{"id":446,"depth":64,"text":447},{"id":662,"depth":64,"text":663},{"id":890,"depth":64,"text":891},{"id":1086,"depth":64,"text":1087},{"id":1276,"depth":64,"text":1277},{"id":1823,"depth":64,"text":1824,"children":2980},[2981,2982],{"id":1828,"depth":70,"text":1829},{"id":2049,"depth":70,"text":2050},{"id":2322,"depth":64,"text":2323},{"id":2431,"depth":64,"text":2432},{"id":2528,"depth":64,"text":2529},{"id":2677,"depth":64,"text":2678},{"id":2802,"depth":64,"text":2962},"A Common Table Expression (CTE) is a named temporary result set defined with WITH, usable within a single SELECT\u002FINSERT\u002FUPDATE\u002FDELETE. CTEs make complex queries readable by breaking them into named stages — and enable recursion (chapter 19).","md",{},"\u002Fsql\u002F07-common-table-expressions",{"title":5,"description":2988},"sql\u002F07-common-table-expressions","awmhWDySnXet-oS4gyKMYO9_xX9eDL01aaDBLApCPS8",1789924654367]