1. 程式人生 > >mysql三-3:完整性約束

mysql三-3:完整性約束

一 介紹

約束條件與資料型別的寬度一樣,都是可選引數

作用:用於保證資料的完整性和一致性
主要分為:

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
)

==================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 ==================default==================== #設定id欄位有預設值後,則無論id欄位是null還是not null,都可以插入空,插入空預設填入default指定的預設值 mysql> create table t3(id int default 1); mysql> alter table t3 modify id int not null default 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('egon'); mysql> select * from student; +------+-----+------+------------+ | name | age | sex | hobby | +------+-----+------+------------+ | egon | 18 | male | play,music | +------+-----+------+------------+
驗證

三 unique

============設定唯一約束 UNIQUE===============
方法一:
create table department1(
id int,
name varchar(20) unique,
comment varchar(100)
);


方法二:
create table department2(
id int,
name varchar(20),
comment varchar(100),
constraint uk_name 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'
View Code
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)
not null+unique的化學反應
create table service(
id int primary key auto_increment,
name varchar(20),
host varchar(15) not null,
port int not null,
unique(host,port) #聯合唯一
);

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.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> insert into service(name,host,port) values('nginx','192.168.0.10',80);
ERROR 1062 (23000): Duplicate entry '192.168.0.10-80' for key 'host'
聯合唯一

四 primary key

從約束角度看primary key欄位的值不為空且唯一,那我們直接使用not null+unique不就可以了嗎,要它幹什麼?

主鍵primary key是innodb儲存引擎組織資料的依據,innodb稱之為索引組織表,一張表中必須有且只有一個主鍵

一個表中可以:

單列做主鍵
多列做主鍵(複合主鍵)

============單列做主鍵===============
#方法一: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)
單列主鍵
==================多列做主鍵================
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
    -> ('egon'),
    -> ('alex')
    -> ;

mysql> select * from student;
+----+------+------+
| id | name | sex  |
+----+------+------+
|  1 | egon | male |
|  2 | alex | male |
+----+------+------+


#也可以指定id
mysql> insert into student values(4,'asb','female');
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values(7,'wsb','female');
Query OK, 1 row affected (0.00 sec)

mysql> select * from student;
+----+------+--------+
| id | name | sex    |
+----+------+--------+
|  1 | egon | male   |
|  2 | alex | male   |
|  4 | asb  | female |
|  7 | wsb  | female |
+----+------+--------+


#對於自增的欄位,在用delete刪除後,再插入值,該欄位仍按照刪除前的位置繼續增長
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('egon');
Query OK, 1 row affected (0.01 sec)

mysql> select * from student;
+----+------+------+
| id | name | sex  |
+----+------+------+
|  1 | egon | male |
+----+------+------+
1 row in set (0.00 sec)
View Code

瞭解知識

#在建立完表後,修改自增欄位的起始值
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 global auto_increment_increment=5;
Query OK, 0 rows affected (0.00 sec)

mysql> set global auto_increment_offset=3;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'auto_incre%'; #需要退出重新登入
+--------------------------+-------+
| 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('egon1'),('egon2'),('egon3');
mysql> select * from student;
+----+-------+------+
| id | name  | sex  |
+----+-------+------+
|  3 | egon1 | male |
|  8 | egon2 | male |
| 13 | egon3 | male |
+----+-------+------+
步長:auto_increment_increment,起始偏移量:auto_increment_offset

六 foreign key

一 快速理解foreign key

員工資訊表有三個欄位:工號  姓名  部門

公司有3個部門,但是有1個億的員工,那意味著部門這個欄位需要重複儲存,部門名字越長,越浪費

解決方法:

我們完全可以定義一個部門表

然後讓員工資訊表關聯該表,如何關聯,即foreign key

#表型別必須是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;


#先往父表department中插入記錄
insert into department values
(1,'歐德博愛技術有限事業部'),
(2,'艾利克斯人力資源部'),
(3,'銷售部');


#再往子表employee中插入記錄
insert into employee values
(1,'egon',1),
(2,'alex1',2),
(3,'alex2',2),
(4,'alex3',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 | egon  |      1 |
|  2 | alex1 |      2 |
|  3 | alex2 |      2 |
|  4 | alex3 |      2 |
+----+-------+--------+


#更新父表department,子表employee中對應的記錄跟著改
mysql> update department set id=22222 where id=2;
mysql> select * from employee;
+----+-------+--------+
| id | name  | dpt_id |
+----+-------+--------+
|  1 | egon  |      1 |
|  3 | alex2 |  22222 |
|  4 | alex3 |  22222 |
|  5 | alex1 |  22222 |
+----+-------+--------+
示範 

二 如何找出兩張表之間的關係 

分析步驟:
#1、先站在左表的角度去找
是否左表的多條記錄可以對應右表的一條記錄,如果是,則證明左表的一個欄位foreign key 右表一個欄位(通常是id)

#2、再站在右表的角度去找
是否右表的多條記錄可以對應左表的一條記錄,如果是,則證明右表的一個欄位foreign key 左表一個欄位(通常是id)

#3、總結:
#多對一:
如果只有步驟1成立,則是左表多對一右表
如果只有步驟2成立,則是右表多對一左表

#多對多
如果步驟1和2同時成立,則證明這兩張表時一個雙向的多對一,即多對多,需要定義一個這兩張表的關係表來專門存放二者的關係

#一對一:
如果1和2都不成立,而是左表的一條記錄唯一對應右表的一條記錄,反之亦然。這種情況很簡單,就是在左表foreign key右表的基礎上,將左表的外來鍵欄位設定成unique即可

三 建立表之間的關係

#一對多或稱為多對一
三張表:出版社,作者資訊,書

一對多(或多對一):一個出版社可以出版多本書

關聯方式: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)
;
View Code
一夫多妻制

#妻子表的丈夫id外來鍵到丈夫表的id
其他例子
#多對多
三張表:出版社,作者資訊,書

多對多:一個作者可以寫多本書,一本書也可以有多個作者,雙向的一對多,即多對多
  
關聯方式: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('egon'),('alex'),('yuanhao'),('wpq');

#每個作者與自己的代表作如下
1 egon: 
      1 九陽神功
      2 九陰真經
      3 九陰白骨爪
      4 獨孤九劍
      5 降龍十巴掌
      6 葵花寶典


2 alex: 
      1 九陽神功
      6 葵花寶典

3 yuanhao:
      4 獨孤九劍
      5 降龍十巴掌
      6 葵花寶典

4 wpq:
      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)
;
View Code
單張表:使用者表+相親關係表,相當於:使用者表+相親關係表+使用者表
多張表:使用者表+使用者與主機關係表+主機表

中間那一張存放關係的表,對外關聯的欄位可以聯合唯一
其他例子
#一對一
兩張表:學生表和客戶表

一對一:一個學生是一個客戶,一個客戶有可能變成一個學校,即一對一的關係

關聯方式: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)
;
View Code
例一:一個使用者只有一個部落格

    使用者表:
    id  name
    1    egon
    2    alex
    3    wupeiqi


    部落格表   
           fk+unique
    id url name_id
    1  xxxx   1
    2  yyyy   3
    3  zzz    2



例二:一個管理員唯一對應一個使用者
    使用者表:
    id user  password
    1  egon    xxxx
    2  alex    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'),
(