有關MySQL的命令語句(三)
五、查詢資料
使用SELECT語句從表或檢視獲取資料。表由行和列組成,如電子表格。
SELECT語句的結果稱為結果集,它是行列表,每行由相同數量的列組成。
SELECT語句由以下列表中所述的幾個子句組成:
語句 | 說明 |
SELECT | 逗號分隔列或星號(*)的列表,表示要返回所有列或表示式 |
FROM | 指定要查詢資料的表或檢視 |
JOIN | 根據某些連線條件從其他表中獲取資料。 |
WHERE 子句(條件查詢) | 行級過濾。按照“條件表示式”指定的條件進行查詢。 |
GROUP BY 子句(分組) | 分組說明。將一組行組合成小分組,並對每個小分組應用聚合函式。按照“屬性名”指定的欄位進行分組。 |
HAVING | 組級過濾。過濾器基於GROUP BY子句定義的小分組。 |
ORDER BY | 輸出排序順序。按照“屬性名”指定的欄位進行排序。排序方式由“asc”和“desc”兩個引數指出。 |
LIMIT(限制結果集) | 限制返回行的數量。 |
語句中的 SELECT 和 FROM語句 是必須的,其他部分是可選的。
【文章結構】
一、資料的準備
二、基本的查詢功能
三、條件查詢
四、查詢排序
五、聚合函式
六、分組查詢
七、連線查詢
八、子查詢
一、資料的準備
-- 建立一個數據庫
create database test_db;
-- 使用資料庫
use pysql;
-- 檢視當前使用的是哪個資料庫
select database();
-- 建立資料表teams, heroes
mysql> create table dept
-> (
-> d_no INT(11) NOT NULL AUTO_INCREMENT,
-> d_name VARCHAR(50) NOT NULL,
-> d_location VARCHAR(100),
->PRIMARY KEY('d_no'),
->UNIQUE KEY'd_no'('d_no')
-> );
mysql> create table employee
-> (
-> e_no INT(11) NOT NULL UNIQUE PRIMARY KEY,
-> e_name VARCHAR(50) NOT NULL,
-> e_gender CHAR(2),
-> dept_no INT(11) NOT NULL,
-> e_job VARCHAR(50) NOT NULL,
-> e_salary INT(11) NOT NULL,
-> hireDate DATE NOT NULL
-> );
-- 檢視資料庫中已有的資料表
show tables;
-- 瞭解資料表的建立方式
show create table dept;
-- 查看錶的基本資料結構
describe/desc 表名;
查看錶的欄位資訊,包括:欄位名、欄位資料型別、是否為主鍵、是否有預設值等
-- 插入資料
mysql> INSERT INTO dept(d_no, d_name, d_location)
-> VALUES(10,'ACCOUNTING','ShangHai'),
->(20,'RESEARCH','BeiJing'),
->(30,'SALES','ShenZhen'),
->(40,'OPERATIONS','FuJian');
mysql> INSERT INTO employee(e_no,e_name,e_gender,dept_no,e_job,e_salary,hireDate)
-> VALUES(1001,'SMITH','m',20,'CLERK',800,'2005-11-12'),
->(1002,'ALLEN','f',30,'SALESMAN',1600,'2003-05-12'),
->(1003,'WARD','f',30,'SALESMAN',1250,'2003-05-12'),
->(1004,'JONES','m',20,'MANAGER',2975,'1998-05-18'),
->(1005,'MARTIN','m',30,'SALESMAN',1250,'2001-06-12'),
->(1006,'BLAKE','f',30,'MANAGER',2850,'1997-02-15'),
->(1007,'CLAKE','m',10,'MANAGER',2450,'2002-09-12'),
->(1008,'SCOTT','m',20,'ANALYST',3000,'2003-05-12'),
->(1009,'KING','f',10,'PRESIDENT',5000,'1995-01-01'),
->(1010,'TURNER','f',30,'SALESMAN',1500,'1997-10-12'),
->(1011,'ADAMS','m',20,'CLERK',1100,'1999-10-05'),
->(1012,'JAMES','f',30,'CLERK',950,'2008-06-15');
二、基本的查詢功能
-- 查詢所有欄位(大資料庫中慎用)
-- select * from 表名;
select * from dept;
-- 查詢指定欄位
-- select 列1, 列2,...from 表名;
-- select 表名.欄位... from 表名
select employee.e_no, employee.e_name from employee;
select e_no, e_name, e_salary employee;
-- 使用as給欄位起別名
-- select 欄位 as 別名 from 表名;
select e_no as "員工編號“, e_name as "員工姓名" from employee;
-- 通過as給表起別名
--select * from dept as h;
-- 消除重複行
-- distinct 欄位 select distinct gender from heroes;
三、條件查詢
-- 比較運算子
-- select ... from 表名 where 條件
-- >
-- 查詢大於18歲的資訊
select * from heroes where age>18;
-- <
-- 查詢id小於5的資訊
select * from heroes where id<5;
-- >=
-- <=
-- 略
-- =
--查詢性別為男的英雄的id和名字
select id,name from heroes where gender=1;
不等於:-- != 或者 <> (<>在很多語言中都不用,所以首選 !=)
select id,name from heroes where gender!=1;
-- 邏輯運算子(與 或 非)
-- and 或 &&
-- 18到50歲之間英雄的資訊
-- 報錯 select * from heroes where age>18 and <50; 判斷語句 左右兩邊都要寫全
select * from heroes where age>18 and age<50;
-- 18歲以上的女性
select * from heroes where age>18 and gender=2;
select * from heroes where age>18 and gender="女";
-- or 或 ||
-- 50歲以上或身高180(包含)以上
select * from heroes where age>50 or height>=180;
-- not 或 !
-- not 加在誰前面就僅僅否定這一個條件,用()解決優先順序的問題,不要死記硬背
-- 不屬於 70歲以上男英雄 的
select * from heroes where not (age>70 and gender=1);
-- 年齡不小於或等於18 的女性英雄.用()解決優先順序的問題
select * from heroes where (not age<=18) and gender=2;
-- 模糊查詢
萬用字元:一種在SQL的WHERE條件子句中擁有特殊意思的字元,SQL語句支援很多萬用字元,可以和LIKE一起使用的萬用字元為‘%’和‘_’。
-- like (效率低)
-- % 替換1個,0個或多個; 匹配任意長度的字元,甚至包括零字元。可放置在任意位置。
-- _替換一個; 一次只能匹配任意一個字元
--查詢姓名中以“賞”開頭的名字
select name from heroes where name like "賞%";
--查詢姓名中有“賞”的名字
select name from heroes where name like "%賞%";
--查詢兩個字的名字
select name from heroes where name like "__";
--查詢至少兩個字的名字
select name from heroes where name like "__%";
-- rlike 正則
-- 查詢以“泰”開始的名字
select name from heroes where name rlike "^泰.*";
-- 查詢以“希”開頭,“爾”結尾的名字
select name from heroes where name rlike "^希.*爾$";
-- 範圍查詢
查詢滿足指定範圍內的條件的記錄,使用IN操作符,將所有檢索條件用()括起來,檢索條件之間用逗號分開,只要滿足條件範圍內的一個值即為匹配項。
-- in (18, 70, 50) 表示在一個非連續的範圍內
-- 查詢年齡為18,70的英雄
select name, age from heroes where age in (18, 70, 50);
-- not in (18, 70, 50) 不在某個非連續的範圍內
select name, age from heroes where age not in (18, 70, 50);
-- between .. and .. 在某個連續的範圍內
用來查詢某個範圍內的值。該操作符的兩個引數,分別為範圍的開始值和結束值。
select name, age from heroes where age between 18 and 50;
-- not between .. and .. 不在某個範圍中,這是一個整體的語句,同時否定between和and的內容
-- 報錯 select name, age from heroes where age not (between 18 and 50);
select name, age from heroes where age not between 18 and 50;
select name, age from heroes where not age between 18 and 50;
-- 判斷為空
空值: 一般表示資料未知、不適用或將在以後新增資料。
-- is null
-- a = None 表示 a沒有指向任何東西,a = "" 表示a指向一個為空的物件
-- 查詢身高為空的資訊
select * from heroes where height is null;
-- 不為空的
select * from heroes where height is not null;
四、查詢排序
-- order by 欄位
-- asc 升序(預設值)
-- desc 降序
-- 先寫那個條件,先按照這個條件排序,相同情況下,按第二個排,否則不生效
①單列排序:
SELECT 欄位名1 FROM 表名 ORDER BY 欄位名1;
--查詢年齡在20-70的男英雄,按照年齡升序排列
select * from heroes where (age between 20 and 70) and gender=1 order by age;
select * from heroes where (age between 20 and 70) and gender=1 order by age asc;
②多列排序:
SELECT 欄位名1,欄位名2 FROM 表名 ORDER BY 欄位名1,欄位名2;
-- 查詢年齡在16-24之間的女性,按身高降序排列,如相同,按年齡升序排列
select * from heroes where (age between 16 and 20) and gender=2 order by height desc,age asc;
③指定排序方向:
SELECT 欄位名1 FROM 表名 ORDER BY 欄位名1 DESC;
(1)order by price //預設升序排列
(2)order by price desc //降序排列
(3)order by price asc //升序排列,可寫可不寫
(4)order by rand() //隨機排列,效率不高
-- 全部人員,按照年齡從小到大排列,身高從高到低
select * from heroes order by age, height desc;
五、聚合函式
函式 |
作用 |
AVG() |
返回某列的平均值 |
COUNT() |
返回某列的行數 |
MAX() |
返回某列的最大值 |
MIN() |
返回某列的最小值 |
SUM() |
返回某列的和 |
GROUP_CONCAT(col) |
返回由屬於一組的列值連線組合而成的結果 |
-- 總數
-- count()函式
統計資料表中包含的記錄函式,或根據查詢結果返回列中包含的資料行數。
count(*):計算表中總行數,不管某列是數值還是空值;
count(欄位名):計算指定列的總行數,計算時將忽略空值的行。
例1:查詢表中的總行數,為結果命名為num,並將其顯示出來;
select count(*) AS num from 表名;
例2:查詢 name 列下的總行數(不包括空值),為結果命名為num,並將其顯示出來;
select count(name) as num from 表名;
例3:計算不同分組中的記錄總數
用count()函式統計不同年齡下,姓名的個數(例如:年齡為21的有Lily,Jack ,Matin,則21對應的count()值為3)
select age,count(name) from 表名 group by age
PS: 在指定列的值為空的行被COUNT()函式忽略;若不指定列,使用COUNT(*)時,則所有記錄都不忽略。
-- 最大值
-- max()函式
返回指定列中的最大值。
MAX()函式可用於查詢數值型別,還可用於查詢字元型別。
字串的大小比較,依據字元的ASCII碼值大小進行比較,從a~z,a的ASCII碼最小,z的最大。
-- 查詢最大的年齡
select age from heroes;
select max(age) from heroes;
-- 查詢女性最高身高
select max(height) as "最高身高" from heroes where gender=2;
-- 求和
-- sum()函式
返回指定列值的總和
例如:查看錶中price欄位列下所有價格的總和。
select sum (price) AS Total from 表名;
PS: SUM()函式在計算時,忽略列值為NULL的行。
-- 所有人身高總和
select sum(height) from heroes;
-- 平均值
-- avg()函式
通過計算返回的行數和每一行資料的和,求得指定列資料的平均值。
PS: AVG()函式使用時,其引數為要計算的列名稱,若要得到多個列的多個平均值,則需在每一列上使用AVG()函式。
-- 女性平均年齡,以下兩種方式均可,此處目的在於說明select後面可以加運算式,
-- 但此類統計中儘量避免第二種方式,例如在此資料表中,如果是平均身高的話,因為有一個null的存在...
select avg(age) from heroes where gender=2;
select sum(age)/count(*) from heroes where gender=2;
六、分組查詢
-- 要和聚合搭配使用,才比較有意義
--[GROUP BY 欄位] [HAVING <條件表示式>]
select 類別, sum(數量) as 數量之和
from A
group by 類別
注:group by語句中select指定的欄位必須是“分組依據欄位”,其他欄位若想出現在select中則必須包含在聚合函式中。
-- 按照性別分組,查詢所有的性別
-- select 可以唯一標記每個分組的...東西 from heroes group by gender;
select gender from heroes group by gender;
-- 計算每種性別有多少人
select gender, count(*) from heroes group by gender;
-- 此處的count(*) 是對每組的計算結果
-- 計算每組中的最大年齡、平均年齡
select gender, max(age) from heroes group by gender;
select gender, avg(age) from heroes group by gender;
-- having
-- HAVING是資料分組之後來過濾選擇分組,WHERE在分組之前用來選擇記錄。另外WHERE排除的記錄不在包括在分組中。
例1:
select 類別, sum(數量) as 數量之和 from A
group by 類別
having sum(數量) >30
例2:
select 類別, SUM(數量)from A
where 數量 >8
group by 類別
having SUM(數量) >10
-- 查詢平均年齡超過30的性別,以及其中包含的人名 ;
select gender, group_concat(name), avg(age) from heroes group by gender having avg(age) > 30;
-- 查詢人數多於2的性別
select gender, group_concat(name), count(*) from heroes group by gender having count(*) > 2;
使用LIMIT限制查詢結果的數量
LIMIT[offset,] 行數
第一個“offser [位移偏移量]”引數:指MySQL從哪一行開始顯示,是一個可選引數。若不指定,將從表中的第一條記錄開始(第一條記錄的位置偏移量為0,第二條記錄的位置偏移量為1…以此類推);
第二個引數“行數”指示返回的記錄條數。
七、關聯查詢
連線查詢為關係資料庫中最重要的查詢,主要包括內連線、外連線等。
1)內連線查詢:INNER JOIN ...ON ...
內連線(INNER JOIN)使用比較運算子進行表間某(些)列資料的比較操作,並列出這些表中與連線條件相匹配的資料行,組合成新紀錄。
PS: INNER JOIN語法是ANSI SQL的標準規範,使用INNER JOIN連線語法能夠確保不會忘記連線條件(ON),且是使用WHERE子句在某些時候會影響查詢的效能。
內連線查詢,取多個表的交集,否則不顯示。
-- select * from 表1 inner join 表2; 將兩張表對應起來
-- 表1 一行一行的來對應表2 所有行
select * from heroes inner join groups;
-- 查詢有能夠對應小隊的的英雄以及小隊資訊
-- select * from 表1 inner join 表2 on 條件;
select * from heroes inner join groups on heroes.grp_id=groups.id;
2)外連線查詢
LEFT JOIN...IN...(左連線):返回包括左表中的所有記錄和右表中連線欄位相等的記錄;
select A.filed, [A.filed2, .... ,] B.filed, [B.filed4...,] from <left table> as A left join <right table> as B on <expression>
假設有A、B兩張表,左連線查詢即 A表在左不動,B表在右滑動,A表與B表通過一個關係來關聯行,B表去匹配A表。
RIGHT JOIN...IN...(右連線):返回包括右表中所有記錄和左表中連線欄位相等的記錄。
在 FROM 子句中使用關鍵字 LEFT OUTER JOIN 或者 LEFT JOIN,用於接收該關鍵字左表(基表)的所有行,並用這些行與該關鍵字右表(參考表)中的行進行匹配,即匹配左表中的每一行及右表中符合條件的行。在左外連線的結果集中,如果匹配到就顯示,匹配不到就顯示為null。
-- right join...on 用的很少
-- 將資料表名字互換位置,用left join即可完成
3)全連線查詢 full join ... on ...
語法:
select ... from <left table> full join <right table> on <expression>
全連線會將兩個表的所有資料查詢出來,不滿足條件的為NULL。
全連線查詢跟全相乘查詢的區別在於,如果某個項不匹配,全相乘不會查出來,全連線會查出來,而連線的另一邊則為NULL。
4)聯合查詢 union
把兩張表的欄位都查出來,沒有對應的值就顯示null,但是注意:mysql是沒有全外連線的(mysql中沒有full outer join關鍵字),想要達到全外連線的效果,可以使用union關鍵字連線左外連線和右外連線。
語法:
select A.field1 as f1, A.field2 as f2 from <table1> A union (select B.field3 as f1, field4 as f2 from <table2> B)
union是求兩個查詢的並集。union合併的是結果集,不區分來自於哪一張表,所以可以合併多張表查詢出來的資料。
4.1、將兩張表的資料合併查詢出來
SELECT id, content, user FROM comment UNION (SELECT id, msg AS content, user FROM feedback);
4.2、union查詢,列名不一致時,以第一條sql語句的列名對齊
SELECT id, content, user FROM comment UNION (SELECT id, msg, user FROM feedback);
4.3、使用union查詢會將重複的行過濾掉
SELECT content,user FROM comment UNION (SELECT msg, user FROM feedback);
4.4、使用union all查詢所有,重複的行不會被過濾
SELECT content,user FROM comment UNION ALL (SELECT msg, user FROM feedback);
4.5、union查詢,如果列數不相等,會報列數不相等錯誤
4.6、union 後的結果集還可以再做篩選
SELECT id,content,user FROM comment UNION ALL (SELECT id, msg, user FROM feedback) ORDER BY id DESC;
union查詢時,order by放在內層sql中是不起作用的;因為union查出來的結果集再排序,內層的排序就沒有意義了;因此,內層的order by排序,在執行期間,被mysql的程式碼分析器給優化掉了。
(SELECT id,content,user FROM comment ORDER BY id DESC) UNION ALL (SELECT id, msg, user FROM feedback ORDER BY id DESC);
order by 如果和limit一起使用,就顯得有意義了,就不會被優化掉。
( SELECT goods_name,cat_id,shop_price FROM goods WHERE cat_id = 3 ORDER BY shop_price DESC LIMIT 3 )
UNION
( SELECT goods_name,cat_id,shop_price FROM goods WHERE cat_id = 4 ORDER BY shop_price DESC LIMIT 2 );
八、子查詢
1、where型子查詢(把內層查詢結果當作外層查詢的比較條件)
(1)查詢id最大的一件商品(使用排序+分頁實現)
SELECT goods_id,goods_name,shop_price FROM goods ORDER BY goods_id DESC LIMIT 1;
(2)查詢id最大的一件商品(使用where子查詢實現)
SELECT goods_id,goods_name,shop_price FROM goods WHERE goods_id = (SELECT MAX(goods_id) FROM goods);
(3)查詢每個類別下id最大的商品(使用where子查詢實現)
SELECT goods_id,goods_name,cat_id,shop_price FROM goods WHERE goods_id IN (SELECT MAX(goods_id) FROM goods GROUP BY cat_id);
2、from型子查詢(把內層的查詢結果當成臨時表,供外層sql再次查詢。查詢結果集可以當成表看待。臨時表要使用一個別名。)
(1)查詢每個類別下id最大的商品(使用from型子查詢)
SELECT goods_id,goods_name,cat_id,shop_price FROM
(SELECT goods_id,goods_name,cat_id,shop_price FROM goods ORDER BY cat_id ASC,goods_id DESC) AS tmp
GROUP BY cat_id;
子查詢查出的結果集看第二張圖,可以看到每個類別的第一條的商品id都為該類別下的最大值。然後將這個結果集作為一張臨時表,巧妙的使用group by 查詢出每個類別下的第一條記錄,即為每個類別下商品id最大。
3、exists型子查詢(把外層sql的結果,拿到內層sql去測試,如果內層的sql成立,則該行取出。內層查詢是exists後的查詢。)
(1)從類別表中取出其類別下有商品的類別(如果該類別下沒有商品,則不取出)[使用where子查詢]
SELECT c.cat_id,c.cat_name FROM category c WHERE c.cat_id IN (SELECT g.cat_id FROM goods g GROUP BY g.cat_id);
(2)從類別表中取出其類別下有商品的類別(如果該類別下沒有商品,則不取出)[使用exists子查詢]
SELECT c.cat_id,c.cat_name FROM category c WHERE EXISTS (SELECT 1 FROM goods g WHERE g.cat_id = c.cat_id);
exists子查詢,如果exists後的內層查詢能查出資料,則表示存在;為空則不存在。
問題1:where和having的區別:
作用的物件不同。WHERE 子句作用於表和檢視,HAVING 子句作用於組。
WHERE 在分組和聚集計算之前選取輸入行(因此,它控制哪些行進入聚集計算), 而HAVING
在分組和聚集之後選取分組的行。因此,WHERE 子句不能包含聚集函式;
因為試圖用聚集函式判斷那些行輸入給聚集運算是沒有意義的。 相反,HAVING 子句總是包含聚集函式。
(嚴格說來,你可以寫不使用聚集的 HAVING 子句,
但這樣做只是白費勁。同樣的條件可以更有效地用於 WHERE 階段。)
我們可以在 WHERE 裡應用數量欄位來限制,因為它不需要聚集。 這樣比在 HAVING 裡增加限制更加高效,因為我們避免了為那些未通過 WHERE 檢查的行進行分組和聚集計算。
即:having一般跟在group by之後,執行記錄組選擇的一部分來工作的。where則是執行所有資料來工作的。
再者having可以用聚合函式,如having sum(qty)>1000