表的約束(二)
?專欄內(nèi)容:
- postgresql內(nèi)核源碼分析
- 手寫數(shù)據(jù)庫toadb
- 并發(fā)編程
個(gè)人主頁:我的主頁
管理社區(qū):開源數(shù)據(jù)庫
座右銘:天行健,君子以自強(qiáng)不息;地勢坤,君子以厚德載物.
一、概述
在數(shù)據(jù)庫中,數(shù)據(jù)類型可以限制數(shù)據(jù)存儲(chǔ)的大小,也能在一定程度上限制存儲(chǔ)的數(shù)據(jù)種類,但是對于數(shù)據(jù)庫應(yīng)用來講,它太寬泛了,比如有些表示人名的字段,就不能為空,貨物數(shù)量的字段,不能為負(fù)值,這與實(shí)際生活符合,而數(shù)據(jù)類型并不能做這些約束,這就需要數(shù)據(jù)庫提供一套更貼近應(yīng)用,或者說與現(xiàn)實(shí)世界更符合的規(guī)則,來約束數(shù)據(jù)的有效性。
約束的定義
數(shù)據(jù)庫的約束是一種規(guī)則,用于限制或規(guī)范數(shù)據(jù)庫中的數(shù)據(jù),確保數(shù)據(jù)的完整性和一致性。這些約束可以定義在表級別或列級別,處理機(jī)制是一致的。約束不占用任何數(shù)據(jù)庫空間,而是存在于數(shù)據(jù)字典中,并在執(zhí)行SQL期間使用。用戶可以指明約束是啟用的還是禁用的,當(dāng)約束啟用時(shí),它增強(qiáng)了數(shù)據(jù)的完整性。
約束的分類
postgresql數(shù)據(jù)庫中的約束類型主要包括以下幾種:
- 主鍵約束(Primary Key):主鍵約束相當(dāng)于唯一約束和非空約束的組合。它確保表中的每一行數(shù)據(jù)都有一個(gè)唯一標(biāo)識符,不允許重復(fù),也不允許出現(xiàn)空值。每個(gè)表最多只允許一個(gè)主鍵。當(dāng)創(chuàng)建主鍵約束時(shí),系統(tǒng)默認(rèn)會(huì)在所在的列或列組合上建立對應(yīng)的唯一索引。
- 外鍵約束(Foreign Key):外鍵約束用于保證一個(gè)或兩個(gè)表之間的參照完整性。它構(gòu)建于一個(gè)表的兩個(gè)字段或是兩個(gè)表的兩個(gè)字段之間的參照關(guān)系。
- 唯一約束(Unique):唯一約束要求指定的表列或列組合的值不能重復(fù),保證數(shù)據(jù)的唯一性。同一個(gè)表可以有多個(gè)唯一約束。在創(chuàng)建唯一約束時(shí),如果不給唯一約束名稱,就默認(rèn)和列名相同。與主鍵約束不同,唯一約束允許空值的存在,但只能出現(xiàn)一個(gè)空值。
- 非空約束(NOT NULL):非空約束用于確保當(dāng)前列的值不為空值。它只能出現(xiàn)在表對象的列上。
- 檢查約束(Check):檢查約束會(huì)判斷指定的條件是否為true,當(dāng)為true時(shí)符合約束。
- 排除約束(Exclude): 在排除約束指定的表達(dá)式返回false或null時(shí),才符合約束。
這些約束在數(shù)據(jù)庫設(shè)計(jì)中起著至關(guān)重要的作用,它們有助于維護(hù)數(shù)據(jù)的準(zhǔn)確性和一致性,防止無效數(shù)據(jù)的插入和更新。
二、唯一約束
唯一約束來限制當(dāng)前列中不能出現(xiàn)重復(fù)值,在postgresql中,創(chuàng)建唯一約束時(shí)會(huì)自動(dòng)創(chuàng)建一個(gè)唯一性索引在對應(yīng)的列上,通過唯一性索引來提高檢查重復(fù)值的效率。
添加唯一約束的SQL語法如下:
CREATE TABLE table_name (
...
column1 data_type UNIQUE,
...
);
也可以在定義的尾部,給多個(gè)列為一組添加唯一約束
CREATE TABLE table_name (
...
column1 data_type ,
column2 data_type ,
...
UNIQUE (column1, column2)
);
當(dāng)表已經(jīng)創(chuàng)建時(shí),可以通過修改表的方式來添加,但是這里與其它不同之處是,有兩種方式,一種是直接添加唯一性約束,當(dāng)然會(huì)自動(dòng)創(chuàng)建唯一性索引;另一種是先創(chuàng)建唯一性索引,再將索引以唯一性約束的形式應(yīng)用到表上。
我們這里演示一下第一種方式
下面演示一下,創(chuàng)建一張表。
postgres=# drop table products ;
DROP TABLE
postgres=# create table products(product_id int primary key,product_name varchar not null,price double precision);
CREATE TABLE
postgres=# \d products
Table "test1.products"
Column | Type | Collation | Nullable | Default
--------------+-------------------+-----------+----------+---------
product_id | integer | | not null |
product_name | character varying | | not null |
price | double precision | | |
Indexes:
"products_pkey" PRIMARY KEY, btree (product_id)
創(chuàng)建了一張products表,然后再通過修改表定義的方式添加唯一性索引。
postgres=# alter table products add constraint products_unique_contraint unique (price);
ALTER TABLE
postgres=# \d products
Table "test1.products"
Column | Type | Collation | Nullable | Default
--------------+-------------------+-----------+----------+---------
product_id | integer | | not null |
product_name | character varying | | not null |
price | double precision | | |
Indexes:
"products_pkey" PRIMARY KEY, btree (product_id)
"products_unique_contraint" UNIQUE CONSTRAINT, btree (price)
可以看到添加成功,索束的名稱由數(shù)據(jù)庫自動(dòng)生成。
插入數(shù)據(jù)試驗(yàn)一下。
postgres=# insert into products values(1,'a',1);
INSERT 0 1
postgres=# insert into products values(2,'b',1);
ERROR: duplicate key value violates unique constraint "products_unique_contraint"
DETAIL: Key (price)=(1) already exists.
插入兩條數(shù)據(jù),在第二條數(shù)據(jù)中,字段price
與第一條數(shù)據(jù)相同,可以看到違反了唯一性約束,報(bào)錯(cuò)插入不成功。
postgres=# insert into products(product_id,product_name) values(2,'b');
INSERT 0 1
postgres=# insert into products(product_id,product_name) values(3,'c');
INSERT 0 1
postgres=# select * from products ;
product_id | product_name | price
------------+--------------+-------
1 | a | 1
2 | b |
3 | c |
(3 rows)
再插入兩條數(shù)據(jù),而字段price
的值并沒有賦值,也就是null值,可以看到都插入成功了,這就是null含義,未知,它與任何值都不相等。
第二種方式式的SQL語法示例如下:
- 先要?jiǎng)?chuàng)建一個(gè)唯一性索引
CREATE UNIQUE INDEX unique_index_name
ON table_name (column1,...);
- 然后將表的列應(yīng)用此唯一性索引
ALTER TABLE table_name
ADD CONSTRAINT unique_constraint_name
UNIQUE USING INDEX unique_index_name;
這里涉及到三個(gè)名字,第一個(gè)是表名,第二個(gè)是唯一性約束的名稱,第三個(gè)是前一步創(chuàng)建的唯一性索引的名稱。
在第一步創(chuàng)建完索引后,其實(shí)通過索引已經(jīng)實(shí)現(xiàn)不能有重復(fù)值出現(xiàn),但是查看表定義時(shí),并不是以約束的形式出現(xiàn),直到第二步添加約束后,可以看到表上出現(xiàn)了唯一性約束。
特別說明的是,null是一個(gè)特殊性,null與null并不相等,在有唯一性約束的列上可能出現(xiàn)多行數(shù)據(jù)都是null的情況,如果要改變這種默認(rèn)行為,可以在unique
關(guān)鍵字后面再追加NULLS NOT DISTINCT
。
增加了NULLS NOT DISTINCT
關(guān)鍵字后,null值也不允許重復(fù)了,下面來演示一下。
postgres=# drop table products ;
DROP TABLE
postgres=# create table products(product_id int primary key,product_name varchar not null,price double precision unique NULLS NOT DISTINCT);
CREATE TABLE
先刪除表,重新創(chuàng)建一下,仍然price字段創(chuàng)建唯一性約束,同時(shí)增加nulls not distinct字段。
postgres=# insert into products(product_id,product_name) values(2,'b');
INSERT 0 1
postgres=# insert into products(product_id,product_name) values(3,'c');
ERROR: duplicate key value violates unique constraint "products_price_key"
DETAIL: Key (price)=(null) already exists.
然后同樣插入兩條price字段為null的數(shù)據(jù),可以看到確實(shí)null值也不能重復(fù)了。
三、非空約束
NULL在數(shù)據(jù)庫中是一個(gè)特殊值,表示不知道、沒有任何信息,它不等于任何值,包括它自己。而在我們現(xiàn)實(shí)生活中,有些信息是必須要存在,比如商品的編號或名稱,如何不存在,那就不能唯一標(biāo)識這種商品,也就意味著這種商品不存在。
為了約束這種情況的發(fā)生,就增加一種not null的約束,它的SQL語法如下:
CREATE TABLE table_name(
...
column1 data_type NOT NULL,
...
);
在某一列定義后加not null
關(guān)鍵字。
當(dāng)然它也可以通過修改表的方式來添加,SQL語法如下:
ALTER TABLE table_name
ALTER COLUMN column1 SET NOT NULL;
定義了主鍵,同時(shí)在product_name
列上定義了not null約束。
查看表的定義信息,
postgres=# \d products
Table "test1.products"
Column | Type | Collation | Nullable | Default
--------------+-------------------+-----------+----------+---------
product_id | integer | | not null |
product_name | character varying | | not null |
price | double precision | | |
Indexes:
"products_pkey" PRIMARY KEY, btree (product_id)
可以看到在Nullable
是否為空列中,有兩個(gè)字段都是not null
約束,主鍵默認(rèn)是帶有非空約束的。
四、檢查約束
check檢查約束,通過一個(gè)結(jié)果為布爾值的表達(dá)式來檢查字段數(shù)據(jù)的有效性,只有表達(dá)式結(jié)果為true時(shí)才能被insert或update,這一約束保證了數(shù)據(jù)的實(shí)體完整性。
check約束可以在創(chuàng)建表時(shí)添加,SQL語法形式如下:
可以在列的定義后面追加check約束,它表明只針對本列的約束
CREATE TABLE table_name(
column1 datatype CHECK(condition_expression1),
column2 datatype CONSTRAINT check_constraint_name CHECK(condition_expression2),
...
);
其中可以用關(guān)鍵字CONSTRAINT
來指定一個(gè)約束的名稱,如果不指定名稱,數(shù)據(jù)庫會(huì)自動(dòng)生成一個(gè)名稱。
也可以將check約束單獨(dú)放為一行定義,它可能會(huì)涉及多行,盡量與它關(guān)聯(lián)的行靠近定義。
CREATE TABLE table_name(
column1 datatype,
...,
column2 datatype,
CONSTRAINT check_constraint_name CHECK(condition_expression1),
...,
column3 datatype,
CHECK(condition_expression2)
...
);
也可以通過修改表的定義來添加,
ALTER TABLE table_name
ADD CONSTRAINT check_constraint_name CHECK (condition_expression1);
下面我們來演示一下check約束,
postgres=# drop table products ;
DROP TABLE
postgres=# CREATE TABLE products (
product_no integer,
name text,
price numeric,
CHECK (price > 0),
discounted_price numeric,
CHECK (discounted_price > 0),
CONSTRAINT valid_discount CHECK (price > discounted_price)
);
CREATE TABLE
在price
,discounted_price
檢查,它們都需要大于0,同時(shí)打折價(jià)discounted_price
必須要小于原價(jià)price
,不能虧本吧。
注意,check約束盡量是簡單的表達(dá)式,它不能區(qū)分update中修改前與修改后的值,另外也不包括自定義的function在表達(dá)式中,因?yàn)樗闹凳遣淮_定的。
五、排除約束
exclusion約束,與check約束正好相反,它也是指定一個(gè)表達(dá)式和操作符,如果表達(dá)式與操作符結(jié)果返回false或null時(shí),就可以插入或更新。
exclusion約束是通過在指定列或幾列上創(chuàng)建gin索引來實(shí)現(xiàn)快速比較。
它主要用途在集合或圖形方面,表達(dá)邏輯是兩行數(shù)據(jù)表示的集合或圖形不包含,不相交。
下面來簡單演示一個(gè)例子。
postgres=# CREATE TABLE circles (
c circle,
EXCLUDE USING gist (c WITH &&)
);
CREATE TABLE
創(chuàng)建一個(gè)記錄圓形的表,增加約束限制,兩個(gè)圓不能有重疊,也就是在c
列上指定操作符為&&
,在檢查時(shí)會(huì)執(zhí)行運(yùn)算c1 && c2
,如果重疊返回值就為true,會(huì)違返exclusion約束。
postgres=# insert into circles values('(0,1),5');
INSERT 0 1
postgres=# select * from circles ;
c
-----------
<(0,1),5>
(1 row)
postgres=# insert into circles values('(0,2),5');
ERROR: conflicting key value violates exclusion constraint "circles_c_excl"
DETAIL: Key (c)=(<(0,2),5>) conflicts with existing key (c)=(<(0,1),5>).
postgres=# insert into circles values('(0,12),5');
INSERT 0 1
圓的輸入是圓心坐標(biāo) + 半徑, 插入一條以(0,1)圓心,半徑為5的圓的數(shù)據(jù),再次插入以(0,2)為圓心半徑為5的圓時(shí),它們會(huì)重疊,違反排除約束。
而我們再次插入一條以(0,12)為圓心半徑為5的圓時(shí),沒有與它重疊的圖形,可以插入成功。
六、總結(jié)
本章節(jié)介紹了在postgresql中的幾種約束,unique唯一性約束,not null非空約束,check條件檢查約束,還有exclusion排除約束,通過原理介紹,并在一些案例中進(jìn)行實(shí)踐來加深理解,當(dāng)然它們都可以組合使用,有效利用約束可以使我們的數(shù)據(jù)更加有效和完整。
七、結(jié)尾
非常感謝大家的支持,在瀏覽的同時(shí)別忘了留下您寶貴的評論,如果覺得值得鼓勵(lì),請點(diǎn)贊,收藏,我會(huì)更加努力!
作者郵箱:study@senllang.onaliyun.com
如有錯(cuò)誤或者疏漏歡迎指出,互相學(xué)習(xí)。文章來源:http://www.zghlxwxcb.cn/news/detail-844276.html
注:未經(jīng)同意,不得轉(zhuǎn)載!文章來源地址http://www.zghlxwxcb.cn/news/detail-844276.html
到了這里,關(guān)于【postgresql 基礎(chǔ)入門】表的約束(二) 唯一unique、非空not null、check、exclusion約束,原理機(jī)制以及多列組合約束的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!