基本查詢語句及方法
測試數據創建
如果在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 分組條件
having
跟where
是一模一樣的,也是用來篩選數據的,但是 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);