Làm thế nào để kiểm tra nếu một bảng tồn tại trong một lược đồ nhất định


149

Postgres 8.4 và các cơ sở dữ liệu lớn hơn chứa các bảng chung trong publiclược đồ và các bảng cụ thể của công ty trong companylược đồ.
companytên lược đồ luôn bắt đầu bằng 'company'và kết thúc bằng số công ty.
Vì vậy, có thể có các lược đồ như:

public
company1
company2
company3
...
companynn

Một ứng dụng luôn hoạt động với một công ty duy nhất.
Điều search_pathnày được chỉ định tương ứng trong chuỗi kết nối odbc hoặc npgsql, như:

search_path='company3,public'

Làm thế nào bạn sẽ kiểm tra nếu một bảng nhất định tồn tại trong một companynlược đồ được chỉ định ?

ví dụ:

select isSpecific('company3','tablenotincompany3schema')

nên trở lại false, và

select isSpecific('company3','tableincompany3schema')

nên trở về true.

Trong mọi trường hợp, hàm chỉ nên kiểm tra companynlược đồ được truyền, không phải các lược đồ khác.

Nếu một bảng đã cho tồn tại trong cả hai publicvà lược đồ đã qua, hàm sẽ trả về true.
Nó sẽ hoạt động cho Postgres 8.4 trở lên.

Câu trả lời:


283

Nó phụ thuộc vào những gì bạn muốn kiểm tra chính xác .

Lược đồ thông tin?

Để tìm "liệu bảng có tồn tại không" ( bất kể ai hỏi ), truy vấn lược đồ thông tin ( information_schema.tables) là không chính xác , nói đúng, bởi vì ( theo tài liệu ):

Chỉ những bảng và chế độ xem được hiển thị rằng người dùng hiện tại có quyền truy cập (bằng cách là chủ sở hữu hoặc có một số đặc quyền).

Truy vấn được cung cấp bởi @kong có thể trả về FALSE, nhưng bảng vẫn có thể tồn tại. Nó trả lời câu hỏi:

Làm cách nào để kiểm tra xem bảng (hoặc dạng xem) có tồn tại không và người dùng hiện tại có quyền truy cập vào bảng không?

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

Lược đồ thông tin chủ yếu hữu ích để duy trì khả năng di động trên các phiên bản chính và trên các RDBMS khác nhau. Nhưng việc triển khai còn chậm, vì Postgres phải sử dụng các quan điểm tinh vi để tuân thủ tiêu chuẩn ( information_schema.tableslà một ví dụ khá đơn giản). Và một số thông tin (như OID) bị mất trong bản dịch từ danh mục hệ thống - mà thực sự mang tất cả thông tin.

Danh mục hệ thống

Câu hỏi của bạn là:

Làm thế nào để kiểm tra xem một bảng có tồn tại?

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

Sử dụng các danh mục hệ thống pg_classpg_namespacetrực tiếp, cũng nhanh hơn đáng kể. Tuy nhiên, mỗi tài liệu vềpg_class :

Các danh mục pg_classdanh mục bảng và hầu hết mọi thứ khác có cột hoặc tương tự như bảng. Điều này bao gồm các chỉ mục (nhưng cũng xem pg_index), trình tự , khung nhìn , khung nhìn cụ thể hóa , kiểu tổng hợpbảng TOAST ;

Đối với câu hỏi cụ thể này, bạn cũng có thể sử dụng chế độ xem hệ thốngpg_tables . Một chút đơn giản và dễ mang theo hơn trên các phiên bản Postgres chính (điều này hầu như không đáng quan tâm đối với truy vấn cơ bản này):

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

Định danh phải là duy nhất trong số tất cả các đối tượng được đề cập ở trên. Nếu bạn muốn hỏi:

Làm thế nào để kiểm tra xem tên của một bảng hoặc đối tượng tương tự trong một lược đồ đã cho có được thực hiện không?

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

Thay thế: chuyển sang regclass

SELECT 'schema_name.table_name'::regclass

Điều này đặt ra một ngoại lệ nếu bảng (tùy chọn đủ điều kiện lược đồ) (hoặc đối tượng khác chiếm tên đó) không tồn tại.

Nếu bạn không đủ điều kiện lược đồ tên bảng, thì một giá trị regclassmặc định cho search_pathvà trả về OID cho bảng đầu tiên được tìm thấy - hoặc một ngoại lệ nếu bảng không nằm trong các lược đồ được liệt kê. Lưu ý rằng các lược đồ hệ thống pg_catalogpg_temp(lược đồ cho các đối tượng tạm thời của phiên hiện tại) tự động là một phần của search_path.

Bạn có thể sử dụng và bắt một ngoại lệ có thể có trong một hàm. Thí dụ:

Một truy vấn như trên tránh được các ngoại lệ có thể xảy ra và do đó nhanh hơn một chút.

to_regclass(rel_name) trong Postgres 9,4+

Bây giờ đơn giản hơn nhiều:

SELECT to_regclass('schema_name.table_name');

Giống như các diễn viên, nhưng nó trở lại ...

... null thay vì ném lỗi nếu không tìm thấy tên


4
từ vỏ:[[ `psql dbname -tAc "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'ejabberd' AND table_name = 'users');"` = 't' ]]
brauliobo

1
Có bất kỳ lý do bạn không sử dụng pg_tables ?
m0meni

1
pg_tablesthực sự là một ý tưởng tốt cho "Làm thế nào để kiểm tra xem bảng có tồn tại không?" (Kiểm tra các bảng chỉ ., Không phải cho mục đích khác, như giải thích ở trên Ngoài ra, pg_tableslà một cái nhìn liên quan đến một số bảng ( pg_class, pg_namespace, pg_tablespace), đó là đắt hơn một chút Lý do quan trọng nhất:. Tôi đang sử dụng để truy vấn pg_classtrực tiếp và không nghĩ về pg_tableskhi viết câu trả lời này. Tôi đã thêm nó ở trên bây giờ, cảm ơn.
Erwin Brandstetter

1
@ sage88: Phải, tôi đã xóa nhận xét không chính xác của mình. Bạn có thể sử dụng pg_my_temp_schema()để lấy OID của lược đồ tạm thời thực tế nếu nó tồn tại. (Nhưng các quan điểm trong information_schemakhông bao gồm OID. Bạn có thể SELECT nspname FROM pg_namespace WHERE OID = pg_my_temp_schema()) Bài kiểm tra của bạn có một số điểm yếu. Một bài kiểm tra chính xác sẽ table_schema LIKE 'pg\_temp\_%'hoặc nghiêm ngặt hơn : table_schema ~ '^pg_temp_\d+$'.
Erwin Brandstetter

1
@PeterKrauss Bạn sẽ gặp lỗi đó nếu bạn cố gắng sử dụng hàm to_reg class trên phiên bản của postgres cũ hơn 9,4. Phải có 9,4+
spetz83

44

Có lẽ sử dụng information_schema :

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

0

Dành cho PostgreSQL 9.3 trở xuống ... Hoặc những người thích tất cả bình thường hóa thành văn bản

Ba hương vị của thư viện SwissKnife cũ của tôi: relname_exists(anyThing), relname_normalized(anyThing)relnamechecked_to_array(anyThing). Tất cả các kiểm tra từ bảng pg_catalog.pg_group và trả về các kiểu dữ liệu phổ quát tiêu chuẩn ( boolean , văn bản hoặc văn bản []).

/**
 * 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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.