- データベース
PostgreSQL relation does not existの原因と直し方
ERROR: relation "users" does not exist は、PostgreSQL がそのテーブル(やビュー・シーケンス)を見つけられなかったときのエラーです。 テーブルが本当に無いとは限らず、名前の大文字小文字・スキーマ・接続先のデータベースの違いで「見えていない」ことがほとんどです。
エラー文の "users" は、PostgreSQL が実際に探した名前です。書いた名前と違っていないかを最初に見ます。
| 原因 | 見分け方 | 直し方 |
|---|---|---|
大文字で作ったテーブル("Users") |
FROM Users と書いたのに、エラー文は "users"(小文字) |
FROM "Users" と引用符で囲む。または小文字の名前で作り直す |
別のスキーマにある(app.items) |
下の SQL で探すと、public 以外のスキーマにある |
app.items と書く、または search_path を設定 |
| 別のデータベースにつないでいる | current_database() が想定と違う |
接続先のデータベース名を直す |
| 作った接続が、まだコミットしていない | 別の接続からだけ見えない | 作った側でコミット |
確認環境:PostgreSQL 16.15、psycopg 3.3.6(確認日 2026年10月11日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PostgreSQL relation does not existの原因と直し方」
まず確認:テーブルは本当にあるか
SELECT current_database(), current_schemas(true);
SELECT table_schema, table_name
FROM information_schema.tables
WHERE lower(table_name) IN ('users', 'items', 'orders')
ORDER BY 1, 2;
reldemo | {pg_catalog,public} ← 接続先のデータベースと、探す対象のスキーマ
app | items ← items は app スキーマにある
public | Users ← Users は大文字の U で作られている
public | orders
lower(table_name) で探すと、大文字小文字に関係なく見つかります。 ここで見つかった「スキーマ」と「正確な名前」で、どの原因かが分かります。psql なら \dt *.* でも一覧を出せます(システムのテーブルも大量に表示されます)。
原因1:大文字で作ったテーブル
CREATE TABLE "Users" (id int, name text); -- 引用符つきで、大文字の U
SELECT * FROM Users;
ERROR: relation "users" does not exist
LINE 1: SELECT * FROM Users;
Users と書いたのに、エラー文は "users" になっています。 PostgreSQL は、引用符で囲まない名前を小文字に変えてから探します(PostgreSQL 公式ドキュメント)。"Users" という名前のテーブルは、引用符で囲んだときだけ見つかります。
| 書き方 | 探す名前 | "Users" で作ったテーブル |
orders で作ったテーブル |
|---|---|---|---|
Users・users・USERS |
users |
見つからない | — |
"Users" |
Users |
見つかる | — |
orders・ORDERS |
orders |
— | 見つかる |
"Orders" |
Orders |
— | 見つからない |
公式ドキュメントでは、FOO・foo・"foo" は同じ、"Foo"・"FOO" はそれぞれ別の名前とされています。一度大文字を含む名前(引用符つき)で作ると、以降ずっと引用符が必要です。
| 直し方 | 書き方 |
|---|---|
| 毎回引用符で囲む | SELECT * FROM "Users"(Java の文字列なら "SELECT * FROM \"Users\"") |
| 小文字の名前に変える(おすすめ) | ALTER TABLE "Users" RENAME TO users; |
大文字を含む名前でテーブルを作るツール・ORM・移行ツールを使っていると、この形になります。MySQL や SQL Server から移した DDL で、名前を引用符で囲んで作った場合も同じです。PostgreSQL では、テーブル名・列名は小文字とアンダースコアにそろえるのが無難です。
原因2:別のスキーマにある(search_path)
CREATE SCHEMA app;
CREATE TABLE app.items (id int);
SELECT * FROM items;
ERROR: relation "items" does not exist
SHOW search_path;
search_path
-----------------
"$user", public ← app スキーマは探す対象に入っていない
スキーマ名を付けずに書いたテーブルは、search_path の順にスキーマを探します(公式ドキュメント)。既定は「ユーザー名と同じスキーマ」と public で、ほかのスキーマのテーブルは、存在していても見つかりません。
| 直し方 | 書き方 | 範囲 |
|---|---|---|
| スキーマ名を付ける | SELECT * FROM app.items; |
その SQL だけ |
| その接続で設定 | SET search_path TO app, public; |
その接続の間 |
| ロール(ユーザー)に設定 | ALTER ROLE tx SET search_path TO app, public; |
次に接続したときから |
| データベースに設定 | ALTER DATABASE reldemo SET search_path TO app, public; |
次に接続したときから |
確認環境でも、ALTER ROLE で設定したあと、接続し直すと search_path が app, public になり、items が見つかりました。アプリの接続 URL に設定する方法もあります(JDBC なら currentSchema=app)。
原因3:接続先のデータベースが違う
(reldemo で作った orders を、txdemo に接続して検索)
ERROR: relation "orders" does not exist
PostgreSQL では、データベースが違うと、テーブルは見えません(スキーマ名を付けても見えない)。開発用と本番用、アプリと psql で、接続先のデータベース名が違っていないかを SELECT current_database(); で確かめます。
原因4:作った接続が、まだコミットしていない
(接続 A で CREATE TABLE newtab、まだコミットしていない)
(接続 B から SELECT * FROM newtab)
UndefinedTable: relation "newtab" does not exist
(接続 A でコミット後)
(接続 B から SELECT * FROM newtab)→ OK
PostgreSQL では、CREATE TABLE もトランザクションの中で実行されます。 作った接続でコミットするまで、ほかの接続からは見えませんでした。マイグレーションのツールやテストで、作成とコミットのタイミングがずれていないかを確かめます。
似たエラー:権限が無い
ERROR: permission denied for table secret
テーブルはあるが、権限が無いときは、does not exist ではなく permission denied になりました。別の問題なので、GRANT で権限を確かめます。
確認の手順(チェックリスト)
| 順番 | 確認すること | 方法 |
|---|---|---|
| 1 | エラー文の名前 | "users" のように、小文字になっていないか |
| 2 | テーブルの実際の名前とスキーマ | information_schema.tables を lower(table_name) で探す |
| 3 | 大文字の名前 | "Users" と引用符で囲む、または小文字に変える |
| 4 | スキーマ | スキーマ名を付ける、search_path を設定 |
| 5 | データベース | SELECT current_database(); |
| 6 | コミット | 作った接続でコミットしたか |
よくある質問
Q. SQLSTATE は何ですか。
A. 42P01(undefined_table)です。psycopg では UndefinedTable の例外になります(確認環境でも同じ)。
Q. シーケンスでも同じエラーになりますか。
A. なります。relation "users_id_seq" does not exist のように、テーブル以外(シーケンス・ビュー・インデックス)でも relation と表示されます。名前とスキーマを同じ手順で確かめます。
まとめ
- テーブルが見つからないエラー。多くは名前・スキーマ・データベースの違い
- 引用符なしの名前は小文字に変えて探す。
"Users"で作ったら、"Users"と書く(小文字に変えるのがおすすめ) - 別のスキーマのテーブルは、
search_pathに無いと見つからない。スキーマ名を付けるか、設定する - データベースが違うと見えない。
current_database()で確かめる - CREATE TABLE もコミットまでほかの接続から見えない
- 権限が無いときは
permission denied(別のエラー)
参考・出典
確認日はいずれも 2026年10月11日です。
- PostgreSQL 16 Documentation「Lexical Structure」(Identifiers and Key Words):https://www.postgresql.org/docs/16/sql-syntax-lexical.html
- PostgreSQL 16 Documentation「Schemas」(The Schema Search Path):https://www.postgresql.org/docs/16/ddl-schemas.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PostgreSQL relation does not existの原因と直し方」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。