Cara memeriksa apakah ada tabel dalam skema yang diberikan

149

Postgres 8.4 dan database yang lebih besar berisi tabel umum dalam publicskema dan tabel khusus perusahaan dalam companyskema.
companynama skema selalu dimulai dengan 'company'dan diakhiri dengan nomor perusahaan.
Jadi mungkin ada skema seperti:

public
company1
company2
company3
...
companynn

Aplikasi selalu bekerja dengan satu perusahaan.
Ini search_pathditentukan sesuai dalam string koneksi odbc atau npgsql, seperti:

search_path='company3,public'

Bagaimana Anda memeriksa jika tabel yang diberikan ada dalam companynskema yang ditentukan ?

misalnya:

select isSpecific('company3','tablenotincompany3schema')

harus kembali false, dan

select isSpecific('company3','tableincompany3schema')

harus kembali true.

Bagaimanapun, fungsi harus memeriksa hanya companynskema yang lulus, bukan skema lainnya.

Jika tabel yang diberikan ada di keduanya publicdan skema yang diteruskan, fungsi harus kembali true.
Ini seharusnya berfungsi untuk Postgres 8.4 atau lebih baru.

Andrus
sumber

Jawaban:

284

Tergantung pada apa yang ingin Anda uji dengan tepat .

Skema informasi?

Untuk menemukan "apakah tabel itu ada" ( tidak peduli siapa yang bertanya ), menanyakan skema informasi ( information_schema.tables) tidak benar , secara tegas, karena ( per dokumentasi ):

Hanya tabel dan tampilan yang ditunjukkan bahwa pengguna saat ini memiliki akses ke (dengan cara menjadi pemilik atau memiliki hak istimewa).

Permintaan yang diberikan oleh @kong dapat kembali FALSE, tetapi tabel masih bisa ada. Itu menjawab pertanyaan:

Bagaimana cara memeriksa apakah tabel (atau tampilan) ada, dan pengguna saat ini memiliki akses ke sana?

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

Skema informasi terutama berguna untuk tetap portabel di versi utama dan di RDBMS yang berbeda. Tetapi implementasinya lambat, karena Postgres harus menggunakan pandangan yang canggih untuk memenuhi standar ( information_schema.tablesadalah contoh yang agak sederhana). Dan beberapa informasi (seperti OID) hilang dalam terjemahan dari katalog sistem - yang sebenarnya membawa semua informasi.

Katalog sistem

Pertanyaan anda adalah:

Bagaimana cara memeriksa apakah suatu tabel ada?

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
   );

Gunakan katalog sistem pg_classdan pg_namespacesecara langsung, yang juga jauh lebih cepat. Namun, per dokumentasi tentangpg_class :

Katalog pg_classkatalog tabel dan sebagian besar segala sesuatu yang memiliki kolom atau mirip dengan tabel. Ini termasuk indeks (tetapi lihat juga pg_index), urutan , tampilan , tampilan terwujud , tipe komposit , dan tabel TOAST ;

Untuk pertanyaan khusus ini, Anda juga dapat menggunakan sistempg_tables . Sedikit lebih sederhana dan lebih portabel di seluruh versi Postgres utama (yang hampir tidak menjadi perhatian untuk permintaan dasar ini):

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

Pengidentifikasi harus unik di antara semua objek yang disebutkan di atas. Jika Anda ingin bertanya:

Bagaimana cara memeriksa apakah nama untuk tabel atau objek serupa dalam skema yang diberikan diambil?

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'
   );

Alternatif: dilemparkan ke regclass

SELECT 'schema_name.table_name'::regclass

Ini memunculkan pengecualian jika tabel (yang memenuhi syarat skema) (atau objek lain yang menggunakan nama itu) tidak ada.

Jika Anda tidak memenuhi syarat skema nama tabel, gips ke regclassdefault ke search_pathdan mengembalikan OID untuk tabel pertama yang ditemukan - atau pengecualian jika tabel tidak ada dalam skema yang terdaftar. Perhatikan bahwa skema sistempg_catalog dan pg_temp(skema untuk objek sementara dari sesi saat ini) secara otomatis menjadi bagian dari search_path.

Anda bisa menggunakannya dan menangkap kemungkinan pengecualian dalam suatu fungsi. Contoh:

Kueri seperti di atas menghindari kemungkinan pengecualian dan karenanya sedikit lebih cepat.

to_regclass(rel_name) dalam Postgres 9.4+

Jauh lebih sederhana sekarang:

SELECT to_regclass('schema_name.table_name');

Sama seperti para pemain, tetapi mengembalikan ...

... null daripada melemparkan kesalahan jika nama tidak ditemukan

Erwin Brandstetter
sumber
4
dari shell:[[ `psql dbname -tAc "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'ejabberd' AND table_name = 'users');"` = 't' ]]
brauliobo
1
Apakah ada alasan Anda tidak menggunakan pg_tables ?
m0meni
1
pg_tablessebenarnya adalah ide yang bagus untuk "Bagaimana cara memeriksa apakah tabel ada?" (Memeriksa tabel hanya ., Bukan untuk tujuan lain, seperti dijelaskan di atas juga, pg_tablesadalah pandangan yang melibatkan beberapa tabel ( pg_class, pg_namespace, pg_tablespace), yang sedikit lebih mahal Yang paling alasan penting. Aku digunakan untuk permintaan pg_classlangsung dan tidak pikirkan pg_tablesketika menulis jawaban ini. Saya menambahkannya di atas sekarang, terima kasih.
Erwin Brandstetter
1
@ sage88: Benar, saya menghapus komentar salah saya. Anda bisa menggunakan pg_my_temp_schema()untuk mendapatkan OID dari skema temp aktual jika ada. (Tapi pandangan dalam information_schemajangan menyertakan OID. Anda bisa SELECT nspname FROM pg_namespace WHERE OID = pg_my_temp_schema()) Tes Anda memiliki beberapa kelemahan. Sebuah tes yang benar akan table_schema LIKE 'pg\_temp\_%'atau ketat: table_schema ~ '^pg_temp_\d+$'.
Erwin Brandstetter
1
@PeterKrauss Anda akan mendapatkan kesalahan itu jika Anda mencoba menggunakan fungsi to_regclass pada versi postgres yang lebih tua dari 9,4. Harus memiliki 9,4+
spetz83
44

Mungkin menggunakan information_schema :

SELECT EXISTS(
    SELECT * 
    FROM information_schema.tables 
    WHERE 
      table_schema = 'company3' AND 
      table_name = 'tableincompany3schema'
);
kong
sumber
0

Untuk PostgreSQL 9.3 atau kurang ... Atau yang suka semua dinormalisasi ke teks

Tiga rasa perpustakaan SwissKnife lama saya: relname_exists(anyThing), relname_normalized(anyThing)dan relnamechecked_to_array(anyThing). Semua cek dari tabel pg_catalog.pg_class , dan mengembalikan tipe data universal standar ( boolean , teks atau teks []).

/**
 * 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;
Peter Krauss
sumber