Debuggear resultados de query — cómo hacerlo en Data 360
Un número volvió mal, en blanco, o stale desde una query de Data 360 o un Calculated Insight. El diagnóstico es siempre el mismo — bajá por las capas desde el modelo hasta la query y encontrá la primera que está rota. El playbook de debugging de resultados de query.
Un número volvió y está mal. O en blanco. O es un número limpio que resulta ser el de ayer. Un tile de dashboard, un count de segmento, un Calculated Insight que lee un segmento, un export que produjo la Query API — el valor está corrido, y nada tiró error para decirte dónde. Los bugs de la capa de query son silenciosos igual que los de mapping: la query corre, el número vuelve, y simplemente está mal. Así que el diagnóstico tiene la misma forma — bajá por las capas desde el modelo hasta la query misma, y la primera capa que está rota es la dueña del bug.
Las capas
[ El MODELO — ¿el objeto/relación siquiera está? ]
↓
[ El MAPPING — ¿el valor aterrizó en el DMO? ]
↓
[ La DEFINICIÓN del CI — ¿grain correcto, measure correcta? ]
↓
[ El REFRESH — ¿el CI sirve el número del último run? ]
↓
[ La QUERY — ¿objeto correcto, count correcto, todas las páginas? ]Empezá arriba. Cada capa asume que la de arriba está sana, así que una respuesta mal en la capa 4 no significa nada si la capa 1 está rota. Bajá en orden; pará en la primera capa que está mal, arreglala ahí, y re-chequeá hacia abajo.
Capa 1 — El modelo: ¿el objeto o la relación siquiera está?
Empezá acá, porque nada de abajo importa si el path que tu query necesita no existe. El síntoma es el más filoso de los cinco: no un número mal sino una query que no podés escribir, o un join que no devuelve nada porque la traversal no está modelada.
- El chequeo — ¿cada objeto del que depende tu número existe como DMO, y cada traversal entre ellos corresponde a una relación que alguien realmente modeló? Un order-count-por-persona necesita el objeto order y un path modelado desde la order hasta el individuo resuelto. Un objeto origen nunca joinea directo a
UnifiedIndividual__dlm; ese path corre por el bridgeIndividualIdentityLink__dlmque mantiene la resolución de identidad. - El síntoma — un
JOINque produce cero filas sin error, o una pregunta que te piden responder y que simplemente no se puede expresar en SQL. Un join sin modelar no falla a los gritos; el path no está disponible y ya. (Ver Gotchas de Query & Insights, gotcha 8.) - El arreglo — modelá la relación, y entonces la query puede atravesarla. Esto es un cambio de arquitectura de datos, no un retoque de query — que es exactamente por qué es la capa 1: descubrirlo último significa que todo lo de abajo se construyó sobre un path que nunca estuvo.
Si el modelo tiene los objetos y las relaciones, el path existe. Bajá.
Capa 2 — El mapping: ¿el valor aterrizó en el DMO?
Si podés escribir la query pero un atributo vuelve en blanco o mal para todas las filas, puede que el valor nunca haya llegado al DMO. Esta es la capa de mapping, y tiene su propio playbook completo — este paso es el hand-off a él.
- El chequeo — leé el atributo directo del DMO para un registro del que sabés la respuesta. ¿Está en blanco, o mal, en reposo — antes de cualquier agregación? Si el atributo crudo del DMO ya está mal, la query es inocente; el valor se rompió upstream.
- El síntoma — un atributo en blanco para todos los registros (un campo sin mapear), o mal para todos (mapeado al atributo equivocado), o mal para algunos (un type mismatch en la ingesta). Ninguno es un bug de query, y ninguna cantidad de reescribir el
SELECTlos arregla. - El arreglo — bajá al diagnóstico de mapping y caminá desde el DLO hacia arriba: debuggear fallas de mapping es la versión capa por capa, y mapear DLOs a DMOs es la superficie que debuggea.
Si el atributo del DMO está correcto en reposo, la data está ahí y armonizada. El bug está en cómo lo computás o lo leés. Bajá.
Capa 3 — La definición del CI: ¿grain correcto, measure correcta?
Si el número mal viene de un Calculated Insight, y está consistentemente mal — el mismo valor equivocado en todos lados donde se lee el CI — sospechá de la definición antes que de nada. Un CI es dimensions y measures, no un SELECT arbitrario, y las dos formas de equivocarlo producen un número que está mal por diseño.
- El chequeo — leé la definición del CI, no su output. ¿Por qué está agrupado (las dimensions), y qué computa (las measures)? ¿El grain coincide con el número que esperabas — una fila por buyer, o por buyer por mes? ¿La measure es la agregación correcta —
COUNT(DISTINCT …)donde querías cosas distintas, noSUMo unCOUNTpelado que cuenta de más? - El síntoma — un número corrido por una cantidad consistente y estructural: inflado porque el grain es demasiado fino y las filas se abren, o mal porque
SUMcontó lo que debía contarse una sola vez. Está mal idénticamente para cada consumidor, que es la pista de que es la definición y no la lectura. - El arreglo — corregí las dimensions o la measure en la definición del CI. Como cada segmento, activación y agente recupera el mismo CI, una definición corregida propaga el arreglo a todos lados — igual que la equivocada propagó el error. (Ver Calculated Insights sobre el grain como la decisión.)
Un CI consistentemente mal es casi siempre la capa 3. Si la definición está bien pero el número sigue corrido, la definición no es el problema — el timing sí. Bajá.
Capa 4 — El refresh: ¿el CI sirve el número del último run?
Si la definición del CI está correcta pero el número está stale — bien para un momento del pasado, mal para ahora — estás leyendo un resultado cacheado. Un CI es exactamente tan fresco como su último run, y entre runs sirve el último valor que computó, sin nada en el consumidor que lo diga.
- El chequeo — ¿cuándo corrió por última vez este CI, y cuál es su cadencia? Compará el timestamp del último run contra la data que esperabas que reflejara. Un CI refrescado a diario alimentando una decisión que se toma cada hora sirve un número de hasta un día de antigüedad, y se ve actual.
- El síntoma — el número estaba bien ayer, o coincide con un estado que la data ya dejó atrás. Re-correr la query del origen en vivo (vía la Query API) devuelve un número distinto y actual al que sirve el CI — ese gap es la staleness.
- El arreglo — hacé coincidir la cadencia de refresh del CI con la decisión más fresca que alimenta, y escribí la cadencia al lado del CI. Si la decisión realmente necesita reacción sub-hora, eso puede significar un streaming CI o una query en vivo en vez de un recompute batch — pero no subas la cadencia más allá de lo que la decisión realmente consume, porque el costo escala con lo que reprocesás. (Ver Calculated Insights sobre freshness.)
Si el CI está fresco y bien definido — o el número no vino de un CI para nada — el problema está en la query que corriste. Bajá.
Capa 5 — La query: ¿objeto correcto, count correcto, todas las páginas?
La última capa es la query misma, y tiene tres modos de falla distintos. Para acá el modelo, el mapping y cualquier CI están sanos, así que un número mal es algo que la query le está haciendo a data correcta.
- Objeto equivocado — consultar el DLO en vez del DMO, o un objeto origen en vez del unificado. Un DLO carga la forma cruda y el desorden del origen; consultalo y heredás los dos. Peor y más común: contar un objeto source-aligned (
ssot__Individual__dlm) cuando querías personas resueltas (UnifiedIndividual__dlm), o al revés. El chequeo: leé tuFROM. ¿Ese objeto contiene lo que creés que estás contando? - Mismatch de conteo de identidad — contar individuos unificados cuando querías filas origen, o al revés. La resolución de identidad fusiona filas origen en un individuo unificado, así que un count de
UnifiedIndividual__dlmes personas y un count del objeto origen es registros — y los dos no van a reconciliar, por diseño. Dos dashboards no coinciden, los dos están "bien" contra objetos distintos, y la tarde se va en descubrirlo. El chequeo: para este número, ¿estás contando personas o registros, y es ese el objeto en tuFROM? - Truncamiento por paginación — leer la página uno y parar. La Query API pagina y se va async para resultados grandes. Código que lee la primera respuesta y para está analizando en silencio una fracción de las filas, sin error que lo marque — el count se ve plausible y simplemente es más chico que la realidad. El chequeo: ¿el caller loopea hasta el final (v2: hasta que
doneseatrue, siguiendonextBatchId; Query Connect: pollear elqueryId, después paginar poroffset/rowLimit), o lee la primera página y la llama la respuesta? (Ver la Query API.)
Un diagnóstico que podés correr
Cuando sospechás del mismatch de conteo de identidad de la capa 5, la confirmación más rápida es contar los dos objetos y ver si el gap es el que debería producir la resolución de identidad. Dos counts de un solo objeto, comparados a ojo — sin join directo:
-- Contá personas resueltas. Son individuos DESPUÉS de que la resolución de
-- identidad fusionó filas origen duplicadas — esperá que sea el número MENOR.
SELECT COUNT(ssot__Id__c) AS unified_individuals
FROM UnifiedIndividual__dlm;-- Contá filas source-aligned, pre-resolución. Esperá que sea MAYOR que el count
-- unificado: varios registros origen colapsan en un individuo unificado.
-- Si estos dos números son iguales, la resolución de identidad no está fusionando
-- nada — eso es un hallazgo de Capa 1/2 (match rules o la key del origen), no un bug de query.
SELECT COUNT(ssot__Id__c) AS source_rows
FROM ssot__Individual__dlm;Si unified_individuals está sensatamente por debajo de source_rows, la resolución hace su trabajo y tu bug es simplemente que contaste el equivocado de los dos — un arreglo de una línea en el FROM. Si los dos son iguales cuando esperabas fusión, el bug no está acá para nada: es el modelo o las match rules (capa 1), y acabás de volver caminando hacia arriba por el stack hasta donde realmente se rompió. La traversal que ata una fila origen a su individuo unificado corre por IndividualIdentityLink__dlm, nunca un join directo — pero para una comparación de counts no joineás para nada, contás cada objeto por su cuenta.
Síntomas comunes mapeados a capas
| Síntoma | Capa probable | Dónde mirar |
|---|---|---|
| JOIN no devuelve nada, o la pregunta no se puede escribir | Modelo | ¿Objeto existe? ¿Relación modelada? |
| Un atributo en blanco o mal para todas las filas, en reposo | Mapping | El mapping de DLO a DMO (debuggealo ahí) |
| Número consistentemente mal, idéntico en todos lados | Definición del CI | Grain (group-by) y measure (SUM vs COUNT(DISTINCT …)) |
| Número limpio pero stale — el valor de ayer | Refresh | Timestamp del último run del CI vs. cadencia |
| Dos counts que no reconcilian | Query — identidad | Individuos unificados vs. filas origen en el FROM |
| Export o integración a la que le faltan casi todas las filas | Query — paginación | ¿El caller pagina hasta el final, o lee la página uno? |
Relacionado
- Gotchas de Query & Insights — los diez modos de falla que esta página diagnostica, en forma de producción
- Calculated Insights — grain, measures y la freshness detrás de las capas 3 y 4
- La Query API — paginación y async, la trampa de truncamiento de la capa 5
- Data Cloud SQL — el dialecto, los tipos de objeto y los counts unificado-vs-origen
- Style Guide de Query & Insights — las convenciones que mantienen una query lo bastante legible para debuggear
- Principios de Data 360 (antes Data Cloud) — por qué el modelo bajo la query es el producto
- Debuggear fallas de mapping — la capa 2 en detalle, el diagnóstico desde el DLO
Reference: