[BZOJ2152] 聰聰可可 - 點分治
阿新 • • 發佈:2020-09-08
目錄
一:單表查詢的語法
select 欄位1,欄位2... from 表名
where 條件
group by field
having 篩選
order by field
limit 限制條數;
二:關鍵字的執行優先順序(重點)
from # 1. 找到表:from where # 2. 拿著where指定的約束條件,去檔案/表中取出一條條記錄 group by # 3. 將取出的一條條記錄進行分組group by,如果沒有group by,則整體作為一組 having # 4. 將分組的結果進行having過濾 select # 5. 執行select distinct # 6. 去重 order by # 7. 將結果按條件排序:order by limit # 8. 限制結果的顯示條數
三:準備表
company.employee 員工id id int 姓名 emp_name varchar 性別 sex enum 年齡 age int 入職日期 hire_date date 崗位 post varchar 職位描述 post_comment varchar 薪水 salary double 辦公室 office int 部門編號 depart_id int
提示: 如果在windows系統中,插入中文字元,select的結果為空白,可以將所有字元編碼統一設定成gbk(個別比較老的windos電腦可能會出現這樣的問題)
reate table emp( id int not null unique 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);
四:條件約束
一:操作
# 查詢id大於等於3小於等於6的資料 --> 比較運算子 & between
select * from emp where id>=3 and id<=6;
select * from emp where id between 3 and 6;
# 查詢薪資是20000或者18000或者17000的資料 --> in & 邏輯運算子or
select * from emp where salary=20000 or salary=17000 or salary=18000;
select * from emp where salary in (20000, 17000, 18000);
# 查詢員工姓名中包含字母o的員工的姓名和薪資 --> 模糊運算like
"""
模糊查詢: 1ike
%: 匹配任意多 個字元
_: 匹配任意單個字元
"""
select name,salary from emp where name like '%o%';
# 查詢員工姓名是由四個字元組成的姓名和薪資 --> 模糊運算__ & char_length()
select name,salary from emp where name like '____';
select name,salary from emp where char_length(name)=4; # 前提: 沒有取消剔除模式pad_char_to_full_length
# 查詢id小於3或者id大於6的資料 --> 比較運算子 & between
select * from emp where id not between 3 and 6;
select * from emp where id<3 or id>6;
# 查詢薪資不在20000,18000,17000範圍的資料 --> not + in
select * from emp where salary not in (20000, 18000, 17000);
# 查詢崗位描述為空的員工姓名和崗位名(易錯題) --> is null & null
# select name,post from emp where post_comment = null; # 注意: 針對null不能用等號
select name,post from emp where post_comment is null;
select name,post from emp where post_comment is not null;
2:總結
# where字句中可用操作
"""
<1> 比較運算子: >, <, >=, <=, <>, !=
<2> 邏輯運算子: and 與 or 與 not
<3> between 與 and
<4> 成員運算: in(值1, 值2, 值3)
<5> 模糊匹配: like "_"或" % "
<6> is null 或 is not null 判斷某個欄位是否為null, 不能用等號,需要用is
"""
# 當前語句執行優先順序
from -> where -> select
# 實際語句執行優先順序過程
from -> where -> group by -> having -> select -> distinct -> order by -> limit
3:小練習
# 1. 檢視崗位是teacher的員工姓名、年齡
select name,age from emp where post='teacher';
# 2. 檢視崗位是teacher且年齡大於30歲的員工姓名、年齡
select name,age from emp where post=teacher and age>30;
# 3. 檢視崗位是teacher且薪資在9000-10000範圍內的員工姓名、年齡、薪資
select name,age,salary from emp where post='teacher' and salary>=9000 and salary<=10000;
select name,age,salary from emp where post='teacher' and salary between 9000 and 10000;
# 4. 檢視崗位描述不為NULL的員工資訊
select * from emp where post_comment not is null;
# 5. 檢視崗位是teacher且薪資是10000或9000或30000的員工姓名、年齡、薪資
select name,age,salary from emp where post='teacher' and salary in (10000, 9000, 30000);
# 6. 檢視崗位是teacher且薪資不是10000或9000或30000的員工姓名、年齡、薪資
select name,age,salary from emp where post='teacher' and salary not in (10000, 9000, 30000);
# 7. 檢視崗位是teacher且名字是jin開頭的員工姓名、年薪
select name,salary*12 from emp where post='teacher' and name like 'jin%';
五:分組查詢
強調:如果我們用unique的欄位作為分組的依據,則每一條記錄自成一組,這種分組沒有意義. 只有多條記錄之間的某個欄位值相同時,就可以使用該欄位用來作為分組的依據
一:操作
# 分組實際應用場景分組應用場景非常的多例如:
"""
男女比例 部門平均薪資 部門禿頭 國家之間資料統計
"""
# 注意1: 分組之後最小可操作單位應該是組, 而不是組內的單個數據.
# 注意2: 如果沒有設定分組嚴格模式的時候是可正常執行, 返回的是分組之後每個組的第一條資料. 如果設定了分組嚴格模會直接報錯. 沒有設定分組嚴格模式的返回值是不符合分組的規範的, 分組之後不應該考慮單個數據, 而應該以組為操作單位. (補充: 分組之後, 只能查到分組的欄位以及組內多條記錄舉和的成果)
# 按照部門分組
select * from emp group by post; # 注意: 沒有設定分組嚴格模式, 這裡會拿到每一個部門中的第一條資料
# 保留嚴格模式+追加分組嚴格模式(注意: 分組嚴格模式只和only_full_group_by有關, 嚴格模式的指定只是為了減輕mysql伺服器存取欄位時超出的壓力, 保證資料的儲存的嚴格性)
set global sql_mode='strict_trans_tables,only_full_group_by';
# 分組預設只能拿到分組的依據. 按照什麼分組就只能拿到什麼組. 其他欄位不能直接獲取, 需要藉助於一些方法(聚合函式).
select post from emp group by post;
"""
什麼時候需要分組啊???
關鍵字: 每個 平均 最高 最低
"""
# 獲取每個部門的最高薪資 --> 聚合函式max() + 取別名as
select post,max(salary) as max_salary from emp group by post;
# 獲取每個部門的最低薪資 --> 聚合函式min()
select post,min(salary) from emp group by post;
# 獲取每個部門的平均薪資 --> 聚合函式avg()
select post,avg(salary) from emp group by post;
# 獲取每個部門的工資總和 --> 聚合函式sum()
select post,sum(salary) from emp group by post;
# 獲取每個部門的人數 --> 聚合函式count()
select post,count(id) from emp group by post; # count最好存放可以標識唯一性的欄位(注意: 最好不要放null關鍵字指定的欄位)
select post,count(post_comment) from emp group by post;
# 查詢分組之後的部門名稱和每個部門下所有的員工姓名 --> 聚合函式group_concat() & group_concat()的拼接操作
# group_concat不單單可以支援你獲取分組之後的其他欄位值, 還支援拼接操作.
select post,group_concat(name) from emp group by post; # group_concat獲取分組之後的欄位中的值
select post,group_concat(name,'_DSB') from emp group by post;
select post,group_concat(name,':',salary) from emp group by post;
# 補充1: 定義顯示格式. 不分組的時候用 concat() 或者 concat_ws() 或者 case語句
"""
concat() 函式用於連線字串
concat_ws("定義統一欄位分割符號", 欄位1, 欄位2) 開頭定義多欄位間自動分隔符
注意: 一個concat或concat_ws只能表示一個欄位, 如果把所有欄位放到裡面拼接顯示格式, 就會顯示成一個欄位中的一列內容
case語句虛擬碼
case
when 條件1 then
結果
when 條件2 then
結果
else
結果
end
"""
select concat('姓名:', name),concat('年齡:', age) from emp;
select concat(name, ':', age, ':', sex) from emp;
select concat_ws(':', name, age, sex) from emp; # 對於上面一條語句重複了多次的`:`, 還是使用下面的這種.
select
(
case
when name='egon' then
name
when name='alex' then
concat(name, '_bigsb')
else
concat(name, 'sb')
end
) as new_name
from emp;
# 補充2: as語法不單單可以給欄位起別名還可以給表起別名, 且as語句可以省略, 但是不推薦省略, 不省略可以讓語義更加的明確.
select * from emp as t1;
select * from emp t1; # as不推薦省略
select emp.id,emp.name from emp;
select emp.id,emp.name from emp as t1; # 報錯: 因為先執行from語句,執行完了from語句當前的表名被改為了t1, 接著select語句執行, select語句無法識別emp.id
# 補充3: 查詢每個人的年薪12薪
select name,salary*12 from emp;
select id, name*12 from emp; # 注意: 字串型別不支援乘除
"""綜合練習where+group by"""
# 統計各部門年齡在30歲以上的員工平均薪資
# 1. 先求所有年齡大於30歲的員工
select * from emp where age>30;
# 2. 再對結果進行分組
select * from emp where age>30 group by post;
# 3. 得出最終結果
select post,avg(salary),group_concat(name, ':', age) from emp where age>30 group by post;
二:分組注意事項
-
關鍵字where和group by同時出現的時候group by必須在where的後面
-
where先對整體資料進行過濾之後再分組操作
-
聚合函式只能在分組之後使用
select id,name,age from emp where max(salary) > 3000; # 錯誤: ERROR 1111 (HY000): Invalid use of group function(無效使用組函式) select max(salary) from emp; # 可以: 不分組預設整體就是一組
三:總結
# 聚合函式
"""
max() as 別名 # as可以給欄位起別名, 也可以直接省略不寫, 但是不推薦. 因為使用as的話可能語意不明確易錯亂. 也可以對錶取別名.
min()
sum()
avg() avg --> average
count() # count最好存放可以標識唯一性的欄位(注意: 最好不要放null關鍵字指定的欄位)
group_concat() # 不僅獲取分組後的其它欄位們, 還可以進行拼接操作.
concat() # 不分組時使用, 可以對查詢出來的欄位進行額外的拼接操作
concat_ws("定義統一欄位分割符號", 欄位1, 欄位2) 開頭定義多欄位間自動分隔符
注意: 一個concat或concat_ws只能表示一個欄位, 如果把所有欄位放到裡面拼接顯示格式, 就會顯示成一個欄位中的一列內容
case語句虛擬碼
case
when 條件1 then
結果
when 條件2 then
結果
else
結果
end
salary * 12 # 支援算符運算, 但是不能爭對字串
"""
# 當前語句執行優先順序
from -> where -> group by -> select
# 實際語句執行優先順序
from -> where -> group by -> having -> select -> distinct -> order by -> limit
四:小練習
# 查出所有員工的名字,薪資,格式為: <名字:egon> <薪資:3000>
select concat('<名字:',name,'> ', '<薪資:',salary,'>') from emp;
# 查出所有的崗位(去掉重複)
select distinct post from emp;
# 查出所有員工名字,以及他們的年薪,年薪的欄位名為annual_year
select name,salary*12 as annual_year from emp;
Copy
# 1. 查詢崗位名以及崗位包含的所有員工名字
select post,group_concat(name) from emp group by post;
# 2. 查詢崗位名以及各崗位內包含的員工個數
seect post,count(id) from emp group by post;
# 3. 查詢公司內男員工和女員工的個數
select sex, count(id) from emp group by sex;
# 4. 查詢崗位名以及各崗位的平均薪資
select post,avg(salary) from emp group by post;
# 5. 查詢崗位名以及各崗位的最高薪資
select post,max(salary) from emp group by post;
# 6. 查詢崗位名以及各崗位的最低薪資
select post,min(salary) from emp group by post;
# 7. 查詢男員工與男員工的平均薪資,女員工與女員工的平均薪資
select sex,avg(salary) from emp group by sex;
五:過濾
一:操作
# 前提: 分組之後的篩選條件
"""
having的語法和where是一致的, 只不過having是在分組之後進行的過濾操作, 即having是可以直接使用聚合函式的.
"""
# 統計各部門年齡在30歲以上的員工平均工資並且保留平均薪資大於10000的部門
select post,avg(salary) from emp
where age>30
group by post
having avg(salary) > 10000;
select post,avg(salary),group_concat(name, ':', age) from emp
where age>30
group by post
having avg(salary)>10000;
# 當前語句的執行優先順序
from -> where -> group by -> having -> select
# 實際語句執行優先順序
from -> where -> group by -> having -> select -> distinct -> order by -> limit
二:小練習
# 1. 查詢各崗位內包含的員工個數小於2的崗位名、崗位內包含員工名字、個數
select post,group_concat(name),count(id) from emp group by post having count(id) <2;
# 2. 查詢各崗位平均薪資大於10000的崗位名、平均工資
select post,avg(salary) from emp group by post having avg(salary) > 10000;
# 3. 查詢各崗位平均薪資大於10000且小於20000的崗位名、平均工資
select post,avg(salary) from emp group by post having avg(salary) between 10000 and 20000;
六:去重
# 一定要注意必須是完全一樣的資料才可以去重!!! 所以去重的時候,一定要注意去除主鍵, 因為主鍵是部位空且唯一的.
"""
[
{'id':1,'name':'jason','age':18},
{'id':2,'name':'jason','age':18},
{'id':3,'name':'egon','age':18}
]
拓展: ORM框架的物件關係對映讓不懂SQL語句的人也能夠非常牛逼的操作資料庫
表 -- 對映成--> 型別
一條條的資料 -- 對映成--> 物件
欄位對應的值 -- 對映成--> 物件的屬性
實現原理:
你在寫類就意味著在建立表
你用類生成物件就意味著在建立資料
你物件點屬性就是在獲取資料欄位對應的值
目的: 就是減輕python程式設計師的壓力只需要會python面向物件的知識點就可以操作MySQL
"""
select distinct id,age from emp; # 注意: 去除被設定成主鍵的id
select distinct age from emp;
# 當前語句的執行優先順序
from -> select -> distinct
# 實際語句的執行優先順序
from -> where -> group by -> having -> select -> distinct -> order by -> limit
七:查詢排序:order by
一:操作
"""
order by預設升序.
預設升序: 後面可以指定asc,可以省略不寫
指定降序: desc
指定多種, 前者相等則按照後者的順序: age desc,salary asc;
"""
select * from emp order by salary; # 升序
select * from emp order by salary asc; # 升序
select * from emp order by salary desc; # 降序
# 先按照age降序排, 如果碰到age相同, 則再按照salary升序排.
select * from emp order by age desc,salary;
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) > 10000
order by avg(salary) desc;
select post,avg(salary) as avg_salary,group_concat(name, ':', age) from emp
where age>10
group by post
having avg(salary)>10000
order by avg_salary desc; # 注意: 這裡為什麼可以使用avg_salary, 是因為執行語句的優先順序select的優先順序高於avg_salary. 當select語句執行完畢avg(salary)被改名成了avg(salary), 所以接著執行order by語句就可以拿到avg_salary去進行排序.
# 當前語句的執行優先順序
from -> where -> group by -> having -> select -> order by
# 實際語句的執行優先順序
from -> where -> group by -> having -> select -> distinct -> order by -> limit
二:練習
# 1. 查詢所有員工資訊,先按照age升序排序,如果age相同則按照hire_date降序排序
select * from emp order by age asc,hire_date desc;
# 2. 查詢各崗位平均薪資大於10000的崗位名、平均工資,結果按平均薪資升序排列
select post,avg(salary) as avg_salary from emp group by post having avg(salary)>10000 order by avg_salary asc;
# 3. 查詢各崗位平均薪資大於10000的崗位名、平均工資,結果按平均薪資降序排列
select post,avg(salary) as avg_salary from emp group by post having avg(salary)>10000 order by avg_salary desc;
八:限制查詢的記錄數:limit
一:操作
"""
目的: 針對資料過多的情況我們通常都是做分頁處理
limit 5,5:
第一個引數是起始位置
第二個引數是展示條數
"""
select * from emp limit 3; # 只展示3條資料
select * from emp limit 0,5; # 第0條開始再往後取5條資料
select * from emp limit 5,5; # 第6條開始再往後取5條資料
# 當前語句的執行優先順序
from -> select -> limit
# 實際語句的執行優先順序
from -> where -> group by -> having -> select -> distinct -> order by
二:小練習
# 分頁顯示,每頁5條
select * from emp limit 5;
select * from emp limit 5,5;
select * from emp limit 10,5;
select * from emp limit 15,5;
九:使用正則表示式查詢
select * from emp where name regexp '^j.*(n|y)$' # 匹配j開頭, n或者y結尾的所有字元.