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

Читаем ошибки BigQuery

10 min
What you'll learn
  • читать анатомию ошибки BigQuery: класс, виновник, позиция [строка:символ]
  • расшифровывать Unrecognized name и подсказку Did you mean
  • разбирать простыню No matching signature: какой аргумент не подошёл и почему
  • по тексту ошибки определять класс отказа, находить место сбоя и выбирать первую проверку

Пакет с пометкой «отказ»

В очереди станции всплыл пакет: запросы, которые прежние операторы отправляли архиву годы назад. Все — с пометкой «отказ». Комендант хочет знать, что в них спрашивали: вдруг операторы искали то же, что и ты.

Открываешь первый — и видишь не данные, а ответ архива: несколько строк текста, координаты, перечень каких-то сигнатур. Архив не молчал. Он объяснял, что не так, — на своём языке.

КВЕРИ: Переводчика с языка отказов у меня нет — есть ты. В пакете пять запросов, отказы разные. Разберём все пять.

A returned-mail alcove: a rack of old capsules sealed with red rejection wax. The cadet has opened one and it projects a diagnostic hologram in four bands of light, a thin beam pointing at the exact spot in the archive columns. The robot cat holds a seal in its paw.
The archive's refusal is not a wall but an explanation: the error class, the culprit, the position and the hint are read part by part.

Отказ №1: Unrecognized name

Первый запрос из пакета:

SELECT name, colonny
FROM fa_users
ORDER BY user_id;

Ответ архива:

Unrecognized name: colonny; Did you mean colony? [at 1:14]

Читаем по частям — порядок годится для любой ошибки BigQuery:

  1. Класс: Unrecognized name — такое имя (колонки или ) не найдено в области видимости запроса.
  2. Виновник: colonny — имя, как ты его написал.
  3. Подсказка: Did you mean colony? — BigQuery ищет похожее имя и чаще всего угадывает. Но это эвристика: при опечатке в две буквы подсказки может не быть, а иногда она предлагает похожую, но не ту колонку. Проверяй по схеме, а не на веру.
  4. Координаты: [at 1:14] — строка 1, символ 14 ТВОЕГО запроса. В запросе на сорок строк это главный навигатор.

Правка очевидна: colony. Ячейка ниже — уже починенная. Теперь поработай с архивом отказов сам: верни опечатку colonny, запусти и сверь текст ошибки с разобранным.

How to read a BigQuery errorUnrecognized name: colonny;Did you mean colony? at [1:14]what happenedthe hint — often the fix itselfwhere exactly: line and column
An error has three useful parts: what happened, the hint, and the position in your query
Рабочая версия. Сломай её обратно — замени colony на colonny — и прочитай отказ архива своими глазами.

Отказ №2: Table not found

SELECT action, COUNT(*) AS n
FROM fa_logs_20291113
GROUP BY action;
Table not found: fa_logs_20291113 [at 2:6]

Класс говорит сам за себя: таблицы нет. У мигранта причины обычно три:

  • опечатка в имени — как с колонкой, только подсказка срабатывает реже;
  • не тот или проект: полный адрес — проект.датасет.таблица; в нём легко забыть бэктики, и тогда FROM my-project.logs.events разваливается на бессмысленное вычитание my - project;
  • таблицы действительно нет: наш журнал шардирован по дням — fa_logs_20291110, ..._11, ..._12. Запрос выше спрашивает шард за 13-е ноября, которого архив уже не записал.

И да: имена таблиц и датасетов в BigQuery чувствительны к региструFA_LOGS_20291112 и fa_logs_20291112 для архива разные имена (имена колонок и функций — нет).

Последний записанный день — 12-е ноября:

Последний шард журнала: три входа и одна синхронизация — 12 ноября 2029 года архив ещё жил.

Отказ №3: No matching signature — простыня, которая читается

Третий запрос пакета хотел посчитать выручку:

SELECT SUM(status) AS paid_total
FROM fa_orders
WHERE status = 'paid';
No matching signature for aggregate function SUM
  Argument types: STRING
  Signature: SUM(INT64)
    Argument 1: Unable to coerce type STRING to expected type INT64
  Signature: SUM(UINT64)
    Argument 1: Unable to coerce type STRING to expected type UINT64
  ...
  Signature: SUM(NUMERIC)
    Argument 1: Unable to coerce type STRING to expected type NUMERIC
  ... [at 1:8]

Выглядит стеной текста, но структура жёсткая:

  1. Класс: No matching signature — функция существует, но НЕ с такими типами аргументов.
  2. Что ты передал: Argument types: STRING.
  3. Список сигнатур — все варианты, которые функция принимает, и по каждому — какой аргумент не влез (Unable to coerce ...). Это не мусор, это список допустимого. Первый вопрос по нему — не «как привести тип», а «тот ли аргумент вообще уехал в функцию». Приводить тип имеет смысл, только когда аргумент верный, а сигнатура его действительно не принимает.

Здесь намерение — сумма по total, а в SUM уехала строковая status: оператор перепутал колонки. BigQuery, в отличие от MySQL, никогда не приводит строку к числу молча:

8901.16 кредитов по десяти оплаченным заказам — выручка станции снабжения за неделю.

Отказы №4 и №5: типы в функции и в операторе

Два последних запроса пакета ломаются одинаково — на типах. Они показаны рядом для сравнения, а координаты в сообщениях относятся к каждому запросу по отдельности, как если бы ты запускал его один:

-- №4: короткий номер заказа — «последние две цифры»
SELECT order_id, SUBSTR(order_id, 2, 2) AS short_id
FROM fa_orders;
-- No matching signature for function SUBSTR
--   Argument types: INT64, INT64, INT64
--   Signature: SUBSTR(STRING, INT64, [INT64])
--     Argument 1: Unable to coerce type INT64 to expected type STRING
--   ... [at 1:18]

-- №5: стыковочный сбор из строкового конфига
SELECT order_id, total + '25' AS with_docking_fee
FROM fa_orders;
-- No matching signature for operator +
--   Argument types: NUMERIC, STRING
--   ... [at 1:18]

Оба отказа — одна привычка мигранта: надежда на неявное приведение. SUBSTR по числу прощал MySQL; число + 'строка' прощают многие языки. BigQuery строг: приводи явно. В №4 намерение строковое — значит CAST(order_id AS STRING); в №5 числовое — значит CAST('25' AS NUMERIC).

Обе правки в одной ячейке:

Число → строка для SUBSTR, строка → число для сложения. Оба CAST безопасны: типы известны заранее, SAFE_CAST не нужен.
Three error classes, three different movesnameUnrecognized namea typo or a foreign columntableNot found: Tablethe three-part addresstypeNo matching signaturean argument of the wrong typethe class of the error names the first thing to check
The class of the error names the first thing to check

Отказы, которых песочница не покажет

Пять разобранных отказов свелись к трём классам — неизвестное имя, отсутствующая таблица и несовпадение сигнатуры: архив (и наша песочница) находит их ещё ДО чтения данных, поэтому такие отказы бесплатны. В BigQuery есть отказы других уровней — у мигранта они вызывают оторопь:

  • биллинг и квоты: Quota exceeded и родня — запрос упёрся в лимиты проекта или в отключённый биллинг; чинится не правкой SQL, а настройками проекта;
  • партиционный фильтр: Cannot query over table ... without a filter over column(s) ... that can be used for partition elimination — владелец таблицы запретил дорогие запросы без фильтра по партиции; что это за механика — глава «Деньги и масштаб»;
  • предел ресурсов: Resources exceeded — запрос слишком прожорлив для выделенной памяти; частый виновник — ORDER BY всего журнала без LIMIT.

В песочнице их не воспроизвести — здесь нет ни биллинга, ни квот. Запомни сам факт: если текст отказа не про имена и не про типы — читай его как счёт или как запрет, а не как баг твоего SQL.

Check yourself
Оператор прислал запрос SELECT SUM(total) AS credits_total FROM fa_payments и отказ: Unrecognized name: total [at 1:12]. В чём причина и какова минимальная правка?
Check yourself
Отказ архива: No matching signature for operator + Argument types: NUMERIC, STRING ... на запросе SELECT total + '25' AS with_fee FROM fa_orders. Какая правка минимальна и сохраняет смысл?
Practice: solve the tasks
Solved 0 of 3 · any 2 is enough to pass
Key takeaways
КлассЧто значитПервая правка
Unrecognized name: x; Did you mean y?имя не найдено; подсказка — эвристикасверь имя со схемой; [row:col] приведёт к месту
Table not foundнет таблицы: опечатка, не тот /проект, регистрполный адрес в бэктиках; существует ли шард?
No matching signatureфункция есть, типы не тенайди в списке сигнатур своё намерение и приведи тип явно
Quota exceeded, Cannot query over table without a filter ...не баг SQL: квоты, биллинг, запрет дорогих запросовнастройки проекта; партиционный фильтр (глава 5)
  • координаты [at строка:символ] — навигатор по твоему запросу
  • BigQuery не приводит типы молча: явный CAST — часть

Документация: сообщения об ошибках, синтаксис запросов.

Глава закрыта: ты говоришь на диалекте, знаешь цену слов и читаешь отказы. Но КВЕРИ уже нашёл в журналах то, чего не понял: DATE_DIFF насчитал целый день там, где прошло два часа. Следующая глава — система типов и четыре вида времени: места, где интуиция Postgres врёт тише всего.