SQL學習精粹之group by分組

group by 有一個原則,面試

不分組必聚合原則:就是 select 後面的全部列中,沒有使用聚合函數的列,必須出如今 group by 後面(重要)sql

1、group by取最大值

面試遇到一個分組取最大值的問題函數

例子一 取最新充值記錄

充值記錄表spa

CREATE TABLE chongZhi (
   id int not null primary key AUTO_INCREMENT COMMENT '主鍵編號',
   userId int COMMENT '充值用戶id',
   jine int  COMMENT '充值金額',
   date date COMMENT '充值日期'
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '充值記錄表';

insert into chongZhi(userId,jine,date) value(1,20,'2016-10-28');
insert into chongZhi(userId,jine,date) value(1,60,'2016-10-29');
insert into chongZhi(userId,jine,date) value(1,55,'2016-10-30');
insert into chongZhi(userId,jine,date) value(2,100,'2016-10-22');
insert into chongZhi(userId,jine,date) value(2,200,'2016-10-28');
insert into chongZhi(userId,jine,date) value(2,300,'2016-10-30');

select userId as 用戶ID,max(date) as 最近充值時間 from chongZhi group by 1;

運行結果code

但是這樣不能顯示充值數據ID和充值金額,否則會違反不分組必聚合原則sql會報錯數學

下面的例子能夠解決這個問題。it

例子二 物料供貨商最新報價

物料報價表:io

根據上面的表 查詢每種物料 每種供貨商 的最新報價:table

與上面問題的不一樣之處:class

①這個是兩個字段肯定惟一性(相似聯合主鍵,這裏是聯合分組)。

②須要顯示本表中其餘的字段。

其實這個差很少。

SQL:

CREATE TABLE `WuLiaoBiao` (
   id int not null primary key AUTO_INCREMENT COMMENT '主鍵編號',
   wuliaoName varchar(50) COMMENT '物料名',
   gongId int COMMENT '供貨商ID',
   price int  COMMENT '價格',
   date date COMMENT '日期'
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '物料表';


insert into WuLiaoBiao(wuliaoName,gongId,price,date) value('物料1',1,500,'2016-10-01');
insert into WuLiaoBiao(wuliaoName,gongId,price,date) value('物料1','1','200','2016-10-02');
insert into WuLiaoBiao(wuliaoName,gongId,price,date) value('物料1','2','300','2016-10-03');
insert into WuLiaoBiao(wuliaoName,gongId,price,date) value('物料1','2','800','2016-10-04');
insert into WuLiaoBiao(wuliaoName,gongId,price,date) value('物料1','2','100','2016-10-05');
insert into WuLiaoBiao(wuliaoName,gongId,price,date) value('物料2','2','190','2016-10-06');
insert into WuLiaoBiao(wuliaoName,gongId,price,date) value('物料2','2','150','2016-10-09');
insert into WuLiaoBiao(wuliaoName,gongId,price,date) value('物料2','1','100','2016-10-19');
insert into WuLiaoBiao(wuliaoName,gongId,price,date) value('物料2','1','200','2016-10-20');
insert into WuLiaoBiao(wuliaoName,gongId,price,date) value('物料2','1','400','2016-10-29');
                                                                                        

select t1.id,t1.wuliaoName,t1.gongId,t1.price,t1.date from WuLiaoBiao t1, 
(select wuliaoName,gongId,max(date) as date from WuLiaoBiao group by wuliaoName,gongId) t2
where t1.wuliaoName=t2.wuliaoName and t1.gongId=t2.gongId and t1.date=t2.date;

運行結果

實現方式,依據【不分組必聚合原則】,由於須要查詢的列有的不在聚合函數內,因此外層主查詢不能分組。使用自己表以及該表的分組查詢結果作自鏈接,where條件爲分組查詢的全部列(包括聚合函數列)和自己表作等值鏈接。

2、group by+case when

例子一  日期勝負表:

score 表:

某球隊須要作天天的比賽勝負數統計:

select date as 時間, count(1) as 比賽總數,
  sum(case when sco='勝' then 1 else 0 end) 勝利數,
  sum(case when sco="負" then 1 else 0 end) 失敗數
  from score group by date;

select date as 時間, count(1) as 比賽總數,
	sum(case sco when '勝' then 1 else 0 end) 勝利數,
	sum(case sco when "負" then 1 else 0 end) 失敗數
	from score group by date;

查詢結果:

例子二  科目成績(行轉列):

數據表:

姓名 科目 分數
張三 語文 80
張三 數學 98
張三 英語 65
李四 語文 70
李四 數學 80
李四 英語 90

指望查詢結果:

姓名 語文 數學 英語
張三 80     98      65
李四 70     80      90

代碼

SQL 代碼  

create table testScore    
(    
   tid int primary key AUTO_INCREMENT,    
   tname varchar(30) null,    
   ttype varchar(10) null,    
   tscor int null   
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '成績表';
     
-- 插入數據    
insert into testScore(tname,ttype,tscor) value('張三','語文',80); 
insert into testScore(tname,ttype,tscor) value('張三','數學',98);   
insert into testScore(tname,ttype,tscor) value('張三','英語',65);    
insert into testScore(tname,ttype,tscor) value ('李四','語文',70);    
insert into testScore(tname,ttype,tscor) value ('李四','數學',80);    
insert into testScore(tname,ttype,tscor) value ('李四','英語',90); 

select tname 姓名 ,
max(case ttype when '語文' then tscor else 0 end) 語文,
max(case ttype when '數學' then tscor else 0 end) 數學,
max(case ttype when '英語' then tscor else 0 end) 英語 
from testScore 
group by tname

實例三 大州人口統計:

有以下數據:(爲了看得更清楚,我並無使用國家代碼,而是直接用國家名做爲Primary Key)

國家(country) 人口(population)
中國 600
美國 100
加拿大 100
英國 200
法國 300
日本 250
德國 200
墨西哥 50
印度 250

根據這個國家人口數據,統計亞洲和北美洲的人口數量。應該獲得下面這個結果。

人口
亞洲 1100
北美洲 250
其餘 700

代碼

SQL 代碼  

SELECT  CASE country
                WHEN '中國'     THEN '亞洲'
                WHEN '印度'     THEN '亞洲'
                WHEN '日本'     THEN '亞洲'
                WHEN '美國'     THEN '北美洲'
                WHEN '加拿大'  THEN '北美洲'
                WHEN '墨西哥'  THEN '北美洲'
        ELSE '其餘' END  AS 大州,
        SUM(population) AS 人口總數
FROM    country_pop
GROUP BY CASE country
                WHEN '中國'     THEN '亞洲'
                WHEN '印度'     THEN '亞洲'
                WHEN '日本'     THEN '亞洲'
                WHEN '美國'     THEN '北美洲'
                WHEN '加拿大'  THEN '北美洲'
                WHEN '墨西哥'  THEN '北美洲'
        ELSE '其餘' END;

一樣的,咱們也能夠用這個方法來判斷工資的等級,並統計每一等級的人數。SQL代碼以下;

SQL 代碼  

SELECT
        CASE WHEN salary <= 500 THEN '1'
             WHEN salary > 500 AND salary <= 600  THEN '2'
             WHEN salary > 600 AND salary <= 800  THEN '3'
             WHEN salary > 800 AND salary <= 1000 THEN '4'
        ELSE NULL END salary_class,
        COUNT(*)
FROM    Table_A
GROUP BY
        CASE WHEN salary <= 500 THEN '1'
             WHEN salary > 500 AND salary <= 600  THEN '2'
             WHEN salary > 600 AND salary <= 800  THEN '3'
             WHEN salary > 800 AND salary <= 1000 THEN '4'
        ELSE NULL END;

對於groupby後面通常都是跟一個列名,但在該例子中經過case語句使分組變得跟強大了。

實例四 人口性別分組(行轉列):

有以下數據

國家(country) 性別(sex) 人口(population)
中國 1 340
中國 2 260
美國 1 45
美國 2 55
加拿大 1 51
加拿大 2 49
英國 1 40
英國 2 60

按照國家和性別進行分組,得出結果以下

國家
中國 340 260
美國 45 55
加拿大 51 49
英國 40 60

代碼

SQL 代碼   

SELECT country,
       SUM( CASE WHEN sex = '1' THEN 
                      population ELSE 0 END),  --男性人口
       SUM( CASE WHEN sex = '2' THEN 
                      population ELSE 0 END)   --女性人口
FROM  Table_A
GROUP BY country;

GROUP BY子句中的NULL值處理

當GROUP BY子句中用於分組的列中出現NULL值時,將如何分組呢?SQL中,NULL不等於NULL(在WHERE子句中有過介紹)。然而,在GROUP BY子句中,卻將全部的NULL值分在同一組,即認爲它們是「相等」的。

3、HAVING子句

GROUP BY子句分組,只是簡單地依據所選列的數據進行分組,將該列具備相同值的行劃爲一組。而實際應用中,每每還須要刪除那些不能知足條件的行組,爲了實現這個功能,SQL提供了HAVING子句。語法以下。

SELECT column, SUM(column)

FROM table

GROUP BY column

HAVING SUM(column) condition value

說明:HAVING一般與GROUP BY子句同時使用。固然,語法中的SUM()函數也能夠是其餘任何聚合函數。DBMS將HAVING子句中的搜索條件應用於GROUP BY子句產生的行組,若是行組不知足搜索條件,就將其從結果表中刪除。

HAVING子句的應用

從TEACHER表中查詢至少有兩位教師的系及教師人數。

實現代碼:

SQL 代碼  

SELECT DNAME, COUNT(*) AS num_teacher

FROM TEACHER

GROUP BY DNAME

HAVING COUNT(*)>=2

HAVING子句與WHERE子句的區別

HAVING子句和WHERE子句的類似之處在於,它也定義搜索條件。但與WHERE子句不一樣,HAVING子句與組有關,而不是與單個的行有關。

一、若是指定了GROUP BY子句,那麼HAVING子句定義的搜索條件將做用於這個GROUP BY子句建立的那些組。

二、若是指定WHERE子句,而沒有指定GROUP BY子句,那麼HAVING子句定義的搜索條件將做用於WHERE子句的輸出,並把這個輸出看做是一個組。

三、若是既沒有指定GROUP BY子句也沒有指定WHERE子句,那麼HAVING子句定義的搜索條件將做用於FROM子句的輸出,並把這個輸出看做是一個組。

四、在SELECT語句中,WHERE和HAVING子句的執行順序不一樣。在本書的5.1.2節介紹的SELECT語句的執行步驟可知,WHERE子句只能接收來自FROM子句的輸入,而HAVING子句則能夠接收來自GROUP BY子句、WHERE子句和FROM子句的輸入。

相關文章
相關標籤/搜索