8. PostgreSQL邏輯結(jié)構(gòu)(2)——表

在PostgreSQL中,數(shù)據(jù)就是存放在數(shù)據(jù)表中,在一張數(shù)據(jù)表中,每一列就是一個(gè)字段,即表示哪種類型的數(shù)據(jù)。每一行就是不同的類型的數(shù)據(jù)。對(duì)數(shù)據(jù)表的操作也是:創(chuàng)建、修改和刪除。

創(chuàng)建表

創(chuàng)建表的基本命令格式如下:

CREATE TABLE table_name (
field1  datatype1,
field2  datatype2,
....
);

格式就是在括號(hào)中一個(gè)字段名和一個(gè)字段類型,字段和字段類型之間用空格隔開。不同字段之間用逗號(hào)隔開。示例如下:

postgres=# CREATE TABLE testdb1 (id int, comments varchar(20)); 
CREATE TABLE
postgres=# \d testdb1;
            Table "public.testdb1"
  Column  |         Type          | Modifiers 
----------+-----------------------+-----------
 id       | integer               | 
 comments | character varying(20) | 

上面的示例中就創(chuàng)建了一個(gè)普通的表,表中沒有任何約束。約束指的是對(duì)表中的字段進(jìn)行一定的限制,或?qū)蓚€(gè)不同的表之間的字段進(jìn)行限制。約束的類型主要包括:

  • 主鍵約束,分為單字段主鍵和多字段主鍵,后者也被稱為復(fù)合主鍵。
  • 外鍵約束,表中的字段和另外一張表里的字段有對(duì)應(yīng)關(guān)系,更新一張表時(shí)必須檢查另外一張表
  • 唯一約束,有這個(gè)約束的字段中的值只能出現(xiàn)一次。一般用于具有唯一性的數(shù)值。
  • 非空約束,即這個(gè)字段的值不能是null
  • 檢查約束,即Check約束,用于定義某些字段的值必須滿足某些要求。

下面幾個(gè)示例中分別顯示了定義表的時(shí)候?qū)Ρ淼哪承┳侄芜M(jìn)行約束。

主鍵約束、唯一約束和非空約束
#上面的表中,我們創(chuàng)建了一個(gè)兩個(gè)字段的表testdb1,且表中沒有任何約束,如下所示:
postgres=# \d testdb1
            Table "public.testdb1"
  Column  |         Type          | Modifiers 
----------+-----------------------+-----------
 id       | integer               | 
 comments | character varying(20) | 

將這個(gè)表的id字段設(shè)置為表的主鍵,可以看到id字段也自動(dòng)變成了非空字段(not null)

postgres=# alter table testdb1 add primary key(id);
ALTER TABLE
postgres=# \d testdb1 
            Table "public.testdb1"
  Column  |         Type          | Modifiers 
----------+-----------------------+-----------
 id       | integer               | not null
 comments | character varying(20) | 
Indexes:
    "testdb1_pkey" PRIMARY KEY, btree (id)

嘗試往表中插入數(shù)據(jù)

postgres=# insert into testdb1 values(1, 'test'); 
INSERT 0 1
postgres=# insert into testdb1 values(2, 'not all'); 
INSERT 0 1
postgres=# insert into testdb1 values(2, 'not you'); 
ERROR:  duplicate key value violates unique constraint "testdb1_pkey"
DETAIL:  Key (id)=(2) already exists.
postgres=# select * from testdb1;
 id | comments 
----+----------
  1 | test
  2 | not all
(2 rows)

上面進(jìn)行了3次插入操作,前兩次插入成功,第三次插入失敗。第三次插入的數(shù)據(jù)中id=2,和第二次插入的數(shù)據(jù)中的id相同,說明主鍵具有唯一性。
通過字段名進(jìn)行插入,如下所示:

postgres=# insert into testdb1(comments) values('not you');
ERROR:  null value in column "id" violates not-null constraint
DETAIL:  Failing row contains (null, not you).

發(fā)現(xiàn)插入出錯(cuò),說明主鍵字段id不會(huì)自增到下一個(gè)值,說明主鍵沒有自增特性
同時(shí)也看到了非空約束的作用,就是限制這個(gè)字段的值不能是一個(gè)空值。

#刪除主鍵約束
postgres=# alter table testdb1 drop constraint testdb1_pkey;
ALTER TABLE

postgres=# \d testdb1;
            Table "public.testdb1"
  Column  |         Type          | Modifiers 
----------+-----------------------+-----------
 id       | integer               | not null
 comments | character varying(20) | 

從上面的示例中講解了主鍵約束的添加、數(shù)值插入、以及刪除操作。從四個(gè)簡(jiǎn)短的小示例中我們看到主鍵約束有以下特性:

  • 非空
  • 唯一性
  • 不會(huì)自動(dòng)增加值

也看到了唯一性約束和非空約束的作用:

  • 唯一約束的字段值不能重復(fù)
  • 非空約束的字段對(duì)應(yīng)的值不能是一個(gè)空值

如果沒有定義主鍵,而是想直接定義另外一個(gè)鍵的類型為唯一約束,可以使用關(guān)鍵字UNIQUE,示例如下:

postgres=# alter table testdb1 add unique(comments);
ALTER TABLE
postgres=# \d testdb1; 
            Table "public.testdb1"
  Column  |         Type          | Modifiers 
----------+-----------------------+-----------
 id       | integer               | not null
 comments | character varying(20) | 
Indexes:
    "testdb1_comments_key" UNIQUE CONSTRAINT, btree (comments)

postgres=# select * from testdb1; 
 id | comments 
----+----------
  1 | test
  2 | not all
(2 rows)

postgres=# insert into testdb1 values(3, 'test');
ERROR:  duplicate key value violates unique constraint "testdb1_comments_key"
DETAIL:  Key (comments)=(test) already exists.

刪除唯一性約束

postgres=# alter table testdb1 drop constraint testdb1_comments_key;
ALTER TABLE
postgres=# \d testdb1; 
            Table "public.testdb1"
  Column  |         Type          | Modifiers 
----------+-----------------------+-----------
 id       | integer               | not null
 comments | character varying(20) | 

在上面示例中,我們將comments字段設(shè)置為UNIQE約束,然后做了一次插入操作,插入的comments值和id=1時(shí)的值相同,此時(shí)發(fā)生了報(bào)錯(cuò)。說明UNIQE約束生效。

而單獨(dú)設(shè)置某個(gè)字段為not null類型字段,則需要使用另外一種命令格式:

#在comments字段上添加非空約束
postgres=# alter table testdb1 alter comments set not null; 
ALTER TABLE
postgres=# \d testdb1; 
            Table "public.testdb1"
  Column  |         Type          | Modifiers 
----------+-----------------------+-----------
 id       | integer               | not null
 comments | character varying(20) | not null
Indexes:
    "testdb1_comments_key" UNIQUE CONSTRAINT, btree (comments)

# 取消comments字段的非空約束
postgres=# alter table testdb1 alter comments drop not null; 
ALTER TABLE
postgres=# \d testdb1;
            Table "public.testdb1"
  Column  |         Type          | Modifiers 
----------+-----------------------+-----------
 id       | integer               | not null
 comments | character varying(20) | 
Indexes:
    "testdb1_comments_key" UNIQUE CONSTRAINT, btree (comments)

從上面兩個(gè)示例中我們還可以總結(jié)出一點(diǎn),主鍵約束和唯一約束,是另外附加給字段的約束類型,因此會(huì)顯示在表信息下面。而非空約束則是字段內(nèi)在的屬性,會(huì)和字段一起顯示。因此會(huì)使用不同的方式來添加和刪除,主鍵約束和唯一約束通過下面的命令來添加和刪除

alter table table_name add constraint_name (field_name); #添加主鍵約束或唯一約束
alter table table_name drop constraint constraint_name;  #刪除主鍵約束或唯一約束

而非空約束則通過修改字段屬性來添加和刪除非空約束,如下所示:

alter table tablename alter field_name set not  null;  #添加非空約束
alter table tablename alter field_name drop not null; #刪除非空約束
外鍵約束

外鍵約束也是一種外部約束,可以使用主鍵約束和唯一約束相同的命令來添加,唯一不同的就是語法,同時(shí)要求關(guān)聯(lián)的字段必須是另外一張表的主鍵。示例如下:
還是前面的testdb1表格,我們新增一個(gè)字段parent_id,要求將這個(gè)字段和一個(gè)新的testdb2表格的id字段進(jìn)行關(guān)聯(lián):

postgres=# \d testdb1;
            Table "public.testdb1"
  Column   |         Type          | Modifiers 
-----------+-----------------------+-----------
 id        | integer               | not null
 comments  | character varying(20) | 

# 增加一個(gè)字段parent_id,命令如下:
postgres=# alter table testdb1 add column parent_id int;
ALTER TABLE
postgres=# \d testdb1; 
            Table "public.testdb1"
  Column   |         Type          | Modifiers 
-----------+-----------------------+-----------
 id        | integer               | not null
 comments  | character varying(20) | 
 parent_id | integer               | 

#字段添加完成后,查看新表testdb2,
postgres=# \d testdb2; 
           Table "public.testdb2"
 Column |         Type          | Modifiers 
--------+-----------------------+-----------
 id     | integer               | not null
 info   | character varying(50) | 
Indexes:
    "testdb2_pkey" PRIMARY KEY, btree (id)

#字段id是表testdb2的主鍵,在testdb1的parent_id字段上增加外鍵約束,命令如下:
postgres=# alter table testdb1 add foreign key(parent_id) references testdb2(id);
ALTER TABLE
postgres=# \d testdb1; 
            Table "public.testdb1"
  Column   |         Type          | Modifiers 
-----------+-----------------------+-----------
 id        | integer               | not null
 comments  | character varying(20) | 
 parent_id | integer               | 
Foreign-key constraints:
    "testdb1_parent_id_fkey" FOREIGN KEY (parent_id) REFERENCES testdb2(id)

## 刪除外鍵約束
postgres=# alter table testdb1 drop constraint testdb1_parent_id_fkey;
ALTER TABLE
postgres=# \d testdb1;
            Table "public.testdb1"
  Column   |         Type          | Modifiers 
-----------+-----------------------+-----------
 id        | integer               | not null
 comments  | character varying(20) | 
 parent_id | integer               | 
#可以看到外鍵約束已經(jīng)刪除。
檢查約束

檢查約束一般是用來限制字段的類型或數(shù)值范圍的,也是一種外部約束,添加和刪除的命令和其他幾種外部約束相同,示例如下:

#添加檢查約束,限制parent_id字段數(shù)值范圍是3~10之間
postgres=# alter table testdb1 add check (parent_id > 3 and parent_id < 10);
ALTER TABLE
postgres=# \d testdb1;
            Table "public.testdb1"
  Column   |         Type          | Modifiers 
-----------+-----------------------+-----------
 id        | integer               | not null
 comments  | character varying(20) | 
 parent_id | integer               | 
Check constraints:
    "testdb1_parent_id_check" CHECK (parent_id > 3 AND parent_id < 10)

postgres=# \d testdb1;
            Table "public.testdb1"
  Column   |         Type          | Modifiers 
-----------+-----------------------+-----------
 id        | integer               | not null
 comments  | character varying(20) | 
 parent_id | integer               | 
Check constraints:
    "testdb1_parent_id_check" CHECK (parent_id > 3 AND parent_id < 10)

#刪除檢查約束,直接根據(jù)約束名稱刪除。
postgres=# alter table testdb1 drop constraint testdb1_parent_id_check;
ALTER TABLE
postgres=# \d testdb1; 
            Table "public.testdb1"
  Column   |         Type          | Modifiers 
-----------+-----------------------+-----------
 id        | integer               | not null
 comments  | character varying(20) | 
 parent_id | integer               | 

修改表

修改表的操作主要有兩種,第一種是修改字段的屬性,第二種是修改字段對(duì)應(yīng)的值。字段的屬性包括:字段類型、字段默認(rèn)值、字段約束等。字段約束的添加和修改我們?cè)谏厦鎰?chuàng)建表的部分已經(jīng)講過,這一部分我們就講解一下字段類型和字段默認(rèn)值的修改。

修改字段類型

基本命令格式如下:
alter table tablename alter field_name type tyepname;
修改字段類型這個(gè)操作要求修改前后的字段類型必須是能夠轉(zhuǎn)換的類型,同時(shí),進(jìn)行類型轉(zhuǎn)換的時(shí)候,會(huì)存在數(shù)據(jù)精度丟失等問題,因此在前期進(jìn)行數(shù)據(jù)庫設(shè)計(jì)的時(shí)候,對(duì)于字段的類型一定要考慮清楚,因?yàn)橐坏┍碇写嫒肓藬?shù)據(jù),再進(jìn)行類型修改的操作是非常危險(xiǎn)的。示例如下:

postgres=# \d testdb1;
            Table "public.testdb1"
  Column   |         Type          | Modifiers 
-----------+-----------------------+-----------
 id        | integer               | not null
 comments  | character varying(20) | 
 parent_id | integer               | 

postgres=# select * from testdb1; 
 id | comments | parent_id 
----+----------+-----------
  1 | test     | 
  2 | not all  | 
(2 rows)

postgres=# alter table testdb1 alter parent_id type varchar(5); 
ALTER TABLE
postgres=# \d testdb1;
            Table "public.testdb1"
  Column   |         Type          | Modifiers 
-----------+-----------------------+-----------
 id        | integer               | not null
 comments  | character varying(20) | 
 parent_id | character varying(5)  | 

上面的示例中直接將testdb1表中的parent_id字段類型從int類型修改為varchar(5)類型,字符類型和整數(shù)類型存在轉(zhuǎn)換關(guān)系,且parent_id字段在表中沒有任何值,因此可以直接轉(zhuǎn)換。
我們?cè)诒碇胁迦胍粋€(gè)值,然后再嘗試將parent_id轉(zhuǎn)換為int類型,如下所示:

postgres=# insert into testdb1 values(3,'no poll', 't');
INSERT 0 1
postgres=# select * from testdb1; 
 id | comments | parent_id 
----+----------+-----------
  1 | test     | 
  2 | not all  | 
  3 | no poll  | t
(3 rows)

postgres=# alter table testdb1 alter parent_id type int; 
ERROR:  column "parent_id" cannot be cast automatically to type integer
HINT:  You might need to specify "USING parent_id::integer".

此時(shí)我們就看到了系統(tǒng)給出的提示,需要使用USING parent_id::integer這樣的關(guān)鍵字來強(qiáng)制轉(zhuǎn)換。我們來嘗試一下,然后看表中的數(shù)值變化:

postgres=# alter table testdb1 alter COLUMN parent_id type int USING parent_id::integer; 
ERROR:  invalid input syntax for integer: "t"

但是這個(gè)時(shí)候發(fā)現(xiàn),它給出提示,t是對(duì)于integer類型來說是一個(gè)錯(cuò)誤的語法,說明我們剛剛插入的值是不能進(jìn)行varchar到integer的轉(zhuǎn)換的,我們將這個(gè)值刪去,然后插入另外一個(gè)字符類型的數(shù)字,再嘗試轉(zhuǎn)換類型看看。

postgres=# delete from testdb1 where id=3; 
DELETE 1

postgres=# insert into testdb1 values(3,'test again', ' 42');
INSERT 0 1
postgres=# select * from testdb1; 
 id |  comments  | parent_id 
----+------------+-----------
  1 | test       | 
  2 | not all    | 
  3 | test again |  42
(3 rows)

postgres=# alter table testdb1 alter column parent_id type int using parent_id::integer; 
ALTER TABLE
postgres=# select * from testdb1; 
 id |  comments  | parent_id 
----+------------+-----------
  1 | test       |          
  2 | not all    |          
  3 | test again |        42
(3 rows)

我們發(fā)現(xiàn)這個(gè)時(shí)候轉(zhuǎn)換成功了,這兩個(gè)小例子說明,要轉(zhuǎn)換類型的字段,轉(zhuǎn)換前后的類型必須是可以轉(zhuǎn)換的,否則會(huì)出錯(cuò)。而至于精度丟失的問題,例如從小數(shù)類型轉(zhuǎn)換成整數(shù)類型,必然會(huì)丟失小數(shù)部分。這個(gè)就留給讀者自己進(jìn)行實(shí)驗(yàn)了。

修改字段默認(rèn)值

給表中插入數(shù)據(jù)的時(shí)候,如果這個(gè)字段沒有給出值,則會(huì)使用默認(rèn)值,對(duì)于某些設(shè)置來說還是非常方便的。默認(rèn)值也屬于字段的內(nèi)在屬性,可以使用和添加非空約束同樣的命令來修改,示例如下:

postgres=# alter table testdb1  alter COLUMN comments set default 'test'; 
ALTER TABLE

在上面的命令中,我將comments字段的默認(rèn)值設(shè)置為'test',下面我們嘗試往testdb1表中再插入數(shù)值,插入的時(shí)候要注意,此時(shí)如果給擁有默認(rèn)值的字段一個(gè)新值,則使用新值,如果不給新值,則使用默認(rèn)值,但是必須顯示指定要賦值的字段,否則會(huì)出錯(cuò)??聪旅娴氖纠?/p>

# 給指定字段賦值,擁有默認(rèn)值的字段使用默認(rèn)值test
postgres=# insert into testdb1(id, parent_id) values(6,30); 
INSERT 0 1
postgres=# select * from testdb1; 
 id |  comments   | parent_id 
----+-------------+-----------
  1 | test        |          
  2 | not all     |          
  6 | test        |        30
(3 rows)

# 給默認(rèn)值字段賦予一個(gè)新值,此時(shí)使用的是新值。
postgres=# insert into testdb1 values(7, 'my test', 32); 
INSERT 0 1
postgres=# select * from testdb1; 
 id |  comments   | parent_id 
----+-------------+-----------
  1 | test        |          
  2 | not all     |          
  3 | test again  |        42
  5 | not a teste |        35
  6 | test        |        30
  7 | my test     |        32
(6 rows)

# 不指定要賦值的字段
postgres=# insert into testdb1 values(8, 35); 
INSERT 0 1
postgres=# select * from testdb1; 
 id |  comments   | parent_id 
----+-------------+-----------
  1 | test        |          
  2 | not all     |          
  6 | test        |        30
  7 | my test     |        32
  8 | 35          |          
(5 rows)

postgres=# insert into testdb1  values(9,,36); 
ERROR:  syntax error at or near ","
LINE 1: insert into testdb1  values(9,,36);
                                      ^

從上面最后兩個(gè)小例子中我們看到,在沒有指定要賦值的字段的時(shí)候,postgresql并不會(huì)自動(dòng)跳過有默認(rèn)值的字段,會(huì)將對(duì)應(yīng)錯(cuò)誤的值賦值給comments字段,也不支持兩個(gè)逗號(hào)表示跳過該字段的功能。因此在給具有默認(rèn)值的字段的表做插入操作時(shí)一定要注意。

刪除表

刪除表應(yīng)該是最簡(jiǎn)單的操作了,命令格式如下:
drop table table_name;
但是要注意的是,如果表的主鍵是另外一張表的外鍵,那么刪除這張表之前一定要先刪除對(duì)應(yīng)表上的外鍵約束,否則會(huì)無法刪除這張表,看下面的示例:

#我們將testdb1的parent_id和testdb2的id設(shè)置為外鍵約束關(guān)系,然后我們來嘗試刪除表testdb2
postgres=# alter table testdb1 add foreign key(parent_id) references testdb2(id); 
ALTER TABLE
postgres=# \d testdb1
                        Table "public.testdb1"
  Column   |         Type          |             Modifiers             
-----------+-----------------------+-----------------------------------
 id        | integer               | not null
 comments  | character varying(20) | default 'test'::character varying
 parent_id | integer               | 
Indexes:
    "testdb1_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "testdb1_parent_id_fkey" FOREIGN KEY (parent_id) REFERENCES testdb2(id)

postgres=# drop table testdb2;
ERROR:  cannot drop table testdb2 because other objects depend on it
DETAIL:  constraint testdb1_parent_id_fkey on table testdb1 depends on table testdb2
HINT:  Use DROP ... CASCADE to drop the dependent objects too.

從提示中我們可以開電腦,無法直接刪除表testdb2,因?yàn)樗闹麈I是testdb1的外鍵,還給出了一個(gè)提示,可以使用DROP .. CASCADE的語法來刪除表testdb2的同事刪除對(duì)應(yīng)依賴的對(duì)象。我們來嘗試一下:

postgres=# DROP table testdb2 CASCADE; 
NOTICE:  drop cascades to constraint testdb1_parent_id_fkey on table testdb1
DROP TABLE
postgres=# \d testdb1; 
                        Table "public.testdb1"
  Column   |         Type          |             Modifiers             
-----------+-----------------------+-----------------------------------
 id        | integer               | not null
 comments  | character varying(20) | default 'test'::character varying
 parent_id | integer               | 
Indexes:
    "testdb1_pkey" PRIMARY KEY, btree (id)

從上面可以看到,我們使用DROP ... CASCADE的語句來刪除表testdb2的時(shí)候,會(huì)自動(dòng)刪除testdb1表上的外鍵約束,而不需要手動(dòng)去刪除對(duì)應(yīng)的外鍵約束。

上面就是表的創(chuàng)建、修改和刪除的所有內(nèi)容。更高深的操作我們?cè)诤竺媛龑W(xué)習(xí)。下一句繼續(xù)學(xué)習(xí)表相關(guān)的內(nèi)容。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,588評(píng)論 19 139
  • 本周日活動(dòng):誕愿有你,夢(mèng)想中國(guó)。旨在為品學(xué)兼優(yōu)的貧困家庭孩子助力夢(mèng)想! 比起上周日的法院活動(dòng):?jiǎn)緪蹥w港心理咨詢走進(jìn)...
    玉葉金枝閱讀 617評(píng)論 0 0
  • 知之真切篤行處即是行 行之明覺精察處即是知 周日有幸去聽了,拆書幫幫主趙周老師的分享《職場(chǎng)...
    李睿是青春無敵美少女閱讀 383評(píng)論 0 2
  • 坐在桌前 不記得添了幾次茶水 屋里沒有聲音 窗外的樹葉還沒有完全凋落 只是一天比一天泛黃發(fā)紅了 秋的味道已慢慢淡了...
    鄉(xiāng)下讀書人閱讀 277評(píng)論 0 0

友情鏈接更多精彩內(nèi)容