[{"data":1,"prerenderedAt":2697},["ShallowReactive",2],{"article-alternates":3,"article-\u002Fru\u002Fdata\u002Fcohort-tablesql-architecture-production-retention":13},{"i18nKey":4,"paths":5},"data-007-2026-05",{"de":6,"en":7,"es":8,"fr":9,"it":10,"ru":11,"tr":12},"\u002Fde\u002Fdata\u002Fcohort-tabellenstruktur","\u002Fen\u002Fdata\u002Fcohort-table-architecture-scaling-retention-analysis-production","\u002Fes\u002Fdata\u002Farquitectura-tabla-cohort","\u002Ffr\u002Fdata\u002Fcohort-table-architecture","\u002Fit\u002Fdata\u002Farchitettura-tabella-cohort-retention-production","\u002Fru\u002Fdata\u002Fcohort-tablesql-architecture-production-retention","\u002Ftr\u002Fdata\u002Fcohort-tablo-mimarisi-retention-analizinin-productionda-olceklenmesi",{"_path":11,"_dir":14,"_draft":15,"_partial":15,"_locale":16,"title":17,"description":18,"publishedAt":19,"modifiedAt":19,"category":14,"i18nKey":4,"tags":20,"readingTime":26,"author":27,"body":28,"_type":2691,"_id":2692,"_source":2693,"_file":2694,"_stem":2695,"_extension":2696},"data",false,"","Cohort Table Architecture: Scaling Retention Analysis in Production","Production-ready BigQuery architecture for cohort analysis across millions of users using materialized views, partitioning, and query cost optimization.","2026-05-22",[21,22,23,24,25],"cohort-analysis","bigquery","materialized-views","retention-engineering","query-optimization",8,"Roibase",{"type":29,"children":30,"toc":2681},"root",[31,39,46,51,531,544,550,578,670,675,681,686,848,860,865,1195,1200,1206,1211,1253,1258,2140,2156,2162,2167,2204,2209,2296,2301,2307,2312,2326,2331,2421,2426,2432,2437,2470,2484,2490,2495,2528,2533,2657,2662,2675],{"type":32,"tag":33,"props":34,"children":35},"element","p",{},[36],{"type":37,"value":38},"text","Retention analysis is one of the most powerful methods for understanding user behavior. But at real scale — millions of events per day, hundreds of thousands of users — naive SQL queries timeout in 30 seconds or exhaust slot capacity. Sustainable cohort analysis in production requires optimizing table architecture for the query engine. In this article, we show how to scale cohort tables on BigQuery using materialized views, partitioning, and incremental refresh strategies.",{"type":32,"tag":40,"props":41,"children":43},"h2",{"id":42},"why-naive-cohort-queries-fail",[44],{"type":37,"value":45},"Why Naive Cohort Queries Fail",{"type":32,"tag":33,"props":47,"children":48},{},[49],{"type":37,"value":50},"Classical cohort analysis works on this logic: find the user's first activity date (cohort_date), calculate subsequent activities as \"Day N\" relative to that date, aggregate retention rates by group. The following SQL is logically correct but breaks in production:",{"type":32,"tag":52,"props":53,"children":57},"pre",{"className":54,"code":55,"language":56,"meta":16,"style":16},"language-sql shiki shiki-themes github-dark","WITH first_event AS (\n  SELECT user_id, MIN(DATE(event_timestamp)) AS cohort_date\n  FROM `project.dataset.events`\n  GROUP BY user_id\n),\ndaily_activity AS (\n  SELECT e.user_id, DATE(e.event_timestamp) AS activity_date\n  FROM `project.dataset.events` e\n  GROUP BY 1,2\n)\nSELECT \n  f.cohort_date,\n  DATE_DIFF(d.activity_date, f.cohort_date, DAY) AS day_n,\n  COUNT(DISTINCT d.user_id) AS retained_users\nFROM first_event f\nJOIN daily_activity d USING(user_id)\nGROUP BY 1,2\nORDER BY 1,2;\n","sql",[58],{"type":32,"tag":59,"props":60,"children":61},"code",{"__ignoreMap":16},[62,90,134,149,163,172,189,253,270,293,302,316,339,401,445,459,483,504],{"type":32,"tag":63,"props":64,"children":67},"span",{"class":65,"line":66},"line",1,[68,74,80,85],{"type":32,"tag":63,"props":69,"children":71},{"style":70},"--shiki-default:#F97583",[72],{"type":37,"value":73},"WITH",{"type":32,"tag":63,"props":75,"children":77},{"style":76},"--shiki-default:#E1E4E8",[78],{"type":37,"value":79}," first_event ",{"type":32,"tag":63,"props":81,"children":82},{"style":70},[83],{"type":37,"value":84},"AS",{"type":32,"tag":63,"props":86,"children":87},{"style":76},[88],{"type":37,"value":89}," (\n",{"type":32,"tag":63,"props":91,"children":93},{"class":65,"line":92},2,[94,99,104,110,115,120,125,129],{"type":32,"tag":63,"props":95,"children":96},{"style":70},[97],{"type":37,"value":98},"  SELECT",{"type":32,"tag":63,"props":100,"children":101},{"style":76},[102],{"type":37,"value":103}," user_id, ",{"type":32,"tag":63,"props":105,"children":107},{"style":106},"--shiki-default:#79B8FF",[108],{"type":37,"value":109},"MIN",{"type":32,"tag":63,"props":111,"children":112},{"style":76},[113],{"type":37,"value":114},"(",{"type":32,"tag":63,"props":116,"children":117},{"style":70},[118],{"type":37,"value":119},"DATE",{"type":32,"tag":63,"props":121,"children":122},{"style":76},[123],{"type":37,"value":124},"(event_timestamp)) ",{"type":32,"tag":63,"props":126,"children":127},{"style":70},[128],{"type":37,"value":84},{"type":32,"tag":63,"props":130,"children":131},{"style":76},[132],{"type":37,"value":133}," cohort_date\n",{"type":32,"tag":63,"props":135,"children":137},{"class":65,"line":136},3,[138,143],{"type":32,"tag":63,"props":139,"children":140},{"style":70},[141],{"type":37,"value":142},"  FROM",{"type":32,"tag":63,"props":144,"children":146},{"style":145},"--shiki-default:#9ECBFF",[147],{"type":37,"value":148}," `project.dataset.events`\n",{"type":32,"tag":63,"props":150,"children":152},{"class":65,"line":151},4,[153,158],{"type":32,"tag":63,"props":154,"children":155},{"style":70},[156],{"type":37,"value":157},"  GROUP BY",{"type":32,"tag":63,"props":159,"children":160},{"style":76},[161],{"type":37,"value":162}," user_id\n",{"type":32,"tag":63,"props":164,"children":166},{"class":65,"line":165},5,[167],{"type":32,"tag":63,"props":168,"children":169},{"style":76},[170],{"type":37,"value":171},"),\n",{"type":32,"tag":63,"props":173,"children":175},{"class":65,"line":174},6,[176,181,185],{"type":32,"tag":63,"props":177,"children":178},{"style":76},[179],{"type":37,"value":180},"daily_activity ",{"type":32,"tag":63,"props":182,"children":183},{"style":70},[184],{"type":37,"value":84},{"type":32,"tag":63,"props":186,"children":187},{"style":76},[188],{"type":37,"value":89},{"type":32,"tag":63,"props":190,"children":192},{"class":65,"line":191},7,[193,197,202,207,212,217,221,225,230,234,239,244,248],{"type":32,"tag":63,"props":194,"children":195},{"style":70},[196],{"type":37,"value":98},{"type":32,"tag":63,"props":198,"children":199},{"style":106},[200],{"type":37,"value":201}," e",{"type":32,"tag":63,"props":203,"children":204},{"style":76},[205],{"type":37,"value":206},".",{"type":32,"tag":63,"props":208,"children":209},{"style":106},[210],{"type":37,"value":211},"user_id",{"type":32,"tag":63,"props":213,"children":214},{"style":76},[215],{"type":37,"value":216},", ",{"type":32,"tag":63,"props":218,"children":219},{"style":70},[220],{"type":37,"value":119},{"type":32,"tag":63,"props":222,"children":223},{"style":76},[224],{"type":37,"value":114},{"type":32,"tag":63,"props":226,"children":227},{"style":106},[228],{"type":37,"value":229},"e",{"type":32,"tag":63,"props":231,"children":232},{"style":76},[233],{"type":37,"value":206},{"type":32,"tag":63,"props":235,"children":236},{"style":106},[237],{"type":37,"value":238},"event_timestamp",{"type":32,"tag":63,"props":240,"children":241},{"style":76},[242],{"type":37,"value":243},") ",{"type":32,"tag":63,"props":245,"children":246},{"style":70},[247],{"type":37,"value":84},{"type":32,"tag":63,"props":249,"children":250},{"style":76},[251],{"type":37,"value":252}," activity_date\n",{"type":32,"tag":63,"props":254,"children":255},{"class":65,"line":26},[256,260,265],{"type":32,"tag":63,"props":257,"children":258},{"style":70},[259],{"type":37,"value":142},{"type":32,"tag":63,"props":261,"children":262},{"style":145},[263],{"type":37,"value":264}," `project.dataset.events`",{"type":32,"tag":63,"props":266,"children":267},{"style":76},[268],{"type":37,"value":269}," e\n",{"type":32,"tag":63,"props":271,"children":273},{"class":65,"line":272},9,[274,278,283,288],{"type":32,"tag":63,"props":275,"children":276},{"style":70},[277],{"type":37,"value":157},{"type":32,"tag":63,"props":279,"children":280},{"style":106},[281],{"type":37,"value":282}," 1",{"type":32,"tag":63,"props":284,"children":285},{"style":76},[286],{"type":37,"value":287},",",{"type":32,"tag":63,"props":289,"children":290},{"style":106},[291],{"type":37,"value":292},"2\n",{"type":32,"tag":63,"props":294,"children":296},{"class":65,"line":295},10,[297],{"type":32,"tag":63,"props":298,"children":299},{"style":76},[300],{"type":37,"value":301},")\n",{"type":32,"tag":63,"props":303,"children":305},{"class":65,"line":304},11,[306,311],{"type":32,"tag":63,"props":307,"children":308},{"style":70},[309],{"type":37,"value":310},"SELECT",{"type":32,"tag":63,"props":312,"children":313},{"style":76},[314],{"type":37,"value":315}," \n",{"type":32,"tag":63,"props":317,"children":319},{"class":65,"line":318},12,[320,325,329,334],{"type":32,"tag":63,"props":321,"children":322},{"style":106},[323],{"type":37,"value":324},"  f",{"type":32,"tag":63,"props":326,"children":327},{"style":76},[328],{"type":37,"value":206},{"type":32,"tag":63,"props":330,"children":331},{"style":106},[332],{"type":37,"value":333},"cohort_date",{"type":32,"tag":63,"props":335,"children":336},{"style":76},[337],{"type":37,"value":338},",\n",{"type":32,"tag":63,"props":340,"children":342},{"class":65,"line":341},13,[343,348,353,357,362,366,371,375,379,383,388,392,396],{"type":32,"tag":63,"props":344,"children":345},{"style":76},[346],{"type":37,"value":347},"  DATE_DIFF(",{"type":32,"tag":63,"props":349,"children":350},{"style":106},[351],{"type":37,"value":352},"d",{"type":32,"tag":63,"props":354,"children":355},{"style":76},[356],{"type":37,"value":206},{"type":32,"tag":63,"props":358,"children":359},{"style":106},[360],{"type":37,"value":361},"activity_date",{"type":32,"tag":63,"props":363,"children":364},{"style":76},[365],{"type":37,"value":216},{"type":32,"tag":63,"props":367,"children":368},{"style":106},[369],{"type":37,"value":370},"f",{"type":32,"tag":63,"props":372,"children":373},{"style":76},[374],{"type":37,"value":206},{"type":32,"tag":63,"props":376,"children":377},{"style":106},[378],{"type":37,"value":333},{"type":32,"tag":63,"props":380,"children":381},{"style":76},[382],{"type":37,"value":216},{"type":32,"tag":63,"props":384,"children":385},{"style":70},[386],{"type":37,"value":387},"DAY",{"type":32,"tag":63,"props":389,"children":390},{"style":76},[391],{"type":37,"value":243},{"type":32,"tag":63,"props":393,"children":394},{"style":70},[395],{"type":37,"value":84},{"type":32,"tag":63,"props":397,"children":398},{"style":76},[399],{"type":37,"value":400}," day_n,\n",{"type":32,"tag":63,"props":402,"children":404},{"class":65,"line":403},14,[405,410,414,419,424,428,432,436,440],{"type":32,"tag":63,"props":406,"children":407},{"style":106},[408],{"type":37,"value":409},"  COUNT",{"type":32,"tag":63,"props":411,"children":412},{"style":76},[413],{"type":37,"value":114},{"type":32,"tag":63,"props":415,"children":416},{"style":70},[417],{"type":37,"value":418},"DISTINCT",{"type":32,"tag":63,"props":420,"children":421},{"style":106},[422],{"type":37,"value":423}," d",{"type":32,"tag":63,"props":425,"children":426},{"style":76},[427],{"type":37,"value":206},{"type":32,"tag":63,"props":429,"children":430},{"style":106},[431],{"type":37,"value":211},{"type":32,"tag":63,"props":433,"children":434},{"style":76},[435],{"type":37,"value":243},{"type":32,"tag":63,"props":437,"children":438},{"style":70},[439],{"type":37,"value":84},{"type":32,"tag":63,"props":441,"children":442},{"style":76},[443],{"type":37,"value":444}," retained_users\n",{"type":32,"tag":63,"props":446,"children":448},{"class":65,"line":447},15,[449,454],{"type":32,"tag":63,"props":450,"children":451},{"style":70},[452],{"type":37,"value":453},"FROM",{"type":32,"tag":63,"props":455,"children":456},{"style":76},[457],{"type":37,"value":458}," first_event f\n",{"type":32,"tag":63,"props":460,"children":462},{"class":65,"line":461},16,[463,468,473,478],{"type":32,"tag":63,"props":464,"children":465},{"style":70},[466],{"type":37,"value":467},"JOIN",{"type":32,"tag":63,"props":469,"children":470},{"style":76},[471],{"type":37,"value":472}," daily_activity d ",{"type":32,"tag":63,"props":474,"children":475},{"style":70},[476],{"type":37,"value":477},"USING",{"type":32,"tag":63,"props":479,"children":480},{"style":76},[481],{"type":37,"value":482},"(user_id)\n",{"type":32,"tag":63,"props":484,"children":486},{"class":65,"line":485},17,[487,492,496,500],{"type":32,"tag":63,"props":488,"children":489},{"style":70},[490],{"type":37,"value":491},"GROUP BY",{"type":32,"tag":63,"props":493,"children":494},{"style":106},[495],{"type":37,"value":282},{"type":32,"tag":63,"props":497,"children":498},{"style":76},[499],{"type":37,"value":287},{"type":32,"tag":63,"props":501,"children":502},{"style":106},[503],{"type":37,"value":292},{"type":32,"tag":63,"props":505,"children":507},{"class":65,"line":506},18,[508,513,517,521,526],{"type":32,"tag":63,"props":509,"children":510},{"style":70},[511],{"type":37,"value":512},"ORDER BY",{"type":32,"tag":63,"props":514,"children":515},{"style":106},[516],{"type":37,"value":282},{"type":32,"tag":63,"props":518,"children":519},{"style":76},[520],{"type":37,"value":287},{"type":32,"tag":63,"props":522,"children":523},{"style":106},[524],{"type":37,"value":525},"2",{"type":32,"tag":63,"props":527,"children":528},{"style":76},[529],{"type":37,"value":530},";\n",{"type":32,"tag":33,"props":532,"children":533},{},[534,536,542],{"type":37,"value":535},"Two major problems here: (1) the ",{"type":32,"tag":59,"props":537,"children":539},{"className":538},[],[540],{"type":37,"value":541},"events",{"type":37,"value":543}," table is fully scanned each time — no partition pruning, (2) for each cohort_date, all user activities are joined — Cartesian explosion risk. At 100M events, this query processes 400GB and completes in 2 minutes, but refreshing daily makes this unsustainable. Your BigQuery bill 10x's by month-end.",{"type":32,"tag":40,"props":545,"children":547},{"id":546},"filtering-load-with-partitioned-base-table",[548],{"type":37,"value":549},"Filtering Load with Partitioned Base Table",{"type":32,"tag":33,"props":551,"children":552},{},[553,555,560,562,568,570,576],{"type":37,"value":554},"First step: partition the ",{"type":32,"tag":59,"props":556,"children":558},{"className":557},[],[559],{"type":37,"value":541},{"type":37,"value":561}," table on ",{"type":32,"tag":59,"props":563,"children":565},{"className":564},[],[566],{"type":37,"value":567},"DATE(event_timestamp)",{"type":37,"value":569},". This ensures only relevant partitions are scanned when the query includes ",{"type":32,"tag":59,"props":571,"children":573},{"className":572},[],[574],{"type":37,"value":575},"WHERE DATE(event_timestamp) BETWEEN X AND Y",{"type":37,"value":577},":",{"type":32,"tag":52,"props":579,"children":581},{"className":54,"code":580,"language":56,"meta":16,"style":16},"CREATE TABLE `project.dataset.events`\nPARTITION BY DATE(event_timestamp)\nCLUSTER BY user_id, event_name\nAS SELECT * FROM ...;\n",[582],{"type":32,"tag":59,"props":583,"children":584},{"__ignoreMap":16},[585,602,625,643],{"type":32,"tag":63,"props":586,"children":587},{"class":65,"line":66},[588,593,598],{"type":32,"tag":63,"props":589,"children":590},{"style":70},[591],{"type":37,"value":592},"CREATE",{"type":32,"tag":63,"props":594,"children":595},{"style":70},[596],{"type":37,"value":597}," TABLE",{"type":32,"tag":63,"props":599,"children":600},{"style":145},[601],{"type":37,"value":148},{"type":32,"tag":63,"props":603,"children":604},{"class":65,"line":92},[605,610,615,620],{"type":32,"tag":63,"props":606,"children":607},{"style":70},[608],{"type":37,"value":609},"PARTITION",{"type":32,"tag":63,"props":611,"children":612},{"style":70},[613],{"type":37,"value":614}," BY",{"type":32,"tag":63,"props":616,"children":617},{"style":70},[618],{"type":37,"value":619}," DATE",{"type":32,"tag":63,"props":621,"children":622},{"style":76},[623],{"type":37,"value":624},"(event_timestamp)\n",{"type":32,"tag":63,"props":626,"children":627},{"class":65,"line":136},[628,633,638],{"type":32,"tag":63,"props":629,"children":630},{"style":76},[631],{"type":37,"value":632},"CLUSTER ",{"type":32,"tag":63,"props":634,"children":635},{"style":70},[636],{"type":37,"value":637},"BY",{"type":32,"tag":63,"props":639,"children":640},{"style":76},[641],{"type":37,"value":642}," user_id, event_name\n",{"type":32,"tag":63,"props":644,"children":645},{"class":65,"line":151},[646,650,655,660,665],{"type":32,"tag":63,"props":647,"children":648},{"style":70},[649],{"type":37,"value":84},{"type":32,"tag":63,"props":651,"children":652},{"style":70},[653],{"type":37,"value":654}," SELECT",{"type":32,"tag":63,"props":656,"children":657},{"style":70},[658],{"type":37,"value":659}," *",{"type":32,"tag":63,"props":661,"children":662},{"style":70},[663],{"type":37,"value":664}," FROM",{"type":32,"tag":63,"props":666,"children":667},{"style":76},[668],{"type":37,"value":669}," ...;\n",{"type":32,"tag":33,"props":671,"children":672},{},[673],{"type":37,"value":674},"Adding clustering on (user_id, event_name) keeps a single user's events in adjacent physical blocks — join performance gains 30-50%. But this alone isn't enough; cohort logic re-runs in every query. This is where materialized views enter.",{"type":32,"tag":40,"props":676,"children":678},{"id":677},"materialized-views-incremental-cohort-table",[679],{"type":37,"value":680},"Materialized Views: Incremental Cohort Table",{"type":32,"tag":33,"props":682,"children":683},{},[684],{"type":37,"value":685},"BigQuery materialized views store query results physically and auto-refresh when base tables change. For cohort analysis, use this structure:",{"type":32,"tag":52,"props":687,"children":689},{"className":54,"code":688,"language":56,"meta":16,"style":16},"CREATE MATERIALIZED VIEW `project.dataset.user_cohorts`\nPARTITION BY cohort_date\nCLUSTER BY user_id\nAS\nSELECT \n  user_id,\n  MIN(DATE(event_timestamp)) AS cohort_date,\n  COUNT(*) AS first_day_events\nFROM `project.dataset.events`\nGROUP BY user_id;\n",[690],{"type":32,"tag":59,"props":691,"children":692},{"__ignoreMap":16},[693,710,725,740,748,759,767,796,825,836],{"type":32,"tag":63,"props":694,"children":695},{"class":65,"line":66},[696,700,705],{"type":32,"tag":63,"props":697,"children":698},{"style":70},[699],{"type":37,"value":592},{"type":32,"tag":63,"props":701,"children":702},{"style":76},[703],{"type":37,"value":704}," MATERIALIZED VIEW ",{"type":32,"tag":63,"props":706,"children":707},{"style":145},[708],{"type":37,"value":709},"`project.dataset.user_cohorts`\n",{"type":32,"tag":63,"props":711,"children":712},{"class":65,"line":92},[713,717,721],{"type":32,"tag":63,"props":714,"children":715},{"style":70},[716],{"type":37,"value":609},{"type":32,"tag":63,"props":718,"children":719},{"style":70},[720],{"type":37,"value":614},{"type":32,"tag":63,"props":722,"children":723},{"style":76},[724],{"type":37,"value":133},{"type":32,"tag":63,"props":726,"children":727},{"class":65,"line":136},[728,732,736],{"type":32,"tag":63,"props":729,"children":730},{"style":76},[731],{"type":37,"value":632},{"type":32,"tag":63,"props":733,"children":734},{"style":70},[735],{"type":37,"value":637},{"type":32,"tag":63,"props":737,"children":738},{"style":76},[739],{"type":37,"value":162},{"type":32,"tag":63,"props":741,"children":742},{"class":65,"line":151},[743],{"type":32,"tag":63,"props":744,"children":745},{"style":70},[746],{"type":37,"value":747},"AS\n",{"type":32,"tag":63,"props":749,"children":750},{"class":65,"line":165},[751,755],{"type":32,"tag":63,"props":752,"children":753},{"style":70},[754],{"type":37,"value":310},{"type":32,"tag":63,"props":756,"children":757},{"style":76},[758],{"type":37,"value":315},{"type":32,"tag":63,"props":760,"children":761},{"class":65,"line":174},[762],{"type":32,"tag":63,"props":763,"children":764},{"style":76},[765],{"type":37,"value":766},"  user_id,\n",{"type":32,"tag":63,"props":768,"children":769},{"class":65,"line":191},[770,775,779,783,787,791],{"type":32,"tag":63,"props":771,"children":772},{"style":106},[773],{"type":37,"value":774},"  MIN",{"type":32,"tag":63,"props":776,"children":777},{"style":76},[778],{"type":37,"value":114},{"type":32,"tag":63,"props":780,"children":781},{"style":70},[782],{"type":37,"value":119},{"type":32,"tag":63,"props":784,"children":785},{"style":76},[786],{"type":37,"value":124},{"type":32,"tag":63,"props":788,"children":789},{"style":70},[790],{"type":37,"value":84},{"type":32,"tag":63,"props":792,"children":793},{"style":76},[794],{"type":37,"value":795}," cohort_date,\n",{"type":32,"tag":63,"props":797,"children":798},{"class":65,"line":26},[799,803,807,812,816,820],{"type":32,"tag":63,"props":800,"children":801},{"style":106},[802],{"type":37,"value":409},{"type":32,"tag":63,"props":804,"children":805},{"style":76},[806],{"type":37,"value":114},{"type":32,"tag":63,"props":808,"children":809},{"style":70},[810],{"type":37,"value":811},"*",{"type":32,"tag":63,"props":813,"children":814},{"style":76},[815],{"type":37,"value":243},{"type":32,"tag":63,"props":817,"children":818},{"style":70},[819],{"type":37,"value":84},{"type":32,"tag":63,"props":821,"children":822},{"style":76},[823],{"type":37,"value":824}," first_day_events\n",{"type":32,"tag":63,"props":826,"children":827},{"class":65,"line":272},[828,832],{"type":32,"tag":63,"props":829,"children":830},{"style":70},[831],{"type":37,"value":453},{"type":32,"tag":63,"props":833,"children":834},{"style":145},[835],{"type":37,"value":148},{"type":32,"tag":63,"props":837,"children":838},{"class":65,"line":295},[839,843],{"type":32,"tag":63,"props":840,"children":841},{"style":70},[842],{"type":37,"value":491},{"type":32,"tag":63,"props":844,"children":845},{"style":76},[846],{"type":37,"value":847}," user_id;\n",{"type":32,"tag":33,"props":849,"children":850},{},[851,853,859],{"type":37,"value":852},"This view computes each user's first-seen date (cohort_date) once and stores it. When new events arrive, BigQuery processes only the delta — no full scan. Partitioning by cohort_date enables pruning on filters like ",{"type":32,"tag":59,"props":854,"children":856},{"className":855},[],[857],{"type":37,"value":858},"WHERE cohort_date = '2026-05-01'",{"type":37,"value":206},{"type":32,"tag":33,"props":861,"children":862},{},[863],{"type":37,"value":864},"Now the retention calculation query shrinks to:",{"type":32,"tag":52,"props":866,"children":868},{"className":54,"code":867,"language":56,"meta":16,"style":16},"SELECT \n  c.cohort_date,\n  DATE_DIFF(DATE(e.event_timestamp), c.cohort_date, DAY) AS day_n,\n  COUNT(DISTINCT e.user_id) AS retained_users\nFROM `project.dataset.user_cohorts` c\nJOIN `project.dataset.events` e \n  ON c.user_id = e.user_id \n  AND DATE(e.event_timestamp) >= c.cohort_date\nWHERE c.cohort_date BETWEEN '2026-05-01' AND '2026-05-15'\nGROUP BY 1,2;\n",[869],{"type":32,"tag":59,"props":870,"children":871},{"__ignoreMap":16},[872,883,903,968,1007,1024,1040,1082,1132,1172],{"type":32,"tag":63,"props":873,"children":874},{"class":65,"line":66},[875,879],{"type":32,"tag":63,"props":876,"children":877},{"style":70},[878],{"type":37,"value":310},{"type":32,"tag":63,"props":880,"children":881},{"style":76},[882],{"type":37,"value":315},{"type":32,"tag":63,"props":884,"children":885},{"class":65,"line":92},[886,891,895,899],{"type":32,"tag":63,"props":887,"children":888},{"style":106},[889],{"type":37,"value":890},"  c",{"type":32,"tag":63,"props":892,"children":893},{"style":76},[894],{"type":37,"value":206},{"type":32,"tag":63,"props":896,"children":897},{"style":106},[898],{"type":37,"value":333},{"type":32,"tag":63,"props":900,"children":901},{"style":76},[902],{"type":37,"value":338},{"type":32,"tag":63,"props":904,"children":905},{"class":65,"line":136},[906,910,914,918,922,926,930,935,940,944,948,952,956,960,964],{"type":32,"tag":63,"props":907,"children":908},{"style":76},[909],{"type":37,"value":347},{"type":32,"tag":63,"props":911,"children":912},{"style":70},[913],{"type":37,"value":119},{"type":32,"tag":63,"props":915,"children":916},{"style":76},[917],{"type":37,"value":114},{"type":32,"tag":63,"props":919,"children":920},{"style":106},[921],{"type":37,"value":229},{"type":32,"tag":63,"props":923,"children":924},{"style":76},[925],{"type":37,"value":206},{"type":32,"tag":63,"props":927,"children":928},{"style":106},[929],{"type":37,"value":238},{"type":32,"tag":63,"props":931,"children":932},{"style":76},[933],{"type":37,"value":934},"), ",{"type":32,"tag":63,"props":936,"children":937},{"style":106},[938],{"type":37,"value":939},"c",{"type":32,"tag":63,"props":941,"children":942},{"style":76},[943],{"type":37,"value":206},{"type":32,"tag":63,"props":945,"children":946},{"style":106},[947],{"type":37,"value":333},{"type":32,"tag":63,"props":949,"children":950},{"style":76},[951],{"type":37,"value":216},{"type":32,"tag":63,"props":953,"children":954},{"style":70},[955],{"type":37,"value":387},{"type":32,"tag":63,"props":957,"children":958},{"style":76},[959],{"type":37,"value":243},{"type":32,"tag":63,"props":961,"children":962},{"style":70},[963],{"type":37,"value":84},{"type":32,"tag":63,"props":965,"children":966},{"style":76},[967],{"type":37,"value":400},{"type":32,"tag":63,"props":969,"children":970},{"class":65,"line":151},[971,975,979,983,987,991,995,999,1003],{"type":32,"tag":63,"props":972,"children":973},{"style":106},[974],{"type":37,"value":409},{"type":32,"tag":63,"props":976,"children":977},{"style":76},[978],{"type":37,"value":114},{"type":32,"tag":63,"props":980,"children":981},{"style":70},[982],{"type":37,"value":418},{"type":32,"tag":63,"props":984,"children":985},{"style":106},[986],{"type":37,"value":201},{"type":32,"tag":63,"props":988,"children":989},{"style":76},[990],{"type":37,"value":206},{"type":32,"tag":63,"props":992,"children":993},{"style":106},[994],{"type":37,"value":211},{"type":32,"tag":63,"props":996,"children":997},{"style":76},[998],{"type":37,"value":243},{"type":32,"tag":63,"props":1000,"children":1001},{"style":70},[1002],{"type":37,"value":84},{"type":32,"tag":63,"props":1004,"children":1005},{"style":76},[1006],{"type":37,"value":444},{"type":32,"tag":63,"props":1008,"children":1009},{"class":65,"line":165},[1010,1014,1019],{"type":32,"tag":63,"props":1011,"children":1012},{"style":70},[1013],{"type":37,"value":453},{"type":32,"tag":63,"props":1015,"children":1016},{"style":145},[1017],{"type":37,"value":1018}," `project.dataset.user_cohorts`",{"type":32,"tag":63,"props":1020,"children":1021},{"style":76},[1022],{"type":37,"value":1023}," c\n",{"type":32,"tag":63,"props":1025,"children":1026},{"class":65,"line":174},[1027,1031,1035],{"type":32,"tag":63,"props":1028,"children":1029},{"style":70},[1030],{"type":37,"value":467},{"type":32,"tag":63,"props":1032,"children":1033},{"style":145},[1034],{"type":37,"value":264},{"type":32,"tag":63,"props":1036,"children":1037},{"style":76},[1038],{"type":37,"value":1039}," e \n",{"type":32,"tag":63,"props":1041,"children":1042},{"class":65,"line":191},[1043,1048,1053,1057,1061,1066,1070,1074,1078],{"type":32,"tag":63,"props":1044,"children":1045},{"style":70},[1046],{"type":37,"value":1047},"  ON",{"type":32,"tag":63,"props":1049,"children":1050},{"style":106},[1051],{"type":37,"value":1052}," c",{"type":32,"tag":63,"props":1054,"children":1055},{"style":76},[1056],{"type":37,"value":206},{"type":32,"tag":63,"props":1058,"children":1059},{"style":106},[1060],{"type":37,"value":211},{"type":32,"tag":63,"props":1062,"children":1063},{"style":70},[1064],{"type":37,"value":1065}," =",{"type":32,"tag":63,"props":1067,"children":1068},{"style":106},[1069],{"type":37,"value":201},{"type":32,"tag":63,"props":1071,"children":1072},{"style":76},[1073],{"type":37,"value":206},{"type":32,"tag":63,"props":1075,"children":1076},{"style":106},[1077],{"type":37,"value":211},{"type":32,"tag":63,"props":1079,"children":1080},{"style":76},[1081],{"type":37,"value":315},{"type":32,"tag":63,"props":1083,"children":1084},{"class":65,"line":26},[1085,1090,1094,1098,1102,1106,1110,1114,1119,1123,1127],{"type":32,"tag":63,"props":1086,"children":1087},{"style":70},[1088],{"type":37,"value":1089},"  AND",{"type":32,"tag":63,"props":1091,"children":1092},{"style":70},[1093],{"type":37,"value":619},{"type":32,"tag":63,"props":1095,"children":1096},{"style":76},[1097],{"type":37,"value":114},{"type":32,"tag":63,"props":1099,"children":1100},{"style":106},[1101],{"type":37,"value":229},{"type":32,"tag":63,"props":1103,"children":1104},{"style":76},[1105],{"type":37,"value":206},{"type":32,"tag":63,"props":1107,"children":1108},{"style":106},[1109],{"type":37,"value":238},{"type":32,"tag":63,"props":1111,"children":1112},{"style":76},[1113],{"type":37,"value":243},{"type":32,"tag":63,"props":1115,"children":1116},{"style":70},[1117],{"type":37,"value":1118},">=",{"type":32,"tag":63,"props":1120,"children":1121},{"style":106},[1122],{"type":37,"value":1052},{"type":32,"tag":63,"props":1124,"children":1125},{"style":76},[1126],{"type":37,"value":206},{"type":32,"tag":63,"props":1128,"children":1129},{"style":106},[1130],{"type":37,"value":1131},"cohort_date\n",{"type":32,"tag":63,"props":1133,"children":1134},{"class":65,"line":272},[1135,1140,1144,1148,1152,1157,1162,1167],{"type":32,"tag":63,"props":1136,"children":1137},{"style":70},[1138],{"type":37,"value":1139},"WHERE",{"type":32,"tag":63,"props":1141,"children":1142},{"style":106},[1143],{"type":37,"value":1052},{"type":32,"tag":63,"props":1145,"children":1146},{"style":76},[1147],{"type":37,"value":206},{"type":32,"tag":63,"props":1149,"children":1150},{"style":106},[1151],{"type":37,"value":333},{"type":32,"tag":63,"props":1153,"children":1154},{"style":70},[1155],{"type":37,"value":1156}," BETWEEN",{"type":32,"tag":63,"props":1158,"children":1159},{"style":145},[1160],{"type":37,"value":1161}," '2026-05-01'",{"type":32,"tag":63,"props":1163,"children":1164},{"style":70},[1165],{"type":37,"value":1166}," AND",{"type":32,"tag":63,"props":1168,"children":1169},{"style":145},[1170],{"type":37,"value":1171}," '2026-05-15'\n",{"type":32,"tag":63,"props":1173,"children":1174},{"class":65,"line":295},[1175,1179,1183,1187,1191],{"type":32,"tag":63,"props":1176,"children":1177},{"style":70},[1178],{"type":37,"value":491},{"type":32,"tag":63,"props":1180,"children":1181},{"style":106},[1182],{"type":37,"value":282},{"type":32,"tag":63,"props":1184,"children":1185},{"style":76},[1186],{"type":37,"value":287},{"type":32,"tag":63,"props":1188,"children":1189},{"style":106},[1190],{"type":37,"value":525},{"type":32,"tag":63,"props":1192,"children":1193},{"style":76},[1194],{"type":37,"value":530},{"type":32,"tag":33,"props":1196,"children":1197},{},[1198],{"type":37,"value":1199},"This query joins the materialized view instead of the base table — rows scanned drop from millions to thousands. But it still scans the daily event table. The next stage adds a pre-aggregated retention layer.",{"type":32,"tag":40,"props":1201,"children":1203},{"id":1202},"pre-aggregated-retention-table-the-final-layer",[1204],{"type":37,"value":1205},"Pre-Aggregated Retention Table: The Final Layer",{"type":32,"tag":33,"props":1207,"children":1208},{},[1209],{"type":37,"value":1210},"Cohort analysis typically examines fixed intervals — Day 0, Day 1, Day 7, Day 30 — not every single day. Using dbt, apply this logic:",{"type":32,"tag":1212,"props":1213,"children":1214},"ol",{},[1215,1229,1234],{"type":32,"tag":1216,"props":1217,"children":1218},"li",{},[1219,1221,1227],{"type":37,"value":1220},"Each day, fetch new cohorts from the ",{"type":32,"tag":59,"props":1222,"children":1224},{"className":1223},[],[1225],{"type":37,"value":1226},"user_cohorts",{"type":37,"value":1228}," view",{"type":32,"tag":1216,"props":1230,"children":1231},{},[1232],{"type":37,"value":1233},"For each cohort, compute the past 30 days of retention (after 30 days, it doesn't change)",{"type":32,"tag":1216,"props":1235,"children":1236},{},[1237,1239,1245,1247],{"type":37,"value":1238},"Write the result to ",{"type":32,"tag":59,"props":1240,"children":1242},{"className":1241},[],[1243],{"type":37,"value":1244},"cohort_retention_summary",{"type":37,"value":1246}," ",{"type":32,"tag":1248,"props":1249,"children":1250},"strong",{},[1251],{"type":37,"value":1252},"incrementally",{"type":32,"tag":33,"props":1254,"children":1255},{},[1256],{"type":37,"value":1257},"The dbt model:",{"type":32,"tag":52,"props":1259,"children":1261},{"className":54,"code":1260,"language":56,"meta":16,"style":16},"{{\n  config(\n    materialized='incremental',\n    unique_key=['cohort_date','day_n'],\n    partition_by={'field':'cohort_date','data_type':'date'},\n    cluster_by=['day_n']\n  )\n}}\n\nWITH cohorts_to_update AS (\n  SELECT DISTINCT cohort_date \n  FROM {{ ref('user_cohorts') }}\n  WHERE cohort_date >= CURRENT_DATE() - 31\n  {% if is_incremental() %}\n    AND cohort_date > (SELECT MAX(cohort_date) FROM {{ this }})\n  {% endif %}\n),\nretention_calc AS (\n  SELECT \n    c.cohort_date,\n    DATE_DIFF(DATE(e.event_timestamp), c.cohort_date, DAY) AS day_n,\n    COUNT(DISTINCT e.user_id) AS retained_users,\n    MAX(c.first_day_events) AS cohort_size\n  FROM {{ ref('user_cohorts') }} c\n  JOIN {{ source('raw','events') }} e \n    ON c.user_id = e.user_id\n  WHERE c.cohort_date IN (SELECT cohort_date FROM cohorts_to_update)\n    AND DATE(e.event_timestamp) >= c.cohort_date\n    AND DATE_DIFF(DATE(e.event_timestamp), c.cohort_date, DAY) \u003C= 30\n  GROUP BY 1,2\n)\nSELECT \n  cohort_date,\n  day_n,\n  retained_users,\n  cohort_size,\n  SAFE_DIVIDE(retained_users, cohort_size) AS retention_rate\nFROM retention_calc;\n",[1262],{"type":32,"tag":59,"props":1263,"children":1264},{"__ignoreMap":16},[1265,1273,1281,1303,1320,1374,1391,1399,1407,1416,1436,1449,1471,1503,1521,1566,1574,1581,1597,1609,1630,1695,1737,1776,1797,1830,1868,1914,1962,2033,2053,2061,2073,2082,2091,2100,2109,2127],{"type":32,"tag":63,"props":1266,"children":1267},{"class":65,"line":66},[1268],{"type":32,"tag":63,"props":1269,"children":1270},{"style":76},[1271],{"type":37,"value":1272},"{{\n",{"type":32,"tag":63,"props":1274,"children":1275},{"class":65,"line":92},[1276],{"type":32,"tag":63,"props":1277,"children":1278},{"style":76},[1279],{"type":37,"value":1280},"  config(\n",{"type":32,"tag":63,"props":1282,"children":1283},{"class":65,"line":136},[1284,1289,1294,1299],{"type":32,"tag":63,"props":1285,"children":1286},{"style":76},[1287],{"type":37,"value":1288},"    materialized",{"type":32,"tag":63,"props":1290,"children":1291},{"style":70},[1292],{"type":37,"value":1293},"=",{"type":32,"tag":63,"props":1295,"children":1296},{"style":145},[1297],{"type":37,"value":1298},"'incremental'",{"type":32,"tag":63,"props":1300,"children":1301},{"style":76},[1302],{"type":37,"value":338},{"type":32,"tag":63,"props":1304,"children":1305},{"class":65,"line":151},[1306,1311,1315],{"type":32,"tag":63,"props":1307,"children":1308},{"style":76},[1309],{"type":37,"value":1310},"    unique_key",{"type":32,"tag":63,"props":1312,"children":1313},{"style":70},[1314],{"type":37,"value":1293},{"type":32,"tag":63,"props":1316,"children":1317},{"style":76},[1318],{"type":37,"value":1319},"['cohort_date','day_n'],\n",{"type":32,"tag":63,"props":1321,"children":1322},{"class":65,"line":165},[1323,1328,1332,1337,1342,1346,1351,1355,1360,1364,1369],{"type":32,"tag":63,"props":1324,"children":1325},{"style":76},[1326],{"type":37,"value":1327},"    partition_by",{"type":32,"tag":63,"props":1329,"children":1330},{"style":70},[1331],{"type":37,"value":1293},{"type":32,"tag":63,"props":1333,"children":1334},{"style":76},[1335],{"type":37,"value":1336},"{",{"type":32,"tag":63,"props":1338,"children":1339},{"style":145},[1340],{"type":37,"value":1341},"'field'",{"type":32,"tag":63,"props":1343,"children":1344},{"style":76},[1345],{"type":37,"value":577},{"type":32,"tag":63,"props":1347,"children":1348},{"style":145},[1349],{"type":37,"value":1350},"'cohort_date'",{"type":32,"tag":63,"props":1352,"children":1353},{"style":76},[1354],{"type":37,"value":287},{"type":32,"tag":63,"props":1356,"children":1357},{"style":145},[1358],{"type":37,"value":1359},"'data_type'",{"type":32,"tag":63,"props":1361,"children":1362},{"style":76},[1363],{"type":37,"value":577},{"type":32,"tag":63,"props":1365,"children":1366},{"style":145},[1367],{"type":37,"value":1368},"'date'",{"type":32,"tag":63,"props":1370,"children":1371},{"style":76},[1372],{"type":37,"value":1373},"},\n",{"type":32,"tag":63,"props":1375,"children":1376},{"class":65,"line":174},[1377,1382,1386],{"type":32,"tag":63,"props":1378,"children":1379},{"style":76},[1380],{"type":37,"value":1381},"    cluster_by",{"type":32,"tag":63,"props":1383,"children":1384},{"style":70},[1385],{"type":37,"value":1293},{"type":32,"tag":63,"props":1387,"children":1388},{"style":76},[1389],{"type":37,"value":1390},"['day_n']\n",{"type":32,"tag":63,"props":1392,"children":1393},{"class":65,"line":191},[1394],{"type":32,"tag":63,"props":1395,"children":1396},{"style":76},[1397],{"type":37,"value":1398},"  )\n",{"type":32,"tag":63,"props":1400,"children":1401},{"class":65,"line":26},[1402],{"type":32,"tag":63,"props":1403,"children":1404},{"style":76},[1405],{"type":37,"value":1406},"}}\n",{"type":32,"tag":63,"props":1408,"children":1409},{"class":65,"line":272},[1410],{"type":32,"tag":63,"props":1411,"children":1413},{"emptyLinePlaceholder":1412},true,[1414],{"type":37,"value":1415},"\n",{"type":32,"tag":63,"props":1417,"children":1418},{"class":65,"line":295},[1419,1423,1428,1432],{"type":32,"tag":63,"props":1420,"children":1421},{"style":70},[1422],{"type":37,"value":73},{"type":32,"tag":63,"props":1424,"children":1425},{"style":76},[1426],{"type":37,"value":1427}," cohorts_to_update ",{"type":32,"tag":63,"props":1429,"children":1430},{"style":70},[1431],{"type":37,"value":84},{"type":32,"tag":63,"props":1433,"children":1434},{"style":76},[1435],{"type":37,"value":89},{"type":32,"tag":63,"props":1437,"children":1438},{"class":65,"line":304},[1439,1444],{"type":32,"tag":63,"props":1440,"children":1441},{"style":70},[1442],{"type":37,"value":1443},"  SELECT DISTINCT",{"type":32,"tag":63,"props":1445,"children":1446},{"style":76},[1447],{"type":37,"value":1448}," cohort_date \n",{"type":32,"tag":63,"props":1450,"children":1451},{"class":65,"line":318},[1452,1456,1461,1466],{"type":32,"tag":63,"props":1453,"children":1454},{"style":70},[1455],{"type":37,"value":142},{"type":32,"tag":63,"props":1457,"children":1458},{"style":76},[1459],{"type":37,"value":1460}," {{ ref(",{"type":32,"tag":63,"props":1462,"children":1463},{"style":145},[1464],{"type":37,"value":1465},"'user_cohorts'",{"type":32,"tag":63,"props":1467,"children":1468},{"style":76},[1469],{"type":37,"value":1470},") }}\n",{"type":32,"tag":63,"props":1472,"children":1473},{"class":65,"line":341},[1474,1479,1484,1488,1493,1498],{"type":32,"tag":63,"props":1475,"children":1476},{"style":70},[1477],{"type":37,"value":1478},"  WHERE",{"type":32,"tag":63,"props":1480,"children":1481},{"style":76},[1482],{"type":37,"value":1483}," cohort_date ",{"type":32,"tag":63,"props":1485,"children":1486},{"style":70},[1487],{"type":37,"value":1118},{"type":32,"tag":63,"props":1489,"children":1490},{"style":76},[1491],{"type":37,"value":1492}," CURRENT_DATE() ",{"type":32,"tag":63,"props":1494,"children":1495},{"style":70},[1496],{"type":37,"value":1497},"-",{"type":32,"tag":63,"props":1499,"children":1500},{"style":106},[1501],{"type":37,"value":1502}," 31\n",{"type":32,"tag":63,"props":1504,"children":1505},{"class":65,"line":403},[1506,1511,1516],{"type":32,"tag":63,"props":1507,"children":1508},{"style":76},[1509],{"type":37,"value":1510},"  {% ",{"type":32,"tag":63,"props":1512,"children":1513},{"style":70},[1514],{"type":37,"value":1515},"if",{"type":32,"tag":63,"props":1517,"children":1518},{"style":76},[1519],{"type":37,"value":1520}," is_incremental() %}\n",{"type":32,"tag":63,"props":1522,"children":1523},{"class":65,"line":447},[1524,1529,1533,1538,1543,1547,1552,1557,1561],{"type":32,"tag":63,"props":1525,"children":1526},{"style":70},[1527],{"type":37,"value":1528},"    AND",{"type":32,"tag":63,"props":1530,"children":1531},{"style":76},[1532],{"type":37,"value":1483},{"type":32,"tag":63,"props":1534,"children":1535},{"style":70},[1536],{"type":37,"value":1537},">",{"type":32,"tag":63,"props":1539,"children":1540},{"style":76},[1541],{"type":37,"value":1542}," (",{"type":32,"tag":63,"props":1544,"children":1545},{"style":70},[1546],{"type":37,"value":310},{"type":32,"tag":63,"props":1548,"children":1549},{"style":106},[1550],{"type":37,"value":1551}," MAX",{"type":32,"tag":63,"props":1553,"children":1554},{"style":76},[1555],{"type":37,"value":1556},"(cohort_date) ",{"type":32,"tag":63,"props":1558,"children":1559},{"style":70},[1560],{"type":37,"value":453},{"type":32,"tag":63,"props":1562,"children":1563},{"style":76},[1564],{"type":37,"value":1565}," {{ this }})\n",{"type":32,"tag":63,"props":1567,"children":1568},{"class":65,"line":461},[1569],{"type":32,"tag":63,"props":1570,"children":1571},{"style":76},[1572],{"type":37,"value":1573},"  {% endif %}\n",{"type":32,"tag":63,"props":1575,"children":1576},{"class":65,"line":485},[1577],{"type":32,"tag":63,"props":1578,"children":1579},{"style":76},[1580],{"type":37,"value":171},{"type":32,"tag":63,"props":1582,"children":1583},{"class":65,"line":506},[1584,1589,1593],{"type":32,"tag":63,"props":1585,"children":1586},{"style":76},[1587],{"type":37,"value":1588},"retention_calc ",{"type":32,"tag":63,"props":1590,"children":1591},{"style":70},[1592],{"type":37,"value":84},{"type":32,"tag":63,"props":1594,"children":1595},{"style":76},[1596],{"type":37,"value":89},{"type":32,"tag":63,"props":1598,"children":1600},{"class":65,"line":1599},19,[1601,1605],{"type":32,"tag":63,"props":1602,"children":1603},{"style":70},[1604],{"type":37,"value":98},{"type":32,"tag":63,"props":1606,"children":1607},{"style":76},[1608],{"type":37,"value":315},{"type":32,"tag":63,"props":1610,"children":1612},{"class":65,"line":1611},20,[1613,1618,1622,1626],{"type":32,"tag":63,"props":1614,"children":1615},{"style":106},[1616],{"type":37,"value":1617},"    c",{"type":32,"tag":63,"props":1619,"children":1620},{"style":76},[1621],{"type":37,"value":206},{"type":32,"tag":63,"props":1623,"children":1624},{"style":106},[1625],{"type":37,"value":333},{"type":32,"tag":63,"props":1627,"children":1628},{"style":76},[1629],{"type":37,"value":338},{"type":32,"tag":63,"props":1631,"children":1633},{"class":65,"line":1632},21,[1634,1639,1643,1647,1651,1655,1659,1663,1667,1671,1675,1679,1683,1687,1691],{"type":32,"tag":63,"props":1635,"children":1636},{"style":76},[1637],{"type":37,"value":1638},"    DATE_DIFF(",{"type":32,"tag":63,"props":1640,"children":1641},{"style":70},[1642],{"type":37,"value":119},{"type":32,"tag":63,"props":1644,"children":1645},{"style":76},[1646],{"type":37,"value":114},{"type":32,"tag":63,"props":1648,"children":1649},{"style":106},[1650],{"type":37,"value":229},{"type":32,"tag":63,"props":1652,"children":1653},{"style":76},[1654],{"type":37,"value":206},{"type":32,"tag":63,"props":1656,"children":1657},{"style":106},[1658],{"type":37,"value":238},{"type":32,"tag":63,"props":1660,"children":1661},{"style":76},[1662],{"type":37,"value":934},{"type":32,"tag":63,"props":1664,"children":1665},{"style":106},[1666],{"type":37,"value":939},{"type":32,"tag":63,"props":1668,"children":1669},{"style":76},[1670],{"type":37,"value":206},{"type":32,"tag":63,"props":1672,"children":1673},{"style":106},[1674],{"type":37,"value":333},{"type":32,"tag":63,"props":1676,"children":1677},{"style":76},[1678],{"type":37,"value":216},{"type":32,"tag":63,"props":1680,"children":1681},{"style":70},[1682],{"type":37,"value":387},{"type":32,"tag":63,"props":1684,"children":1685},{"style":76},[1686],{"type":37,"value":243},{"type":32,"tag":63,"props":1688,"children":1689},{"style":70},[1690],{"type":37,"value":84},{"type":32,"tag":63,"props":1692,"children":1693},{"style":76},[1694],{"type":37,"value":400},{"type":32,"tag":63,"props":1696,"children":1698},{"class":65,"line":1697},22,[1699,1704,1708,1712,1716,1720,1724,1728,1732],{"type":32,"tag":63,"props":1700,"children":1701},{"style":106},[1702],{"type":37,"value":1703},"    COUNT",{"type":32,"tag":63,"props":1705,"children":1706},{"style":76},[1707],{"type":37,"value":114},{"type":32,"tag":63,"props":1709,"children":1710},{"style":70},[1711],{"type":37,"value":418},{"type":32,"tag":63,"props":1713,"children":1714},{"style":106},[1715],{"type":37,"value":201},{"type":32,"tag":63,"props":1717,"children":1718},{"style":76},[1719],{"type":37,"value":206},{"type":32,"tag":63,"props":1721,"children":1722},{"style":106},[1723],{"type":37,"value":211},{"type":32,"tag":63,"props":1725,"children":1726},{"style":76},[1727],{"type":37,"value":243},{"type":32,"tag":63,"props":1729,"children":1730},{"style":70},[1731],{"type":37,"value":84},{"type":32,"tag":63,"props":1733,"children":1734},{"style":76},[1735],{"type":37,"value":1736}," retained_users,\n",{"type":32,"tag":63,"props":1738,"children":1740},{"class":65,"line":1739},23,[1741,1746,1750,1754,1758,1763,1767,1771],{"type":32,"tag":63,"props":1742,"children":1743},{"style":106},[1744],{"type":37,"value":1745},"    MAX",{"type":32,"tag":63,"props":1747,"children":1748},{"style":76},[1749],{"type":37,"value":114},{"type":32,"tag":63,"props":1751,"children":1752},{"style":106},[1753],{"type":37,"value":939},{"type":32,"tag":63,"props":1755,"children":1756},{"style":76},[1757],{"type":37,"value":206},{"type":32,"tag":63,"props":1759,"children":1760},{"style":106},[1761],{"type":37,"value":1762},"first_day_events",{"type":32,"tag":63,"props":1764,"children":1765},{"style":76},[1766],{"type":37,"value":243},{"type":32,"tag":63,"props":1768,"children":1769},{"style":70},[1770],{"type":37,"value":84},{"type":32,"tag":63,"props":1772,"children":1773},{"style":76},[1774],{"type":37,"value":1775}," cohort_size\n",{"type":32,"tag":63,"props":1777,"children":1779},{"class":65,"line":1778},24,[1780,1784,1788,1792],{"type":32,"tag":63,"props":1781,"children":1782},{"style":70},[1783],{"type":37,"value":142},{"type":32,"tag":63,"props":1785,"children":1786},{"style":76},[1787],{"type":37,"value":1460},{"type":32,"tag":63,"props":1789,"children":1790},{"style":145},[1791],{"type":37,"value":1465},{"type":32,"tag":63,"props":1793,"children":1794},{"style":76},[1795],{"type":37,"value":1796},") }} c\n",{"type":32,"tag":63,"props":1798,"children":1800},{"class":65,"line":1799},25,[1801,1806,1811,1816,1820,1825],{"type":32,"tag":63,"props":1802,"children":1803},{"style":70},[1804],{"type":37,"value":1805},"  JOIN",{"type":32,"tag":63,"props":1807,"children":1808},{"style":76},[1809],{"type":37,"value":1810}," {{ source(",{"type":32,"tag":63,"props":1812,"children":1813},{"style":145},[1814],{"type":37,"value":1815},"'raw'",{"type":32,"tag":63,"props":1817,"children":1818},{"style":76},[1819],{"type":37,"value":287},{"type":32,"tag":63,"props":1821,"children":1822},{"style":145},[1823],{"type":37,"value":1824},"'events'",{"type":32,"tag":63,"props":1826,"children":1827},{"style":76},[1828],{"type":37,"value":1829},") }} e \n",{"type":32,"tag":63,"props":1831,"children":1833},{"class":65,"line":1832},26,[1834,1839,1843,1847,1851,1855,1859,1863],{"type":32,"tag":63,"props":1835,"children":1836},{"style":70},[1837],{"type":37,"value":1838},"    ON",{"type":32,"tag":63,"props":1840,"children":1841},{"style":106},[1842],{"type":37,"value":1052},{"type":32,"tag":63,"props":1844,"children":1845},{"style":76},[1846],{"type":37,"value":206},{"type":32,"tag":63,"props":1848,"children":1849},{"style":106},[1850],{"type":37,"value":211},{"type":32,"tag":63,"props":1852,"children":1853},{"style":70},[1854],{"type":37,"value":1065},{"type":32,"tag":63,"props":1856,"children":1857},{"style":106},[1858],{"type":37,"value":201},{"type":32,"tag":63,"props":1860,"children":1861},{"style":76},[1862],{"type":37,"value":206},{"type":32,"tag":63,"props":1864,"children":1865},{"style":106},[1866],{"type":37,"value":1867},"user_id\n",{"type":32,"tag":63,"props":1869,"children":1871},{"class":65,"line":1870},27,[1872,1876,1880,1884,1888,1893,1897,1901,1905,1909],{"type":32,"tag":63,"props":1873,"children":1874},{"style":70},[1875],{"type":37,"value":1478},{"type":32,"tag":63,"props":1877,"children":1878},{"style":106},[1879],{"type":37,"value":1052},{"type":32,"tag":63,"props":1881,"children":1882},{"style":76},[1883],{"type":37,"value":206},{"type":32,"tag":63,"props":1885,"children":1886},{"style":106},[1887],{"type":37,"value":333},{"type":32,"tag":63,"props":1889,"children":1890},{"style":70},[1891],{"type":37,"value":1892}," IN",{"type":32,"tag":63,"props":1894,"children":1895},{"style":76},[1896],{"type":37,"value":1542},{"type":32,"tag":63,"props":1898,"children":1899},{"style":70},[1900],{"type":37,"value":310},{"type":32,"tag":63,"props":1902,"children":1903},{"style":76},[1904],{"type":37,"value":1483},{"type":32,"tag":63,"props":1906,"children":1907},{"style":70},[1908],{"type":37,"value":453},{"type":32,"tag":63,"props":1910,"children":1911},{"style":76},[1912],{"type":37,"value":1913}," cohorts_to_update)\n",{"type":32,"tag":63,"props":1915,"children":1917},{"class":65,"line":1916},28,[1918,1922,1926,1930,1934,1938,1942,1946,1950,1954,1958],{"type":32,"tag":63,"props":1919,"children":1920},{"style":70},[1921],{"type":37,"value":1528},{"type":32,"tag":63,"props":1923,"children":1924},{"style":70},[1925],{"type":37,"value":619},{"type":32,"tag":63,"props":1927,"children":1928},{"style":76},[1929],{"type":37,"value":114},{"type":32,"tag":63,"props":1931,"children":1932},{"style":106},[1933],{"type":37,"value":229},{"type":32,"tag":63,"props":1935,"children":1936},{"style":76},[1937],{"type":37,"value":206},{"type":32,"tag":63,"props":1939,"children":1940},{"style":106},[1941],{"type":37,"value":238},{"type":32,"tag":63,"props":1943,"children":1944},{"style":76},[1945],{"type":37,"value":243},{"type":32,"tag":63,"props":1947,"children":1948},{"style":70},[1949],{"type":37,"value":1118},{"type":32,"tag":63,"props":1951,"children":1952},{"style":106},[1953],{"type":37,"value":1052},{"type":32,"tag":63,"props":1955,"children":1956},{"style":76},[1957],{"type":37,"value":206},{"type":32,"tag":63,"props":1959,"children":1960},{"style":106},[1961],{"type":37,"value":1131},{"type":32,"tag":63,"props":1963,"children":1965},{"class":65,"line":1964},29,[1966,1970,1975,1979,1983,1987,1991,1995,1999,2003,2007,2011,2015,2019,2023,2028],{"type":32,"tag":63,"props":1967,"children":1968},{"style":70},[1969],{"type":37,"value":1528},{"type":32,"tag":63,"props":1971,"children":1972},{"style":76},[1973],{"type":37,"value":1974}," DATE_DIFF(",{"type":32,"tag":63,"props":1976,"children":1977},{"style":70},[1978],{"type":37,"value":119},{"type":32,"tag":63,"props":1980,"children":1981},{"style":76},[1982],{"type":37,"value":114},{"type":32,"tag":63,"props":1984,"children":1985},{"style":106},[1986],{"type":37,"value":229},{"type":32,"tag":63,"props":1988,"children":1989},{"style":76},[1990],{"type":37,"value":206},{"type":32,"tag":63,"props":1992,"children":1993},{"style":106},[1994],{"type":37,"value":238},{"type":32,"tag":63,"props":1996,"children":1997},{"style":76},[1998],{"type":37,"value":934},{"type":32,"tag":63,"props":2000,"children":2001},{"style":106},[2002],{"type":37,"value":939},{"type":32,"tag":63,"props":2004,"children":2005},{"style":76},[2006],{"type":37,"value":206},{"type":32,"tag":63,"props":2008,"children":2009},{"style":106},[2010],{"type":37,"value":333},{"type":32,"tag":63,"props":2012,"children":2013},{"style":76},[2014],{"type":37,"value":216},{"type":32,"tag":63,"props":2016,"children":2017},{"style":70},[2018],{"type":37,"value":387},{"type":32,"tag":63,"props":2020,"children":2021},{"style":76},[2022],{"type":37,"value":243},{"type":32,"tag":63,"props":2024,"children":2025},{"style":70},[2026],{"type":37,"value":2027},"\u003C=",{"type":32,"tag":63,"props":2029,"children":2030},{"style":106},[2031],{"type":37,"value":2032}," 30\n",{"type":32,"tag":63,"props":2034,"children":2036},{"class":65,"line":2035},30,[2037,2041,2045,2049],{"type":32,"tag":63,"props":2038,"children":2039},{"style":70},[2040],{"type":37,"value":157},{"type":32,"tag":63,"props":2042,"children":2043},{"style":106},[2044],{"type":37,"value":282},{"type":32,"tag":63,"props":2046,"children":2047},{"style":76},[2048],{"type":37,"value":287},{"type":32,"tag":63,"props":2050,"children":2051},{"style":106},[2052],{"type":37,"value":292},{"type":32,"tag":63,"props":2054,"children":2056},{"class":65,"line":2055},31,[2057],{"type":32,"tag":63,"props":2058,"children":2059},{"style":76},[2060],{"type":37,"value":301},{"type":32,"tag":63,"props":2062,"children":2064},{"class":65,"line":2063},32,[2065,2069],{"type":32,"tag":63,"props":2066,"children":2067},{"style":70},[2068],{"type":37,"value":310},{"type":32,"tag":63,"props":2070,"children":2071},{"style":76},[2072],{"type":37,"value":315},{"type":32,"tag":63,"props":2074,"children":2076},{"class":65,"line":2075},33,[2077],{"type":32,"tag":63,"props":2078,"children":2079},{"style":76},[2080],{"type":37,"value":2081},"  cohort_date,\n",{"type":32,"tag":63,"props":2083,"children":2085},{"class":65,"line":2084},34,[2086],{"type":32,"tag":63,"props":2087,"children":2088},{"style":76},[2089],{"type":37,"value":2090},"  day_n,\n",{"type":32,"tag":63,"props":2092,"children":2094},{"class":65,"line":2093},35,[2095],{"type":32,"tag":63,"props":2096,"children":2097},{"style":76},[2098],{"type":37,"value":2099},"  retained_users,\n",{"type":32,"tag":63,"props":2101,"children":2103},{"class":65,"line":2102},36,[2104],{"type":32,"tag":63,"props":2105,"children":2106},{"style":76},[2107],{"type":37,"value":2108},"  cohort_size,\n",{"type":32,"tag":63,"props":2110,"children":2112},{"class":65,"line":2111},37,[2113,2118,2122],{"type":32,"tag":63,"props":2114,"children":2115},{"style":76},[2116],{"type":37,"value":2117},"  SAFE_DIVIDE(retained_users, cohort_size) ",{"type":32,"tag":63,"props":2119,"children":2120},{"style":70},[2121],{"type":37,"value":84},{"type":32,"tag":63,"props":2123,"children":2124},{"style":76},[2125],{"type":37,"value":2126}," retention_rate\n",{"type":32,"tag":63,"props":2128,"children":2130},{"class":65,"line":2129},38,[2131,2135],{"type":32,"tag":63,"props":2132,"children":2133},{"style":70},[2134],{"type":37,"value":453},{"type":32,"tag":63,"props":2136,"children":2137},{"style":76},[2138],{"type":37,"value":2139}," retention_calc;\n",{"type":32,"tag":33,"props":2141,"children":2142},{},[2143,2145,2154],{"type":37,"value":2144},"This model updates only the last 31 days of cohorts daily. Retention for cohorts older than 31 days is frozen — no recalculation. Slot consumption drops 95%. In the ",{"type":32,"tag":2146,"props":2147,"children":2151},"a",{"href":2148,"rel":2149},"https:\u002F\u002Fwww.roibase.com.tr\u002Fru\u002Fretention-engineering-cdp",[2150],"nofollow",[2152],{"type":37,"value":2153},"Retention Engineering & CDP",{"type":37,"value":2155}," process, this table connects directly to dashboards — BI tools (Looker, Metabase) return results in 100ms.",{"type":32,"tag":40,"props":2157,"children":2159},{"id":2158},"query-cost-and-partition-expiration-strategy",[2160],{"type":37,"value":2161},"Query Cost and Partition Expiration Strategy",{"type":32,"tag":33,"props":2163,"children":2164},{},[2165],{"type":37,"value":2166},"On BigQuery, storage is cheap ($0.02\u002FGB\u002Fmonth), compute is expensive ($5\u002FTB of data scanned). Since retention analysis is retrospective, old partitions get queried frequently. Two optimizations:",{"type":32,"tag":1212,"props":2168,"children":2169},{},[2170,2187],{"type":32,"tag":1216,"props":2171,"children":2172},{},[2173,2178,2180,2185],{"type":32,"tag":1248,"props":2174,"children":2175},{},[2176],{"type":37,"value":2177},"Partition expiration:",{"type":37,"value":2179}," Auto-delete ",{"type":32,"tag":59,"props":2181,"children":2183},{"className":2182},[],[2184],{"type":37,"value":541},{"type":37,"value":2186}," partitions older than 90 days — after cohort calculation completes, raw events aren't needed.",{"type":32,"tag":1216,"props":2188,"children":2189},{},[2190,2195,2196,2202],{"type":32,"tag":1248,"props":2191,"children":2192},{},[2193],{"type":37,"value":2194},"Periodically update clustering statistics:",{"type":37,"value":1246},{"type":32,"tag":59,"props":2197,"children":2199},{"className":2198},[],[2200],{"type":37,"value":2201},"ANALYZE TABLE ... UPDATE STATISTICS",{"type":37,"value":2203}," — the query optimizer picks better execution plans.",{"type":32,"tag":33,"props":2205,"children":2206},{},[2207],{"type":37,"value":2208},"Sample cost comparison (100M events\u002Fday, 1M users):",{"type":32,"tag":2210,"props":2211,"children":2212},"table",{},[2213,2237],{"type":32,"tag":2214,"props":2215,"children":2216},"thead",{},[2217],{"type":32,"tag":2218,"props":2219,"children":2220},"tr",{},[2221,2227,2232],{"type":32,"tag":2222,"props":2223,"children":2224},"th",{},[2225],{"type":37,"value":2226},"Method",{"type":32,"tag":2222,"props":2228,"children":2229},{},[2230],{"type":37,"value":2231},"Data Scanned\u002FDay",{"type":32,"tag":2222,"props":2233,"children":2234},{},[2235],{"type":37,"value":2236},"Monthly Compute Cost",{"type":32,"tag":2238,"props":2239,"children":2240},"tbody",{},[2241,2260,2278],{"type":32,"tag":2218,"props":2242,"children":2243},{},[2244,2250,2255],{"type":32,"tag":2245,"props":2246,"children":2247},"td",{},[2248],{"type":37,"value":2249},"Naive query (full scan)",{"type":32,"tag":2245,"props":2251,"children":2252},{},[2253],{"type":37,"value":2254},"12TB",{"type":32,"tag":2245,"props":2256,"children":2257},{},[2258],{"type":37,"value":2259},"$600",{"type":32,"tag":2218,"props":2261,"children":2262},{},[2263,2268,2273],{"type":32,"tag":2245,"props":2264,"children":2265},{},[2266],{"type":37,"value":2267},"Partitioned + materialized view",{"type":32,"tag":2245,"props":2269,"children":2270},{},[2271],{"type":37,"value":2272},"800GB",{"type":32,"tag":2245,"props":2274,"children":2275},{},[2276],{"type":37,"value":2277},"$40",{"type":32,"tag":2218,"props":2279,"children":2280},{},[2281,2286,2291],{"type":32,"tag":2245,"props":2282,"children":2283},{},[2284],{"type":37,"value":2285},"Pre-aggregated table (incremental)",{"type":32,"tag":2245,"props":2287,"children":2288},{},[2289],{"type":37,"value":2290},"50GB",{"type":32,"tag":2245,"props":2292,"children":2293},{},[2294],{"type":37,"value":2295},"$2.50",{"type":32,"tag":33,"props":2297,"children":2298},{},[2299],{"type":37,"value":2300},"Adding the pre-aggregate layer cuts compute costs 240x. This margin matters in production — especially if retention analysis refreshes hourly.",{"type":32,"tag":40,"props":2302,"children":2304},{"id":2303},"real-time-cohort-analysis-tradeoff",[2305],{"type":37,"value":2306},"Real-Time Cohort Analysis Tradeoff",{"type":32,"tag":33,"props":2308,"children":2309},{},[2310],{"type":37,"value":2311},"Materialized views and pre-aggregation introduce latency: data lags 1-5 minutes. If you need real-time cohort analysis (e.g., for the first 24 hours), use a hybrid:",{"type":32,"tag":2313,"props":2314,"children":2315},"ul",{},[2316,2321],{"type":32,"tag":1216,"props":2317,"children":2318},{},[2319],{"type":37,"value":2320},"Stream inserts + real-time query for the last 24 hours (skip cache)",{"type":32,"tag":1216,"props":2322,"children":2323},{},[2324],{"type":37,"value":2325},"Pre-aggregate table for older data",{"type":32,"tag":33,"props":2327,"children":2328},{},[2329],{"type":37,"value":2330},"The BI query unions both sources:",{"type":32,"tag":52,"props":2332,"children":2334},{"className":54,"code":2333,"language":56,"meta":16,"style":16},"SELECT * FROM cohort_retention_summary WHERE cohort_date \u003C CURRENT_DATE()\nUNION ALL\nSELECT * FROM realtime_cohort_view WHERE cohort_date = CURRENT_DATE();\n",[2335],{"type":32,"tag":59,"props":2336,"children":2337},{"__ignoreMap":16},[2338,2376,2384],{"type":32,"tag":63,"props":2339,"children":2340},{"class":65,"line":66},[2341,2345,2349,2353,2358,2362,2366,2371],{"type":32,"tag":63,"props":2342,"children":2343},{"style":70},[2344],{"type":37,"value":310},{"type":32,"tag":63,"props":2346,"children":2347},{"style":70},[2348],{"type":37,"value":659},{"type":32,"tag":63,"props":2350,"children":2351},{"style":70},[2352],{"type":37,"value":664},{"type":32,"tag":63,"props":2354,"children":2355},{"style":76},[2356],{"type":37,"value":2357}," cohort_retention_summary ",{"type":32,"tag":63,"props":2359,"children":2360},{"style":70},[2361],{"type":37,"value":1139},{"type":32,"tag":63,"props":2363,"children":2364},{"style":76},[2365],{"type":37,"value":1483},{"type":32,"tag":63,"props":2367,"children":2368},{"style":70},[2369],{"type":37,"value":2370},"\u003C",{"type":32,"tag":63,"props":2372,"children":2373},{"style":76},[2374],{"type":37,"value":2375}," CURRENT_DATE()\n",{"type":32,"tag":63,"props":2377,"children":2378},{"class":65,"line":92},[2379],{"type":32,"tag":63,"props":2380,"children":2381},{"style":70},[2382],{"type":37,"value":2383},"UNION ALL\n",{"type":32,"tag":63,"props":2385,"children":2386},{"class":65,"line":136},[2387,2391,2395,2399,2404,2408,2412,2416],{"type":32,"tag":63,"props":2388,"children":2389},{"style":70},[2390],{"type":37,"value":310},{"type":32,"tag":63,"props":2392,"children":2393},{"style":70},[2394],{"type":37,"value":659},{"type":32,"tag":63,"props":2396,"children":2397},{"style":70},[2398],{"type":37,"value":664},{"type":32,"tag":63,"props":2400,"children":2401},{"style":76},[2402],{"type":37,"value":2403}," realtime_cohort_view ",{"type":32,"tag":63,"props":2405,"children":2406},{"style":70},[2407],{"type":37,"value":1139},{"type":32,"tag":63,"props":2409,"children":2410},{"style":76},[2411],{"type":37,"value":1483},{"type":32,"tag":63,"props":2413,"children":2414},{"style":70},[2415],{"type":37,"value":1293},{"type":32,"tag":63,"props":2417,"children":2418},{"style":76},[2419],{"type":37,"value":2420}," CURRENT_DATE();\n",{"type":32,"tag":33,"props":2422,"children":2423},{},[2424],{"type":37,"value":2425},"Real-time views are expensive, but since they run only on the latest cohort, total compute impact stays bounded.",{"type":32,"tag":40,"props":2427,"children":2429},{"id":2428},"cohort-segmentation-and-cardinality-explosion",[2430],{"type":37,"value":2431},"Cohort Segmentation and Cardinality Explosion",{"type":32,"tag":33,"props":2433,"children":2434},{},[2435],{"type":37,"value":2436},"Breaking retention down by user segments (platform, country, acquisition channel) can trigger cardinality issues. For instance: 5 segments × 30 days × 365 cohorts = 54,750 unique rows. Solutions:",{"type":32,"tag":1212,"props":2438,"children":2439},{},[2440,2450,2460],{"type":32,"tag":1216,"props":2441,"children":2442},{},[2443,2448],{"type":32,"tag":1248,"props":2444,"children":2445},{},[2446],{"type":37,"value":2447},"Limit segment count:",{"type":37,"value":2449}," Analyze the top 3-5 segments; create separate tables for others.",{"type":32,"tag":1216,"props":2451,"children":2452},{},[2453,2458],{"type":32,"tag":1248,"props":2454,"children":2455},{},[2456],{"type":37,"value":2457},"Dynamic segmentation:",{"type":37,"value":2459}," Add segment data at query time via join, not pre-aggregation — maintains query flexibility at the cost of slot usage.",{"type":32,"tag":1216,"props":2461,"children":2462},{},[2463,2468],{"type":32,"tag":1248,"props":2464,"children":2465},{},[2466],{"type":37,"value":2467},"Rollup table:",{"type":37,"value":2469}," Build a separate weekly-cohort retention table — cardinality drops 85%.",{"type":32,"tag":33,"props":2471,"children":2472},{},[2473,2475,2482],{"type":37,"value":2474},"In Roibase's ",{"type":32,"tag":2146,"props":2476,"children":2479},{"href":2477,"rel":2478},"https:\u002F\u002Fwww.roibase.com.tr\u002Fru\u002Fverianalizi",[2150],[2480],{"type":37,"value":2481},"Data Analytics & Insights Engineering",{"type":37,"value":2483}," process, we link segment strategy to acquisition source attribution — cohort analysis connects directly to channel performance.",{"type":32,"tag":40,"props":2485,"children":2487},{"id":2486},"monitoring-and-regression-detection",[2488],{"type":37,"value":2489},"Monitoring and Regression Detection",{"type":32,"tag":33,"props":2491,"children":2492},{},[2493],{"type":37,"value":2494},"Monitor your production cohort pipeline with these metrics:",{"type":32,"tag":2313,"props":2496,"children":2497},{},[2498,2508,2518],{"type":32,"tag":1216,"props":2499,"children":2500},{},[2501,2506],{"type":32,"tag":1248,"props":2502,"children":2503},{},[2504],{"type":37,"value":2505},"Query slot time:",{"type":37,"value":2507}," Daily refresh's BigQuery slot consumption — sudden spikes signal cardinality explosion or partition pruning loss.",{"type":32,"tag":1216,"props":2509,"children":2510},{},[2511,2516],{"type":32,"tag":1248,"props":2512,"children":2513},{},[2514],{"type":37,"value":2515},"Row count delta:",{"type":37,"value":2517}," Rows added per refresh — excess indicates duplicate events.",{"type":32,"tag":1216,"props":2519,"children":2520},{},[2521,2526],{"type":32,"tag":1248,"props":2522,"children":2523},{},[2524],{"type":37,"value":2525},"Retention rate stddev:",{"type":37,"value":2527}," Day 1 retention swinging 10%+ unexpectedly is a data quality red flag.",{"type":32,"tag":33,"props":2529,"children":2530},{},[2531],{"type":37,"value":2532},"Add these checks as dbt tests:",{"type":32,"tag":52,"props":2534,"children":2538},{"className":2535,"code":2536,"language":2537,"meta":16,"style":16},"language-yaml shiki shiki-themes github-dark","tests:\n  - dbt_utils.expression_is_true:\n      expression: \"retention_rate BETWEEN 0 AND 1\"\n  - dbt_utils.recency:\n      datepart: day\n      field: cohort_date\n      interval: 1\n","yaml",[2539],{"type":32,"tag":59,"props":2540,"children":2541},{"__ignoreMap":16},[2542,2556,2573,2591,2607,2624,2640],{"type":32,"tag":63,"props":2543,"children":2544},{"class":65,"line":66},[2545,2551],{"type":32,"tag":63,"props":2546,"children":2548},{"style":2547},"--shiki-default:#85E89D",[2549],{"type":37,"value":2550},"tests",{"type":32,"tag":63,"props":2552,"children":2553},{"style":76},[2554],{"type":37,"value":2555},":\n",{"type":32,"tag":63,"props":2557,"children":2558},{"class":65,"line":92},[2559,2564,2569],{"type":32,"tag":63,"props":2560,"children":2561},{"style":76},[2562],{"type":37,"value":2563},"  - ",{"type":32,"tag":63,"props":2565,"children":2566},{"style":2547},[2567],{"type":37,"value":2568},"dbt_utils.expression_is_true",{"type":32,"tag":63,"props":2570,"children":2571},{"style":76},[2572],{"type":37,"value":2555},{"type":32,"tag":63,"props":2574,"children":2575},{"class":65,"line":136},[2576,2581,2586],{"type":32,"tag":63,"props":2577,"children":2578},{"style":2547},[2579],{"type":37,"value":2580},"      expression",{"type":32,"tag":63,"props":2582,"children":2583},{"style":76},[2584],{"type":37,"value":2585},": ",{"type":32,"tag":63,"props":2587,"children":2588},{"style":145},[2589],{"type":37,"value":2590},"\"retention_rate BETWEEN 0 AND 1\"\n",{"type":32,"tag":63,"props":2592,"children":2593},{"class":65,"line":151},[2594,2598,2603],{"type":32,"tag":63,"props":2595,"children":2596},{"style":76},[2597],{"type":37,"value":2563},{"type":32,"tag":63,"props":2599,"children":2600},{"style":2547},[2601],{"type":37,"value":2602},"dbt_utils.recency",{"type":32,"tag":63,"props":2604,"children":2605},{"style":76},[2606],{"type":37,"value":2555},{"type":32,"tag":63,"props":2608,"children":2609},{"class":65,"line":165},[2610,2615,2619],{"type":32,"tag":63,"props":2611,"children":2612},{"style":2547},[2613],{"type":37,"value":2614},"      datepart",{"type":32,"tag":63,"props":2616,"children":2617},{"style":76},[2618],{"type":37,"value":2585},{"type":32,"tag":63,"props":2620,"children":2621},{"style":145},[2622],{"type":37,"value":2623},"day\n",{"type":32,"tag":63,"props":2625,"children":2626},{"class":65,"line":174},[2627,2632,2636],{"type":32,"tag":63,"props":2628,"children":2629},{"style":2547},[2630],{"type":37,"value":2631},"      field",{"type":32,"tag":63,"props":2633,"children":2634},{"style":76},[2635],{"type":37,"value":2585},{"type":32,"tag":63,"props":2637,"children":2638},{"style":145},[2639],{"type":37,"value":1131},{"type":32,"tag":63,"props":2641,"children":2642},{"class":65,"line":191},[2643,2648,2652],{"type":32,"tag":63,"props":2644,"children":2645},{"style":2547},[2646],{"type":37,"value":2647},"      interval",{"type":32,"tag":63,"props":2649,"children":2650},{"style":76},[2651],{"type":37,"value":2585},{"type":32,"tag":63,"props":2653,"children":2654},{"style":106},[2655],{"type":37,"value":2656},"1\n",{"type":32,"tag":33,"props":2658,"children":2659},{},[2660],{"type":37,"value":2661},"Failed tests trigger Slack\u002FPagerDuty alerts — no manual review needed.",{"type":32,"tag":33,"props":2663,"children":2664},{},[2665,2667,2673],{"type":37,"value":2666},"Cohort table architecture elevates retention analysis from \"ad-hoc query\" to \"production data product.\" Materialized views enable incremental refresh, partitioning enables query pruning, pre-aggregation optimizes slots — each layer cuts costs 10x. Running retention analysis across millions of users and billions of events now reduces to a 100ms dashboard query. Deciding ",{"type":32,"tag":2668,"props":2669,"children":2670},"em",{},[2671],{"type":37,"value":2672},"which",{"type":37,"value":2674}," retention patterns to monitor is still your job — but processing the data at this speed is now engineering, not a problem.",{"type":32,"tag":2676,"props":2677,"children":2678},"style",{},[2679],{"type":37,"value":2680},"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);}",{"title":16,"searchDepth":136,"depth":136,"links":2682},[2683,2684,2685,2686,2687,2688,2689,2690],{"id":42,"depth":92,"text":45},{"id":546,"depth":92,"text":549},{"id":677,"depth":92,"text":680},{"id":1202,"depth":92,"text":1205},{"id":2158,"depth":92,"text":2161},{"id":2303,"depth":92,"text":2306},{"id":2428,"depth":92,"text":2431},{"id":2486,"depth":92,"text":2489},"markdown","content:ru:data:cohort-tablesql-architecture-production-retention.md","content","ru\u002Fdata\u002Fcohort-tablesql-architecture-production-retention.md","ru\u002Fdata\u002Fcohort-tablesql-architecture-production-retention","md",1780898617036]