1. 程式人生 > 其它 >MySQL 資料庫 之 外來鍵 & SQL查詢語句詳解

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. 查詢男員工與男員工的平均薪資,女員工與女員工的平均薪資
  1. 查詢崗位名以及崗位包含的所有員工名字
select post as '崗位',group_concat(name) as '員工' from emp group by post;
  1. 查詢崗位名以及各崗位內包含的員工個數
 select post as '崗位',count(post) from emp group by post;
  1. 查詢公司內男員工和女員工的個數
select sex as '性別',count(sex) as '人數' from emp group by sex;
  1. 查詢崗位名以及各崗位的平均薪資
select post as '崗位',avg(salary) as '平均薪資' from emp group by post;
  1. 查詢崗位名以及各崗位的最高薪資
select post as '崗位',max(salary) as '最高薪資' from emp group by post;
  1. 查詢崗位名以及各崗位的最低薪資
select post as '崗位',min(salary) as '最低薪資' from emp group by post;
  1. 查詢男員工與男員工的平均薪資,女員工與女員工的平均薪資
 select sex as '性別',group_concat(name) as '員工',avg(salary) as '平均薪資' from emp group by sex;