Читаем ошибки BigQuery
What you'll learn
- читать анатомию ошибки BigQuery: класс, виновник, позиция
[строка:символ] - расшифровывать
Unrecognized nameи подсказкуDid you mean - разбирать простыню
No matching signature: какой аргумент не подошёл и почему - по тексту ошибки определять класс отказа, находить место сбоя и выбирать первую проверку
Пакет с пометкой «отказ»
В очереди станции всплыл пакет: запросы, которые прежние операторы отправляли архиву годы назад. Все — с пометкой «отказ». Комендант хочет знать, что в них спрашивали: вдруг операторы искали то же, что и ты.
Открываешь первый — и видишь не данные, а ответ архива: несколько строк текста, координаты, перечень каких-то сигнатур. Архив не молчал. Он объяснял, что не так, — на своём языке.
КВЕРИ: Переводчика с языка отказов у меня нет — есть ты. В пакете пять запросов, отказы разные. Разберём все пять.

Отказ №1: Unrecognized name
Первый запрос из пакета:
SELECT name, colonny
FROM fa_users
ORDER BY user_id;
Ответ архива:
Unrecognized name: colonny; Did you mean colony? [at 1:14]
Читаем по частям — порядок годится для любой ошибки BigQuery:
- Класс:
Unrecognized name— такое имя (колонки или ) не найдено в области видимости запроса. - Виновник:
colonny— имя, как ты его написал. - Подсказка:
Did you mean colony?— BigQuery ищет похожее имя и чаще всего угадывает. Но это эвристика: при опечатке в две буквы подсказки может не быть, а иногда она предлагает похожую, но не ту колонку. Проверяй по схеме, а не на веру. - Координаты:
[at 1:14]— строка 1, символ 14 ТВОЕГО запроса. В запросе на сорок строк это главный навигатор.
Правка очевидна: 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-е ноября:
Отказ №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]
Выглядит стеной текста, но структура жёсткая:
- Класс:
No matching signature— функция существует, но НЕ с такими типами аргументов. - Что ты передал:
Argument types: STRING. - Список сигнатур — все варианты, которые функция принимает, и по каждому — какой аргумент не влез (
Unable to coerce ...). Это не мусор, это список допустимого. Первый вопрос по нему — не «как привести тип», а «тот ли аргумент вообще уехал в функцию». Приводить тип имеет смысл, только когда аргумент верный, а сигнатура его действительно не принимает.
Здесь намерение — сумма по total, а в SUM уехала строковая status: оператор перепутал колонки. BigQuery, в отличие от MySQL, никогда не приводит строку к числу молча:
Отказы №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).
Обе правки в одной ячейке:
Отказы, которых песочница не покажет
Пять разобранных отказов свелись к трём классам — неизвестное имя, отсутствующая таблица и несовпадение сигнатуры: архив (и наша песочница) находит их ещё ДО чтения данных, поэтому такие отказы бесплатны. В 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.
SELECT SUM(total) AS credits_total FROM fa_payments и отказ: Unrecognized name: total [at 1:12]. В чём причина и какова минимальная правка?No matching signature for operator + Argument types: NUMERIC, STRING ... на запросе SELECT total + '25' AS with_fee FROM fa_orders. Какая правка минимальна и сохраняет смысл?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 врёт тише всего.