1. 程式人生 > 實用技巧 >[BZOJ2152] 聰聰可可 - 點分治

[BZOJ2152] 聰聰可可 - 點分治

目錄

一:單表查詢的語法

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;

二:分組注意事項

  1. 關鍵字where和group by同時出現的時候group by必須在where的後面

  2. where先對整體資料進行過濾之後再分組操作

  3. 聚合函式只能在分組之後使用

    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結尾的所有字元.