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

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

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

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

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

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

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

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

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

Курсант впервые входит в Дальний архив: зал построен не из полок со строками, а из высоких светящихся КОЛОНН данных; слабый сигнал из мёртвого сектора будит три из них, рядом висит трёхэтажная рамка адреса, робот-кот следит за ожившими колоннами.
Архив хранит данные колонками, а не строками, и адресует таблицу тремя именами — отсюда и цена запроса, и вся разница с Postgres.

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

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

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

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

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

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

Адрес таблицы: три имени вместо одногоPostgresFROM usersтекущая схемаBigQuery`arena.far.fa_users`проектдатасеттаблицабэктики нужны, потому что в имени проекта бывает дефис
Полный адрес таблицы: проект, датасет и имя — вместо одной текущей схемы
Первый 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 прячет, REPLACE подменяетSELECT * EXCEPT(tags)idnamecityplantagsSELECT * REPLACE(UPPER(city) AS city)idnameCITYplantagsостальные колонки перечислять не нужно
Скрыть колонку или подменить её выражением, не перечисляя остальные
Реестр без служебных колонок — и без перечисления остальных.

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

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

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

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

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

Закрепление: реши задачи
Решено 0 из 2 · для зачёта достаточно 1
Главное из урока
  • полный адрес таблицы: `проект.датасет.таблица` в бэктиках; песочница уже стоит внутри
  • BigQuery хранит данные колонками: платишь за прочитанные колонки, SELECT * — самый дорогой способ спросить
  • SELECT * EXCEPT(a, b) — все колонки, кроме названных; SELECT * REPLACE(expr AS col) — подмена на лету
  • колонка бывает массивом: ARRAY<STRING> живёт в ячейке, ARRAY_LENGTH меряет её длину, пустой массив ≠

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