«relation does not exist» в PostgreSQL: разбор и фикс

SQL Автор: Среда и версия: PostgreSQL 16.11
содержание

relation does not exist означает, что PostgreSQL не нашёл объект с таким именем там, где искал. Слово «relation» покрывает таблицы, представления и последовательности разом. Причин у сообщения шесть, и опечатка среди них только первая. Всё дальше воспроизведено на PostgreSQL 16.11.

Данные те же, что в остальных статьях справочника: таблицы users и orders.

SQL / 01

Синтаксис разобран, имя таблицы не найдено

Синтаксис разобран, имя таблицы не найдено01 Запрос SELECT * FROM zakazy; После FROM PostgreSQL ищет объект с этим именем. 02 Сообщение relation "zakazy" does not exist Проверь имя, схему и подключение к базе. 03 В этом примере SELECT * FROM orders; Исправляем zakazy на фактическое имя orders.01ЗапросSELECT * FROM zakazy;После FROM PostgreSQL ищет объект с этим именем.02Сообщениеrelation "zakazy"does not existПроверь имя, схему и подключение к базе.03В этом примереSELECT * FROM orders;Исправляем zakazy на фактическое имя orders.
Начинай с имени в кавычках: PostgreSQL показывает объект, который он действительно искал; затем сверяй его с каталогом и схемой.

Как читать сообщение

SELECT * FROM zakazy;
ERROR:  relation "zakazy" does not exist
LINE 1: SELECT * FROM zakazy;
                      ^

Три строки, и каждая полезна. В первой стоит имя, которое база искала, в том виде, в каком она его поняла. Во второй сам запрос, в третьей стрелка на место. Начинать разбор надо с кавычек в первой строке: если имя там выглядит не так, как ты писал, причина уже найдена.

Для колонок сообщение другое и часто идёт с подсказкой:

SELECT nam FROM users;
ERROR:  column "nam" does not exist
LINE 1: SELECT nam FROM users;
               ^
HINT:  Perhaps you meant to reference the column "users.name".

Строку HINT пропускают чаще всего, а она обычно и содержит ответ. PostgreSQL ищет похожие имена сам.

Когда колонка указана с префиксом таблицы, сообщение меняет форму и подсказки не будет:

SELECT u.amount FROM users u;
ERROR:  column u.amount does not exist
LINE 1: SELECT u.amount FROM users u;
               ^

Имя без кавычек и без HINT означает, что база поняла запрос буквально: у таблицы за псевдонимом u такой колонки нет. Сумма лежит в orders, соединение не написано.

Таблица лежит в другой схеме

Схему в запросе обычно не пишут, и база ищет по search_path. Если таблица создана в другой схеме, её не видно:

SELECT * FROM orders_2025;
ERROR:  relation "orders_2025" does not exist
LINE 1: SELECT * FROM orders_2025;
                      ^

С явным указанием схемы тот же запрос отрабатывает:

SELECT count(*) FROM archive.orders_2025;
count
0

Проверить, где база вообще ищет, можно одной командой:

SHOW search_path;
search_path
relart

Отсюда типовой сюжет: коллега создал таблицу под своей ролью, она попала в его схему, а у тебя в search_path её нет. Лечится либо полным именем схема.таблица, либо правкой search_path.

Кавычки и регистр: самая дорогая причина

PostgreSQL приводит имена без кавычек к нижнему регистру. Имя в двойных кавычках сохраняется как есть. Отсюда следует, что таблица, созданная как "Orders", и запрос FROM Orders — это разные объекты.

Если таблицы в нижнем регистре нет, ошибка честно об этом скажет:

ERROR:  relation "orders" does not exist
LINE 1: SELECT * FROM Orders;
                      ^

Обрати внимание: в кавычках стоит orders строчными, хотя в запросе написано Orders. Это и есть отпечаток сворачивания регистра, главный признак того, что таблицу надо искать в кавычках.

Опаснее случай, когда таблица в нижнем регистре всё-таки есть. Тогда ошибки не будет вовсе:

SELECT id, amount FROM Orders ORDER BY id LIMIT 3;
idamount
10900
112400
121200

Запрос прочитал таблицу orders, а не "Orders", и ни одна строка об этом не сообщила. В кавычках он читает другую таблицу с другими колонками. Именно поэтому имена в кавычках считают плохой практикой: они создают два разных объекта, различимых только кавычками, и цена ошибки — молча неверный отчёт.

Из того же правила растёт и другая ошибка. Двойные кавычки в SQL — это имя, а не текст. Строку пишут одинарными:

SELECT * FROM users WHERE name = "Аня";
ERROR:  column "Аня" does not exist
LINE 1: SELECT * FROM users WHERE name = "Аня";
                                         ^
HINT:  Perhaps you meant to reference the column "users.id".

База искала колонку с именем «Аня». Подсказка тут бесполезна, зато само сообщение «column» вместо «relation» указывает на причину: в кавычках оказалось значение.

Псевдоним закрывает исходное имя

Как только у таблицы появился псевдоним, обращаться к ней по имени уже нельзя:

SELECT orders.amount FROM orders o;
ERROR:  invalid reference to FROM-clause entry for table "orders"
LINE 1: SELECT orders.amount FROM orders o;
               ^
HINT:  Perhaps you meant to reference the table alias "o".

Формулировка другая, а причина того же класса: имя не найдено там, где база его ищет. Подсказка называет решение прямо.

Алиас колонки не виден в WHERE

Имя, придуманное в SELECT, в WHERE ещё не существует:

SELECT amount * 2 AS doubled FROM orders WHERE doubled > 1000;
ERROR:  column "doubled" does not exist
LINE 1: SELECT amount * 2 AS doubled FROM orders WHERE doubled > 100...
                                                       ^

Причина в порядке выполнения: WHERE отрабатывает до SELECT, поэтому алиаса на тот момент нет. А вот ORDER BY идёт после и алиас видит:

SELECT amount * 2 AS doubled FROM orders ORDER BY doubled DESC LIMIT 2;
doubled
4800
2400

Лечится повтором выражения в WHERE или обёрткой запроса в подзапрос. Полный порядок выполнения предложений разобран в статье про ORDER BY, а асимметрия с HAVING — в статье про GROUP BY.

CTE живёт только внутри своего запроса

Временное имя из WITH существует до точки с запятой и ни секундой дольше:

WITH paid AS (SELECT * FROM orders WHERE status = 'paid')
SELECT count(*) FROM paid;
count
5

Следующий запрос уже его не видит:

SELECT count(*) FROM paid;
ERROR:  relation "paid" does not exist
LINE 1: SELECT count(*) FROM paid;
                             ^

Ошибка выглядит как пропавшая таблица, хотя таблицы никогда и не было. Подробно про область видимости и цепочки WITH — в статье про CTE.

Как найти, где лежит таблица

Когда причина неочевидна, спрашивают у самой базы. Системный каталог покажет все схемы, в которых встречается имя:

SELECT table_schema, table_name FROM information_schema.tables
WHERE table_name = 'orders_2025'
ORDER BY table_schema;
table_schematable_name
archiveorders_2025

Здесь же ловится и регистр: имя в каталоге хранится ровно так, как записано при создании, поэтому строка Orders в выдаче сразу объясняет, почему запрос без кавычек её не находит. В psql то же самое делают команды \dt *.* для таблиц и \dn для схем.

Порядок разбора

Шесть проверок по убыванию частоты:

  1. Прочитай имя в кавычках из текста ошибки. Оно отличается от написанного, значит дело в регистре или кавычках.
  2. Посмотри строку HINT, если она есть. Для опечаток в колонках она обычно содержит готовый ответ.
  3. Проверь SHOW search_path и поищи объект в information_schema.tables.
  4. Убедись, что таблица не за псевдонимом, а колонка не за чужим префиксом.
  5. Проверь, не алиас ли это из SELECT, использованный в WHERE.
  6. Проверь, не CTE ли это из предыдущего запроса.

Частые вопросы

Почему запрос работал вчера, а сегодня нет

Чаще всего сменилось подключение: другая база, другая роль, другой search_path. Сравни SHOW search_path и SELECT current_database() в обоих сеансах.

Как проверить существование таблицы без ошибки

SELECT to_regclass('public.orders') возвращает NULL, если объекта нет, и не падает. Для условного удаления есть DROP TABLE IF EXISTS, а проверку наличия строк делает EXISTS, и это совсем другая задача.

Чем relation отличается от table в сообщении

Ничем важным для разбора. PostgreSQL называет relation всё табличное: таблицы, представления, материализованные представления, последовательности. Если имя занято представлением, а ты ждёшь таблицу, сообщения об ошибке не будет вовсе.

Почему PostgreSQL приводит имена к нижнему регистру

Так требует стандарт SQL, только он предписывает верхний регистр, а PostgreSQL исторически выбрал нижний. Практический вывод один: не создавай объекты в кавычках, тогда регистр перестанет иметь значение.

Что делать с ошибкой синтаксиса вместо этой

Это другой класс сообщений, и разбирается он иначе. Как читать near ... at line N и почему опечатку надо искать перед указанным местом, разобрано в статье про синтаксическую ошибку.

Где потренироваться

В пути «SQL для аналитиков» на Koddo запросы пишутся в браузере с автопроверкой, и все эти ошибки встречаются на живых данных: соединения без псевдонимов, алиасы в фильтрах, WITH из нескольких шагов. Проверить себя можно на задаче про повторные отклики.

Источники