MySQL 資料庫 之 外來鍵 & SQL查詢語句詳解
內容概要
- 外來鍵
- 表與表之間關係
- 一對多
- 多對多
- 一對一
- 沒有關係
- 操作表的SQL語句(ALTER)
- SQL 查詢關鍵字
- 查詢語句之 where
- 查詢語句之分組 group by
- 分組後篩選 having
- 去重 distinct
- 排序 order by
- 限制查詢資料條數 limit
- 正則查詢 regexp
- 聚合函式函式 max min sum avg count
- 連線欄位 concat
內容詳細
外來鍵
什麼是外來鍵?
用於建立表與表之間聯絡的欄位,也是一種約束條件,
在 SQL 語句中,建立外來鍵的關鍵字是 :
foreign key(本表字段名) references 被關聯的表名(id) foreign key(dep_id) references dep(id)
為什麼要有外來鍵?
假設有這樣一張員工資訊資料表:
![image-20220221151359232]
上面這張表有以下缺陷:
- 1、表的重點不突出,到底是員工表還是部門表
- 2、表中部門和部門資訊兩個欄位的資料很多在重複
- 3、表的拓展性極差,只要教學部改一個名字,整張表都要跟著改掉,牽一髮而動全身
如何建立外來鍵?
解決以上表的缺陷:
將上述一張表拆分成兩張表,然後通過給一個表新增一個外來鍵欄位,將兩張表聯絡起來
外來鍵欄位>>>:部門編號
其實就是用來標識表與表之間的資料關係
簡單的理解為該欄位可以讓你去到其他表中查詢資料
表與表之間關係
表關係總共就四種:
一對多、多對多、一對一、沒有關係
判斷表關係的方式:換位思考
補充:級聯更新,級聯刪除,也就是兩表之間的資料改動可以進行同步
on update cascade # 級聯更新
on delete cascade, # 級聯刪除
1.一對多
以員工和部門表為例 先站在員工表的基礎之上 問:一個員工資訊能否對應多個部門資訊 答:不可以 再站在部門表的基礎之上 問:一個部門資訊能否對應多個員工資訊 答:可以 結論:一個可以一個不可以 那麼表關係就是"一對多" 員工表是多 部門表是一 """ 針對一對多的表關係 外來鍵欄位建在多的一方 """ # 表關係沒有'多對一'一說 都是'一對多'
使用SQL語句建立真正意義上的表關係:
注意:先建立不含外來鍵欄位的基本表,再新增外來鍵欄位
# 先建立被關聯的表 部門表
create table dep(
id int primary key auto_increment,
dep_name varchar(32),
dep_desc varchar(254)
);
# 再建立含有外來鍵的表 員工資訊表
create table emp(
id int primary key auto_increment,
name varchar(32),
age int,
dep_id int,
foreign key(dep_id) references dep(id)
);
2.多對多關係
注意:多對多關係的表需要建立第三個表來存放兩表之間的對應關係
以書籍表與作者表為例
先站在書籍表的基礎之上
問:一個書籍資訊能否對應多個作者資訊
答:可以
再站在作者表的基礎之上
問:一個作者資訊能否對應多個書籍資訊
答:可以
結論:兩個都可以 那麼表關係就是"多對多"
# 多對多表關係 需要單獨開設第三張表儲存(並且第三張表可以不繫結)
# 先建立兩張多對多關係的表
create table book(
id int primary key auto_increment,
title varchar(32),
price float(6,2)
);
create table author(
id int primary key auto_increment,
name varchar(32),
age int
);
# 建立第三張表存放兩表之間的對應關係
create table book2author(
id int primary key auto_increment,
author_id int,
book_id int,
foreign key(author_id) references author(id)
on update cascade # 級聯更新
on delete cascade, # 級聯刪除
foreign key(book_id) references book(id)
on update cascade # 級聯更新
on delete cascade # 級聯刪除
);
3.一對一表關係
一對一與一對多的區別是,一對一外來鍵欄位必須唯一
作者表與作者詳情表
先站在作者表的基礎之上
問:一個作者資訊能否對應多個作者詳情資訊
答:不可以
再站在作者詳情表的基礎之上
問:一個作者詳情資訊能否對應多個作者資訊
答:不可以
結論:兩個都不可以
那麼表關係可能是"一對一"或者"沒有關係"
# 外來鍵欄位建在任何一方都可以 但是推薦建在查詢頻率較高的表中
SQL 語句建立:
# 表一
create table author(
id int primary key auto_increment,
name varchar(32),
age int,
author_id int unique, # 一對一與一對多的區別是,一對一外來鍵欄位必須唯一
foreign key(author_id) references author_detail(id)
on update cascade # 級聯更新
on delete cascade # 級聯刪除
);
# 表二
create table author_detail(
id int primary key auto_increment,
phone varchar(32),
address varchar(32)
);
**補充: **
1.在建立表的時候 需要先建立被關聯表(沒有外來鍵欄位的表)
2.在插入新資料的時候 應該先確保被關聯表中有資料
3.在插入新資料的時候 外來鍵欄位只能填寫被關聯表中已經存在的資料
4.在修改和刪除被關聯表中的資料的時候 無法直接操作
如果想要資料之間自動修改和刪除需要新增額外的配置
由於外來鍵有實質性的諸多約束 當表特別多的時候外來鍵的增多反而會增加耦合程度
所以在實際開發專案中 有時候並不會使用外來鍵建立表關係
而是通過SQL語句層面 建立邏輯意義上的表關係
eg:操作員工表的sql執行完畢之後 立刻跟著執行操作部門的sql
操作表的SQL語句(ALTER)
show tables;
desc 表名;
create table t1(id int);
alter table t1 change id nid int;
drop table t1;
語法:
1. 修改表名
ALTER TABLE 表名
RENAME 新表名;
2. 增加欄位
ALTER TABLE 表名
ADD 欄位名 資料型別 [完整性約束條件…],
ALTER TABLE 表名
ADD 欄位名 資料型別 [完整性約束條件…] FIRST;
ALTER TABLE 表名
ADD 欄位名 資料型別 [完整性約束條件…] AFTER 欄位名;
3. 刪除欄位
ALTER TABLE 表名
DROP 欄位名;
4. 修改欄位 # modify只能改欄位資料型別完整約束,不能改欄位名,但是change可以!
ALTER TABLE 表名
MODIFY 欄位名 資料型別 [完整性約束條件…];
ALTER TABLE 表名
CHANGE 舊欄位名 新欄位名 舊資料型別 [完整性約束條件…];
SQL 查詢關鍵字
資料準備:
# 資料準備
create table emp(
id int primary key auto_increment,
name varchar(20) not null,
sex enum('male','female') not null default 'male', #大部分是男的
age int(3) unsigned not null default 28,
hire_date date not null,
post varchar(50),
post_comment varchar(100),
salary double(15,2),
office int, #一個部門一個屋子
depart_id int
);
#插入記錄
#三個部門:教學,銷售,運營
insert into emp(name,sex,age,hire_date,post,salary,office,depart_id) values
('jason','male',18,'20170301','張江第一帥形象代言',7300.33,401,1), #以下是教學部
('tom','male',78,'20150302','teacher',1000000.31,401,1),
('kevin','male',81,'20130305','teacher',8300,401,1),
('tony','male',73,'20140701','teacher',3500,401,1),
('owen','male',28,'20121101','teacher',2100,401,1),
('jack','female',18,'20110211','teacher',9000,401,1),
('jenny','male',18,'19000301','teacher',30000,401,1),
('sank','male',48,'20101111','teacher',10000,401,1),
('哈哈','female',48,'20150311','sale',3000.13,402,2),#以下是銷售部門
('呵呵','female',38,'20101101','sale',2000.35,402,2),
('西西','female',18,'20110312','sale',1000.37,402,2),
('樂樂','female',18,'20160513','sale',3000.29,402,2),
('拉拉','female',28,'20170127','sale',4000.33,402,2),
('僧龍','male',28,'20160311','operation',10000.13,403,3), #以下是運營部門
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬銀','female',18,'20130311','operation',19000,403,3),
('程咬銅','male',18,'20150411','operation',18000,403,3),
('程咬鐵','female',18,'20140512','operation',17000,403,3);
查詢關鍵字之select與from
from控制的是查詢哪張表
select控制的是查詢表裡面的哪些欄位
select * from emp;
select id,name from emp;
查詢關鍵字之where篩選
where篩選功能
"""
模糊查詢:沒有明確的篩選條件
關鍵字:like
關鍵符號:
%:匹配任意個數任意字元
_:匹配單個個數任意字元
show variables like '%mode%se';
"""
# 1.查詢id大於等於3小於等於6的資料
select id,name from emp where id >= 3 and id <= 6;
select * from emp where id between 3 and 6;
# 2.查詢薪資是20000或者18000或者17000的資料
select * from emp where salary = 20000 or salary = 18000 or salary = 17000;
select * from emp where salary in (20000,18000,17000); # 簡寫
# 3.查詢員工姓名中包含o字母的員工姓名和薪資
# 在你剛開始接觸mysql查詢的時候,建議你按照查詢的優先順序順序拼寫出你的sql語句
"""
先是查哪張表 from emp
再是根據什麼條件去查 where name like ‘%o%’
再是對查詢出來的資料篩選展示部分 select name,salary
"""
select name,salary from emp where name like '%o%';
# 4.查詢員工姓名是由四個字元組成的員工姓名與其薪資
select name,salary from emp where name like '____';
select name,salary from emp where char_length(name) = 4;
# 5.查詢id小於3或者大於6的資料
select * from emp where id not between 3 and 6;
# 6.查詢薪資不在20000,18000,17000範圍的資料
select * from emp where salary not in (20000,18000,17000);
# 7.查詢崗位描述為空的員工名與崗位名 針對null不能用等號,只能用is
select name,post from emp where post_comment = NULL; # 查詢為空!
select name,post from emp where post_comment is NULL;
select name,post from emp where post_comment is not NULL;
聚合函式
聚合函式主要就是配合分組一起使用
max min sum count avg
查詢關鍵字之group by分組
有些查詢條件,是要對錶的某一些欄位進行分組查詢的
比如: 查詢每個部門的平均薪資,需要對部門欄位進行分組
按照某個指定的條件將單個單個的個體分成一個個整體
eg: 按照男女將人分組
按照膚色分組
按照年齡分組
分組後只能獲取分組的那個欄位的資料,其他資料都不能直接獲取
但是在5.6版本中,獲取了也不會報錯,需要手動設定嚴格模式:
set global sql_mode = 'only_full_group_by,STRICT_TRANS_TABLES,PAD_CHAR_TO_FULL_LENGTH';
# 資料分組應用場景:每個部門的平均薪資,男女比例等
# 1.按部門分組
select * from emp group by post; # 分組後取出的是每個組的第一條資料
select id,name,sex from emp group by post; # 驗證
"""
設定sql_mode為only_full_group_by,意味著以後但凡分組,只能取到分組的依據,
不應該在去取組裡面的單個元素的值,那樣的話分組就沒有意義了,因為不分組就是對單個元素資訊的隨意獲取
"""
set global sql_mode="strict_trans_tables,only_full_group_by";
# 重新連結客戶端
select * from emp group by post; # 報錯
select id,name,sex from emp group by post; # 報錯
select post from emp group by post; # 獲取部門資訊
# 強調:只要分組了,就不能夠再“直接”查詢到單個數據信息了,只能獲取到組名
# 2.獲取每個部門的最高工資
# 以組為單位統計組內資料>>>聚合查詢(聚集到一起合成為一個結果)
# 每個部門的最高工資
select post,max(salary) from emp group by post;
補充:在顯示的時候還可以給欄位取別名
select post as '部門',max(salary) as '最高工資' from emp group by post;
as也可以省略 但是不推薦省 因為寓意不明確
# 每個部門的最低工資
select post,min(salary) from emp group by post;
# 每個部門的平均工資
select post,avg(salary) from emp group by post;
# 每個部門的工資總和
select post,sum(salary) from emp group by post;
# 每個部門的人數
select post,count(id) from emp group by post;
統計的時候只要是非空欄位 效果都是一致的
這裡顯示age,salary,id最後演示特殊情況post_comment
**補充: **
# group_concat 分組之後使用
如果真的需要獲取分組意外的資料欄位 可以使用group_concat()
# 每個部門的員工姓名
select post,group_concat(name) from emp group by post;
select post,group_concat(name,'|',sex) from emp group by post;
# concat 不分組使用
select concat(name,sex) from emp;
select concat(name,'|',sex) from emp;
having過濾
"""
where與having都是篩選功能 但是有區別
where在分組之前對資料進行篩選
having在分組之後對資料進行篩選
我們一定要有一個簡單的認識 一條SQL語句的結果也可以看成是一張全新的表
"""
select post,avg(salary) from emp where age>30 group by post having avg(salary)>10000;
關鍵字之distinct去重
# 對有重複的展示資料進行去重操作 一定要是重複的資料
select distinct id,age from emp;
select distinct post from emp;
關鍵字之order by排序
select * from emp order by salary asc; #預設升序排
select * from emp order by salary desc; #降序排
#先按照age降序排,在年輕相同的情況下再按照薪資升序排
select * from emp order by age desc,salary asc;
# 統計各部門年齡在10歲以上的員工平均工資,並且保留平均工資大於1000的部門,然後對平均工資進行排序
select post,avg(salary) from emp where age>10 group by post having avg(salary)>1000 order by avg(salary) desc;
關鍵字之limit分頁
# 限制展示條數
select * from emp limit 3;
# 查詢工資最高的人的詳細資訊
select * from emp order by salary desc limit 1;
# 分頁顯示
select * from emp limit 0,5; # 第一個引數表示起始位置,第二個引數表示的是條數,不是索引位置
select * from emp limit 5,5;
關鍵字之regexp正則
select * from emp where name regexp '^j.*(n|y)$';
判斷表關係
# 班級表
cid caption
# 學生表
sid sname gender class_id
# 老師表
tid tname
# 課程表
cid cname teacher_id
# 成績表
sid student_id course_id number
1. 查詢崗位名以及崗位包含的所有員工名字
2. 查詢崗位名以及各崗位內包含的員工個數
3. 查詢公司內男員工和女員工的個數
4. 查詢崗位名以及各崗位的平均薪資
5. 查詢崗位名以及各崗位的最高薪資
6. 查詢崗位名以及各崗位的最低薪資
7. 查詢男員工與男員工的平均薪資,女員工與女員工的平均薪資
- 查詢崗位名以及崗位包含的所有員工名字
select post as '崗位',group_concat(name) as '員工' from emp group by post;
- 查詢崗位名以及各崗位內包含的員工個數
select post as '崗位',count(post) from emp group by post;
- 查詢公司內男員工和女員工的個數
select sex as '性別',count(sex) as '人數' from emp group by sex;
- 查詢崗位名以及各崗位的平均薪資
select post as '崗位',avg(salary) as '平均薪資' from emp group by post;
- 查詢崗位名以及各崗位的最高薪資
select post as '崗位',max(salary) as '最高薪資' from emp group by post;
- 查詢崗位名以及各崗位的最低薪資
select post as '崗位',min(salary) as '最低薪資' from emp group by post;
- 查詢男員工與男員工的平均薪資,女員工與女員工的平均薪資
select sex as '性別',group_concat(name) as '員工',avg(salary) as '平均薪資' from emp group by sex;