(3.4)表相關操作之完整性約束
一、介紹
約束條件與資料型別的寬度一樣,都是可選引數
作用:用於保證資料的完整性和一致性
主要分為:
PRIMARY KEY (PK) 標識該欄位為該表的主鍵,可以唯一的標識記錄 FOREIGN KEY (FK) 標識該欄位為該表的外來鍵 NOT NULL 標識該欄位不能為空 UNIQUE KEY (UK) 標識該欄位的值是唯一的 AUTO_INCREMENT 標識該欄位的值自動增長(整數型別,而且為主鍵) DEFAULT 為該欄位設定預設值 UNSIGNED 無符號 ZEROFILL 使用0填充
說明:
1,是否允許為空,預設NULL,可設定NOT NULL,欄位不允許為空,必須賦值 2,欄位是否有預設值,預設的預設值是NULL,如果插入記錄時不給欄位賦值,此欄位使用預設值 sex enum('male','female') not null default 'male' age int unsigned NOT NULL default 20 必須為正值(無符號) 不允許為空 預設是20 3,是否是key 主鍵 primary key 外來鍵 foreign key 索引 (index,unique...)
二、not null 與 default
是否可空,null表示空,非字串
not null - 不可空
null - 可空
預設值,建立列時可以指定預設值,當插入資料時如果未主動設定,則自動新增預設值
create table tb1( nid int not null defalut 2, num int not null )
驗證:
1)not null
==================not null==================== mysql> create table t1(id int); # id欄位預設可以插入空 mysql> desc t1; +-------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra |+-------+---------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | +-------+---------+------+-----+---------+-------+ mysql> insert into t1 values(); # 可以插入空 mysql> create table t2(id int not null); # 設定欄位id不為空 mysql> desc t2; +-------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+-------+ | id | int(11) | NO | | NULL | | +-------+---------+------+-----+---------+-------+ mysql> insert into t2 values(); # 不能插入空 ERROR 1364 (HY000): Field 'id' doesn't have a default value
2)default
==================default==================== #設定id欄位有預設值後,則無論id欄位是null還是not null,都可以插入空,插入空預設填入default指定的預設值 mysql> create table t3(id int default 1); # 插入空值,會變成預設值 1 mysql> alter table t3 modify id int not null default 1; # 改成不為空,但如果插入空值,還是會變成預設值 1 ==================綜合練習==================== mysql> create table student( -> name varchar(20) not null, -> age int(3) unsigned not null default 18, -> sex enum('male','female') default 'male', -> hobby set('play','study','read','music') default 'play,music' -> ); mysql> desc student; +-------+------------------------------------+------+-----+------------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+------------------------------------+------+-----+------------+-------+ | name | varchar(20) | NO | | NULL | | | age | int(3) unsigned | NO | | 18 | | | sex | enum('male','female') | YES | | male | | | hobby | set('play','study','read','music') | YES | | play,music | | +-------+------------------------------------+------+-----+------------+-------+ mysql> insert into student(name) values('zixi'); mysql> select * from student; +------+-----+------+------------+ | name | age | sex | hobby | +------+-----+------+------------+ | zixi | 18 | male | play,music | +------+-----+------+------------+
三、unique
設定唯一約束:
1)單列唯一(單列主鍵)
# 單列唯一,單列主鍵
============設定唯一約束 UNIQUE=============== 方法一: create table department1( # 存放部門資訊 id int, name char(20) unique, # 部門名不能重複,設定唯一 comment char(100) ); 方法二: create table department2( id int, name varchar(20), comment varchar(100), unique(name) # 單列唯一 ); mysql> insert into department1 values(1,'IT','技術'); Query OK, 1 row affected (0.00 sec) mysql> insert into department1 values(1,'IT','技術'); ERROR 1062 (23000): Duplicate entry 'IT' for key 'name'
not null + unique 的化學反應:
mysql> create table t1(id int not null unique); Query OK, 0 rows affected (0.02 sec) mysql> desc t1; +-------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+---------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | +-------+---------+------+-----+---------+-------+ 1 row in set (0.00 sec)
2)聯合唯一(多列唯一):
mysql> create table service( -> id int, -> name char(20), -> ip char(15) not null, -> port int not null, -> unique(ip,port) # 聯合唯一 -> ); Query OK, 0 rows affected (0.30 sec) mysql> desc service; +-------+----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+-------+ | id | int | YES | | NULL | | | name | char(20) | YES | | NULL | | | ip | char(15) | NO | PRI | NULL | | | port | int | NO | PRI | NULL | | +-------+----------+------+-----+---------+-------+ 4 rows in set (0.00 sec) mysql> insert into service values -> (1,'nginx','192.168.0.10',80), -> (2,'haproxy','192.168.0.20',80), -> (3,'mysql','192.168.0.30',3306); Query OK, 3 rows affected (0.07 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> insert into service(name,host,port) values('nginx','192.168.0.10',80); ERROR 1054 (42S22): Unknown column 'host' in 'field list' mysql>
四、primary key
從約束角度看 primary key欄位的值不為空且唯一,那我們直接使用 not null + unique不就可以了嗎,要它幹什麼?
主鍵 primary key是 innodb儲存引擎組織資料的依據,innodb稱之為索引組織表,一張表中必須有且只有一個主鍵,主鍵可以幫你組織表的資料,可以提高查詢速度。
一個表中可以:
1,單列做主鍵:
============單列做主鍵=============== # 方法一:not null+unique create table department1( id int not null unique, # 主鍵 name varchar(20) not null unique, comment varchar(100) ); mysql> desc department1; +---------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+--------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(20) | NO | UNI | NULL | | | comment | varchar(100) | YES | | NULL | | +---------+--------------+------+-----+---------+-------+ rows in set (0.01 sec) # 方法二:在某一個欄位後用 primary key create table department2( id int primary key, # 主鍵 name varchar(20), comment varchar(100) ); mysql> desc department2; +---------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+--------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(20) | YES | | NULL | | | comment | varchar(100) | YES | | NULL | | +---------+--------------+------+-----+---------+-------+ rows in set (0.00 sec) # 方法三:在所有欄位後單獨定義 primary key create table department3( id int, name varchar(20), comment varchar(100), constraint pk_name primary key(id); # 建立主鍵併為其命名pk_name mysql> desc department3; +---------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+--------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(20) | YES | | NULL | | | comment | varchar(100) | YES | | NULL | | +---------+--------------+------+-----+---------+-------+ rows in set (0.01 sec)
2,複合主鍵(多列做主鍵)
==================多列做主鍵================ create table service( ip varchar(15), port char(5), service_name varchar(10) not null, primary key(ip,port) ); mysql> desc service; +--------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------------+-------------+------+-----+---------+-------+ | ip | varchar(15) | NO | PRI | NULL | | | port | char(5) | NO | PRI | NULL | | | service_name | varchar(10) | NO | | NULL | | +--------------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) mysql> insert into service values -> ('172.16.45.10','3306','mysqld'), -> ('172.16.45.11','3306','mariadb') -> ; Query OK, 2 rows affected (0.00 sec) Records: 2 Duplicates: 0 Warnings: 0 mysql> insert into service values ('172.16.45.10','3306','nginx'); ERROR 1062 (23000): Duplicate entry '172.16.45.10-3306' for key 'PRIMARY'
五、auto_increment
約束欄位為自動增長,被約束的欄位必須同時被 key約束。
# 不指定id,則自動增長 create table student( id int primary key auto_increment, name varchar(20), sex enum('male','female') default 'male' ); mysql> desc student; +-------+-----------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-----------------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(20) | YES | | NULL | | | sex | enum('male','female') | YES | | male | | +-------+-----------------------+------+-----+---------+----------------+ mysql> insert into student(name) values -> ('zixi'), -> ('suos') -> ; mysql> select * from student; +----+------+------+ | id | name | sex | +----+------+------+ | 1 | zixi | male | | 2 | suos | male | +----+------+------+ # 也可以指定id mysql> insert into student values(4,'asb','female'); Query OK, 1 row affected (0.00 sec) mysql> insert into student values(7,'bsb','female'); Query OK, 1 row affected (0.00 sec) mysql> select * from student; +----+------+--------+ | id | name | sex | +----+------+--------+ | 1 | zixi | male | | 2 | suos | male | | 4 | asb | female | | 7 | bsb | female | +----+------+--------+ # 對於自增的欄位,在用 delete刪除後,再插入值,該欄位仍按照刪除前的位置繼續增長,delete 經常與 where連用。 mysql> delete from student; Query OK, 4 rows affected (0.00 sec) mysql> select * from student; Empty set (0.00 sec) mysql> insert into student(name) values('ysb'); mysql> select * from student; +----+------+------+ | id | name | sex | +----+------+------+ | 8 | ysb | male | +----+------+------+ # 應該用 truncate清空表,比起 delete一條一條地刪除記錄,truncate是直接清空表,在刪除大表時用它 mysql> truncate student; Query OK, 0 rows affected (0.01 sec) mysql> insert into student(name) values('zixi'); Query OK, 1 row affected (0.01 sec) mysql> select * from student; +----+------+------+ | id | name | sex | +----+------+------+ | 1 | zixi | male | +----+------+------+ 1 row in set (0.00 sec)
步長:auto_increment_increment,起始偏移量:auto_increment_offset (瞭解)
# 在建立完表後,修改自增欄位的起始值 mysql> create table student( -> id int primary key auto_increment, -> name varchar(20), -> sex enum('male','female') default 'male' -> ); mysql> alter table student auto_increment=3; mysql> show create table student; ....... ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 mysql> insert into student(name) values('egon'); Query OK, 1 row affected (0.01 sec) mysql> select * from student; +----+------+------+ | id | name | sex | +----+------+------+ | 3 | egon | male | +----+------+------+ row in set (0.00 sec) mysql> show create table student; ....... ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 # 也可以建立表時指定auto_increment的初始值,注意初始值的設定為表選項,應該放到括號外 create table student( id int primary key auto_increment, name varchar(20), sex enum('male','female') default 'male' )auto_increment=3; # 設定步長 sqlserver自增步長: # 基於表級別 create table t1( id int。。。 )engine=innodb,auto_increment=2 步長=2 default charset=utf8 mysql自增的步長: show session variables like 'auto_inc%'; # 基於會話級別 set session auth_increment_increment=2 # 修改會話級別的步長 # 基於全域性級別的 set global auth_increment_increment=2 # 修改全域性級別的步長(所有會話都生效) # !!!注意了注意了注意了!!! If the value of auto_increment_offset is greater than that of auto_increment_increment, the value of auto_increment_offset is ignored. 翻譯:如果auto_increment_offset的值大於auto_increment_increment的值,則auto_increment_offset的值會被忽略 ,這相當於第一步步子就邁大了,扯著了蛋 比如:設定auto_increment_offset=3,auto_increment_increment=2 mysql> set session auto_increment_increment=5; # session 本次會話 Query OK, 0 rows affected (0.00 sec) mysql> set global auto_increment_offset=3; # global 全域性,需要 exit退出,重新載入一邊,才好用 Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'auto_in%'; # 查變數, % 是任意字元的所有變數 +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | auto_increment_increment | 1 | | auto_increment_offset | 1 | +--------------------------+-------+ create table student( id int primary key auto_increment, name varchar(20), sex enum('male','female') default 'male' ); mysql> insert into student(name) values('zixi1'),('zixi2'),('zixi3'); mysql> select * from student; +----+-------+------+ | id | name | sex | +----+-------+------+ | 3 | zixi1 | male | | 8 | zixi2 | male | | 13 | zixi3 | male | +----+-------+------+
六、foreign key(外來鍵)
1,快速理解 foreign key:
建立表之間的關係
員工資訊表有三個欄位:工號 姓名 部門
公司有3個部門,但是有 1個億的員工,那意味著部門這個欄位需要重複儲存,部門名字越長,越浪費
解決方法:
我們完全可以定義一個部門表
然後讓員工資訊表關聯該表,如何關聯,即 foreign key
最好是從邏輯意義上實現兩張表之間的關係,從應用程式中控制,不要在資料庫中把表建上硬性關係,把兩張表耦合到一起了,寫專案的時候,首先是設計表和結構,把表之間的關聯關係做好,一旦用 外來鍵做這種硬性的關係,意味著所有的表被你強耦合到一起了,一旦以後涉及到擴充套件,會非常麻煩。
示範:
# 1,建立表關係
# 表型別必須是 innodb儲存引擎,且被關聯的欄位,即 references指定的另外一個表的欄位,必須保證唯一 create table department( id int primary key, name varchar(20) not null )engine=innodb; # dpt_id外來鍵,關聯父表(department主鍵id),同步更新,同步刪除 create table employee( id int primary key, name varchar(20) not null, dpt_id int, constraint fk_name foreign key(dpt_id) references department(id) on delete cascade # 刪除同步 on update cascade # 更新同步 )engine=innodb;
# 2,往表中插入資料 # 先往父表 department中插入記錄 insert into department values (1,'艾歐尼亞'), (2,'德瑪西亞'), (3,'無畏先鋒'); # 再往子表 employee中插入記錄 insert into employee values (1,'suos',1), (2,'zixi1',2), (3,'zixi2',2), (4,'zixi3',2), (5,'李坦克',3), (6,'劉飛機',3), (7,'張火箭',3), (8,'林子彈',3), (9,'加特林',3) ; # 刪父表 department,子表 employee中對應的記錄跟著刪 mysql> delete from department where id=3; mysql> select * from employee; +----+-------+--------+ | id | name | dpt_id | +----+-------+--------+ | 1 | suos | 1 | | 2 | zixi1 | 2 | | 3 | zixi2 | 2 | | 4 | zixi3 | 2 | +----+-------+--------+ # 更新父表 department,子表 employee中對應的記錄跟著改 mysql> update department set id=22222 where id=2; mysql> select * from employee; +----+-------+--------+ | id | name | dpt_id | +----+-------+--------+ | 1 | suos | 1 | | 3 | zixi2 | 22222 | | 4 | zixi3 | 22222 | | 5 | zixi1 | 22222 | +----+-------+--------+
2,如何找出兩張表之間的關係
分析步驟: # 1、先站在左表的角度去找 是否左表的多條記錄可以對應右表的一條記錄,如果是,則證明左表的一個欄位foreign key 右表一個欄位(通常是id)
# 多個員工對應一個部門,而多個部門不能對應一個員工(大公司) # 2、再站在右表的角度去找 是否右表的多條記錄可以對應左表的一條記錄,如果是,則證明右表的一個欄位foreign key 左表一個欄位(通常是id) # 3、總結: # 多對一: 如果只有步驟1成立,則是左表多對一右表 如果只有步驟2成立,則是右表多對一左表 # 多對多 如果步驟1和2同時成立,則證明這兩張表是一個雙向的多對一,即多對多,需要定義一個這兩張表的關係表來專門存放二者的關係 # 一對一: 如果1和2都不成立,而是左表的一條記錄唯一對應右表的一條記錄,反之亦然。這種情況很簡單,就是在左表foreign key右表的基礎上,將左表的外來鍵欄位設定成unique即可
3,建立表之間的關係
1)多對一
# 一對多或稱為多對一 三張表:出版社,作者資訊,書 一對多(或多對一):一個出版社可以出版多本書 關聯方式:foreign key
===================== 多對一 =====================
# 出版社 create table press( id int primary key auto_increment, name varchar(20) );
# 書 create table book( id int primary key auto_increment, name varchar(20), press_id int not null, foreign key(press_id) references press(id) on delete cascade on update cascade ); insert into press(name) values ('北京工業地雷出版社'), ('人民音樂不好聽出版社'), ('智慧財產權沒有用出版社') ; insert into book(name,press_id) values ('九陽神功',1), ('九陰真經',2), ('九陰白骨爪',2), ('獨孤九劍',3), ('降龍十巴掌',2), ('葵花寶典',3) ;
其他例子:
一夫多妻制 # 妻子表的丈夫id外來鍵到丈夫表的id
2)多對多
# 多對多 三張表:出版社,作者資訊,書 多對多:一個作者可以寫多本書,一本書也可以有多個作者,雙向的一對多,即多對多 關聯方式:foreign key+一張新的表
=====================多對多===================== create table author( id int primary key auto_increment, name varchar(20) ); # 這張表就存放作者表與書表的關係,即查詢二者的關係查這表就可以了 create table author2book( id int not null unique auto_increment, author_id int not null, book_id int not null, constraint fk_author foreign key(author_id) references author(id) on delete cascade on update cascade, constraint fk_book foreign key(book_id) references book(id) on delete cascade on update cascade, primary key(author_id,book_id) ); # 插入四個作者,id依次排開 insert into author(name) values('zixi'),('suos'),('jiej'),('ruiw'); # 每個作者與自己的代表作如下 1 zixi: 1 九陽神功 2 九陰真經 3 九陰白骨爪 4 獨孤九劍 5 降龍十巴掌 6 葵花寶典 2 suos: 1 九陽神功 6 葵花寶典 3 jiej: 4 獨孤九劍 5 降龍十巴掌 6 葵花寶典 4 ruiw: 1 九陽神功 insert into author2book(author_id,book_id) values (1,1), (1,2), (1,3), (1,4), (1,5), (1,6), (2,1), (2,6), (3,4), (3,5), (3,6), (4,1) ;
其他例子:
# 單張表:使用者表+相親關係表,相當於:使用者表+相親關係表+使用者表 # 多張表:使用者表+使用者與主機關係表+主機表 # 中間那一張存放關係的表,對外關聯的欄位可以聯合唯一
3)一對一
# 一對一 兩張表:學生表和客戶表 一對一:一個學生是一個客戶,一個客戶有可能變成一個學校,即一對一的關係 關聯方式:foreign key + unique
# 一定是 student來 foreign key表 customer,這樣就保證了: # 1 學生一定是一個客戶, # 2 客戶不一定是學生,但有可能成為一個學生 create table customer( id int primary key auto_increment, name varchar(20) not null, qq varchar(10) not null, phone char(16) not null ); create table student( id int primary key auto_increment, class_name varchar(20) not null, customer_id int unique, # 該欄位一定要是唯一的 foreign key(customer_id) references customer(id) # 外來鍵的欄位一定要保證unique on delete cascade on update cascade ); # 增加客戶 insert into customer(name,qq,phone) values ('李飛機','31811231',13811341220), ('王大炮','123123123',15213146809), ('守榴彈','283818181',1867141331), ('吳坦克','283818181',1851143312), ('贏火箭','888818181',1861243314), ('戰地雷','112312312',18811431230) ; # 增加學生 insert into student(class_name,customer_id) values ('脫產3班',3), ('週末19期',4), ('週末19期',5) ;
其他例子:
例一:一個使用者只有一個部落格 使用者表: id name 1 zixi 2 suos 3 jiej 部落格表 fk+unique id url name_id 1 xxxx 1 2 yyyy 3 3 zzz 2 例二:一個管理員唯一對應一個使用者 使用者表: id user password 1 zixi xxxx 2 suos yyyy 管理員表: fk+unique id user_id password 1 1 xxxxx 2 2 yyyyy
七、作業
練習:賬號資訊表,使用者組,主機表,主機組
#使用者表 create table user( id int not null unique auto_increment, username varchar(20) not null, password varchar(50) not null, primary key(username,password) ); insert into user(username,password) values ('root','123'), ('egon','456'), ('alex','alex3714') ; #使用者組表 create table usergroup( id int primary key auto_increment, groupname varchar(20) not null unique ); insert into usergroup(groupname) values ('IT'), ('Sale'), ('Finance'), ('boss') ; #主機表 create table host( id int primary key auto_increment, ip char(15) not null unique default '127.0.0.1' ); insert into host(ip) values ('172.16.45.2'), ('172.16.31.10'), ('172.16.45.3'), ('172.16.31.11'), ('172.10.45.3'), ('172.10.45.4'), ('172.10.45.5'), ('192.168.1.20'), ('192.168.1.21'), ('192.168.1.22'), ('192.168.2.23'), ('192.168.2.223'), ('192.168.2.24'), ('192.168.3.22'), ('192.168.3.23'), ('192.168.3.24') ; #業務線表 create table business( id int primary key auto_increment, business varchar(20) not null unique ); insert into business(business) values ('輕鬆貸'), ('隨便花'), ('大富翁'), ('窮一生') ; #建關係:user與usergroup create table user2usergroup( id int not null unique auto_increment, user_id int not null, group_id int not null, primary key(user_id,group_id), foreign key(user_id) references user(id), foreign key(group_id) references usergroup(id) ); insert into user2usergroup(user_id,group_id) values (1,1), (1,2), (1,3), (1,4), (2,3), (2,4), (3,4) ; #建關係:host與business create table host2business( id int not null unique auto_increment, host_id int not null, business_id int not null, primary key(host_id,business_id), foreign key(host_id) references host(id), foreign key(business_id) references business(id) ); insert into host2business(host_id,business_id) values (1,1), (1,2), (1,3), (2,2), (2,3), (3,4) ; #建關係:user與host create table user2host( id int not null unique auto_increment, user_id int not null, host_id int not null, primary key(user_id,host_id), foreign key(user_id) references user(id), foreign key(host_id) references host(id) ); insert into user2host(user_id,host_id) values (1,1), (1,2), (1,3), (1,4), (1,5), (1,6), (1,7), (1,8), (1,9), (1,10), (1,11), (1,12), (1,13), (1,14), (1,15), (1,16), (2,2), (2,3), (2,4), (2,5), (3,10), (3,11), (3,12) ;result
作業: