MySQL完整性約束


一 介紹

  約束條件與數據類型的寬度一樣,都是可選參數

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

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
  );

  先說一點:在我們插入數據的時候,可以這么寫insert into tb1(nid,num) values(1,‘chao’);就是在插入輸入的時候,指定字段插入數據,如果我在只給num插入值,可以這樣寫insert into tb1(num) values('chao');還可以插入數據的時候,指定插入數據字段的順序:把nid和num換個位置,但是對應插入的值也要換位置。注意:即便是你只給一個字段傳值了,那么也是生成一整條記錄,這條記錄的其他字段的值如果可以為空,那么他們就都是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('chao');
mysql> select * from student;
+------+-----+------+------------+
| name | age | sex  | hobby      |
+------+-----+------+------------+
| chao|  18 | male | play,music |
+------+-----+------+------------+
not null和default測試

   注意一點:如果是非嚴格模式,int類型不傳值的話會默認為0,因為null不是int類型的,字段是int類型,所以他會自動將null變為0

三 unique

  獨一無二,唯一屬性:id,身份證號等

  是一種key,唯一鍵,是在數據類型之外的附加屬性,其實還有加速查詢的作用,后面再講這個。

============設置唯一約束 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'
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稱之為索引組織表,一張表中必須有且只有一個主鍵

  

  一個表中可以:

    單列做主鍵
    多列做主鍵(復合主鍵或者叫做聯合主鍵)

unique key和primary key都是MySQL的特殊類型,不僅僅是個字段約束條件,還稱為索引,可以加快查詢速度,這個索引功能我們后面再講,現在只講一下這些key作為約束條件的效果。

    關於主鍵的強調內容:
        1.一張表中必須有,並且只能由一個主鍵字段:innodb引擎下存儲表數據的時候,會通過你的主鍵字段的數據來組織管理所有的數據,將數據做成一種樹形結構的數據結構,幫你較少IO次數,提高獲取定位數據、獲取數據的速度,優化查詢。
            解釋:如果我們在一張表中沒有設置primary key,那么mysql在創建表的時候,會按照順序從上到下遍歷你設置的字段,直到找到一個not null unique的字段,自動識別成主鍵pri,通過desc可以看到,這樣是不是不好啊,所以我們在創建表的時候,要給他一個主鍵,讓他優化的時候用,如果沒有pri也沒有not null unique字段,那么innodb引擎下的mysql被逼無奈,你沒有設置主鍵字段,主鍵又有不為空且唯一的約束,又不能擅自給你的字段加上這些約束,那么沒辦法,它只能給你添加一個隱藏字段來幫你組織數據,如果是這樣,你想想,主鍵是不是幫我們做優化查詢用的啊,這個優化是我們可以通過主鍵來查詢數據:例如:如果我們將id設置為主鍵,當我們查一個id為30的數據的時候,也就是select * from tb1 where id=30;這個查詢語句的速度非常快,不需要遍歷前面三十條數據,就好像我們使用的字典似的,找一個字,不需要一頁一頁的翻書,可以首先看目錄,然后看在哪一節,然后看在哪一頁,一步步的范圍,然后很快就找到了,這就像我們說的mysql的索引(主鍵、唯一鍵)的工作方式,一步一步的縮小范圍來查找,幾步就搞定了,所以通過主鍵你能夠快速的查詢到你所需要的數據,所以,如果你的主鍵是mysql幫你加的隱藏的字段,你查詢數據的時候,就不能將這個隱藏字段作為條件來查詢數據了,就不能享受到優化后的查詢速度了,對么
        
        2.一張表里面,通常都應該有一個id字段,而且通常把這個id字段作為主鍵,當然你非要讓其他的字段作為主鍵也是可以的,看你自己的設計,創建表的時候,一般都會寫create table t1(id int primary key);id int primary key這個東西在建表的時候直接就寫上
關於主鍵的通俗解釋和強調內容(重點*****)

 

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會被默認當成主鍵

 

============單列做主鍵===============
#方法一: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)
單列主鍵測試

 

聯合主鍵
        和聯合唯一是類似的,
        mysql> create table t10(
            ->id int,
            ->port int,
            ->primary key(id,port)
            -> );
        Query OK, 0 rows affected (0.45 sec)

        mysql> desc t10;
        +-------+---------+------+-----+---------+-------+
        | Field | Type    | Null | Key | Default | Extra |
        +-------+---------+------+-----+---------+-------+
        | id    | int(11) | NO   | PRI | 0       |       | 
        | port  | int(11) | NO   | PRI | 0       |       |
        +-------+---------+------+-----+---------+-------+
        2 rows in set (0.10 sec)
      
        看key,兩個都寫的是pri,兩個聯合起來作為主鍵,他們兩個作為一個主鍵,不能再有其他的主鍵了,也就是在創建表的時候,只能出現一次primary key方法。
        有同學說,老師,我不寫primary key行不,只寫一個not null unique字段,當然行,但是我們應該這樣做嗎,是不是不應該啊,所以以后設置主鍵的時候,就使用primary key來指定
聯合主鍵解釋

 

==================多列做主鍵================
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

  之前我們插入數據的時候,id也需要自己來寫,是不是很麻煩啊,我們是不是想,只要有一條記錄就直接插入進去啊,不需要考慮說,你現在存儲到第多少條數據了,對不對,所以出現了一個叫做auto_increment的屬性

  約束字段為自動增長,被約束的字段必須同時被key約束,也就是說只能給約束成key的字段加自增屬性,默認起始位置為1,步長也為1.

 

#不指定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)
auto_increment測試

 

#在創建完表后,修改自增字段的起始值
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個億的員工,那意味着部門這個字段需要重復存儲,部門名字越長,越浪費

    那這就體現出來了三個缺點:

      1.表的組織結構不清晰:員工的信息、部門的信息等等都摻在一張表里面。

      2.浪費空間,每一條信息都包含員工和部門,多個員工從屬一個部門,也需要每個員工的信息里都包含着部門的信息,浪費硬盤空間。

      3.擴展性極差:如果想修改一個部門的信息,比如修改部門名稱,那么這個包含員工和部門信息的表中的所有的包含這個部門信息的數據都需要進行修改,那么修改起來就非常麻煩,這是非常致命的缺點。

    解決方法:(畫一個excel表格來表示一下效果~~)

      我們完全可以定義一個部門表,解耦和

      我們雖然將部門表提出來了,但是員工表本身是和部門有聯系的,你光把部門信息提出來還是不夠的,還需要建立關聯

      然后讓員工信息表關聯該表,如何關聯,即foreign key 

      

 

     在解釋一下:數據要拆到不同表里面存着,你要站在兩個表的角度來看兩者之間的關系,你站在部門表的角度看,一個部門包含多個員工,站在員工表看,多個員工屬於一個部門,以我們上課來舉個例子看:現在的多個老師可以講一個課程python,那么老師對於課程表來說就是多對一個關系,那這是不是就是最終關系呢,我們還需要站在課程表的角度來看,多個課程能不能被一個老師教啊,這個看業務場景,你看咱們學校就不行,講python的只能講python,但是我們上的小學,初中,高中是不是多個課程可以被一個老師教啊,所以從老男孩的業務來看,課程表對老師表是一對一的,即便是你多個老師可以講這一門課程,但是這一門可能對應的那幾個老師只能講這一門,不能講其他的課程,所以他們只是單純的多對一的關系,多個老師對應一門課程,但是小學、初中、高中的業務,多個老師可以教一門課程,同樣這多個老師每個老師又可以教多門課程,那么從課程表角度來看,多個課程也能從屬一個老師,所以是多對多的關系:看下圖

    

  二 一對多的關系  

    我們在看看員工和部門這個多對一的關系表:

    

    如果我們沒有做強制的約束關系,那么在員工表里面那個部門id可以隨便寫,即便是部門表里面沒有這個id號,它也是可以寫的,但是這樣寫就錯了,因為業務不允許,並且這個數據完全沒用,根本就不存在這個部門,哪里來的這個部門的員工呢,對不對,所以要做一個硬性的關系,你員工里面的部門id一定要來自於部門表的id字段。怎么來做這個硬性關系呢,通過外鍵foreign key,怎么叫外鍵,就是跟外部的一個表進行關聯,建立這種硬性的關系,就叫做外鍵,就像我們上面這兩個表似的,左邊的員工表有一個字段(部門id字段)來自於右邊的部門表,那么我們就可以通過數據庫在員工表的部門id字段加上一個foreign key,外鍵關聯到右邊部門表的id字段,這樣就建立了這種硬性的關系了,之前我們是看着兩張表之間有關系,但是沒有做強制約束,還是兩張普通的表,操作其中任何一個,另外一個也沒問題,但是加上了這種強制關系之后,他們兩個的操作也就都關聯起來了,具體操作看下面的代碼:

    

 

    部門表是被關聯的表,員工表是關聯表,也就是員工表要關聯部門表,對吧,如果我們先創建員工表,在創建員工表的時候加外鍵關系,就會報錯,看效果:

    

    所以我們應該先建立部門表,也就是被關聯的表,因為關聯表中的字段的數據是來根據被關聯表的被關聯字段的數據而來的。

     

    然后看一下表結構:

    

    表創建好了,如果我們直接給員工表插入幾條數據,那么會報錯,因為,你的部門還沒有呢,你的員工表里面的那個dep_id外鍵字段的數據從何而來啊?看效果:

    

    然后我們先插入部門的數據,然后再插入員工的數據:

    

    然后查看一下數據:

    

    數據沒問題了,但是你有沒有發現一個問題,就是員工表的id從6開始的,因為我們前面插入了5條數據,失敗了,雖然失敗了,但是id自動增長了。

    所以有引出一個問題,如果想讓id從頭開始,我們可以把這些數據刪掉,用delete的刪除是沒用的,需要用truncate來刪除,這是清空表的意思。

    看一下delete:

     

    delete不是用來清空表的,是用來刪除一些你想刪除的符合某些條件的數據,一般用在delete from tb1 where id>20;這樣的,如果要清空表,讓id置零,使用truncate

    再看一下truncate:

    

    然后查看一下數據看看:

    

    ok,大家練習一下吧~~~~

   

    我們來看一下,如果對關聯的表進行修改的話會有什么效果,首先我們先修改一下部門表的id字段中的某個數據,將id的值改一下

    

    報錯了,那我們改一改員工表里面的外鍵字段dep_id,改它的值來試試:

    

    還是報錯了!我靠,那我試試刪除一下試試,解散一個部門,刪除他的數據:

    

    報錯了!不讓你刪除,因為你刪除之后,員工表里面的之前屬於這個部門的記錄找不到對應的部門id了,就報錯了

    那我刪除一下員工表里面關於這個要被解散的部門的員工數據,按理說是不是應該沒問題啊,來看看效果:

    

    刪除成功了,完全沒問題啊,那么關於這個部門的所有員工數據都被刪除了,也就是說,你這個部門下面沒有任何員工了,沒有了限制了相當於,所以我們嘗試一下看看現在能不能刪除部門表里面的這個部門了

    

    ok~可以刪除了

    雖然我們修改部門表或者員工表里面的部門id,但是我們可以刪除,但是刪除這個被關聯表部門表的數據的時候由於有關聯關系的存在,所以刪除的時候也很麻煩,要先將關聯數據刪除,才能刪除被關聯的表的數據。

    剛才我們刪除了教學部這個部門,當我們想解散這個部門的時候,首先想到的是什么,是不是我們的部門表,想直接操作部門表進行刪除,對吧,想修改部門的id號,是不是首先想到的也是操作部門表進行修改,把部門的id修改了,但是我們由於關聯關系的存在,不得不考慮關聯表中的數據,對不對,所以操作就變得很麻煩了,有沒有簡單的方法呢?我們想做的是不是說,我想刪除一個部門,直接刪除部門表里面的數據就行了,是不是達到這個效果,刪除一個部門的時候,與這個部門關聯的所有的員工表的那些數據都跟着刪除,或者我更新部門表中一個部門的id號,那么關聯的員工表中的關聯字段的部門id號跟着自動更新了,

     

  

    看一下解決辦法:

    首先我們把之前的兩個表刪除了,能先刪除部門表嗎?如果刪了部門表,你的員工表是不是找不到對應關系了,你說會不會報錯啊,所以先刪除員工表:

      1.先刪除關聯表,再刪除被關聯表,然后我們重新建立兩個表,然后建表的時候說一下咱們的解決方案。

      

 

      2.重建表,我們現在要解決的問題是:我們要達到一個在做某個表(被關聯表)更新或者刪除操作的時候,關聯表的數據同步的進行更新和刪除的效果,所以我們在建表的時候,可以加上兩個功能:同步更新和同步刪除:看看如何實現:在建立關聯關系的時候,加上這兩句: on delete cascade和 on update cascade

        

      然后把我們之間的表和數據都插入進去:然后再進行更新刪除操作:

        

 

      然后我們再直接刪除部門表里面的數據的時候,你看看結果:

        

      成功了,並且員工表里面關聯部門表id的數據也都刪除了,是不是達到了我們剛才想要實現的效果呀

      下面我們來看一下更新操作,我們之前說更新一個部門的id號,注意一個問題昂,我更新部門的名稱,你說有影響嗎?肯定沒有啊,因為我員工表並不是關聯的部門的名稱字段,而是關聯的部門的id字段,你改部門名稱沒關系,我通過你的id照樣找到你,但是你如果改了id號,那么我員工表里面的id號和你不匹配了,我就沒法找到你,所有當你直接更新部門的id的時候,我就給你報錯了,大哥,你想改的是關聯字段啊,考慮一下關聯表的數據們的感受行不行。我們來看一下加上 on update cascade之后的效果:

       

      將部門id為2的部門的id改成了200,完全ok,員工表里面之前關聯id為2的部門的數據都改成了關聯id為200的數據了。說明同步更新也是沒問題的。

 

   我們總結一下foreign key的下面幾個約束作用:

    1、先要建立被關聯的表才能建立關聯表

    2、在插入數據記錄的時候,要先想被關聯表中插入數據,才能往關聯表里面插入數據

    3、更新或者刪除數據的時候,都需要考慮關聯表和被關聯表的關系

      解決方案:

        a.刪除表的時候,先刪除關聯表,再刪除被關聯表

        b.重建表的時候,在加外鍵關聯的時候加上這兩句:on delete cascade 和 on update cascade

   

表類型必須是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) #這句話的意思是constraint 是聲明我們要建立一個約束啦,fk_name是約束的名稱,foreign key是約束的類型,整體的意思是,我要創建一個名為fk_name的外鍵關聯啦,這個constraint就是一個聲明的作用,在創建外鍵的時候不加constraint fk_name也是沒問題的。先理解一下就行了,后面我們會細講的。
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,'chao',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 | chao  |      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 | chao  |      1 |
|  3 | alex2 |  22222 |
|  4 | alex3 |  22222 |
|  5 | alex1 |  22222 |
+----+-------+--------+
簡單測試

 

  一對多的內容大致就說完了,我們看一下多對多的關系

  三 多對多關系

     我們上面大致提了一下多對多的關系,下面我們通過一個例子來細講一下,這個例子就用-->書和出版社的關系來看吧:

    

    上面是一對多沒問題,我們再來看看書和作者的關系:

    

    一本書可以有多個作者,一個作者可不可以寫多本書,兩者之間是不是站在誰的角度去看都是一個一對多的關系啊,那這就是多對多的關系,那我們創建表的時候,需要將兩個表都加一個foreign key的字段,但是你添加字段的時候,你想想,能直接給兩個表都這一個foreign key字段嗎,兩個誰先創建,誰后創建,是不是都不行啊,兩個表的創建是不是都依賴着另外一張表啊,所以我們之前的加外鍵字段的方式對於這種多對多的關系是不是就不好用啦,怎么辦,我們需要通過第三張表來緩和一下兩者的關系,通過第三張表來創建雙方的關系

    我們先創建書表和作者表,然后創建第三張表,第三張表就需要有一個字段外鍵關聯書表,還有一個字段外鍵關聯作者表

    

    然后我們如果想查一下alex出了哪些書,你可以怎么查,想一下,首先在author作者表里面找一個alex的id是多少,alex的id為2,然后找一個第三張表里面author_id為2的數據中book的id,然后拿着這些book的id去book表里面找對應的book名稱,你就能夠知道alex這個作者出了哪幾本書了,對不對,這就是一個多表查詢的一個思路

    來我們創建一下試試看(學了foreign key,這個東西是不是很簡單啊,兩個foreign key嘛~~)

    

    

    建立前兩張表,插入數據,建立第三張表

    然后給第三張表插入一些數據:

    

    查看一下數據:

    

    數據就創建好了,多對多就講完了~~~~

  

  四 一對一關系

    我們來以咱們學校的學生來舉例:

    最開始你只是一個客戶,可能還處於咨詢考慮的階段,還沒有轉化為學生,也有的客戶已經轉換為學生了,說白了就是你交錢了,哈哈

    那我們來建兩個表:客戶表和學生表

    

    客戶表里面存着客戶的信息,學生表里面存着客戶轉換為學生之后的學生信息,那么這兩個表是什么關系呢?你想一下,學生是不是從客戶轉換過來的,那么一個學生能對應多個用戶的信息嗎?當然是不能的,那么一個客戶能對應多個學生的信息嗎,當然也是不能的,那么他們兩個就是一對一的關系,那這個關系該怎么建立呢?我們知道通過外鍵可以建立關系,如果在客戶表里面加外鍵關聯學生表的話,那說明你的學生表必須先被創建出來,這樣肯定是不對的,因為你的客戶表先有的,才能轉換為學生,那如果在學生表加外鍵關聯客戶表的話,貌似是可以的,不過一個學生只對應一個客戶,那么這個關系怎么加呢,外鍵我們知道是一對多的,那怎么搞?我們可以把這個關聯字段設置成唯一的,不就可以了嗎,我既和你有關聯,我還不能重復,那就做到了我和你一對一的關聯關系。

    

    

 

  表關系的總結  

分析步驟:
#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)
一對多的簡單示例

 

#多對多
三張表:出版社,作者信息,書

多對多:一個作者可以寫多本書,一本書也可以有多個作者,雙向的一對多,即多對多
  
關聯方式: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)
;
多對多簡單示例

  中間那一張存放關系的表,對外關聯的字段可以聯合唯一

 

#一對一
兩張表:學生表和客戶表

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

關聯方式: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    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
一對一其他例子

 

  了解:將來你們接觸某一些大型項目的時候,盡量不要給表建立外鍵關系,因為外鍵直接在數據庫級別就變成耦合的了,那么我們要拓展或者刪除或者更改某些數據庫或者數據表的時候,拓展起來就比較難,我們可以自己從自己的程序代碼的邏輯層面上將這些關聯關系建立好,有很多公司就是這么做的,利於拓展,如果我們加了很多的foreign key ,那么當你想刪除一個表的時候,可能會牽一發而動全身,了解一下就可以了

   查看所有外鍵的名稱的方法: select REFERENCED_TABLE_SCHEMA,REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME,table_name,CONSTRAINT_NAME from information_schema.key_column_usage;  #包含我們創建外鍵的時候,mysql幫我們自動生成的外鍵名稱。

  外鍵這個key的名稱我們可以通過constraint來指定:

  刪除外鍵關聯,添加外鍵字段並添加外鍵關聯:


mysql> desc e3; +-------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | xx | char(11) | YES | | NULL | | | ee_id | int(11) | YES | MUL | NULL | | +-------+----------+------+-----+---------+----------------+ 3 rows in set (0.10 sec) mysql> alter table e3 drop ee_id; #直接刪除外鍵字段是不可以的 ERROR 1553 (HY000): Cannot drop index 'ee_id': needed in a foreign key constraint mysql> alter table e3 drop foreign key e3_ibfk_1; #通過上面的方法找到這個表的外鍵字段,然后先解除外鍵字段的關系,才能刪除外鍵字段 Query OK, 0 rows affected (0.11 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> desc e3; +-------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | xx | char(11) | YES | | NULL | | | ee_id | int(11) | YES | MUL | NULL | | +-------+----------+------+-----+---------+----------------+ 3 rows in set (0.10 sec) #解除了外鍵關系之后,是可以隨意插入數據的,就沒有了外鍵的約束 #但是表結構的key那一項里面還是顯示MUL,不過沒關系,已經沒有外鍵約束的效果了,大家可以插入一條原來那個關聯表的字段中不存在的數據來試一試,肯定是沒問題的,我沒有保存下來,就不給大家演示啦,然后然后我們就可以刪除這個外鍵字段了 mysql> alter table e3 drop ee_id; Query OK, 0 rows affected (0.65 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> desc e3; +-------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | xx | char(11) | YES | | NULL | | +-------+----------+------+-----+---------+----------------+ 2 rows in set (0.10 sec)

#看添加外鍵字段和外鍵關聯:
首先創建一個e2表,包含一個id字段,別忘了id字段最少也要是unique屬性,primary key當然最好啦

  mysql> alter table e3 add ee_id int;
  Query OK, 0 rows affected (0.64 sec)
  Records: 0 Duplicates: 0 Warnings: 0

  mysql> alter table e3 add foreign key(ee_id) references e2(id);
  Query OK, 0 rows affected (0.83 sec)
  Records: 0 Duplicates: 0 Warnings: 0

  #添加關聯刪除和關聯更新的操作:當刪除主表數據的時候,從表中有關的數據都跟着刪除,當主表的關系字段修改的時候,從表對應的關系字段的值也更着更新。

  alter table 從表 add foreign key(從表字段) references 主表(主表字段) on delete cascade on update cascade;

  #另外,能夠作為主表(也就是多對一關系的那個一表的被關聯的那個字段)的關系字段的約束最少要是唯一的unique屬性。

     

    外鍵約束有三種約束模式(都是針對父表的約束):

    模式一: district 嚴格約束(默認的 ),父表不能刪除或者更新已經被子表數據引用的記錄

    模式二:cascade 級聯模式:父表的操作,對應的子表關聯的數據也跟着操作 。

    模式三:set null:置空模式,父表操作之后,子表對應的數據(外鍵字段)也跟着被置空。

    通常的一個合理的約束模式是:刪除的時候子表置空;更新的時候子表級聯。

    指定模式的語法:foreign key(外鍵字段)references 父表(主鍵字段)on delete 模式 on update 模式;

    注意:刪除置空的前提條件是 外鍵字段允許為空,不然外鍵會創建失敗。

    外鍵雖然很強大,能夠進行各種約束,但是外鍵的約束降低了數據的可控性和可拓展性。通常在實際開發時,很少使用外鍵來約束。

作業:

    將下面的角色關系在數據庫中創建好,並且插入一些數據進去,自行看看該如何設計,其實下面感覺已經給你寫出來了都。

    

 

回到頂部


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM