PostgreSQL 查看表的主外鍵等約束關系詳解

我就廢話不多說瞭,大傢還是直接看代碼吧~

SELECT
   tc.constraint_name, tc.table_name, kcu.column_name, 
   ccu.table_name AS foreign_table_name,
   ccu.column_name AS foreign_column_name,
   tc.is_deferrable,tc.initially_deferred
 FROM 
   information_schema.table_constraints AS tc 
   JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
   JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
 WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = 'your table name';

constraint_type有四種:

UNIQUE、PRIMARY KEY、CHECK、FOREIGN KEY

通過修改上邊sql語句的table_name和constraint_type來進行相應的查詢

補充:PostgreSQL查詢約束和創建刪除約束

查詢約束constraint

SELECT
   tc.constraint_name, tc.table_name, kcu.column_name, 
   ccu.table_name AS foreign_table_name,
   ccu.column_name AS foreign_column_name,
   tc.is_deferrable,tc.initially_deferred
 FROM 
   information_schema.table_constraints AS tc 
   JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
   JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
 WHERE constraint_type = 'UNIQUE' AND tc.table_name = 'table_name'; 

constraint_type有四種:

UNIQUE、PRIMARY KEY、CHECK、FOREIGN KEY, 通過修改上邊sql語句的table_name和constraint_type來進行相應的查詢。

添加約束

ALTER TABLE table_name ADD CONSTRAINT uk_users_name1 UNIQUE (NAME);

刪除約束

alter table table_name drop constraint if EXISTS uk_users_name1;

補充:PostgreSQL的依賴約束(系統表pg_depend和pg_constraint)詳解

pg_depend是postgres的一張系統表,用來記錄數據庫對象之間的依賴關系,除瞭常見的主外鍵,還有其他一些內部依賴關系,可以通過這個系統表呈現出來。

一、表結構:

postgres=# \d+ pg_depend
            Table "pg_catalog.pg_depend"
  Column  | Type  | Modifiers | Storage | Stats target | Description
-------------+---------+-----------+---------+--------------+-------------
 classid   | oid   | not null | plain  |       | 系統OID
 objid    | oid   | not null | plain  |       | 對象OID
 objsubid  | integer | not null | plain  |       |
 refclassid | oid   | not null | plain  |       | 引用系統OID
 refobjid  | oid   | not null | plain  |       | 引用對象ID
 refobjsubid | integer | not null | plain  |       |
 deptype   | "char" | not null | plain  |       | pg_depend類型
Indexes:
  "pg_depend_depender_index" btree (classid, objid, objsubid)
  "pg_depend_reference_index" btree (refclassid, refobjid, refobjsubid)
Has OIDs: no

–BTW:OID是Object Identifier的縮寫,是對象ID的意思,因為是無符號的4字節類型,不夠足夠大,所以一般不用來做主鍵使用,僅系統內部,比如系統表等應用,可以與一些整型數字進行轉換。與之相關的系統參數是default_with_oids,默認是off

postgres=# \d pg_constraint
   Table "pg_catalog.pg_constraint"
  Column   |   Type   | Modifiers 
---------------+--------------+-----------
 conname    | name     | not null    -- 約束名
 connamespace | oid     | not null    -- 約束所在命名空間的OID
 contype    | "char"    | not null    -- 約束類型
 condeferrable | boolean   | not null    -- 約束是否可以推遲
 condeferred  | boolean   | not null    -- 缺省情況下,約束是否可以推遲
 convalidated | boolean   | not null    -- 約束是否經過驗證
 conrelid   | oid     | not null    -- 約束所在的表的OID
 contypid   | oid     | not null    -- 約束所在的域的OID
 conindid   | oid     | not null    -- 如果是唯一、主鍵、外鍵或排除約束,則為支持這個約束的索引;否則為0
 confrelid   | oid     | not null    -- 如果是外鍵,則為參考的表;否則為 0
 confupdtype  | "char"    | not null    -- 外鍵更新操作代碼
 confdeltype  | "char"    | not null    -- 外鍵刪除操作代碼
 confmatchtype | "char"    | not null    -- 外鍵匹配類型
 conislocal  | boolean   | not null    
 coninhcount  | integer   | not null    -- 約束直接繼承祖先的數量
 connoinherit | boolean   | not null    
 conkey    | smallint[]  |     -- 如果是表約束(包含外鍵,但是不包含約束觸發器),則是約束字段的列表
 confkey    | smallint[]  |     -- 如果是一個外鍵,是參考的字段的列表
 conpfeqop   | oid[]    |     -- 如果是一個外鍵,是PK = FK比較的相等操作符的列表
 conppeqop   | oid[]    |    -- 如果是一個外鍵,是PK = PK比較的相等操作符的列表
 conffeqop   | oid[]    |     -- 如果是一個外鍵,是FK = FK比較的相等操作符的列表
 conexclop   | oid[]    |     -- 如果是一個排除約束,是每個字段排除操作符的列表
 conbin    | pg_node_tree |     -- 如果是一個檢查約束,那就是其表達式的內部形式
 consrc    | text     |     -- 如果是檢查約束,則是表達式的人類可讀形式
Indexes:
  "pg_constraint_oid_index" UNIQUE, btree (oid)
  "pg_constraint_conname_nsp_index" btree (conname, connamespace)
  "pg_constraint_conrelid_index" btree (conrelid)
  "pg_constraint_contypid_index" btree (contypid)

pg_depend.deptype字段類型9.1之後多瞭一個extension的類型,目前類型有

DEPENDENCY_NORMAL (n)   :普通的依賴對象,如表與schema的關系
DEPENDENCY_AUTO (a)    :自動的依賴對象,如主鍵約束
DEPENDENCY_INTERNAL (i)  :內部的依賴對象,通常是對象本身
DEPENDENCY_EXTENSION (e) :9.1新增的的擴展依賴
DEPENDENCY_PIN (p)    :系統內置的依賴

二、例子

wiki上有一個SQL可以列出系統和用戶對象的各種依賴關系,低版本的可以看wiki上的另一個寫法

SELECT classid::regclass AS "depender object class",
  CASE classid
    WHEN 'pg_class'::regclass THEN objid::regclass::text
    WHEN 'pg_type'::regclass THEN objid::regtype::text
    WHEN 'pg_proc'::regclass THEN objid::regprocedure::text
    ELSE objid::text
  END AS "depender object identity",
  objsubid,
  refclassid::regclass AS "referenced object class",
  CASE refclassid
    WHEN 'pg_class'::regclass THEN refobjid::regclass::text
    WHEN 'pg_type'::regclass THEN refobjid::regtype::text
    WHEN 'pg_proc'::regclass THEN refobjid::regprocedure::text
    ELSE refobjid::text
  END AS "referenced object identity",
  refobjsubid,
  CASE deptype
    WHEN 'p' THEN 'pinned'
    WHEN 'i' THEN 'internal'
    WHEN 'a' THEN 'automatic'
    WHEN 'n' THEN 'normal'
  END AS "dependency type"
FROM pg_catalog.pg_depend WHERE (objid >= 16384 OR refobjid >= 16384);

BTW:我通常喜歡在where後面加個條件 and deptype <>’i’ 排除internal依賴

建一張普通的表,執行上面的SQL

postgres=# create table tbl_parent(id int);
CREATE TABLE
postgres=# 執行上面的SQL;
 depender object class | depender object identity | objsubid | referenced object class | referenced object identity | refobjsubid | dependency type
-----------------------+--------------------------+----------+-------------------------+------------- pg_class       | tbl_parent        |    0 | pg_namespace      | 2200            |      0 | normal
(1 row)

–普通用戶來看隻是建瞭個表,但是沒有約束,其實因為這個表是建立在schema下面,表是依賴於schema上面的

加一個主鍵約束

postgres=# alter table tbl_parent add primary key(id);
ALTER TABLE
 depender object class | depender object identity | objsubid | referenced object class | referenced object identity | refobjsubid | dependency type
-----------------------+--------------------------+----------+-------------------------+------- pg_class       | tbl_parent        |    0 | pg_namespace      | 2200            |      0 | normal
 pg_constraint     | 16469          |    0 | pg_class        | tbl_parent         |      1 | automatic
(2 rows)

–多瞭一個約束的信息,下面的這條信息表明這個主鍵約束是依賴於表上的,並且是自動模式,詳細信息可以在系統表pg_constrant裡面查詢

三、非正常刪除

正常情況下用戶刪除有依賴關系的對象時會提示需要先刪除最裡層沒依賴的對象,但是如果通過刪除系統表,但又刪得不對,就會導致異常,比如上面這個例子會出現 cache lookup failed for constraint

postgres=# select oid,conname,connamespace,contype from pg_constraint where conname like 'tbl_parent%';
 oid |   conname   | connamespace | contype
-------+-----------------+--------------+---------
 16469 | tbl_parent_pkey |     2200 | p
(1 row)
 
postgres=# delete from pg_constraint where conname like 'tbl_parent%';
DELETE 1
postgres=# select oid,conname,connamespace,contype from pg_constraint where conname like 'tbl_parent%';
 oid | conname | connamespace | contype
-----+---------+--------------+---------
(0 rows)
 
postgres=# drop table tbl_parent;
ERROR: cache lookup failed for constraint 16469  --16496是約束的OID
postgres=#

–出現這個問題,是因為手工把約束對象刪除瞭,但是在pg_depend依賴關系裡面卻仍然存在關系,所以刪除該表時發現最裡層的依賴對象找不到瞭就報錯瞭,

解決:

1.手工恢復該表的約束對象,比較難也比較煩

2.刪除該表所有的系統依賴信息 上面的問題需要刪除

postgres=# delete from pg_depend where objid = 16469 or refobjid = 16469 ;
DELETE 2
postgres=# drop table tbl_parent;
DROP TABLE

3.要說一點的是不要去手工刪除一些系統表信息來達到刪除約束的目的,容易因刪不幹凈而造成各種異常

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。如有錯誤或未考慮完全的地方,望不吝賜教。

推薦閱讀: