特定のスキーマにテーブルが存在するかどうかを確認する方法


149

Postgres 8.4以降のデータベースには、publicスキーマ内の共通テーブルとスキーマ内の会社固有のテーブルが含まれていcompanyます。
companyスキーマ名は常に'company'会社番号で始まり、会社番号で終わります。
したがって、次のようなスキーマがある場合があります。

public
company1
company2
company3
...
companynn

アプリケーションは常に1つの会社で動作します。同様に、ODBCまたはNpgsqlの接続文字列に応じて指定されています。
search_path

search_path='company3,public'

特定のcompanynスキーマが指定されたスキーマに存在するかどうかをどのように確認しますか?

例えば:

select isSpecific('company3','tablenotincompany3schema')

返す必要がありますfalse、と

select isSpecific('company3','tableincompany3schema')

戻る必要がありtrueます。

いずれの場合も、関数はcompanyn渡されたスキーマのみをチェックし、他のスキーマはチェックしないでください。

指定されたテーブルがpublic、渡されたスキーマの両方に存在する場合、関数はを返す必要がありますtrue
Postgres 8.4以降で動作するはずです。

回答:


283

正確に何をテストしたいかによります。

情報スキーマ?

(テーブルが存在するかどうか)(誰が質問しているかに関係なく)を見つけるために、情報スキーマ(information_schema.tables)へのクエリは、厳密には(ドキュメントごとに)次の理由により正しくありません

現在のユーザーが(所有者になるか、または何らかの特権を持つことによって)アクセスできるテーブルとビューのみが表示されます。

@kongによって提供されるクエリはを返すことができますFALSEが、テーブルはまだ存在できます。それは質問に答えます:

テーブル(またはビュー)が存在し、現在のユーザーがそれにアクセスできるかどうかを確認する方法は?

SELECT EXISTS (
   SELECT FROM information_schema.tables 
   WHERE  table_schema = 'schema_name'
   AND    table_name   = 'table_name'
   );

情報スキーマは主に、メジャーバージョン間および異なるRDBMS間で移植性を維持するのに役立ちます。しかし、Postgresは標準に準拠するために高度なビューを使用する必要があるため、実装は低速です(information_schema.tablesかなり単純な例です)。また、一部の情報(OIDなど)は、実際にはすべての情報を運ぶシステムカタログから変換中に失われます。

システムカタログ

あなたの質問は:

テーブルが存在するかどうかを確認するにはどうすればよいですか?

SELECT EXISTS (
   SELECT FROM pg_catalog.pg_class c
   JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
   WHERE  n.nspname = 'schema_name'
   AND    c.relname = 'table_name'
   AND    c.relkind = 'r'    -- only tables
   );

システムカタログpg_classpg_namespace直接使用します。これもかなり高速です。ただし、次のドキュメントに従ってpg_class

pg_classカタログは、テーブルと、列を含むその他のほとんどすべてのテーブルをカタログ化します。これには、インデックス(ただし参照もpg_index)、シーケンスビューマテリアライズドビュー複合タイプ、およびTOASTテーブルが含まれます。

この特定の質問では、システムビューをpg_tables使用することもできます。Postgresの主要なバージョン間で少しシンプルで移植性が高い(この基本的なクエリではほとんど問題にならない):

SELECT EXISTS (
   SELECT FROM pg_tables
   WHERE  schemaname = 'schema_name'
   AND    tablename  = 'table_name'
   );

識別子は、上記のすべてのオブジェクト間で一意である必要があります。質問したい場合:

特定のスキーマ内のテーブルまたは同様のオブジェクトの名前が使用されているかどうかを確認するにはどうすればよいですか?

SELECT EXISTS (
   SELECT FROM pg_catalog.pg_class c
   JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
   WHERE  n.nspname = 'schema_name'
   AND    c.relname = 'table_name'
   );

代替:キャスト regclass

SELECT 'schema_name.table_name'::regclass

これは、例外を発生させ(スキーマ修飾)テーブル(またはその名前を占める他のオブジェクト)が存在しない場合。

テーブル名をスキーマ修飾しない場合、regclassデフォルトのへのキャストによりsearch_path、最初に見つかったテーブルのOIDが返されます。または、テーブルがリストされているスキーマのいずれにもない場合は例外が返されます。システムスキーマpg_catalogおよびpg_temp(現在のセッションの一時オブジェクトのスキーマ)は自動的にの一部になることに注意してくださいsearch_path

これを使用して、関数で起こり得る例外をキャッチできます。例:

上記のようなクエリは、起こり得る例外を回避するため、わずかに高速です。

to_regclass(rel_name) Postgres 9.4以降

はるかに簡単になりました:

SELECT to_regclass('schema_name.table_name');

キャストと同じです、戻ります...

...名前が見つからない場合にエラーをスローするのではなくnull


4
シェルから:[[ `psql dbname -tAc "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'ejabberd' AND table_name = 'users');"` = 't' ]]
brauliobo

1
pg_tablesを使用しない理由はありますか?
m0meni 2017年

1
pg_tables「テーブルが存在するかどうかを確認する方法」の実際には良いアイデアです。(のためのチェックテーブルのみ。上記で説明のように、ではなく、他の目的のためにも、pg_tablesいくつかのテーブル(関係する図でありpg_classpg_namespacepg_tablespace少し高価である)、最も重要な理由:私は、クエリに使用されていますpg_class直接及びませんでした考えるpg_tablesこの答えを書くとき、私は上記の今これを追加しました、感謝。。
アーウィンBrandstetter

1
@ sage88:そう、私は間違ったコメントを削除しました。pg_my_temp_schema()実際の一時スキーマが存在する場合は、それを使用して実際の一時スキーマを取得できます。(しかし、ビューにinformation_schemaはOIDが含まれていません。可能ですSELECT nspname FROM pg_namespace WHERE OID = pg_my_temp_schema())テストにはいくつかの弱点があります。正しいテストはtable_schema LIKE 'pg\_temp\_%'次のとおりtable_schema ~ '^pg_temp_\d+$'です。
Erwin Brandstetter 2017年

1
@PeterKrauss 9.4より古いバージョンのpostgresでto_regclass関数を使用しようとすると、このエラーが発生します。9.4以上が必要
spetz83


0

PostgreSQL 9.3以下の場合...またはテキストに正規化されたすべてが好きな人

私の古いSwissKnifeライブラリーの3つのフレーバー:relname_exists(anyThing)relname_normalized(anyThing)およびrelnamechecked_to_array(anyThing)。すべてのチェックはpg_catalog.pg_classテーブルから行われ、標準のユニバーサルデータ型(booleantextまたはtext [])を返します。

/**
 * From my old SwissKnife Lib to your SwissKnife. License CC0.
 * Check and normalize to array the free-parameter relation-name.
 * Options: (name); (name,schema), ("schema.name"). Ignores schema2 in ("schema.name",schema2).
 */
CREATE FUNCTION relname_to_array(text,text default NULL) RETURNS text[] AS $f$
     SELECT array[n.nspname::text, c.relname::text]
     FROM   pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace,
            regexp_split_to_array($1,'\.') t(x) -- not work with quoted names
     WHERE  CASE
              WHEN COALESCE(x[2],'')>'' THEN n.nspname = x[1]      AND c.relname = x[2]
              WHEN $2 IS NULL THEN           n.nspname = 'public'  AND c.relname = $1
              ELSE                           n.nspname = $2        AND c.relname = $1
            END
$f$ language SQL IMMUTABLE;

CREATE FUNCTION relname_exists(text,text default NULL) RETURNS boolean AS $wrap$
  SELECT EXISTS (SELECT relname_to_array($1,$2))
$wrap$ language SQL IMMUTABLE;

CREATE FUNCTION relname_normalized(text,text default NULL,boolean DEFAULT true) RETURNS text AS $wrap$
  SELECT COALESCE(array_to_string(relname_to_array($1,$2), '.'), CASE WHEN $3 THEN '' ELSE NULL END)
$wrap$ language SQL IMMUTABLE;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.