Другой диалект

Вход в Дальний архив

12 min
O que você vai aprender
  • адресовать таблицы BigQuery: проект.датасет.таблица и зачем нужны бэктики
  • прятать и подменять колонки без перечисления: SELECT * EXCEPT(...) и SELECT * REPLACE(...)
  • читать ячейку с массивом: таблица BigQuery не обязана быть плоской
  • понимать, почему в колоночном хранилище выбор колонок определяет объём чтения

Сигнал, который не был шумом

Эпилог прошлой экспедиции кончался слабым сигналом из мёртвого сектора. Восемь недель станция считала его эхом. Сегодня приёмник поймал его снова — и на этот раз в нём был заголовок.

«Дальний архив» — колоночное мега-хранилище последней земной колонии. Не витрина магазина и не аналитический склад: журнал ЖИЗНИ колонии за годы, слишком большой, чтобы читать его строками.

У архива есть особенность, которой не было ни у одной базы курса: счётчик. Каждый запрос к нему питается энергией умирающей станции-донора, и счётчик списывает её за каждый прочитанный байт. Здесь нельзя просто «посмотреть все данные» — придётся научиться спрашивать точно.

КВЕРИ смотрит на заголовок дольше обычного и впервые за два курса говорит:

Этого я не знаю. Учимся вместе. Но правило понял сразу: этот архив считает деньги за каждый прочитанный байт.

The cadet enters the Far Archive for the first time: the hall is built not of shelves of rows but of tall luminous COLUMNS of data; a faint signal from the dead sector wakes three of them, a three-tier address frame hovers nearby, and the robot cat watches the newly lit columns.
The archive stores data in columns, not rows, and addresses a table by three names — that is where both the query price and the whole difference from Postgres begin.

Адрес таблицы: три имени вместо одного

В Postgres ты писал FROM users — и база искала таблицу в текущей схеме. В BigQuery полный адрес таблицы трёхэтажный:

SELECT * FROM `project.dataset.table`
  • проект — чей это архив (и чей счёт оплачивает запрос);
  • — раздел архива, аналог схемы;
  • таблица — сама таблица.

Бэктики ` — не украшение: без них имя с дефисом или точкой развалится на части. Привычка обрамлять полный адрес бэктиками — первая, которую стоит завести.

Песочница курса уже подключена К ДАТАСЕТУ архива, поэтому в ячейках ниже хватит короткого имени таблицы — как если бы ты уже сделал USE dataset. Полный адрес понадобится, когда таблиц станет много и они окажутся в разных разделах.

Первая таблица архива — реестр жителей колонии fa_users. Запусти:

Table address: three names instead of onePostgresFROM userscurrent schemaBigQuery`arena.far.fa_users`projectdatasettablebackticks are needed because a project id may contain a hyphen
The full table address: project, dataset and name — instead of one current schema
Первый SELECT к Дальнему архиву — здесь * уместен: таблица учебная, и нам нужна её форма целиком. На рабочем журнале так не спрашивают, и почему — разберём в следующем уроке. Посмотри внимательно на колонку tags.

Почему этот архив — колоночный

В строчной базе (Postgres, MySQL) данные лежат строками: прочитать одну строку дёшево, прочитать одну колонку из миллиарда строк — дорого, потому что диск всё равно тащит строки целиком.

BigQuery хранит данные колонками: значения status всех заказов лежат рядом, отдельно от items и total. Отсюда два следствия, которые перевернут твои привычки:

  1. Запрос платит только за колонки, которые ЧИТАЕТ. SELECT name FROM fa_users трогает одну колонку; SELECT * — все.
  2. «Достать одну строку по id» — здесь худший, а не лучший сценарий: нет, архив создан для вопросов «по всем строкам сразу».

Счётчик станции списывает энергию ровно по этому правилу — за прочитанные колонки. Полный прейскурант — в следующем уроке; пока запомни главное: SELECT * в BigQuery — это не лень, это счёт.

SELECT * EXCEPT — спрятать, не перечисляя

В Postgres, чтобы вывести «все колонки, кроме одной», ты перечислял руками все остальные. В таблице на 40 колонок это 39 строк боли.

BigQuery решает это одной конструкцией:

SELECT * EXCEPT(tags) FROM fa_users

«Все колонки, кроме tags». Работает и со списком: EXCEPT(tags, joined_at). Проверь:

EXCEPT hides, REPLACE substitutesSELECT * EXCEPT(tags)idnamecityplantagsSELECT * REPLACE(UPPER(city) AS city)idnameCITYplantagsthe other columns need not be listed
Hide a column or swap it for an expression without listing the rest
Реестр без служебных колонок — и без перечисления остальных.

SELECT * REPLACE — подменить на лету

Вторая конструкция той же семьи: оставить все колонки, но одну подменить выражением, не трогая остальные:

SELECT * REPLACE(UPPER(status) AS status) FROM fa_orders

Имя после AS обязано совпадать с именем существующей колонки — REPLACE именно подменяет, а не добавляет. Запусти и посмотри на колонку status: REPLACE подменил её значения, а остальные колонки остались на своих местах.

Та же таблица заказов, но status приведён к верхнему регистру прямо в звёздочке.

Ячейка-массив: таблица перестала быть плоской

Вернись к первому выводу: в колонке tags лежали не строки — списки строк. В Postgres ты бы вынес их в отдельную таблицу user_tags и соединял через . BigQuery разрешает колонке иметь тип ARRAY<STRING> — весь список живёт прямо в ячейке.

Длину списка меряет ARRAY_LENGTH:

SELECT name, ARRAY_LENGTH(tags) AS n FROM fa_users

У кого из жителей колонии больше всего меток — и у кого их нет вовсе?

ARRAY_LENGTH считает элементы массива; пустой массив даёт 0.
Check yourself
В Postgres ты писал: SELECT id, name, city, phone, email, plan, created_at, updated_at, source, score, manager, note FROM leads — все колонки, кроме raw_payload. Как это сказать в BigQuery одной строкой?
Check yourself
Что вернёт SELECT ARRAY_LENGTH(tags) FROM fa_users для жителя, у которого метки не заполнены — в ячейке пустой массив []?

Клиффхэнгер: как развернуть массив в строки

Список в ячейке — это удобно ровно до того момента, как ты захочешь посчитать по элементам: сколько жителей носит метку «инженер»? Для этого массив нужно развернуть в строки — в Postgres это был unnest() из бокового соединения, в BigQuery это целая идиома CROSS JOIN UNNEST, у которой есть подвох, молча теряющий строки.

Ей посвящена глава «Массивы и структуры». А прежде чем архив пустит тебя глубже, он потребует понять свой счётчик — следующий урок про то, за что именно ты платишь.

Закрепи сегодняшнее на задачах тренажёра:

Practice: solve the tasks
Solved 0 of 2 · any 1 is enough to pass
Principais pontos
  • полный адрес таблицы: `проект.датасет.таблица` в бэктиках; песочница уже стоит внутри
  • BigQuery хранит данные колонками: платишь за прочитанные колонки, SELECT * — самый дорогой способ спросить
  • SELECT * EXCEPT(a, b) — все колонки, кроме названных; SELECT * REPLACE(expr AS col) — подмена на лету
  • колонка бывает массивом: ARRAY<STRING> живёт в ячейке, ARRAY_LENGTH меряет её длину, пустой массив ≠

Дальше — счётчик архива: за что списываются байты и почему LIMIT не спасает.