Oracle和MySQL分組查詢GROUP BY

Oracle和MySQL分組查詢GROUP BY




真題一、Oracle和MySQL中的分組(GROUP BY)有什麼區別?

答案:Oracle對於GROUP BY是嚴格的,全部要SELECT出來的字段必須在GROUP BY後邊出現,不然會報錯:ORA-00979: not a GROUP BY expression。而MySQL則不一樣,若是SELECT出來的字段在GROUP BY後面沒有出現,那麼會隨機取出一個值,而這樣查詢出來的數據不許確,語義也不明確。因此,做者建議在寫SQL語句的時候,應該給數據庫一個很是明確的指令,而不是讓數據庫去猜想,這也是寫SQL語句的一個很是良好的習慣。
下面給出一個示例。有一張T_MAX_LHR表,數據以下圖所示,有3個字段ARTICLE、AUTHOR和PRICE。請選出每一個AUTHOR的PRICE最高的記錄(要包含全部字段)。

ARTICLE mysql

AUTHOR 面試

PRICE sql

0001 數據庫

B express

3.99 微信

0002 網絡

A oracle

10.99 ide

0003 函數

C

1.69

0004

B

19.95

0005

A

6.96

首先給出建表語句:
CREATE TABLE T_MAX_LHR (ARTICLE VARCHAR2(30),AUTHOR VARCHAR2(30),PRICE NUMBER); --Oracle
--CREATE TABLE T_MAX_LHR (ARTICLE VARCHAR(30),AUTHOR VARCHAR(30),PRICE FLOAT); --MySQL oracle通用
INSERT INTO T_MAX_LHR VALUES ('0001','B',3.99);
INSERT INTO T_MAX_LHR VALUES ('0002','A',10.99);
INSERT INTO T_MAX_LHR VALUES ('0003','C',1.69);
INSERT INTO T_MAX_LHR VALUES ('0004','B',19.95);
INSERT INTO T_MAX_LHR VALUES ('0005','A',6.96);
COMMIT;
SELECT * FROM T_MAX_LHR;
在Oracle中的數據:
LHR@orclasm > SELECT * FROM T_MAX_LHR;
ARTICLE  AUTHOR        PRICE
-------- -------- ----------
0001     B              3.99
0002     A             10.99
0003     C              1.69
0004     B             19.95
0005     A              6.96
在MySQL中的數據:
mysql> SELECT * FROM T_MAX_LHR;
+---------+--------+-------+
| ARTICLE | AUTHOR | PRICE |
+---------+--------+-------+
| 0001    | B      |  3.99 |
| 0002    | A      | 10.99 |
| 0003    | C      |  1.69 |
| 0004    | B      | 19.95 |
| 0005    | A      |  6.96 |
+---------+--------+-------+
5 rows in set (0.00 sec)
分析數據後,正確答案應該是:

ARTICLE

AUTHOR

PRICE

0002

A

10.99

0003

C

1.69

0004

B

19.95

對於這個例子,很容易想到的SQL語句以下所示:
SELECT T.ARTICLE,T.AUTHOR,MAX(T.PRICE) FROM T_MAX_LHR T GROUP BY T.AUTHOR; 
SELECT * FROM T_MAX_LHR T GROUP BY T.AUTHOR;
在Oracle中執行上面的SQL語句報錯:
LHR@orclasm > SELECT T.ARTICLE,T.AUTHOR,MAX(T.PRICE) FROM T_MAX_LHR T GROUP BY T.AUTHOR; 
SELECT T.ARTICLE,T.AUTHOR,MAX(T.PRICE) FROM T_MAX_LHR T GROUP BY T.AUTHOR
       *
ERROR at line 1:
ORA-00979: not a GROUP BY expression




LHR@orclasm > SELECT * FROM T_MAX_LHR T GROUP BY T.AUTHOR;
SELECT * FROM T_MAX_LHR T GROUP BY T.AUTHOR
       *
ERROR at line 1:
ORA-00979: not a GROUP BY expression
在MySQL中執行一樣的SQL語句不會報錯:
mysql> select version();
+-------------------------------------------+
| version()                                 |
+-------------------------------------------+
| 5.6.21-enterprise-commercial-advanced-log |
+-------------------------------------------+


mysql> SELECT T.ARTICLE,T.AUTHOR,MAX(T.PRICE) FROM T_MAX_LHR T GROUP BY T.AUTHOR;
+---------+--------+--------------+
| ARTICLE | AUTHOR | MAX(T.PRICE) |
+---------+--------+--------------+
| 0002    | A      |        10.99 |
| 0001    | B      |        19.95 |
| 0003    | C      |         1.69 |
+---------+--------+--------------+
3 rows in set (0.00 sec)


mysql> SELECT * FROM T_MAX_LHR T GROUP BY T.AUTHOR;
+---------+--------+-------+
| ARTICLE | AUTHOR | PRICE |
+---------+--------+-------+
| 0002    | A      | 10.99 |
| 0001    | B      |  3.99 |
| 0003    | C      |  1.69 |
+---------+--------+-------+
3 rows in set (0.00 sec)
雖然執行不報錯,能夠查詢出數據,可是從結果來看數據並非最終想要的結果,甚至數據是錯亂的。下面給出幾種正確的寫法(在Oracle和MySQL中都可執行):
(1)使用相關子查詢
SELECT *
  FROM T_MAX_LHR T
 WHERE (T.AUTHOR, T.PRICE) IN (SELECT NT.AUTHOR, MAX(NT.PRICE) PRICE
                                 FROM T_MAX_LHR NT
                                GROUP BY NT.AUTHOR)
 ORDER BY T.ARTICLE;


SELECT *
  FROM T_MAX_LHR T
 WHERE T.PRICE = (SELECT MAX(NT.PRICE) PRICE
                    FROM T_MAX_LHR NT
                   WHERE T.AUTHOR = NT.AUTHOR)
 ORDER BY T.ARTICLE;


(2)使用非相關子查詢
SELECT T.*
  FROM T_MAX_LHR T
  JOIN (SELECT NT.AUTHOR, MAX(NT.PRICE) PRICE
          FROM T_MAX_LHR NT
         GROUP BY NT.AUTHOR) T1
    ON T.AUTHOR = T1.AUTHOR
   AND T.PRICE = T1.PRICE
 ORDER BY T.ARTICLE;
(3)使用LEFT JOIN語句
SELECT T.*
  FROM T_MAX_LHR T
  LEFT OUTER JOIN T_MAX_LHR T1
    ON T.AUTHOR = T1.AUTHOR
   AND T.PRICE < T1.PRICE
 WHERE T1.ARTICLE IS NULL
 ORDER BY T.ARTICLE;
在Oracle中的執行結果:
LHR@orclasm > SELECT T.*
  2    FROM T_MAX_LHR T
  3    LEFT OUTER JOIN T_MAX_LHR T1
  4      ON T.AUTHOR = T1.AUTHOR
  5     AND T.PRICE < T1.PRICE
  6   WHERE T1.ARTICLE IS NULL
  7   ORDER BY T.ARTICLE;


ARTICLE  AUTHOR        PRICE
-------- -------- ----------
0002     A             10.99
0003     C              1.69
0004     B             19.95
在MySQL中的執行結果:
mysql> SELECT T.*
    ->   FROM T_MAX_LHR T
    ->   LEFT OUTER JOIN T_MAX_LHR T1
    ->     ON T.AUTHOR = T1.AUTHOR
    ->    AND T.PRICE < T1.PRICE
    ->  WHERE T1.ARTICLE IS NULL
    ->  ORDER BY T.ARTICLE;
+---------+--------+-------+
| ARTICLE | AUTHOR | PRICE |
+---------+--------+-------+
| 0002    | A      | 10.99 |
| 0003    | C      |  1.69 |
| 0004    | B      | 19.95 |
+---------+--------+-------+
3 rows in set (0.00 sec)


真題二、Oracle和MySQL中的分組(GROUP BY)後的聚合函數分別是什麼?
答案:在Oracle中,能夠用WM_CONCAT函數或LISTAGG分析函數;在MySQL中可使用GROUP_CONCAT函數。示例以下:
首先給出建表語句:
CREATE TABLE T_MAX_LHR (ARTICLE VARCHAR2(30),AUTHOR VARCHAR2(30),PRICE NUMBER); --Oracle
--CREATE TABLE T_MAX_LHR (ARTICLE VARCHAR(30),AUTHOR VARCHAR(30),PRICE FLOAT); --MySQL oracle通用
INSERT INTO T_MAX_LHR VALUES ('0001','B',3.99);
INSERT INTO T_MAX_LHR VALUES ('0002','A',10.99);
INSERT INTO T_MAX_LHR VALUES ('0003','C',1.69);
INSERT INTO T_MAX_LHR VALUES ('0004','B',19.95);
INSERT INTO T_MAX_LHR VALUES ('0005','A',6.96);
COMMIT;
SELECT * FROM T_MAX_LHR;
在MySQL中:
mysql> SELECT T.AUTHOR, GROUP_CONCAT(T.ARTICLE), GROUP_CONCAT(T.PRICE)
    ->   FROM T_MAX_LHR T
    ->  GROUP BY T.AUTHOR; 
+--------+-------------------------+-----------------------+
| AUTHOR | GROUP_CONCAT(T.ARTICLE) | GROUP_CONCAT(T.PRICE) |
+--------+-------------------------+-----------------------+
| A      | 0002,0005               | 10.99,6.96            |
| B      | 0001,0004               | 3.99,19.95            |
| C      | 0003                    | 1.69                  |
+--------+-------------------------+-----------------------+
3 rows in set (0.00 sec)
在Oracle中:
LHR@orclasm >  SELECT T.AUTHOR, WM_CONCAT(T.ARTICLE) ARTICLE, WM_CONCAT(T.PRICE)  PRICE
  2    FROM T_MAX_LHR T
  3   GROUP BY T.AUTHOR;


AUTHOR   ARTICLE         PRICE
-------- --------------- ---------------
A        0002,0005       10.99,6.96
B        0001,0004       3.99,19.95
C        0003            1.69


LHR@orclasm >  SELECT T.AUTHOR,
  2          LISTAGG(T.ARTICLE, ',') WITHIN GROUP(ORDER BY T.PRICE) ARTICLE,
  3          LISTAGG(T.PRICE, ',') WITHIN GROUP(ORDER BY T.PRICE) PRICE
  4     FROM T_MAX_LHR T
  5    GROUP BY T.AUTHOR;


AUTHOR   ARTICLE         PRICE
-------- --------------- ---------------
A        0005,0002       6.96,10.99
B        0001,0004       3.99,19.95
C        0003            1.69







<span "="" style="font-family:宋體, Arial;color:#EE33EE;font-size:16px;white-space:normal;background-color:#FFFFFF;">  原做者不知道是誰了,這個圖不是小麥苗畫的。






MySQL分組查詢group by使用示例




(1) group by的含義:將查詢結果按照1個或多個字段進行分組,字段值相同的爲一組
(2) group by可用於單個字段分組,也可用於多個字段分組

select from employee;

+------+------+--------+------+------+-------------+

| num  | d_id | name   | age  | sex  | homeaddr    |

+------+------+--------+------+------+-------------+

|    1001 | 張三   |   26 | 男   | beijinghdq  |

|    1002 | 李四   |   24 | 女   | beijingcpq  |

|    1003 | 王五   |   25 | 男   | changshaylq |

|    1004 | Aric   |   15 | 男   | England     |

+------+------+--------+------+------+-------------+
select from employee group by d_id,sex;
select from employee group by sex;

+------+------+--------+------+------+------------+

| num  | d_id | name   | age  | sex  | homeaddr  

|+------+------+--------+------+------+------------+

|    1002 | 李四   |   24 | 女   | beijingcpq |

|    1001 | 張三   |   26 | 男   | beijinghdq |

+------+------+--------+------+------+------------+


根據sex字段來分組,sex字段的所有值只有兩個('男'和'女'),因此分爲了兩組 當group by單獨使用時,只顯示出每組的第一條記錄 因此group by單獨使用時的實際意義不大


group by + group_concat()


(1) group_concat(字段名)能夠做爲一個輸出字段來使用,
(2) 表示分組以後,根據分組結果,使用group_concat()來放置每一組的某字段的值的集合

select sex from employee group by sex;

+------+

| sex  |

+------+

| 女   |

| 男   |

+------+


select sex,group_concat(name) from employee group by sex;

+------+--------------------+

| sex  | group_concat(name) |

+------+--------------------+

| 女   | 李四               |

| 男   | 張三,王五,Aric     |

+------+--------------------+


select sex,group_concat(d_id) from employee group by sex;

+------+--------------------+

| sex  | group_concat(d_id) |

+------+--------------------+

| 女   1002               |

| 男   1001,1003,1004     |

+------+--------------------+


group by + 集合函數


(1) 經過group_concat()的啓發,咱們既然能夠統計出每一個分組的某字段的值的集合,那麼咱們也能夠經過集合函數來對這個"值的集合"作一些操做


select sex,group_concat(age) from employee group by sex;

+------+-------------------+

| sex  | group_concat(age) |

+------+-------------------+

| 女   24                |

| 男   26,25,15          |

+------+-------------------+

 


分別統計性別爲男/女的人年齡平均值

select sex,avg(age) from employee group by sex;

+------+----------+

| sex  avg(age) |

+------+----------+

| 女   |  24.0000 |

| 男   |  22.0000 |

+------+----------+
分別統計性別爲男/女的人的個數

select sex,count(sex) from employee group by sex;

+------+------------+

| sex  count(sex) |

+------+------------+

| 女   |          |

| 男   |          |

+------+------------+


group by + having


(1) having 條件表達式:用來分組查詢後指定一些條件來輸出查詢結果
(2) having做用和where同樣,但having只能用於group by

select sex,count(sex) from employee group by sex having count(sex)>2;

+------+------------+

| sex  count(sex) |

+------+------------+

| 男   |          |

+------+------------+


group by + with rollup


(1) with rollup的做用是:在最後新增一行,來記錄當前列裏全部記錄的總和

select sex,count(age) from employee group by sex with rollup;

+------+------------+

| sex  count(age) |

+------+------------+

| 女   |          |

| 男   |          |

NULL |          |

+------+------------+

select sex,group_concat(age) from employee group by sex with rollup;

相關文章
相關標籤/搜索