テーブル一覧の取得でつまずく場面は、コマンドを知らないことよりも「返ってきた一覧が全部だと思い込む」ところに集中します。\dtで何も出ないのに実際には表が存在する、開発環境と本番で行数が合わない、監査用に作った読み取り専用ロールで一覧を取ると半分しか見えない。いずれも取得手段ごとに対象範囲と権限の扱いが違うことが原因です。
この記事では、psqlのメタコマンド・information_schema・pg_catalogという3系統について、それぞれが何を対象にして何を落とすのかを一次情報の定義から整理しました。あわせてデータベース一覧やロール一覧のコマンド、シェルから機械可読で取り出す書き方、棚卸し表へ落とすときの注意まで扱います。データベース全般の位置づけはデータベースとは?種類・DBMS・RDBとNoSQLの選び方、導入そのものの手順はPostgreSQLのインストール手順で扱っています。動作確認は18系(2026年8月時点の最新メジャーは18.6)を前提としました。
まとめ:テーブル一覧の取得で先に押さえる4点
結論は次の4点です。第一に、対話操作なら\dtで十分ですが、これはsearch_path上で可視なスキーマだけが対象で、システムスキーマは既定で除外されます。全スキーマを見るならパターンにアスタリスクを2つ並べた形を渡します。
第二に、SQLで取る場合はinformation_schema.tablesとpg_catalog.pg_tablesで結果が変わります。前者はビューの定義自体に権限判定が組み込まれており、所有者でも表権限でもないオブジェクトは行ごと返りません。棚卸しや監査のように「存在するものを漏れなく数える」用途では後者を使います。
第三に、行数が想定と合わない典型は3つです。子パーティションが親と一緒に並ぶ、ビューや外部テーブルが混ざる、権限で落ちている。この3つを切り分ければ大半の食い違いは説明が付くはずです。
第四に、自動化するならpsqlの-Atや--csvで整形を外し、件数列にはreltuplesをそのまま使わないことです。統計が未収集の表では-1が返ります。以下、それぞれの根拠と書き方を順に見ていきます。
psqlのメタコマンドで一覧を出すときの既定の対象範囲と確認手順
PostgreSQLにはMySQLのSHOW TABLESに相当する文がありません。そのぶんpsql側のメタコマンドが揃っており、対話操作ではこちらが速いです。画面上で一覧を見たいときは、DBeaverのナビゲータからスキーマを参照する方法もあわせて検討できます。両者の設計差についてはPostgreSQLとMySQLの違いを徹底比較で扱いました。
既定の一覧がsearch_path上の可視オブジェクトに限られる理由
もっとも短い手順は、対象データベースへ接続して\dtを打つことです。
psql -d shopdb
shopdb=# \dt
List of relations
Schema | Name | Type | Owner
--------+--------+-------+-------
public | orders | table | app
public | users | table | app
ここで出ているのは「すべての表」ではありません。psqlがこのとき組み立てるSQLには、スキーマ名がpg_catalogとinformation_schemaのどちらでもないという条件に加えて、pg_table_is_visibleによる判定が入ります。可視とは、そのオブジェクトのスキーマがsearch_pathに含まれ、かつ同名同種のオブジェクトがsearch_pathのより手前に存在しない状態を指します。
つまりsalesスキーマへ表を作ってもsearch_pathがpublicのままなら、一覧には現れません。「作ったはずの表が見えない」という相談のかなりの割合は、権限ではなくこの可視性の問題でしょう。search_pathを触らずに確認したいときは、次項のパターン指定を使ってください。search_path自体の解決順序と設定できるスコープはPostgreSQLのスキーマ運用|search_pathの解決順とpublic権限で整理しています。
スキーマ修飾のパターンとS修飾子で表示範囲を広げる書き方と注意点
パターンを省略した\dtは、可視なオブジェクトだけを並べる指定と同じ意味になります。可視性に関係なくデータベース全体を見たいときは、スキーマ側にもワイルドカードを置いた形を渡します。
\dt *.* -- 可視性を問わず全スキーマの表
\dt sales.* -- salesスキーマだけ
\dt orders* -- 名前がordersで始まる表
\dtS -- システムカタログの表も含める
\dt+ sales.* -- 容量と永続性を追加
末尾にSを付けるとシステムオブジェクトが含まれます。これは\dnや\duなど他の一覧系コマンドでも共通の修飾で、ドキュメント上も「既定ではユーザーが作成したオブジェクトのみを表示し、パターンかS修飾子を与えるとシステムオブジェクトを含める」と定義されています。日々の確認では素の\dt、棚卸しではワイルドカード付きと使い分けるのが実務的です。
プラス付きで表示される容量と永続性の欄を読むときの2つの注意点
コマンド名の末尾にプラスを付けると、各オブジェクトに永続性(permanent、temporary、unlogged)とディスク上の物理サイズ、コメントが並びます。ここで表示されるサイズはそのリレーション本体のもので、付随する索引やTOAST領域を合算した値ではありません。
移行前の容量見積もりのように合算値が要る場面では、後述するpg_total_relation_sizeを使ってください。\dt+の数字だけで「合計してもダンプ容量に届かない」と悩むのは、この差を見落としているためです。
データベース一覧やロール一覧を出すコマンドと相当SQLの対応関係
一覧を求める場面はテーブルだけではありません。接続先の切り替えや権限の確認では、データベース・ロール・スキーマの一覧が同時に要ります。
接続先と権限の全体像を3つのコマンドで押さえる手順と確認項目
\lはデータベースの名前・所有者・文字コード・アクセス権限を並べます。プラスを付けるとサイズと既定のテーブルスペースが加わりますが、サイズが出るのは対象データベースへCONNECT権限を持つ場合か、スーパーユーザーあるいはpg_read_all_statsロールの権限を持つ場合に限られます。文字コードが導入時のロケールで決まる仕組みはインストール手順の記事で詳しく扱いました。
\duはロール一覧で、ユーザーとグループが統合された経緯から\dgと等価です。既定ではユーザー作成のロールだけが並び、\duSとするとpg_read_all_statsのような定義済みロールも現れます。スキーマ一覧は\dnで、権限まで見るなら\dn+を使います。
メタコマンドを同等のSQLへ置き換えるときの参照先対応と選定基準
メタコマンドはpsqlの機能なので、アプリケーションや監視スクリプトからは使えません。同じ情報をSQLで取るときの対応は次のとおりです。
| 目的 | メタコマンド | 相当するSQLの参照先 |
|---|---|---|
| テーブル一覧 | \dt | pg_catalog.pg_tables |
| ビュー一覧 | \dv | pg_catalog.pg_views |
| データベース一覧 | \l | pg_catalog.pg_database |
| ロール一覧 | \du | pg_catalog.pg_roles |
| スキーマ一覧 | \dn | pg_catalog.pg_namespace |
| 列の定義 | \d 表名 | information_schema |
対応表を自分で確かめたいときは、psqlを-E付きで起動してください。メタコマンドが内部で発行しているSQLがそのまま画面に出ます。同じことは接続後に\set ECHO_HIDDEN onとしても実現できます。手元の版で実際に投げられている問い合わせを写し取れるので、スクリプト化の下地としては確実な方法です。
information_schemaとpg_catalogで結果が変わる理由と選び分け
SQLでテーブル一覧を取る方法は大きく2系統あります。標準SQLに沿ったinformation_schemaと、PostgreSQL固有のpg_catalogです。どちらも正しく動きますが、返す範囲が違います。
標準ビュー側が権限の有無で行そのものを落とす条件と判定の仕組み
標準寄りの書き方は次の形です。
SELECT table_schema, table_name, table_type
FROM information_schema.tables
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY 1, 2;
table_typeにはBASE TABLE、VIEW、FOREIGN、LOCAL TEMPORARYのいずれかが入るため、表だけが欲しいならBASE TABLEで絞ります。ここで見落とされやすいのが権限の扱いです。ドキュメントには「現在のユーザーがアクセスできる(所有者であるか、何らかの権限を持つ)テーブルとビューのみが表示される」と明記されています。
18系のビュー定義を追うと、WHERE句はpg_has_roleで所有者ロールを持つこと、has_table_privilegeで表権限を持つこと、has_any_column_privilegeで列権限を持つこと、のいずれかを満たす条件です。列に値が入らないのではなく、条件から外れた表は行ごと消えます。棚卸し用のロールにSELECT権限を配り忘れると、一覧に出てこないまま「そんな表は無い」と結論してしまう事故が起きます。逆に、アプリケーションから「自分が触れる表だけ」を得たい場面では、この絞り込みがそのまま望ましい挙動になるでしょう。
pg_tablesが対象にするrelkindの範囲とビューの分かれ方
PostgreSQL固有の書き方はpg_catalog.pg_tablesです。
SELECT schemaname, tablename, tableowner, hasindexes
FROM pg_catalog.pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY 1, 2;
このビューの定義はrelkindがrかpのものに限られ、通常の表とパーティション親テーブルだけを対象にします。ビューはpg_views、マテリアライズドビューはpg_matviewsと別ビューへ分かれており、こちらは表側の一覧には混ざりません。マテリアライズドビューの位置づけはマテリアライズドビューとは?通常ビューとの違いで整理しています。
権限による絞り込みが定義に入っていない点が、標準ビュー側との決定的な差です。所有者・索引の有無・行レベルセキュリティの有効可否まで列が揃うので、資産棚卸しや移行前調査の起点にはこちらが向きます。
pg_classへ直接当てて対象の条件を自分で決める書き方と絞り込み条件
もう一段細かく制御したいならpg_classとpg_namespaceを直接結合します。relkindは次の値を取ります。
| relkind | 意味 | 一覧での扱い |
|---|---|---|
| r | 通常のテーブル | pg_tablesに出る |
| p | パーティション親 | pg_tablesに出る |
| v | ビュー | pg_viewsへ分離 |
| m | マテビュー | pg_matviewsへ分離 |
| f | 外部テーブル | 標準側でFOREIGN |
| S | シーケンス | 表の一覧には非表示 |
| t | TOASTテーブル | システム側に格納 |
この表を踏まえると、欲しい条件をそのまま書き下せます。
SELECT n.nspname, c.relname, c.relkind
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 'p')
AND NOT c.relispartition
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY 1, 2;
一覧に混ざるオブジェクトと落ちるオブジェクトの見分け方と判定条件
取得手段を決めても、行数が期待と合わないことがあります。原因はたいてい次の2つに絞られます。
子パーティションで行数が膨らむ条件と除外の指定方法と確認手順
psqlの\dtは子パーティションを除外しません。親テーブルと同じ一覧へ子も並ぶため、月次で切っているテーブルが数年分あると一覧が数百行に膨れます。子だけを外したいなら、前掲のSQLのようにrelispartitionの否定条件を付けるのが確実です。
親の側だけを見たい対話操作では\dPが使えます。パーティション化されたリレーションを一覧する指定で、tやiを付ければ表と索引を絞れます。プラスを付けると各パーティションのサイズ合計まで出るため、肥大の当たりを付けるにはこちらが速いでしょう。手元のpsqlで使えるかどうかはクライアント側の版に依存するので、判断が付かないときはPostgreSQLのバージョン確認方法で版の読み方を確認してください。
一時テーブルと他セッションの領域が見えない前提の理解と確認方法
一時テーブルはセッションごとの一時スキーマに置かれます。自分のセッションで作ったものはinformation_schema.tablesにLOCAL TEMPORARYとして出ますが、他のセッションの一時スキーマは定義側で除外されており、一覧には現れません。「バッチ実行中に作られているはずの作業表が見えない」という現象は、権限ではなくこの仕組みによるものです。一時表を挟んで差分を一括反映する書き方はPostgreSQLのUPSERT実装|ON CONFLICTの競合ターゲット指定とMERGE文の使い分けで扱いました。
マネージドサービスでも見え方は変わります。管理用ロールに与えられる権限が抑えられている場合、拡張が作る内部オブジェクトや一部のシステム情報が伏せられることがあります。サーバーレス構成での権限の考え方はNeonとは?サーバーレスPostgresの構成・料金と採用判断で扱いました。
シェルから取り出して定例の棚卸し表へ落とすまでの手順と保存形式
ここまでの内容を、月次や四半期で回せる形へ落とします。棚卸しの目的は、表の数を数えることではありません。消してよい表と手を入れてはいけない表を判別できる状態を保つことにあります。
整形を外して機械可読な出力にするための3つの指定と具体的な出力例
対話用の枠線と見出しが付いたままでは後段の処理が面倒になります。-Aで桁揃えを外し、-tで見出しと件数フッタを落とし、-Fで区切り文字を指定します。CSVとして扱うなら--csvが簡潔です。
psql -d shopdb -Atc "SELECT tablename FROM pg_tables WHERE schemaname = 'public'"
psql -d shopdb --csv -c "SELECT schemaname, tablename, tableowner FROM pg_tables"
複数データベースをまたぐ棚卸しでは、データベース一覧を先にSQLで集め、名前ごとに接続し直す二段構えにします。-cで渡した文字列は単一のリクエストとして送られ、明示的なBEGINとCOMMITを書かない限り1トランザクションとして実行される点は覚えておいてください。
件数とサイズの列を足すときにreltuplesが使えない場面
棚卸し表には件数とサイズを添えたくなります。サイズはpg_total_relation_sizeで索引とTOASTを含む合計が取れるため、そのまま使えます。
SELECT c.relname,
pg_size_pretty(pg_total_relation_size(c.oid)) AS total,
c.reltuples::bigint AS est_rows
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND n.nspname = 'public'
ORDER BY pg_total_relation_size(c.oid) DESC;
注意が要るのは件数側です。reltuplesはプランナ向けの推定値で、VACUUMやANALYZEなどで更新されます。一度もVACUUMもANALYZEもされていない表では-1が返り、行数が不明であることを示します。棚卸し表の件数列にそのまま流すと、新規作成直後の表が-1行として並ぶわけです。正確な値が要るなら対象を絞ってCOUNT関数で数え、規模の把握が目的ならサイズ列を主にして推定値は参考欄へ回すのが現実的でしょう。
棚卸しを内製で回す条件と保守運用へ渡すときの線引きと判断基準
一覧の取得自体は数分の作業ですが、棚卸しとして成立させるには継続が要ります。内製で回してよいのは、次の3条件がそろうときです。第一に、本番へ読み取り専用で接続できる経路が用意されていること。第二に、取得結果を前回分と比較して差分を追える置き場があること。第三に、増えた表と消えた表について開発側へ確認できる担当者がいること。
逆に、本番接続を特定の担当者しか持たない、結果を残す場所が個人の端末にしかない、確認の宛先が決まっていないという状態では、一覧を取っても判断材料になりません。その場合は保守運用・内製化支援のように、棚卸しの型づくりと定例化の部分だけを切り出して外部へ委ねる進め方があります。手を動かす部分より、判断の宛先を決めるほうが難しいためです。
維持工数を含めた費用の全体像から考えたい場合は、PostgreSQLのライセンスと価格:無償の範囲と実際に払う費用で5年総額の見方を示しました。ライセンス費が0円でも、こうした棚卸しの人件費は必ず発生します。
よくある質問
PostgreSQLにSHOW TABLESはありますか?
ありません。psqlの\dtがもっとも近い操作で、SQLで取るならpg_catalog.pg_tablesかinformation_schema.tablesを参照します。MySQLから移ってきた直後はSHOW TABLESを打って構文エラーになりがちですが、対応する文が用意されていないだけで、機能が不足しているわけではありません。
表があるはずなのに一覧が空で返るのはなぜですか?
多い順に3つの原因があります。接続先のデータベースが違う、表が置かれたスキーマがsearch_pathに入っていない、そのロールに権限が無い。まず\conninfoで接続先を確かめ、次にスキーマ側までワイルドカードを広げたパターンで探してみてください。それでも出ないなら権限を疑う順序になります。
information_schemaとpg_tablesはどちらを使うべきですか?
用途で分かれます。複数のDBMSへ同じ問い合わせを投げたい、あるいは接続ユーザーが触れる範囲だけを列挙したいならinformation_schemaです。存在するオブジェクトを漏れなく数える棚卸しや監査、所有者や索引の有無まで一度に見たい場合はpg_catalog側を選びます。
アプリ用のユーザーにテーブル一覧を取らせても問題ありませんか?
information_schema経由であれば、そのユーザーが権限を持つ範囲しか返らないため、想定外の情報が漏れる余地は小さいです。一方でpg_catalogのビューは権限で絞られないため、スキーマ構造そのものを見せたくない場面では参照先を分けてください。運用の実務では、棚卸し用ロールと業務用ロールを別に用意するのが安全でしょう。
テーブル一覧と同時に行数まで一度に出せますか?
推定値でよければpg_classのreltuplesを結合するだけで済みます。ただし統計が未収集の表は-1になるため、そのまま報告資料へ載せないでください。正確な行数が要る場合は対象表を絞ったうえでCOUNT関数を実行することになり、大きな表では全体走査の負荷が乗ります。定例で回すなら、サイズ列を主軸にして件数は必要な表だけ実測する形が扱いやすいです。