MySQL-復雜查詢及條件-起別名-多表查詢-04


目錄

基本查詢語句及方法

測試數據創建

如果在windows系統中,插入中文字符,select的結果為空白,可以將所有字符編碼統一設置成gbk(或者參照我安裝配置MySQL的博客,將所有字符編碼設置為 utf8)

創建數據庫與表

create database db1;
use db1;

create table emp(
  id int not null unique auto_increment,
  # 表內沒有字段是primary key,innodb的機制,一個表沒有primaryk key時會自動將 not null + unique的鍵自動升級為 primary key 主鍵
  name varchar(20) not null,
  sex enum('male','female') not null default 'male',  # 大部分是男的
  age int(3) unsigned not null default 28,  # 這個3 只限制了顯示寬度,並不影響存儲
  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),
('egon','male',78,'20150302','teacher',1000000.31,401,1),
('kevin','male',81,'20130305','teacher',8300,401,1),
('tank','male',73,'20140701','teacher',3500,401,1),
('owen','male',28,'20121101','teacher',2100,401,1),
('jerry','female',18,'20110211','teacher',9000,401,1),
('nick','male',18,'19000301','teacher',30000,401,1),
('sean','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 emp;

另一種結果排版 \G

當表字段特別多的時候,結果的排版可能會出現混亂的現象,你可以在語句最后加 \G 來改變排版,方便查看

比較差的展示結果,這種情況就非常適合用 \G 來看數據

簡單查詢語句的書寫與執行順序

查詢語句書寫

查詢出 emp 表中id 在 3~6 的員工詳細信息

思路:從emp 表中,查 id 大於3 且 小於 6 的數據

語句

select * from emp where id > 3 and id < 6;

這里僅為了演示書寫順序,不考慮其他寫法

執行順序

最先執行的是 from,來確定到底是哪張表

然后執行 where,根據條件篩選數據

最后執行 select,來拿篩選出來的數據中的(某些,select 后面跟的字段名)字段

科普-- 起別名

關鍵字 as

  • 可以給表起別名
  • 可以給查詢出來的虛擬表(查詢結果)起別名
  • 可以給字段起別名
  • 可以給函數的結果取別名(max、min 等)

寫法

要起別名的對象 as 別名 或者 直接 要起別名的對象 別名

不過盡量還是用as ,不用as 可能語義不明確

給函數結果起別名

... max(hire_date) as max_date ...

給表起別名

select ... from emp as t1 ....

給查詢出來的虛擬表取別名

... (select * from emp) as t2 ...

給字段起別名

select name as '姓名', post '部門' from emp;

可以對字段做四則運算(加減乘數)

查一下 jason 的年薪

select name as '姓名', salary * 12 as '年薪' from emp where name = 'jason';

concat 格式化拼接字段

可以按指定格式拼接字段

select concat('oldboy_', name, '_', id), sex, post, salary from emp;

concat_ws 用指定字符拼接字段

select concat_ws(':', name, sex, age) from emp;

定制化查詢結果

復雜查詢實現小竅門:

寫sql語句的時候,千萬不要急着一口氣寫完(切忌心浮氣躁)

前期按照歩鄹一步步寫將前一步操作產生的結果當成是一張新的表,然后基於該表再進行其他操作,寫一步查詢看一下結果然后基於當前結果再往后寫

我們查詢數據一般都需要做一些過濾,單純靠 select * from 表名; 就無法達到要求,此時我們可以通過

常見的數據定制化關鍵字(非多表查詢)

  • where 條件過濾數據

    一般配合一堆聚合函數使用

  • group by 對數據進行分組

    • having 對分組的結果再進行條件過濾(必須跟在 group by 語句后面)

  • distinct 對查詢結果去重

  • order by 對查詢結果排序

  • limit 限制顯示數據條數

where 結合過濾條件過濾結果

> < = != <= >= <> 比較運算符

# 查詢出 emp 表中, id 大於3 的員工信息
select * from emp where id > 3;

and or not 與或非連接多個條件

一般用來連接多個條件

and 並且

or 或

not 非

is

針對 null 判斷的時候只能用 is 不能用 =

案例

# and
# 1.查詢id大於等於3小於等於6的數據
select id,name from emp where id >= 3 and id <= 6;

# or
# 2.查詢薪資是20000或者18000或者17000的數據
select * from emp where salary = 20000 or salary = 18000 or salary = 17000;

# not
# 5.查詢id小於3或者大於6的數據
select *  from emp where id not between 3 and 6;

# is
# 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;

范圍

between ... and ... 表示范圍(整型字段可用)

in

and or not 結果其他關鍵字組合

案例

# between ... and ... 
# 1.查詢id大於等於3小於等於6的數據
select *  from emp where id between 3 and 6;

# in
# 2.查詢薪資是20000或者18000或者17000的數據
select * from emp where salary in (20000,18000,17000);

# and or not
# 在上一模塊中有案例

exists 是否存在

# EXISTS 關鍵字表示存在
# 返回值是 True 或者 False
select * from emp where exists (select id from dep where id > 203);  # 用到了下面的子查詢(知識點)

like 模糊匹配

一般包含有 ... 之類的查詢都會用 like 關鍵字,模糊匹配

小技巧

是否含有用 % 包圍起來

固定長度用 來占位,一個 _ 表示一個字符

案例

# 3.查詢員工姓名中包含o字母的員工姓名和薪資
select name,salary from emp where name like '%o%';

# 4.查詢員工姓名是由四個字符組成的員工姓名與其薪資
# 方案一:用四個 _ 代替四個字符
select name,salary from emp where name like '____';
# 方案二:利用 char_length(字段名) 來獲取字段長度
select name,salary from emp where char_length(name) = 4;  # 注意 sql_mode 里 PAD_CHAR_TO_FULL_LENGTH 這么個約束,否則 char類型定長可能會受影響

regexp 正則

可用正則規則匹配字符串作為查詢條件

select * from emp where name regexp '^j.*(n|y)$';

group by 分組

分組之后應該做到最小單位是組,而不應該再展示組內的單個信息

MySQL 中分組之后,只能拿到分組的字段信息無法直接獲取其他字段信息

但是你可以通過其他方法(如:聚合函數)間接地獲取

分組相當於打包,聚合函數可以對包里每一個元素進行處理,最終拿出想要的

剛開始查詢表,一定要按照最基本的步驟,先確定是哪張表,再確定查這張表也沒有限制條件,再確定是否需要分類,最后再確定需要什么字段對應的信息

應用場景

每個部門的平均薪資,男女比例等

分組嚴格模式(推薦開啟)

select * from emp group by post; 如果你的MySQL不報錯,說明分組的嚴格模式沒有設置

--> only_full_group_by 限制分組

非分組嚴格模式下

設置分組嚴格模式(其他的嚴格模式別忘寫了)

set global sql_mode='strict_trans_tables,pad_char_to_full_length,only_full_group_by';

pad_char_to_full_length 驗證 char varchar占用空間用,會影響 char_length() 獲取的長度

strict_trans_tables 限制 sql 不能不合規則的直接報錯

分組嚴格模式下執行,直接報錯(day37.emp.id --> 數據庫day37.表emp.字段id),字段不在分組里

having 分組條件

havingwhere 是一模一樣的,也是用來篩選數據的,但是 having 必須在 group by 后面使用

where是對整體數據做一個初步的篩選,而having是對分組之后的數據再進行一次針對性的篩選

select post, avg(salary) from emp where age > 30 group by post having avg(salary) > 10000;

統計各部門年齡在30歲以上的員工平均工資,並且保留平均工資大於10000的部門

分組結合聚合函數

只能在分組之后使用(如果沒有寫group by ,默認所有數據就是一組

也可以說是 where 不能用聚合函數(執行順序過了 where之后就可也以算分組之后了--> 執行順序)

max min avg sum count

能夠獲取到分組之后除了分組依據以外的字段,將該字段作為函數的條件

# 強調:只要分組了,就不能夠再“直接”查找到單個數據信息了,只能獲取到組名
# 2.獲取每個部門的最高工資  
# 以組為單位統計組內數據>>>聚合查詢(聚集到一起合成為一個結果)
# 每個部門的最高工資
select post,max(salary) from emp group by post;
# 每個部門的最低工資
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;

# 每個部門的人數
# 在統計分組內個數的時候,填寫任意非空字段都可以完成計數(推薦使用能夠標識數據的字段,比如id字段)
select post,count(id) from emp group by post;

group_concat 分組拼接記錄字段

能夠獲取到分組之后除了分組依據以外的字段,還能做拼接操作

# 3.查詢分組之后的部門名稱和每個部門下所有的學生姓名
# group_concat(分組之后用)不僅可以用來顯示除分組外字段還有拼接字符串的作用
select post,group_concat(name) from emp group by post;

select post,group_concat(name,"_SB") from emp group by post;

select post,group_concat(name,": ",salary) from emp group by post;

select post,group_concat(salary) from emp group by post;


# 4.補充concat(不分組時用)拼接字符串達到更好的顯示效果 as語法使用(前面有講到)
select name as 姓名,salary as 薪資 from emp;
select concat("NAME: ",name) as 姓名,concat("SAL: ",salary) as 薪資 from emp;

/*
concat  在不分組情況下使用
group_concat  用在分組之后
*/

distinct 去重

對整個查詢(查詢出的虛擬表)結果中重復的數據去重,重復必須數據是一模一樣的才能去重,只要有一個(字段)不一樣都不能算是重復的數據

如果你查詢出來的數據中包含主鍵(非空且唯一),那么不可能去重成功

個人推薦理解成作用於上一步查詢結果的(不要以為像order by一樣修飾某個字段)

select distinct age, id from emp;

查詢結果有重復的情況下,會自動去除重復

select distinct sex, age, id from emp order by sex, age asc;

記錄沒有重復,distinct 無效

order by 排序

order by 有升序(ASC)、降序(DESC)兩種排序規則,默認升序

多個排序字段時,放前面的作為優先排序條件,相同再按照后面的字段排序

select post, avg(salary) from emp where age>10 group by post having avg(salary) > 1000 order by avg(salary);

單個字段排序

select * from emp order by sex, age asc;

多字段排序

limit 限制展示數據的條數

select * from emp limit 3;

當limit 只有一個參數的時候,表示的是從第一條開始只展示幾條

select * from emp limit 5,5;

當limit 有兩個參數的時候,第一個參數表的起始位置,第二個參數表示從起始位置開始往后展示的條數

練習

查詢工資最高的人的詳細信息

select * from emp order by salary desc limit 1;

應用場景

分頁數據展示,每頁只展示多少條,每頁展示的內容,是第幾條到第幾條

究極版執行順序書寫順序

書寫順序(除了 select ... from ... 其他是可選的)

select distinct 字段1,字段2(有分組時只能寫分組字段或聚合函數) from 表名
where 條件(不能用聚合函數)
group by (單個)字段
having 條件
order by 字段1,字段2 排序規則
limit 起始位置,條數;

執行順序

from
where
group by
	having  # 必須跟在 group by 后面
# 后4個順序不太重要,也不一定對
order by
limit
distinct
select

多表查詢

前言

在昨天的知識點中,員工信息全存為一張表不太合理,我們選擇了拆表,分析了表關系,最終拆分成了員工表與部門表兩張表,表示拆分好了,但怎么去查詢數據呢?

實現多表查詢,有下面兩種方式

  • 聯表查詢
  • 子查詢

每一次的查詢結果都是一張虛擬表,我們可以用 as 關鍵字給虛擬表取別名,然后將其當做普通表作為查詢條件使用

測試數據創建

創建數據庫與表

create database db2;
use db2;

create table dep(
id int,
name varchar(20) 
);

create table emp(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int
);

插入表記錄數據

insert into dep values
(200,'技術'),
(201,'人力資源'),
(202,'銷售'),
(203,'運營');

insert into emp(name,sex,age,dep_id) values
('jason','male',18,200),
('egon','female',48,201),
('kevin','male',38,201),
('nick','female',28,202),
('owen','male',18,200),
('jerry','female',18,204);

# 當初為什么我們要分表,就是為了方便管理,在硬盤上確實是多張表,但是到了內存中我們應該把他們再拼成一張表進行查詢才合理

笛卡爾集/積 -- 科普

笛卡爾集的列數為每個表的列數之和,笛卡爾集的行數為每個表的行數相乘

我們經常做的多表查詢就是在笛卡爾集中通過篩選條件得出的數據,所以笛卡爾集是多表查詢的基礎

select * from emp, dep; 結果是一個笛卡爾集/積

select * from emp;

select * from dep;

后面跟條件也可以達到多表查詢

select * from emp, dep where emp.dep_id = dep.id

聯表查詢

通過下面四種連接語句來實現多表查詢

inner/left/right join ...左右是表 on ... 后面可以跟條件

內連接 inner join ... on

僅保留兩張表有對應關系的記錄

select * from emp inner join dep on emp.dep_id=dep.id;

左連接 left join ... on

在內連接的基礎上保留左表沒有對應關系的記錄

select * from emp left join dep on emp.dep_id = dep.id;

右連接 right join ... on

在內連接的基礎上保留右表沒有對應關系的記錄

select * from emp right join dep on emp.dep_id = dep.id;

全連接 union ... on

不常用

在內連接的基礎上保留左、右面表沒有對應關系的的記錄

寫法:只需要在左連接和右連接的sql 語句中間加個union就變成了全連接

select * from emp left join dep on emp.dep_id = dep.id  # 左連接 sql,后面不要加分號
union
select * from emp right join dep on emp.dep_id = dep.id;  # 右連接 sql

子查詢

將一個查詢語句用括號括起來,將查詢結果(虛擬表)作為另外一個 sql 語句的查詢條件

ps:表的查詢結果可以作為其他表的查詢條件,也可以通過起別名的方式把它作為一張虛擬表跟其他表做關聯查詢

# 1.查詢部門是技術或者人力資源的員工信息

# 思路
# 先獲取技術部和人力資源部的id號,再去員工表里面根據前面的id篩選出符合要求的員工信息
select * from emp where dep_id in (select id from dep where name = "技術" or name = "人力資源");

select * from emp;

select id from dep where name = "技術" or name = "人力資源";

# 2.每個部門最新入職的員工

# 思路
# 先查每個部門最新入職的員工,再按部門對應上聯表查詢

select t1.id, t1.name, t1.hire_date, t1.post from emp as t1
inner join
# 根據分組求出最新入職員工
(select post,max(hire_date) as max_date from emp group by post) as t2
on t1.post = t2.post
where t1.hire_date = t2.max_date;

完整表信息

練習小案例

# 平均年齡在25歲以上的部門名

# 聯表
select dep.name from dep inner join emp on emp.dep_id = dep.id group by dep.name having avg(age) > 25;

# 子查詢
select name from dep where dep.id in (select dep_id from emp group by dep_id having avg(age) > 25);

聯表查詢思路

1.先把兩張表連起來,把結果查出來看看,再接着往下寫

select * from dep inner join emp on emp.dep_id = dep.id;

2.再根據部門分組,篩選出平均年齡大於25的部門名

group by dep.name having avg(age) > 25

合並上一步的語句(* --> dep.name)

select dep.name from dep inner join emp on emp.dep_id = dep.id group by dep.name having avg(age) > 25;

子查詢思路

1.先根據 部門id 分組,查出平均年齡大於 25多的部門id

select dep_id from emp group by dep_id having avg(age) > 25;

2.將上一步的 部門id 作為篩選條件 聯合上部門表查出來

select name from dep where dep.id in (select dep_id from emp group by dep_id having avg(age) > 25);


免責聲明!

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



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