[{"data":1,"prerenderedAt":1968},["ShallowReactive",2],{"article-alternates":3,"article-\u002Fes\u002Fdata\u002Farquitectura-tabla-cohorte-escalando-analisis-retencion":13},{"i18nKey":4,"paths":5},"data-007-2026-07",{"de":6,"en":7,"es":8,"fr":9,"it":10,"ru":11,"tr":12},"\u002Fde\u002Fdata\u002Fcohort-table-architecture-production-retention-scaling","\u002Fen\u002Fdata\u002Fcohort-table-architecture-scaling-retention-analysis-production","\u002Fes\u002Fdata\u002Farquitectura-tabla-cohorte-escalando-analisis-retencion","\u002Ffr\u002Fdata\u002Farchitecture-table-cohort-analyse-retention","\u002Fit\u002Fdata\u002Farchitettura-tabella-cohort-scalabilita-retention-analysis-production","\u002Fru\u002Fdata\u002Farkhitektura-tablitsy-kogort-masshtabirovanie-analiza-sokhraneniya-v-production","\u002Ftr\u002Fdata\u002Fcohort-tablo-mimarisi-retention-analizinin-productionda-olceklenmesi",{"_path":8,"_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":1962,"_id":1963,"_source":1964,"_file":1965,"_stem":1966,"_extension":1967},"data",false,"","Arquitectura de Tabla Cohorte: Escalando Análisis de Retención en Producción","Materialized views, particionamiento y optimización de costos de queries para escalar análisis de cohortes en producción. Arquitectura concreta de tabla en BigQuery y dbt.","2026-07-12",[21,22,23,24,25],"analisis-cohorte","bigquery","materialized-views","optimizacion-queries","retencion",8,"Roibase",{"type":29,"children":30,"toc":1952},"root",[31,39,46,51,56,61,67,89,107,122,148,333,339,344,365,378,1437,1442,1449,1454,1459,1495,1508,1514,1519,1529,1539,1551,1556,1562,1567,1605,1764,1769,1868,1874,1879,1936,1941,1946],{"type":32,"tag":33,"props":34,"children":35},"element","p",{},[36],{"type":37,"value":38},"text","El análisis de retención es una de las métricas más críticas en datos de marketing. Para entender qué grupo de usuarios se queda cuánto tiempo y qué campaña genera valor duradero, necesitas tablas de cohortes. El problema: las queries de cohortes clásicas cuando se ejecutan nuevamente cada vez en millones de filas de datos de eventos, los costos de query alcanzan tamaños astronómicos. En producción, necesitas una arquitectura de cohortes que se actualice cada mañana, que devuelva resultados en 3 segundos cuando el analista hace una query, pero que simultáneamente minimice costos con la estrategia de particionamiento correcta. En este artículo explicamos paso a paso una arquitectura concreta de tabla de cohortes en BigQuery y dbt, la estrategia de materialized views y la optimización de costos de queries.",{"type":32,"tag":40,"props":41,"children":43},"h2",{"id":42},"por-qué-la-tabla-de-cohortes-debe-ser-una-tabla-separada",[44],{"type":37,"value":45},"Por qué la tabla de cohortes debe ser una tabla separada",{"type":32,"tag":33,"props":47,"children":48},{},[49],{"type":37,"value":50},"No puedes calcular retención desde la tabla de eventos raw cada vez. Si una empresa de e-commerce tiene 50 millones de eventos diarios, responder a la pregunta \"¿Cuál es la tasa de actividad en el día 30 para usuarios registrados en enero de 2026?\" requiere que BigQuery escanee 1.500 millones de filas. Esta query tarda 10-15 segundos y procesa 200-300 GB. Si el analista extrae 20 segmentos de cohortes diferentes al día, el costo mensual de queries supera $500.",{"type":32,"tag":33,"props":52,"children":53},{},[54],{"type":37,"value":55},"La tabla de cohortes resuelve este problema: pre-agrupas los datos de eventos por grupo, pre-calculas las métricas de cada cohorte cada día y las almacenas. Así, cuando el analista ejecuta una query, BigQuery solo escanea la tabla de cohortes, no toca los datos de eventos raw. 1.000 cohortes × 90 días × 5 métricas = 450.000 filas. Una query en esta tabla tarda 200 ms y procesa 5 MB.",{"type":32,"tag":33,"props":57,"children":58},{},[59],{"type":37,"value":60},"Pero este enfoque genera un nuevo problema: ¿cómo se actualiza la tabla de cohortes? ¿Recalculas toda la historia cada vez que llegan nuevos eventos? ¿Usas updates incrementales? ¿Qué estrategia de particionamiento optimiza tanto el rendimiento de queries como el costo de actualización? Las respuestas están en el diseño de materialized views e incrementales dbt models.",{"type":32,"tag":40,"props":62,"children":64},{"id":63},"estrategia-de-particionamiento-cohort_date-u-observation_date",[65],{"type":37,"value":66},"Estrategia de particionamiento: ¿cohort_date u observation_date?",{"type":32,"tag":33,"props":68,"children":69},{},[70,72,79,81,87],{"type":37,"value":71},"La elección de la clave de partición de la tabla de cohortes es crítica. Tienes dos opciones: la fecha de creación de la cohorte (",{"type":32,"tag":73,"props":74,"children":76},"code",{"className":75},[],[77],{"type":37,"value":78},"cohort_date",{"type":37,"value":80},") y la fecha de observación (",{"type":32,"tag":73,"props":82,"children":84},{"className":83},[],[85],{"type":37,"value":86},"observation_date",{"type":37,"value":88},").",{"type":32,"tag":33,"props":90,"children":91},{},[92,105],{"type":32,"tag":93,"props":94,"children":95},"strong",{},[96,98,103],{"type":37,"value":97},"Partición ",{"type":32,"tag":73,"props":99,"children":101},{"className":100},[],[102],{"type":37,"value":78},{"type":37,"value":104},":",{"type":37,"value":106}," Particiona por la fecha de primera actividad del usuario. La cohorte de enero de 2026 en una partición, febrero en otra. Ventaja: cuando se crea una cohorte nueva, solo escribes en esa partición, las particiones antiguas no se tocan. Desventaja: extraer los 90 días de datos de retención de una misma cohorte requiere que BigQuery escanee 90 particiones diferentes. El rendimiento cae.",{"type":32,"tag":33,"props":108,"children":109},{},[110,120],{"type":32,"tag":93,"props":111,"children":112},{},[113,114,119],{"type":37,"value":97},{"type":32,"tag":73,"props":115,"children":117},{"className":116},[],[118],{"type":37,"value":86},{"type":37,"value":104},{"type":37,"value":121}," Una partición para cada día. Si hoy es 12 de julio, la partición de 12 de julio contiene las métricas de hoy para todas las cohortes. Ventaja: queries como \"¿cuál es la tendencia de retención de los últimos 7 días?\" escanean solo 7 particiones. Desventaja: tienes que actualizar todas las cohortes cada día; el costo de updates incrementales es alto.",{"type":32,"tag":33,"props":123,"children":124},{},[125,127,132,134,139,141,146],{"type":37,"value":126},"La respuesta correcta es ",{"type":32,"tag":93,"props":128,"children":129},{},[130],{"type":37,"value":131},"una arquitectura híbrida con dos tablas:",{"type":37,"value":133}," una \"snapshot table\" (",{"type":32,"tag":73,"props":135,"children":137},{"className":136},[],[138],{"type":37,"value":86},{"type":37,"value":140}," partitioned) y una \"aggregated table\" (",{"type":32,"tag":73,"props":142,"children":144},{"className":143},[],[145],{"type":37,"value":78},{"type":37,"value":147}," partitioned). La tabla snapshot se actualiza diariamente y alimenta los dashboards de los analistas. La tabla agregada se actualiza semanalmente y se usa para comparaciones de cohortes profundas. Esta estructura sigue las mejores prácticas de BigQuery: separación narrow-wide table.",{"type":32,"tag":149,"props":150,"children":154},"pre",{"className":151,"code":152,"language":153,"meta":16,"style":16},"language-sql shiki shiki-themes github-dark","-- Esquema de tabla snapshot (particionada por observation_date)\nCREATE TABLE `analytics.cohort_retention_snapshot`\nPARTITION BY observation_date\nCLUSTER BY cohort_date, channel, device_category\nAS\nSELECT\n  observation_date,\n  cohort_date,\n  channel,\n  device_category,\n  cohort_size,\n  day_n,\n  active_users,\n  retention_rate\nFROM ...\n","sql",[155],{"type":32,"tag":73,"props":156,"children":157},{"__ignoreMap":16},[158,170,191,211,230,239,248,257,265,274,283,292,301,310,319],{"type":32,"tag":159,"props":160,"children":163},"span",{"class":161,"line":162},"line",1,[164],{"type":32,"tag":159,"props":165,"children":167},{"style":166},"--shiki-default:#6A737D",[168],{"type":37,"value":169},"-- Esquema de tabla snapshot (particionada por observation_date)\n",{"type":32,"tag":159,"props":171,"children":173},{"class":161,"line":172},2,[174,180,185],{"type":32,"tag":159,"props":175,"children":177},{"style":176},"--shiki-default:#F97583",[178],{"type":37,"value":179},"CREATE",{"type":32,"tag":159,"props":181,"children":182},{"style":176},[183],{"type":37,"value":184}," TABLE",{"type":32,"tag":159,"props":186,"children":188},{"style":187},"--shiki-default:#9ECBFF",[189],{"type":37,"value":190}," `analytics.cohort_retention_snapshot`\n",{"type":32,"tag":159,"props":192,"children":194},{"class":161,"line":193},3,[195,200,205],{"type":32,"tag":159,"props":196,"children":197},{"style":176},[198],{"type":37,"value":199},"PARTITION",{"type":32,"tag":159,"props":201,"children":202},{"style":176},[203],{"type":37,"value":204}," BY",{"type":32,"tag":159,"props":206,"children":208},{"style":207},"--shiki-default:#E1E4E8",[209],{"type":37,"value":210}," observation_date\n",{"type":32,"tag":159,"props":212,"children":214},{"class":161,"line":213},4,[215,220,225],{"type":32,"tag":159,"props":216,"children":217},{"style":207},[218],{"type":37,"value":219},"CLUSTER ",{"type":32,"tag":159,"props":221,"children":222},{"style":176},[223],{"type":37,"value":224},"BY",{"type":32,"tag":159,"props":226,"children":227},{"style":207},[228],{"type":37,"value":229}," cohort_date, channel, device_category\n",{"type":32,"tag":159,"props":231,"children":233},{"class":161,"line":232},5,[234],{"type":32,"tag":159,"props":235,"children":236},{"style":176},[237],{"type":37,"value":238},"AS\n",{"type":32,"tag":159,"props":240,"children":242},{"class":161,"line":241},6,[243],{"type":32,"tag":159,"props":244,"children":245},{"style":176},[246],{"type":37,"value":247},"SELECT\n",{"type":32,"tag":159,"props":249,"children":251},{"class":161,"line":250},7,[252],{"type":32,"tag":159,"props":253,"children":254},{"style":207},[255],{"type":37,"value":256},"  observation_date,\n",{"type":32,"tag":159,"props":258,"children":259},{"class":161,"line":26},[260],{"type":32,"tag":159,"props":261,"children":262},{"style":207},[263],{"type":37,"value":264},"  cohort_date,\n",{"type":32,"tag":159,"props":266,"children":268},{"class":161,"line":267},9,[269],{"type":32,"tag":159,"props":270,"children":271},{"style":207},[272],{"type":37,"value":273},"  channel,\n",{"type":32,"tag":159,"props":275,"children":277},{"class":161,"line":276},10,[278],{"type":32,"tag":159,"props":279,"children":280},{"style":207},[281],{"type":37,"value":282},"  device_category,\n",{"type":32,"tag":159,"props":284,"children":286},{"class":161,"line":285},11,[287],{"type":32,"tag":159,"props":288,"children":289},{"style":207},[290],{"type":37,"value":291},"  cohort_size,\n",{"type":32,"tag":159,"props":293,"children":295},{"class":161,"line":294},12,[296],{"type":32,"tag":159,"props":297,"children":298},{"style":207},[299],{"type":37,"value":300},"  day_n,\n",{"type":32,"tag":159,"props":302,"children":304},{"class":161,"line":303},13,[305],{"type":32,"tag":159,"props":306,"children":307},{"style":207},[308],{"type":37,"value":309},"  active_users,\n",{"type":32,"tag":159,"props":311,"children":313},{"class":161,"line":312},14,[314],{"type":32,"tag":159,"props":315,"children":316},{"style":207},[317],{"type":37,"value":318},"  retention_rate\n",{"type":32,"tag":159,"props":320,"children":322},{"class":161,"line":321},15,[323,328],{"type":32,"tag":159,"props":324,"children":325},{"style":176},[326],{"type":37,"value":327},"FROM",{"type":32,"tag":159,"props":329,"children":330},{"style":207},[331],{"type":37,"value":332}," ...\n",{"type":32,"tag":40,"props":334,"children":336},{"id":335},"trade-off-entre-materialized-views-e-incrementales-dbt-models",[337],{"type":37,"value":338},"Trade-off entre materialized views e incrementales dbt models",{"type":32,"tag":33,"props":340,"children":341},{},[342],{"type":37,"value":343},"En BigQuery, una materialized view (MV) hace refresh incremental automático — cuando llegan nuevos eventos, re-ejecuta la query base y cachea el resultado. Pero las MVs tienen 3 limitaciones: número de joins (máximo 5), uso de window functions (no permitido), y gestión de particiones (no manual).",{"type":32,"tag":33,"props":345,"children":346},{},[347,349,355,357,363],{"type":37,"value":348},"El cálculo de cohortes generalmente requiere 3+ joins (tablas users, events, subscriptions) y necesita window functions como ",{"type":32,"tag":73,"props":350,"children":352},{"className":351},[],[353],{"type":37,"value":354},"LAG()",{"type":37,"value":356},", ",{"type":32,"tag":73,"props":358,"children":360},{"className":359},[],[361],{"type":37,"value":362},"FIRST_VALUE()",{"type":37,"value":364},". En este caso, no puedes usar MVs. La alternativa: dbt incremental model.",{"type":32,"tag":33,"props":366,"children":367},{},[368,370,376],{"type":37,"value":369},"Un dbt incremental model te permite definir una estrategia de merge personalizada. Cada día actualizas solo las particiones de los últimos 7 días (",{"type":32,"tag":73,"props":371,"children":373},{"className":372},[],[374],{"type":37,"value":375},"WHERE observation_date >= CURRENT_DATE() - 7",{"type":37,"value":377},"). Este enfoque reduce el costo de queries en 85%. Ejemplo de dbt model:",{"type":32,"tag":149,"props":379,"children":381},{"className":151,"code":380,"language":153,"meta":16,"style":16},"{{ config(\n    materialized='incremental',\n    partition_by={\n      \"field\": \"observation_date\",\n      \"data_type\": \"date\"\n    },\n    cluster_by=['cohort_date', 'channel'],\n    incremental_strategy='insert_overwrite'\n) }}\n\nWITH daily_cohorts AS (\n  SELECT\n    DATE(first_seen_at) AS cohort_date,\n    user_id,\n    acquisition_channel AS channel\n  FROM {{ ref('users') }}\n  WHERE first_seen_at IS NOT NULL\n),\n\ndaily_activity AS (\n  SELECT\n    DATE(event_timestamp) AS activity_date,\n    user_id,\n    COUNT(*) AS event_count\n  FROM {{ ref('events') }}\n  WHERE event_name IN ('page_view', 'purchase')\n  {% if is_incremental() %}\n    AND DATE(event_timestamp) >= CURRENT_DATE() - 7\n  {% endif %}\n  GROUP BY 1, 2\n)\n\nSELECT\n  a.activity_date AS observation_date,\n  c.cohort_date,\n  c.channel,\n  DATE_DIFF(a.activity_date, c.cohort_date, DAY) AS day_n,\n  COUNT(DISTINCT c.user_id) AS cohort_size,\n  COUNT(DISTINCT a.user_id) AS active_users,\n  SAFE_DIVIDE(COUNT(DISTINCT a.user_id), COUNT(DISTINCT c.user_id)) AS retention_rate\nFROM daily_cohorts c\nLEFT JOIN daily_activity a\n  ON c.user_id = a.user_id\nWHERE a.activity_date >= c.cohort_date\n{% if is_incremental() %}\n  AND a.activity_date >= CURRENT_DATE() - 7\n{% endif %}\nGROUP BY 1, 2, 3, 4\n",[382],{"type":32,"tag":73,"props":383,"children":384},{"__ignoreMap":16},[385,393,416,433,455,472,480,497,514,522,531,554,562,584,592,609,632,651,660,668,685,693,715,723,757,778,820,839,877,886,909,917,925,933,962,983,1004,1065,1110,1152,1229,1242,1256,1295,1334,1351,1388,1397],{"type":32,"tag":159,"props":386,"children":387},{"class":161,"line":162},[388],{"type":32,"tag":159,"props":389,"children":390},{"style":207},[391],{"type":37,"value":392},"{{ config(\n",{"type":32,"tag":159,"props":394,"children":395},{"class":161,"line":172},[396,401,406,411],{"type":32,"tag":159,"props":397,"children":398},{"style":207},[399],{"type":37,"value":400},"    materialized",{"type":32,"tag":159,"props":402,"children":403},{"style":176},[404],{"type":37,"value":405},"=",{"type":32,"tag":159,"props":407,"children":408},{"style":187},[409],{"type":37,"value":410},"'incremental'",{"type":32,"tag":159,"props":412,"children":413},{"style":207},[414],{"type":37,"value":415},",\n",{"type":32,"tag":159,"props":417,"children":418},{"class":161,"line":193},[419,424,428],{"type":32,"tag":159,"props":420,"children":421},{"style":207},[422],{"type":37,"value":423},"    partition_by",{"type":32,"tag":159,"props":425,"children":426},{"style":176},[427],{"type":37,"value":405},{"type":32,"tag":159,"props":429,"children":430},{"style":207},[431],{"type":37,"value":432},"{\n",{"type":32,"tag":159,"props":434,"children":435},{"class":161,"line":213},[436,441,446,451],{"type":32,"tag":159,"props":437,"children":438},{"style":187},[439],{"type":37,"value":440},"      \"field\"",{"type":32,"tag":159,"props":442,"children":443},{"style":207},[444],{"type":37,"value":445},": ",{"type":32,"tag":159,"props":447,"children":448},{"style":187},[449],{"type":37,"value":450},"\"observation_date\"",{"type":32,"tag":159,"props":452,"children":453},{"style":207},[454],{"type":37,"value":415},{"type":32,"tag":159,"props":456,"children":457},{"class":161,"line":232},[458,463,467],{"type":32,"tag":159,"props":459,"children":460},{"style":187},[461],{"type":37,"value":462},"      \"data_type\"",{"type":32,"tag":159,"props":464,"children":465},{"style":207},[466],{"type":37,"value":445},{"type":32,"tag":159,"props":468,"children":469},{"style":187},[470],{"type":37,"value":471},"\"date\"\n",{"type":32,"tag":159,"props":473,"children":474},{"class":161,"line":241},[475],{"type":32,"tag":159,"props":476,"children":477},{"style":207},[478],{"type":37,"value":479},"    },\n",{"type":32,"tag":159,"props":481,"children":482},{"class":161,"line":250},[483,488,492],{"type":32,"tag":159,"props":484,"children":485},{"style":207},[486],{"type":37,"value":487},"    cluster_by",{"type":32,"tag":159,"props":489,"children":490},{"style":176},[491],{"type":37,"value":405},{"type":32,"tag":159,"props":493,"children":494},{"style":207},[495],{"type":37,"value":496},"['cohort_date', 'channel'],\n",{"type":32,"tag":159,"props":498,"children":499},{"class":161,"line":26},[500,505,509],{"type":32,"tag":159,"props":501,"children":502},{"style":207},[503],{"type":37,"value":504},"    incremental_strategy",{"type":32,"tag":159,"props":506,"children":507},{"style":176},[508],{"type":37,"value":405},{"type":32,"tag":159,"props":510,"children":511},{"style":187},[512],{"type":37,"value":513},"'insert_overwrite'\n",{"type":32,"tag":159,"props":515,"children":516},{"class":161,"line":267},[517],{"type":32,"tag":159,"props":518,"children":519},{"style":207},[520],{"type":37,"value":521},") }}\n",{"type":32,"tag":159,"props":523,"children":524},{"class":161,"line":276},[525],{"type":32,"tag":159,"props":526,"children":528},{"emptyLinePlaceholder":527},true,[529],{"type":37,"value":530},"\n",{"type":32,"tag":159,"props":532,"children":533},{"class":161,"line":285},[534,539,544,549],{"type":32,"tag":159,"props":535,"children":536},{"style":176},[537],{"type":37,"value":538},"WITH",{"type":32,"tag":159,"props":540,"children":541},{"style":207},[542],{"type":37,"value":543}," daily_cohorts ",{"type":32,"tag":159,"props":545,"children":546},{"style":176},[547],{"type":37,"value":548},"AS",{"type":32,"tag":159,"props":550,"children":551},{"style":207},[552],{"type":37,"value":553}," (\n",{"type":32,"tag":159,"props":555,"children":556},{"class":161,"line":294},[557],{"type":32,"tag":159,"props":558,"children":559},{"style":176},[560],{"type":37,"value":561},"  SELECT\n",{"type":32,"tag":159,"props":563,"children":564},{"class":161,"line":303},[565,570,575,579],{"type":32,"tag":159,"props":566,"children":567},{"style":176},[568],{"type":37,"value":569},"    DATE",{"type":32,"tag":159,"props":571,"children":572},{"style":207},[573],{"type":37,"value":574},"(first_seen_at) ",{"type":32,"tag":159,"props":576,"children":577},{"style":176},[578],{"type":37,"value":548},{"type":32,"tag":159,"props":580,"children":581},{"style":207},[582],{"type":37,"value":583}," cohort_date,\n",{"type":32,"tag":159,"props":585,"children":586},{"class":161,"line":312},[587],{"type":32,"tag":159,"props":588,"children":589},{"style":207},[590],{"type":37,"value":591},"    user_id,\n",{"type":32,"tag":159,"props":593,"children":594},{"class":161,"line":321},[595,600,604],{"type":32,"tag":159,"props":596,"children":597},{"style":207},[598],{"type":37,"value":599},"    acquisition_channel ",{"type":32,"tag":159,"props":601,"children":602},{"style":176},[603],{"type":37,"value":548},{"type":32,"tag":159,"props":605,"children":606},{"style":207},[607],{"type":37,"value":608}," channel\n",{"type":32,"tag":159,"props":610,"children":612},{"class":161,"line":611},16,[613,618,623,628],{"type":32,"tag":159,"props":614,"children":615},{"style":176},[616],{"type":37,"value":617},"  FROM",{"type":32,"tag":159,"props":619,"children":620},{"style":207},[621],{"type":37,"value":622}," {{ ref(",{"type":32,"tag":159,"props":624,"children":625},{"style":187},[626],{"type":37,"value":627},"'users'",{"type":32,"tag":159,"props":629,"children":630},{"style":207},[631],{"type":37,"value":521},{"type":32,"tag":159,"props":633,"children":635},{"class":161,"line":634},17,[636,641,646],{"type":32,"tag":159,"props":637,"children":638},{"style":176},[639],{"type":37,"value":640},"  WHERE",{"type":32,"tag":159,"props":642,"children":643},{"style":207},[644],{"type":37,"value":645}," first_seen_at ",{"type":32,"tag":159,"props":647,"children":648},{"style":176},[649],{"type":37,"value":650},"IS NOT NULL\n",{"type":32,"tag":159,"props":652,"children":654},{"class":161,"line":653},18,[655],{"type":32,"tag":159,"props":656,"children":657},{"style":207},[658],{"type":37,"value":659},"),\n",{"type":32,"tag":159,"props":661,"children":663},{"class":161,"line":662},19,[664],{"type":32,"tag":159,"props":665,"children":666},{"emptyLinePlaceholder":527},[667],{"type":37,"value":530},{"type":32,"tag":159,"props":669,"children":671},{"class":161,"line":670},20,[672,677,681],{"type":32,"tag":159,"props":673,"children":674},{"style":207},[675],{"type":37,"value":676},"daily_activity ",{"type":32,"tag":159,"props":678,"children":679},{"style":176},[680],{"type":37,"value":548},{"type":32,"tag":159,"props":682,"children":683},{"style":207},[684],{"type":37,"value":553},{"type":32,"tag":159,"props":686,"children":688},{"class":161,"line":687},21,[689],{"type":32,"tag":159,"props":690,"children":691},{"style":176},[692],{"type":37,"value":561},{"type":32,"tag":159,"props":694,"children":696},{"class":161,"line":695},22,[697,701,706,710],{"type":32,"tag":159,"props":698,"children":699},{"style":176},[700],{"type":37,"value":569},{"type":32,"tag":159,"props":702,"children":703},{"style":207},[704],{"type":37,"value":705},"(event_timestamp) ",{"type":32,"tag":159,"props":707,"children":708},{"style":176},[709],{"type":37,"value":548},{"type":32,"tag":159,"props":711,"children":712},{"style":207},[713],{"type":37,"value":714}," activity_date,\n",{"type":32,"tag":159,"props":716,"children":718},{"class":161,"line":717},23,[719],{"type":32,"tag":159,"props":720,"children":721},{"style":207},[722],{"type":37,"value":591},{"type":32,"tag":159,"props":724,"children":726},{"class":161,"line":725},24,[727,733,738,743,748,752],{"type":32,"tag":159,"props":728,"children":730},{"style":729},"--shiki-default:#79B8FF",[731],{"type":37,"value":732},"    COUNT",{"type":32,"tag":159,"props":734,"children":735},{"style":207},[736],{"type":37,"value":737},"(",{"type":32,"tag":159,"props":739,"children":740},{"style":176},[741],{"type":37,"value":742},"*",{"type":32,"tag":159,"props":744,"children":745},{"style":207},[746],{"type":37,"value":747},") ",{"type":32,"tag":159,"props":749,"children":750},{"style":176},[751],{"type":37,"value":548},{"type":32,"tag":159,"props":753,"children":754},{"style":207},[755],{"type":37,"value":756}," event_count\n",{"type":32,"tag":159,"props":758,"children":760},{"class":161,"line":759},25,[761,765,769,774],{"type":32,"tag":159,"props":762,"children":763},{"style":176},[764],{"type":37,"value":617},{"type":32,"tag":159,"props":766,"children":767},{"style":207},[768],{"type":37,"value":622},{"type":32,"tag":159,"props":770,"children":771},{"style":187},[772],{"type":37,"value":773},"'events'",{"type":32,"tag":159,"props":775,"children":776},{"style":207},[777],{"type":37,"value":521},{"type":32,"tag":159,"props":779,"children":781},{"class":161,"line":780},26,[782,786,791,796,801,806,810,815],{"type":32,"tag":159,"props":783,"children":784},{"style":176},[785],{"type":37,"value":640},{"type":32,"tag":159,"props":787,"children":788},{"style":207},[789],{"type":37,"value":790}," event_name ",{"type":32,"tag":159,"props":792,"children":793},{"style":176},[794],{"type":37,"value":795},"IN",{"type":32,"tag":159,"props":797,"children":798},{"style":207},[799],{"type":37,"value":800}," (",{"type":32,"tag":159,"props":802,"children":803},{"style":187},[804],{"type":37,"value":805},"'page_view'",{"type":32,"tag":159,"props":807,"children":808},{"style":207},[809],{"type":37,"value":356},{"type":32,"tag":159,"props":811,"children":812},{"style":187},[813],{"type":37,"value":814},"'purchase'",{"type":32,"tag":159,"props":816,"children":817},{"style":207},[818],{"type":37,"value":819},")\n",{"type":32,"tag":159,"props":821,"children":823},{"class":161,"line":822},27,[824,829,834],{"type":32,"tag":159,"props":825,"children":826},{"style":207},[827],{"type":37,"value":828},"  {% ",{"type":32,"tag":159,"props":830,"children":831},{"style":176},[832],{"type":37,"value":833},"if",{"type":32,"tag":159,"props":835,"children":836},{"style":207},[837],{"type":37,"value":838}," is_incremental() %}\n",{"type":32,"tag":159,"props":840,"children":842},{"class":161,"line":841},28,[843,848,853,857,862,867,872],{"type":32,"tag":159,"props":844,"children":845},{"style":176},[846],{"type":37,"value":847},"    AND",{"type":32,"tag":159,"props":849,"children":850},{"style":176},[851],{"type":37,"value":852}," DATE",{"type":32,"tag":159,"props":854,"children":855},{"style":207},[856],{"type":37,"value":705},{"type":32,"tag":159,"props":858,"children":859},{"style":176},[860],{"type":37,"value":861},">=",{"type":32,"tag":159,"props":863,"children":864},{"style":207},[865],{"type":37,"value":866}," CURRENT_DATE() ",{"type":32,"tag":159,"props":868,"children":869},{"style":176},[870],{"type":37,"value":871},"-",{"type":32,"tag":159,"props":873,"children":874},{"style":729},[875],{"type":37,"value":876}," 7\n",{"type":32,"tag":159,"props":878,"children":880},{"class":161,"line":879},29,[881],{"type":32,"tag":159,"props":882,"children":883},{"style":207},[884],{"type":37,"value":885},"  {% endif %}\n",{"type":32,"tag":159,"props":887,"children":889},{"class":161,"line":888},30,[890,895,900,904],{"type":32,"tag":159,"props":891,"children":892},{"style":176},[893],{"type":37,"value":894},"  GROUP BY",{"type":32,"tag":159,"props":896,"children":897},{"style":729},[898],{"type":37,"value":899}," 1",{"type":32,"tag":159,"props":901,"children":902},{"style":207},[903],{"type":37,"value":356},{"type":32,"tag":159,"props":905,"children":906},{"style":729},[907],{"type":37,"value":908},"2\n",{"type":32,"tag":159,"props":910,"children":912},{"class":161,"line":911},31,[913],{"type":32,"tag":159,"props":914,"children":915},{"style":207},[916],{"type":37,"value":819},{"type":32,"tag":159,"props":918,"children":920},{"class":161,"line":919},32,[921],{"type":32,"tag":159,"props":922,"children":923},{"emptyLinePlaceholder":527},[924],{"type":37,"value":530},{"type":32,"tag":159,"props":926,"children":928},{"class":161,"line":927},33,[929],{"type":32,"tag":159,"props":930,"children":931},{"style":176},[932],{"type":37,"value":247},{"type":32,"tag":159,"props":934,"children":936},{"class":161,"line":935},34,[937,942,947,952,957],{"type":32,"tag":159,"props":938,"children":939},{"style":729},[940],{"type":37,"value":941},"  a",{"type":32,"tag":159,"props":943,"children":944},{"style":207},[945],{"type":37,"value":946},".",{"type":32,"tag":159,"props":948,"children":949},{"style":729},[950],{"type":37,"value":951},"activity_date",{"type":32,"tag":159,"props":953,"children":954},{"style":176},[955],{"type":37,"value":956}," AS",{"type":32,"tag":159,"props":958,"children":959},{"style":207},[960],{"type":37,"value":961}," observation_date,\n",{"type":32,"tag":159,"props":963,"children":965},{"class":161,"line":964},35,[966,971,975,979],{"type":32,"tag":159,"props":967,"children":968},{"style":729},[969],{"type":37,"value":970},"  c",{"type":32,"tag":159,"props":972,"children":973},{"style":207},[974],{"type":37,"value":946},{"type":32,"tag":159,"props":976,"children":977},{"style":729},[978],{"type":37,"value":78},{"type":32,"tag":159,"props":980,"children":981},{"style":207},[982],{"type":37,"value":415},{"type":32,"tag":159,"props":984,"children":986},{"class":161,"line":985},36,[987,991,995,1000],{"type":32,"tag":159,"props":988,"children":989},{"style":729},[990],{"type":37,"value":970},{"type":32,"tag":159,"props":992,"children":993},{"style":207},[994],{"type":37,"value":946},{"type":32,"tag":159,"props":996,"children":997},{"style":729},[998],{"type":37,"value":999},"channel",{"type":32,"tag":159,"props":1001,"children":1002},{"style":207},[1003],{"type":37,"value":415},{"type":32,"tag":159,"props":1005,"children":1007},{"class":161,"line":1006},37,[1008,1013,1018,1022,1026,1030,1035,1039,1043,1047,1052,1056,1060],{"type":32,"tag":159,"props":1009,"children":1010},{"style":207},[1011],{"type":37,"value":1012},"  DATE_DIFF(",{"type":32,"tag":159,"props":1014,"children":1015},{"style":729},[1016],{"type":37,"value":1017},"a",{"type":32,"tag":159,"props":1019,"children":1020},{"style":207},[1021],{"type":37,"value":946},{"type":32,"tag":159,"props":1023,"children":1024},{"style":729},[1025],{"type":37,"value":951},{"type":32,"tag":159,"props":1027,"children":1028},{"style":207},[1029],{"type":37,"value":356},{"type":32,"tag":159,"props":1031,"children":1032},{"style":729},[1033],{"type":37,"value":1034},"c",{"type":32,"tag":159,"props":1036,"children":1037},{"style":207},[1038],{"type":37,"value":946},{"type":32,"tag":159,"props":1040,"children":1041},{"style":729},[1042],{"type":37,"value":78},{"type":32,"tag":159,"props":1044,"children":1045},{"style":207},[1046],{"type":37,"value":356},{"type":32,"tag":159,"props":1048,"children":1049},{"style":176},[1050],{"type":37,"value":1051},"DAY",{"type":32,"tag":159,"props":1053,"children":1054},{"style":207},[1055],{"type":37,"value":747},{"type":32,"tag":159,"props":1057,"children":1058},{"style":176},[1059],{"type":37,"value":548},{"type":32,"tag":159,"props":1061,"children":1062},{"style":207},[1063],{"type":37,"value":1064}," day_n,\n",{"type":32,"tag":159,"props":1066,"children":1068},{"class":161,"line":1067},38,[1069,1074,1078,1083,1088,1092,1097,1101,1105],{"type":32,"tag":159,"props":1070,"children":1071},{"style":729},[1072],{"type":37,"value":1073},"  COUNT",{"type":32,"tag":159,"props":1075,"children":1076},{"style":207},[1077],{"type":37,"value":737},{"type":32,"tag":159,"props":1079,"children":1080},{"style":176},[1081],{"type":37,"value":1082},"DISTINCT",{"type":32,"tag":159,"props":1084,"children":1085},{"style":729},[1086],{"type":37,"value":1087}," c",{"type":32,"tag":159,"props":1089,"children":1090},{"style":207},[1091],{"type":37,"value":946},{"type":32,"tag":159,"props":1093,"children":1094},{"style":729},[1095],{"type":37,"value":1096},"user_id",{"type":32,"tag":159,"props":1098,"children":1099},{"style":207},[1100],{"type":37,"value":747},{"type":32,"tag":159,"props":1102,"children":1103},{"style":176},[1104],{"type":37,"value":548},{"type":32,"tag":159,"props":1106,"children":1107},{"style":207},[1108],{"type":37,"value":1109}," cohort_size,\n",{"type":32,"tag":159,"props":1111,"children":1113},{"class":161,"line":1112},39,[1114,1118,1122,1126,1131,1135,1139,1143,1147],{"type":32,"tag":159,"props":1115,"children":1116},{"style":729},[1117],{"type":37,"value":1073},{"type":32,"tag":159,"props":1119,"children":1120},{"style":207},[1121],{"type":37,"value":737},{"type":32,"tag":159,"props":1123,"children":1124},{"style":176},[1125],{"type":37,"value":1082},{"type":32,"tag":159,"props":1127,"children":1128},{"style":729},[1129],{"type":37,"value":1130}," a",{"type":32,"tag":159,"props":1132,"children":1133},{"style":207},[1134],{"type":37,"value":946},{"type":32,"tag":159,"props":1136,"children":1137},{"style":729},[1138],{"type":37,"value":1096},{"type":32,"tag":159,"props":1140,"children":1141},{"style":207},[1142],{"type":37,"value":747},{"type":32,"tag":159,"props":1144,"children":1145},{"style":176},[1146],{"type":37,"value":548},{"type":32,"tag":159,"props":1148,"children":1149},{"style":207},[1150],{"type":37,"value":1151}," active_users,\n",{"type":32,"tag":159,"props":1153,"children":1155},{"class":161,"line":1154},40,[1156,1161,1166,1170,1174,1178,1182,1186,1191,1195,1199,1203,1207,1211,1215,1220,1224],{"type":32,"tag":159,"props":1157,"children":1158},{"style":207},[1159],{"type":37,"value":1160},"  SAFE_DIVIDE(",{"type":32,"tag":159,"props":1162,"children":1163},{"style":729},[1164],{"type":37,"value":1165},"COUNT",{"type":32,"tag":159,"props":1167,"children":1168},{"style":207},[1169],{"type":37,"value":737},{"type":32,"tag":159,"props":1171,"children":1172},{"style":176},[1173],{"type":37,"value":1082},{"type":32,"tag":159,"props":1175,"children":1176},{"style":729},[1177],{"type":37,"value":1130},{"type":32,"tag":159,"props":1179,"children":1180},{"style":207},[1181],{"type":37,"value":946},{"type":32,"tag":159,"props":1183,"children":1184},{"style":729},[1185],{"type":37,"value":1096},{"type":32,"tag":159,"props":1187,"children":1188},{"style":207},[1189],{"type":37,"value":1190},"), ",{"type":32,"tag":159,"props":1192,"children":1193},{"style":729},[1194],{"type":37,"value":1165},{"type":32,"tag":159,"props":1196,"children":1197},{"style":207},[1198],{"type":37,"value":737},{"type":32,"tag":159,"props":1200,"children":1201},{"style":176},[1202],{"type":37,"value":1082},{"type":32,"tag":159,"props":1204,"children":1205},{"style":729},[1206],{"type":37,"value":1087},{"type":32,"tag":159,"props":1208,"children":1209},{"style":207},[1210],{"type":37,"value":946},{"type":32,"tag":159,"props":1212,"children":1213},{"style":729},[1214],{"type":37,"value":1096},{"type":32,"tag":159,"props":1216,"children":1217},{"style":207},[1218],{"type":37,"value":1219},")) ",{"type":32,"tag":159,"props":1221,"children":1222},{"style":176},[1223],{"type":37,"value":548},{"type":32,"tag":159,"props":1225,"children":1226},{"style":207},[1227],{"type":37,"value":1228}," retention_rate\n",{"type":32,"tag":159,"props":1230,"children":1232},{"class":161,"line":1231},41,[1233,1237],{"type":32,"tag":159,"props":1234,"children":1235},{"style":176},[1236],{"type":37,"value":327},{"type":32,"tag":159,"props":1238,"children":1239},{"style":207},[1240],{"type":37,"value":1241}," daily_cohorts c\n",{"type":32,"tag":159,"props":1243,"children":1245},{"class":161,"line":1244},42,[1246,1251],{"type":32,"tag":159,"props":1247,"children":1248},{"style":176},[1249],{"type":37,"value":1250},"LEFT JOIN",{"type":32,"tag":159,"props":1252,"children":1253},{"style":207},[1254],{"type":37,"value":1255}," daily_activity a\n",{"type":32,"tag":159,"props":1257,"children":1259},{"class":161,"line":1258},43,[1260,1265,1269,1273,1277,1282,1286,1290],{"type":32,"tag":159,"props":1261,"children":1262},{"style":176},[1263],{"type":37,"value":1264},"  ON",{"type":32,"tag":159,"props":1266,"children":1267},{"style":729},[1268],{"type":37,"value":1087},{"type":32,"tag":159,"props":1270,"children":1271},{"style":207},[1272],{"type":37,"value":946},{"type":32,"tag":159,"props":1274,"children":1275},{"style":729},[1276],{"type":37,"value":1096},{"type":32,"tag":159,"props":1278,"children":1279},{"style":176},[1280],{"type":37,"value":1281}," =",{"type":32,"tag":159,"props":1283,"children":1284},{"style":729},[1285],{"type":37,"value":1130},{"type":32,"tag":159,"props":1287,"children":1288},{"style":207},[1289],{"type":37,"value":946},{"type":32,"tag":159,"props":1291,"children":1292},{"style":729},[1293],{"type":37,"value":1294},"user_id\n",{"type":32,"tag":159,"props":1296,"children":1298},{"class":161,"line":1297},44,[1299,1304,1308,1312,1316,1321,1325,1329],{"type":32,"tag":159,"props":1300,"children":1301},{"style":176},[1302],{"type":37,"value":1303},"WHERE",{"type":32,"tag":159,"props":1305,"children":1306},{"style":729},[1307],{"type":37,"value":1130},{"type":32,"tag":159,"props":1309,"children":1310},{"style":207},[1311],{"type":37,"value":946},{"type":32,"tag":159,"props":1313,"children":1314},{"style":729},[1315],{"type":37,"value":951},{"type":32,"tag":159,"props":1317,"children":1318},{"style":176},[1319],{"type":37,"value":1320}," >=",{"type":32,"tag":159,"props":1322,"children":1323},{"style":729},[1324],{"type":37,"value":1087},{"type":32,"tag":159,"props":1326,"children":1327},{"style":207},[1328],{"type":37,"value":946},{"type":32,"tag":159,"props":1330,"children":1331},{"style":729},[1332],{"type":37,"value":1333},"cohort_date\n",{"type":32,"tag":159,"props":1335,"children":1337},{"class":161,"line":1336},45,[1338,1343,1347],{"type":32,"tag":159,"props":1339,"children":1340},{"style":207},[1341],{"type":37,"value":1342},"{% ",{"type":32,"tag":159,"props":1344,"children":1345},{"style":176},[1346],{"type":37,"value":833},{"type":32,"tag":159,"props":1348,"children":1349},{"style":207},[1350],{"type":37,"value":838},{"type":32,"tag":159,"props":1352,"children":1354},{"class":161,"line":1353},46,[1355,1360,1364,1368,1372,1376,1380,1384],{"type":32,"tag":159,"props":1356,"children":1357},{"style":176},[1358],{"type":37,"value":1359},"  AND",{"type":32,"tag":159,"props":1361,"children":1362},{"style":729},[1363],{"type":37,"value":1130},{"type":32,"tag":159,"props":1365,"children":1366},{"style":207},[1367],{"type":37,"value":946},{"type":32,"tag":159,"props":1369,"children":1370},{"style":729},[1371],{"type":37,"value":951},{"type":32,"tag":159,"props":1373,"children":1374},{"style":176},[1375],{"type":37,"value":1320},{"type":32,"tag":159,"props":1377,"children":1378},{"style":207},[1379],{"type":37,"value":866},{"type":32,"tag":159,"props":1381,"children":1382},{"style":176},[1383],{"type":37,"value":871},{"type":32,"tag":159,"props":1385,"children":1386},{"style":729},[1387],{"type":37,"value":876},{"type":32,"tag":159,"props":1389,"children":1391},{"class":161,"line":1390},47,[1392],{"type":32,"tag":159,"props":1393,"children":1394},{"style":207},[1395],{"type":37,"value":1396},"{% endif %}\n",{"type":32,"tag":159,"props":1398,"children":1400},{"class":161,"line":1399},48,[1401,1406,1410,1414,1419,1423,1428,1432],{"type":32,"tag":159,"props":1402,"children":1403},{"style":176},[1404],{"type":37,"value":1405},"GROUP BY",{"type":32,"tag":159,"props":1407,"children":1408},{"style":729},[1409],{"type":37,"value":899},{"type":32,"tag":159,"props":1411,"children":1412},{"style":207},[1413],{"type":37,"value":356},{"type":32,"tag":159,"props":1415,"children":1416},{"style":729},[1417],{"type":37,"value":1418},"2",{"type":32,"tag":159,"props":1420,"children":1421},{"style":207},[1422],{"type":37,"value":356},{"type":32,"tag":159,"props":1424,"children":1425},{"style":729},[1426],{"type":37,"value":1427},"3",{"type":32,"tag":159,"props":1429,"children":1430},{"style":207},[1431],{"type":37,"value":356},{"type":32,"tag":159,"props":1433,"children":1434},{"style":729},[1435],{"type":37,"value":1436},"4\n",{"type":32,"tag":33,"props":1438,"children":1439},{},[1440],{"type":37,"value":1441},"Cuando este model se ejecuta cada día, solo sobrescribe las particiones de los últimos 7 días. El costo de procesamiento de BigQuery cae de 20 GB diarios a 2 GB. Ahorro de costo de queries anual: $2400.",{"type":32,"tag":1443,"props":1444,"children":1446},"h3",{"id":1445},"selección-de-clave-de-clustering",[1447],{"type":37,"value":1448},"Selección de clave de clustering",{"type":32,"tag":33,"props":1450,"children":1451},{},[1452],{"type":37,"value":1453},"Las particiones no son suficientes; también necesitas clustering. La tabla de cohortes se filtra en 3 dimensiones: cohort_date (tiempo), channel (fuente), device_category (dispositivo). En BigQuery, el orden de la clave de clustering es importante: el campo con mayor cardinalidad debe ir primero.",{"type":32,"tag":33,"props":1455,"children":1456},{},[1457],{"type":37,"value":1458},"Análisis de cardinalidad:",{"type":32,"tag":1460,"props":1461,"children":1462},"ul",{},[1463,1474,1484],{"type":32,"tag":1464,"props":1465,"children":1466},"li",{},[1467,1472],{"type":32,"tag":73,"props":1468,"children":1470},{"className":1469},[],[1471],{"type":37,"value":78},{"type":37,"value":1473},": 365 valores (1 año)",{"type":32,"tag":1464,"props":1475,"children":1476},{},[1477,1482],{"type":32,"tag":73,"props":1478,"children":1480},{"className":1479},[],[1481],{"type":37,"value":999},{"type":37,"value":1483},": 15-20 valores (organic, paid_search, social, email...)",{"type":32,"tag":1464,"props":1485,"children":1486},{},[1487,1493],{"type":32,"tag":73,"props":1488,"children":1490},{"className":1489},[],[1491],{"type":37,"value":1492},"device_category",{"type":37,"value":1494},": 3-4 valores (desktop, mobile, tablet)",{"type":32,"tag":33,"props":1496,"children":1497},{},[1498,1500,1506],{"type":37,"value":1499},"Orden correcto: ",{"type":32,"tag":73,"props":1501,"children":1503},{"className":1502},[],[1504],{"type":37,"value":1505},"CLUSTER BY cohort_date, channel, device_category",{"type":37,"value":1507},". Este orden acelera 10x queries como \"¿cuál es la retención en el día 30 para usuarios mobile de Instagram en Q4 2025?\".",{"type":32,"tag":40,"props":1509,"children":1511},{"id":1510},"optimización-de-costos-de-queries-profundidad-de-pre-agregación",[1512],{"type":37,"value":1513},"Optimización de costos de queries: profundidad de pre-agregación",{"type":32,"tag":33,"props":1515,"children":1516},{},[1517],{"type":37,"value":1518},"La granularidad de la tabla de cohortes también determina el balance costo-rendimiento. ¿Almacenas una fila separada para cada combinación cohort × channel × device × day_n, o solo el total general?",{"type":32,"tag":33,"props":1520,"children":1521},{},[1522,1527],{"type":32,"tag":93,"props":1523,"children":1524},{},[1525],{"type":37,"value":1526},"Opción 1: Tabla granular",{"type":37,"value":1528}," — cada combinación cohort × channel × device × day_n es una fila separada. Número total de filas: 365 cohortes × 20 canales × 4 dispositivos × 90 días = 2,6 millones de filas. Ventaja: el analista puede hacer pivot en el segmento que desee. Desventaja: costo de storage más alto ($50\u002FTB → $0,15 mensual).",{"type":32,"tag":33,"props":1530,"children":1531},{},[1532,1537],{"type":32,"tag":93,"props":1533,"children":1534},{},[1535],{"type":37,"value":1536},"Opción 2: Tabla agregada",{"type":37,"value":1538}," — solo cohort × day_n, sin desglose por channel ni device. Número total de filas: 365 × 90 = 32.850 filas. Ventaja: storage y costo de queries mínimo. Desventaja: no puedes hacer breakdown por canal.",{"type":32,"tag":33,"props":1540,"children":1541},{},[1542,1544,1549],{"type":37,"value":1543},"El enfoque correcto es ",{"type":32,"tag":93,"props":1545,"children":1546},{},[1547],{"type":37,"value":1548},"dos tablas en dos niveles:",{"type":37,"value":1550}," core metrics granular (con desglose por channel y device), extended metrics agregadas (solo cohort_date × day_n). Esta estructura optimiza storage mientras mantiene flexibilidad analítica. La tabla de core metrics alimenta los dashboards, la de extended metrics se usa para análisis ad-hoc.",{"type":32,"tag":33,"props":1552,"children":1553},{},[1554],{"type":37,"value":1555},"Además, define una política de expiración de particiones en BigQuery: las particiones más antiguas de 90 días se eliminan automáticamente. El análisis de retención rara vez mira más allá de 90 días; esta política reduce el costo anual de storage en 60%.",{"type":32,"tag":40,"props":1557,"children":1559},{"id":1558},"resolver-el-problema-de-identity-resolution-a-nivel-de-cohorte",[1560],{"type":37,"value":1561},"Resolver el problema de identity resolution a nivel de cohorte",{"type":32,"tag":33,"props":1563,"children":1564},{},[1565],{"type":37,"value":1566},"El punto más oscuro del análisis de cohortes: colisiones de user_id y resolución de identidad. Si un usuario se registra en desktop pero hace transacciones en mobile, se crean dos user_id diferentes. Si la tabla de cohortes no fusiona estas dos identidades, la retención se calcula 20% más baja.",{"type":32,"tag":33,"props":1568,"children":1569},{},[1570,1572,1578,1580,1588,1590,1596,1598,1604],{"type":37,"value":1571},"La solución: antes de crear la tabla de cohortes, fusiona con la tabla de identity graph. El ",{"type":32,"tag":73,"props":1573,"children":1575},{"className":1574},[],[1576],{"type":37,"value":1577},"canonical_user_id",{"type":37,"value":1579}," que estableciste en el proceso de ",{"type":32,"tag":1017,"props":1581,"children":1585},{"href":1582,"rel":1583},"https:\u002F\u002Fwww.roibase.com.tr\u002Fes\u002Ffirstparty",[1584],"nofollow",[1586],{"type":37,"value":1587},"Data Primaria & Arquitectura de Medición",{"type":37,"value":1589}," entra en juego aquí. En el dbt model, usa la vista ",{"type":32,"tag":73,"props":1591,"children":1593},{"className":1592},[],[1594],{"type":37,"value":1595},"users_unified",{"type":37,"value":1597}," en lugar de la tabla ",{"type":32,"tag":73,"props":1599,"children":1601},{"className":1600},[],[1602],{"type":37,"value":1603},"users",{"type":37,"value":946},{"type":32,"tag":149,"props":1606,"children":1608},{"className":151,"code":1607,"language":153,"meta":16,"style":16},"WITH unified_users AS (\n  SELECT\n    canonical_user_id,\n    MIN(first_seen_at) AS cohort_date,\n    ARRAY_AGG(DISTINCT acquisition_channel IGNORE NULLS ORDER BY first_seen_at LIMIT 1)[OFFSET(0)] AS channel\n  FROM {{ ref('users_unified') }}\n  GROUP BY 1\n)\n",[1609],{"type":32,"tag":73,"props":1610,"children":1611},{"__ignoreMap":16},[1612,1632,1639,1647,1667,1725,1745,1757],{"type":32,"tag":159,"props":1613,"children":1614},{"class":161,"line":162},[1615,1619,1624,1628],{"type":32,"tag":159,"props":1616,"children":1617},{"style":176},[1618],{"type":37,"value":538},{"type":32,"tag":159,"props":1620,"children":1621},{"style":207},[1622],{"type":37,"value":1623}," unified_users ",{"type":32,"tag":159,"props":1625,"children":1626},{"style":176},[1627],{"type":37,"value":548},{"type":32,"tag":159,"props":1629,"children":1630},{"style":207},[1631],{"type":37,"value":553},{"type":32,"tag":159,"props":1633,"children":1634},{"class":161,"line":172},[1635],{"type":32,"tag":159,"props":1636,"children":1637},{"style":176},[1638],{"type":37,"value":561},{"type":32,"tag":159,"props":1640,"children":1641},{"class":161,"line":193},[1642],{"type":32,"tag":159,"props":1643,"children":1644},{"style":207},[1645],{"type":37,"value":1646},"    canonical_user_id,\n",{"type":32,"tag":159,"props":1648,"children":1649},{"class":161,"line":213},[1650,1655,1659,1663],{"type":32,"tag":159,"props":1651,"children":1652},{"style":729},[1653],{"type":37,"value":1654},"    MIN",{"type":32,"tag":159,"props":1656,"children":1657},{"style":207},[1658],{"type":37,"value":574},{"type":32,"tag":159,"props":1660,"children":1661},{"style":176},[1662],{"type":37,"value":548},{"type":32,"tag":159,"props":1664,"children":1665},{"style":207},[1666],{"type":37,"value":583},{"type":32,"tag":159,"props":1668,"children":1669},{"class":161,"line":232},[1670,1675,1679,1684,1689,1694,1699,1703,1708,1712,1717,1721],{"type":32,"tag":159,"props":1671,"children":1672},{"style":207},[1673],{"type":37,"value":1674},"    ARRAY_AGG(",{"type":32,"tag":159,"props":1676,"children":1677},{"style":176},[1678],{"type":37,"value":1082},{"type":32,"tag":159,"props":1680,"children":1681},{"style":207},[1682],{"type":37,"value":1683}," acquisition_channel ",{"type":32,"tag":159,"props":1685,"children":1686},{"style":176},[1687],{"type":37,"value":1688},"IGNORE",{"type":32,"tag":159,"props":1690,"children":1691},{"style":176},[1692],{"type":37,"value":1693}," NULLS",{"type":32,"tag":159,"props":1695,"children":1696},{"style":176},[1697],{"type":37,"value":1698}," ORDER BY",{"type":32,"tag":159,"props":1700,"children":1701},{"style":207},[1702],{"type":37,"value":645},{"type":32,"tag":159,"props":1704,"children":1705},{"style":176},[1706],{"type":37,"value":1707},"LIMIT",{"type":32,"tag":159,"props":1709,"children":1710},{"style":729},[1711],{"type":37,"value":899},{"type":32,"tag":159,"props":1713,"children":1714},{"style":207},[1715],{"type":37,"value":1716},")[OFFSET(0)] ",{"type":32,"tag":159,"props":1718,"children":1719},{"style":176},[1720],{"type":37,"value":548},{"type":32,"tag":159,"props":1722,"children":1723},{"style":207},[1724],{"type":37,"value":608},{"type":32,"tag":159,"props":1726,"children":1727},{"class":161,"line":241},[1728,1732,1736,1741],{"type":32,"tag":159,"props":1729,"children":1730},{"style":176},[1731],{"type":37,"value":617},{"type":32,"tag":159,"props":1733,"children":1734},{"style":207},[1735],{"type":37,"value":622},{"type":32,"tag":159,"props":1737,"children":1738},{"style":187},[1739],{"type":37,"value":1740},"'users_unified'",{"type":32,"tag":159,"props":1742,"children":1743},{"style":207},[1744],{"type":37,"value":521},{"type":32,"tag":159,"props":1746,"children":1747},{"class":161,"line":250},[1748,1752],{"type":32,"tag":159,"props":1749,"children":1750},{"style":176},[1751],{"type":37,"value":894},{"type":32,"tag":159,"props":1753,"children":1754},{"style":729},[1755],{"type":37,"value":1756}," 1\n",{"type":32,"tag":159,"props":1758,"children":1759},{"class":161,"line":26},[1760],{"type":32,"tag":159,"props":1761,"children":1762},{"style":207},[1763],{"type":37,"value":819},{"type":32,"tag":33,"props":1765,"children":1766},{},[1767],{"type":37,"value":1768},"Este enfoque calcula correctamente la retención cross-device. En producción, genera una diferencia de 15-25% en métricas de retención. Cuando la tabla de identity resolution se actualiza, también debe materializarse la tabla de cohortes — por eso define dependencies en el DAG de dbt:",{"type":32,"tag":149,"props":1770,"children":1774},{"className":1771,"code":1772,"language":1773,"meta":16,"style":16},"language-yaml shiki shiki-themes github-dark","models:\n  - name: cohort_retention_snapshot\n    config:\n      materialized: incremental\n    depends_on:\n      - ref('users_unified')\n","yaml",[1775],{"type":32,"tag":73,"props":1776,"children":1777},{"__ignoreMap":16},[1778,1792,1814,1826,1843,1855],{"type":32,"tag":159,"props":1779,"children":1780},{"class":161,"line":162},[1781,1787],{"type":32,"tag":159,"props":1782,"children":1784},{"style":1783},"--shiki-default:#85E89D",[1785],{"type":37,"value":1786},"models",{"type":32,"tag":159,"props":1788,"children":1789},{"style":207},[1790],{"type":37,"value":1791},":\n",{"type":32,"tag":159,"props":1793,"children":1794},{"class":161,"line":172},[1795,1800,1805,1809],{"type":32,"tag":159,"props":1796,"children":1797},{"style":207},[1798],{"type":37,"value":1799},"  - ",{"type":32,"tag":159,"props":1801,"children":1802},{"style":1783},[1803],{"type":37,"value":1804},"name",{"type":32,"tag":159,"props":1806,"children":1807},{"style":207},[1808],{"type":37,"value":445},{"type":32,"tag":159,"props":1810,"children":1811},{"style":187},[1812],{"type":37,"value":1813},"cohort_retention_snapshot\n",{"type":32,"tag":159,"props":1815,"children":1816},{"class":161,"line":193},[1817,1822],{"type":32,"tag":159,"props":1818,"children":1819},{"style":1783},[1820],{"type":37,"value":1821},"    config",{"type":32,"tag":159,"props":1823,"children":1824},{"style":207},[1825],{"type":37,"value":1791},{"type":32,"tag":159,"props":1827,"children":1828},{"class":161,"line":213},[1829,1834,1838],{"type":32,"tag":159,"props":1830,"children":1831},{"style":1783},[1832],{"type":37,"value":1833},"      materialized",{"type":32,"tag":159,"props":1835,"children":1836},{"style":207},[1837],{"type":37,"value":445},{"type":32,"tag":159,"props":1839,"children":1840},{"style":187},[1841],{"type":37,"value":1842},"incremental\n",{"type":32,"tag":159,"props":1844,"children":1845},{"class":161,"line":232},[1846,1851],{"type":32,"tag":159,"props":1847,"children":1848},{"style":1783},[1849],{"type":37,"value":1850},"    depends_on",{"type":32,"tag":159,"props":1852,"children":1853},{"style":207},[1854],{"type":37,"value":1791},{"type":32,"tag":159,"props":1856,"children":1857},{"class":161,"line":241},[1858,1863],{"type":32,"tag":159,"props":1859,"children":1860},{"style":207},[1861],{"type":37,"value":1862},"      - ",{"type":32,"tag":159,"props":1864,"children":1865},{"style":187},[1866],{"type":37,"value":1867},"ref('users_unified')\n",{"type":32,"tag":40,"props":1869,"children":1871},{"id":1870},"checklist-de-producción-monitoreo-y-alertas",[1872],{"type":37,"value":1873},"Checklist de producción: monitoreo y alertas",{"type":32,"tag":33,"props":1875,"children":1876},{},[1877],{"type":37,"value":1878},"Cuando la tabla de cohortes va a producción, monitorea 3 métricas continuamente:",{"type":32,"tag":1880,"props":1881,"children":1882},"ol",{},[1883,1901,1926],{"type":32,"tag":1464,"props":1884,"children":1885},{},[1886,1891,1893,1899],{"type":32,"tag":93,"props":1887,"children":1888},{},[1889],{"type":37,"value":1890},"Freshness:",{"type":37,"value":1892}," ¿Cuándo se actualizó la última partición? En dbt-core, define una prueba ",{"type":32,"tag":73,"props":1894,"children":1896},{"className":1895},[],[1897],{"type":37,"value":1898},"freshness",{"type":37,"value":1900},"; si una partición tiene más de 24 horas sin actualización, envía una alerta a Slack.",{"type":32,"tag":1464,"props":1902,"children":1903},{},[1904,1909,1911,1917,1919,1925],{"type":32,"tag":93,"props":1905,"children":1906},{},[1907],{"type":37,"value":1908},"Row count drift:",{"type":37,"value":1910}," Si el ",{"type":32,"tag":73,"props":1912,"children":1914},{"className":1913},[],[1915],{"type":37,"value":1916},"cohort_size",{"type":37,"value":1918}," de hoy difiere más de 30% del de ayer, hay un problema en el pipeline de datos. Usa una query programada de BigQuery con control ",{"type":32,"tag":73,"props":1920,"children":1922},{"className":1921},[],[1923],{"type":37,"value":1924},"STDDEV()",{"type":37,"value":946},{"type":32,"tag":1464,"props":1927,"children":1928},{},[1929,1934],{"type":32,"tag":93,"props":1930,"children":1931},{},[1932],{"type":37,"value":1933},"Query cost spike:",{"type":37,"value":1935}," Si el costo promedio de queries en la tabla de cohortes sube de $0,01 a $0,10, el partition pruning no funciona. Revisa la tabla INFORMATION_SCHEMA.JOBS.",{"type":32,"tag":33,"props":1937,"children":1938},{},[1939],{"type":37,"value":1940},"Configura un dashboard de Google Cloud Monitoring para estas 3 métricas. Cuando los umbrales se superan, dispara una integración con PagerDuty. La arquitectura de cohortes en producción no es \"build and forget\", requiere monitoreo continuo.",{"type":32,"tag":33,"props":1942,"children":1943},{},[1944],{"type":37,"value":1945},"Cuando la arquitectura de tabla de cohortes está bien construida, el análisis de retención se convierte en un producto de ingeniería: se actualiza cada mañana, el analista extrae insights en 3 segundos, los costos de queries son predecibles. La estrategia de particionamiento de BigQuery, el dbt incremental model y la integración de identity resolution son los 3 pilares de esta arquitectura. Para escalar el análisis de cohortes en producción, debes profundizar en detalles técnicos — pero la recompensa es medible: ahorro anual de más de $5.000 en costos de queries y métricas de retención 20% más precisas.",{"type":32,"tag":1947,"props":1948,"children":1949},"style",{},[1950],{"type":37,"value":1951},"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":193,"depth":193,"links":1953},[1954,1955,1956,1959,1960,1961],{"id":42,"depth":172,"text":45},{"id":63,"depth":172,"text":66},{"id":335,"depth":172,"text":338,"children":1957},[1958],{"id":1445,"depth":193,"text":1448},{"id":1510,"depth":172,"text":1513},{"id":1558,"depth":172,"text":1561},{"id":1870,"depth":172,"text":1873},"markdown","content:es:data:arquitectura-tabla-cohorte-escalando-analisis-retencion.md","content","es\u002Fdata\u002Farquitectura-tabla-cohorte-escalando-analisis-retencion.md","es\u002Fdata\u002Farquitectura-tabla-cohorte-escalando-analisis-retencion","md",1785103520146]