1. 程式人生 > 實用技巧 >有關MySQL的命令語句(三)

有關MySQL的命令語句(三)

五、查詢資料

使用SELECT語句從表或檢視獲取資料。表由行和列組成,如電子表格。

SELECT語句的結果稱為結果集,它是行列表,每行由相同數量的列組成。

SELECT語句由以下列表中所述的幾個子句組成

語句 說明
SELECT 逗號分隔列或星號(*)的列表,表示要返回所有列或表示式
FROM 指定要查詢資料的表或檢視
JOIN 根據某些連線條件從其他表中獲取資料。
WHERE 子句(條件查詢) 行級過濾。按照“條件表示式”指定的條件進行查詢。
GROUP BY 子句(分組) 分組說明。將一組行組合成小分組,並對每個小分組應用聚合函式。按照“屬性名”指定的欄位進行分組。
HAVING 組級過濾。過濾器基於GROUP BY子句定義的小分組。
ORDER BY 輸出排序順序。按照“屬性名”指定的欄位進行排序。排序方式由“asc”和“desc”兩個引數指出。
LIMIT(限制結果集) 限制返回行的數量。

語句中的 SELECTFROM語句 是必須的,其他部分是可選的。

【文章結構】

一、資料的準備

二、基本的查詢功能

三、條件查詢

四、查詢排序

五、聚合函式

六、分組查詢

七、連線查詢

八、子查詢

一、資料的準備

-- 建立一個數據庫
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