docs(course): маркеры «сверить-на-стенде» заменены живыми числами (#23)
Зачем: тикет #23 — каждое цитируемое число курса сверено с живым стендом; до этого в уроках стояли заглушки. Что: - уроки 00–04: 18 маркеров заполнены числами свежего импорта (280 437 событий, нули в таблицах ошибок, счётчики ODS/DDS); - лаба 07: таблица manifest после дня 4 (374 092 / 34 801 / 5 388), переходящие визиты по стыкам (35/22/26), числа после дня 5; - лаба 08: каноническая граница трёх дней, пример замера свежести (лаг 3:45 модельного времени до догона, ETL ~29 с) и вернувшегося пользователя; две живые поправки разбора времени: убран принудительный UTC в разборе STG и суффикс +00:00 в сравнении границы (ловились только на живом стенде). Проверка: детерминизм подтверждён двумя независимыми циклами сброс→импорт→инкремент (числа manifest и checksum_sha256 дней 4 и 5 совпали бит в бит); chain-check зелёный; сценарий лабы 08 прогнан вживую, включая стоп/продолжение и красный full_refresh=false из урока 4; grep «сверить-на-стенде» пуст; ссылки и якоря целы; make test (219+31) и make lint зелёные; /ai-text-lint по лабам — без существенных находок. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -93,25 +93,23 @@ ODS мы пересобираем целиком, одной задачей Airf
|
||||
|
||||
```
|
||||
Статистика ODS:
|
||||
┌─table──────────────────────┬─rows─┐
|
||||
│ ods.browser_event │ ... │
|
||||
│ ods.location_event │ ... │
|
||||
│ ods.device_by_click │ ... │
|
||||
│ ods.geo_by_click │ ... │
|
||||
│ ods.browser_event_errors │ 0 │
|
||||
│ ods.location_event_errors │ 0 │
|
||||
│ ods.device_by_click_errors │ 0 │
|
||||
│ ods.geo_by_click_errors │ 0 │
|
||||
└────────────────────────────┴──────┘
|
||||
┌─table──────────────────────┬───rows─┐
|
||||
│ ods.browser_event │ 280437 │
|
||||
│ ods.location_event │ 280437 │
|
||||
│ ods.device_by_click │ 26083 │
|
||||
│ ods.geo_by_click │ 26083 │
|
||||
│ ods.browser_event_errors │ 0 │
|
||||
│ ods.location_event_errors │ 0 │
|
||||
│ ods.device_by_click_errors │ 0 │
|
||||
│ ods.geo_by_click_errors │ 0 │
|
||||
└────────────────────────────┴────────┘
|
||||
```
|
||||
<!-- сверить-на-стенде -->
|
||||
|
||||
Прочитаем эту табличку — в ней три вещи, которые стоит заметить.
|
||||
|
||||
**Все четыре `*_errors` — по нулям.** Значит, стартовая история чистая: ни одна запись не дала
|
||||
ошибки разбора, столбец `parse_errors` у всех пустой. Это нормально — данные стенда аккуратные.
|
||||
Ошибки мы увидим в секции 4, когда сами их устроим.
|
||||
<!-- сверить-на-стенде -->
|
||||
|
||||
**`browser` и `location` идут в одном зерне события.** Сколько событий пришло, столько строк
|
||||
и ожидаем увидеть после типизации, если ключи валидны.
|
||||
@@ -134,9 +132,10 @@ SELECT count() AS stg_rows,
|
||||
FROM stg.geo_raw;
|
||||
```
|
||||
|
||||
`distinct_clicks` должен быть меньше или равен `stg_rows` и совпадать с числом строк в
|
||||
`ods.geo_by_click`. Значит, это схлопнутые повторы по `click_id`, а не пропавшие данные.
|
||||
Ничего не потерялось молча.
|
||||
На эталонном мире запрос возвращает `stg_rows = 280437` и
|
||||
`distinct_clicks = 26083`. Второе число совпадает с числом строк в
|
||||
`ods.geo_by_click`. Значит, это схлопнутые повторы по `click_id`, а не пропавшие
|
||||
данные. Ничего не потерялось молча.
|
||||
|
||||
---
|
||||
|
||||
@@ -245,11 +244,10 @@ ORDER BY (click_id)
|
||||
Урок про типы — так давай **намеренно ошибёмся типом** и посмотрим, что будет. Это самый
|
||||
поучительный момент урока.
|
||||
|
||||
Возьмём координату `geo_latitude` — широту. Это дробное число, например `50.82709`. Достаём мы
|
||||
Возьмём координату `geo_latitude` — широту. Это дробное число, например `-7.60361`. Достаём мы
|
||||
её через `toFloat64OrNull` — «привести к дробному числу». Заменим тип на целочисленный —
|
||||
`toInt64OrNull`, «привести к целому». Для строки `"50.82709"` целого числа не получится
|
||||
`toInt64OrNull`, «привести к целому». Для строки `"-7.60361"` целого числа не получится
|
||||
(там точка, дробная часть), и функция вернёт `NULL`. То есть широта просто исчезнет.
|
||||
<!-- сверить-на-стенде -->
|
||||
|
||||
Из секции 3 помним: разбор продублирован, поэтому правок будет **две** — в обоих `INSERT`
|
||||
блока `GEO EVENTS`. Открой `sql/ods/20_stg_to_ods.sql`, найди оба вхождения и в каждом замени
|
||||
@@ -271,10 +269,13 @@ make transform
|
||||
И смотрим на ту же «Статистику ODS». Таблица ошибок гео, которая была пустой, теперь полная:
|
||||
|
||||
```
|
||||
│ ods.geo_by_click │ ... │
|
||||
│ ods.geo_by_click_errors │ ... │ ← было 0
|
||||
│ ods.geo_by_click │ ≈ 26083 │
|
||||
│ ods.geo_by_click_errors │ 280437 │ ← было 0
|
||||
```
|
||||
|
||||
В таблице ошибок число точное. В `ods.geo_by_click` после фонового схлопывания
|
||||
останется `26083` строки, но сразу после прогона число может быть больше.
|
||||
|
||||
А в самой основной таблице широта пропала — но не молча, рядом стоит метка:
|
||||
|
||||
```sql
|
||||
@@ -285,11 +286,10 @@ LIMIT 4;
|
||||
|
||||
```
|
||||
┌─click_id─────┬─geo_latitude─┬─geo_longitude─┬─parse_errors─────────┐
|
||||
│ 58cdfc1e-... │ ᴺᵁᴸᴸ │ -0.2 │ ['bad_geo_latitude'] │
|
||||
│ 9ffd819b-... │ ᴺᵁᴸᴸ │ 85.37752 │ ['bad_geo_latitude'] │
|
||||
│ cee12466-... │ ᴺᵁᴸᴸ │ -8.07257 │ ['bad_geo_latitude'] │
|
||||
│ a8e39850-... │ ᴺᵁᴸᴸ │ 37.92792 │ ['bad_geo_latitude'] │
|
||||
└──────────────┴──────────────┴───────────────┴──────────────────────┘
|
||||
```
|
||||
<!-- сверить-на-стенде -->
|
||||
|
||||
Вот теперь видно всё разом — и DQ-split, и «двойной учёт» из секции 3 вживую. Строки с валидным
|
||||
`click_id` остались в основной таблице с пометкой `bad_geo_latitude`. И те же записи попали в
|
||||
@@ -304,7 +304,6 @@ LIMIT 4;
|
||||
> это молча срезало миллисекунды, и время по всему стенду уехало в `1970-01-21`. Ничто на это
|
||||
> не указывало — поймали только прогоном на стенде. Мораль урока: тип выбирают осознанно, даже
|
||||
> когда функция «не падает».
|
||||
<!-- сверить-на-стенде -->
|
||||
|
||||
**Верни как было.** Откати обе правки — верни `toFloat64OrNull` в оба места. Если запутался,
|
||||
проще одной командой откатить весь файл к версии из репозитория:
|
||||
@@ -317,7 +316,6 @@ make transform
|
||||
После этого `geo_by_click_errors` снова `0`, широта на месте. А если стенд совсем
|
||||
«поплыл», пройди
|
||||
[канонический сброс](../README.md#подготовка-и-канонический-сброс).
|
||||
<!-- сверить-на-стенде -->
|
||||
|
||||
---
|
||||
|
||||
@@ -329,7 +327,6 @@ make transform
|
||||
| почему `device`/`geo` меньше событий | запрос `uniqExact(click_id)` по `stg.geo_raw` | число различных `click_id` совпадает с `ods.geo_by_click` |
|
||||
| правка из секции 4 | блок «Статистика ODS» | `ods.geo_by_click_errors` прыгнул с `0` на ненулевое число |
|
||||
| правка из секции 4 | `SELECT geo_latitude, parse_errors FROM ods.geo_by_click` | широта `NULL`, в `parse_errors` — `bad_geo_latitude` |
|
||||
<!-- сверить-на-стенде -->
|
||||
|
||||
---
|
||||
|
||||
@@ -340,7 +337,6 @@ make transform
|
||||
- скрин блока «Статистика ODS», где после правки `ods.geo_by_click_errors` ушёл с `0`
|
||||
на ненулевое число;
|
||||
- либо выборка из `ods.geo_by_click` с пустой широтой и меткой `bad_geo_latitude` рядом.
|
||||
<!-- сверить-на-стенде -->
|
||||
|
||||
И проверь себя на словах — примерно эти вопросы всплывут на еженедельном созвоне:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user