1. 程式人生 > >我的Python成長之路--Day46-單表查詢、多表查詢、子查詢

我的Python成長之路--Day46-單表查詢、多表查詢、子查詢

目錄

聚合函式

內連線查詢

子查詢

1、資料的增刪改

對資料的增刪改我們之前已經詳細介紹過了,在這裡我們著重介紹資料的查詢操作,對資料的增刪改進行簡單的回顧

增加資料
insert [into] 表名[(可選欄位名)] values(一堆值1),(一堆值2),.....
into 可以省略
表名後的欄位可以選
如果寫了 後面的values中的值必須與表名後的欄位一一對應
如果沒寫 後面的values中的值必須與表的所有欄位一一對應
values後面可以給多組值 用逗號隔開

刪除資料
delete from 表名[where 條件]
條件不寫 是刪除所有記錄 是一行一行刪除   注意自增id 不會歸零
truncate 重建表  先記錄表結構 刪除整個表再重新建出來表 自增id 會歸零

更新資料資料
update 表名 set 欄位名 = 值[,欄位2 = 值2],[where 條件]
可以一次性修改多個欄位的值用逗號隔開
條件如果不寫 修改所有記錄

2、單表查詢

首先我們來看一下不帶關鍵字的的查詢:
select {1.*|2.欄位名|3.四則運算|4.聚合函式} from 表名 [where...] 

1.查詢整張表格的完整內容
2.查詢指定欄位名的內容
3.對查詢出來的欄位進行四則運算
4.使用相應的聚合函式查詢

下面我們看一下帶關鍵字的單表查詢的完整語法;

SELECT [distinct]欄位1,欄位2... FROM 表名
                  WHERE 條件
                  GROUP BY field
                  HAVING 篩選
                  ORDER BY field
                  LIMIT 限制條數

每個關鍵字所代表的的含義:
from: 開啟硬碟上的檔案讀取表格內容,會返回一個表格內容的data
where:對from返回的資料進行初步的條件篩選,有索引的會快很多,沒有索引就會一行一行的進行篩選(屬於讀取資料的一部分)
group by:將取出的一條條記錄進行分組group by,如果沒有group by,則整體作為一組將分組的結果進行having過濾
having:對分組後的資料進行進一步的過濾
distinct:去除重複,整條記錄完全沒有重複的欄位才算沒有重複
order by
:對資料按照升序或者降序進行排序
limit:限制顯示資料的條數

關鍵字的執行順序(優先順序):
執行順序詳情參考:(

點選前往)
重點中的重點:關鍵字的執行優先順序
from
where
group by
having
select
distinct
order by
limi
t                                                                                                                                                                                                                                                                                                                                                                                                                下面我們來通過一個具體的例子,來介紹簡單查詢和關鍵字查詢以及各個關鍵字的使用和注意事項:
準備表格,

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

#建立表
create table employee(
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 employee(name,sex,age,hire_date,post,salary,office,depart_id) values
('egon','male',18,'20170301','老男孩駐沙河辦事處外交大使',7300.33,401,1), #以下是教學部
('alex','male',78,'20150302','teacher',1000000.31,401,1),
('wupeiqi','male',81,'20130305','teacher',8300,401,1),
('yuanhao','male',73,'20140701','teacher',3500,401,1),
('liwenzhou','male',28,'20121101','teacher',2100,401,1),
('jingliyang','female',18,'20110211','teacher',9000,401,1),
('jinxin','male',18,'19000301','teacher',30000,401,1),
('成龍','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)
;

#ps:如果在windows系統中,插入中文字元,select的結果為空白,可以將所有字元編碼統一設定成gbk

不帶關鍵字的簡單查詢                                                                                                                                                                      簡單查詢
    SELECT id,name,sex,age,hire_date,post,post_comment,salary,office,depart_id 
    FROM employee;

    SELECT * FROM employee;

    SELECT name,salary FROM employee;

避免重複DISTINCT
 
  SELECT DISTINCT post FROM employee;    

通過四則運算查詢
    SELECT name, salary*12 FROM employee;
    SELECT name, salary*12 AS Annual_salary FROM employee;
    SELECT name, salary*12 Annual_salary FROM employee;

定義顯示格式
   CONCAT() 函式用於連線字串
   SELECT CONCAT('姓名: ',name,'  年薪: ', salary*12)  AS Annual_salary 
   FROM employee;
   
   CONCAT_WS() 第一個引數為分隔符
   SELECT CONCAT_WS(':',name,salary*12)  AS Annual_salary 
   FROM employee;

結合CASE語句:
   SELECT
       (
           CASE
           WHEN NAME = 'egon' THEN
               NAME
           WHEN NAME = 'alex' THEN
               CONCAT(name,'_BIGSB')
           ELSE
               concat(NAME, 'SB')
           END
       ) as new_name
   FROM
       emp;

下面我們來介紹一下帶關鍵字的單表查詢語句及關鍵字的作用和注意事項

WHERE約束

where字句中可以使用:

1. 比較運算子:> < >= <= <> !=
2. between 80 and 100         值在80到100之間,包含80和100
3. in(80,90,100)                    值是10或20或30
4. like 'egon%'
    pattern可以是%或_,
    %表示任意多字元
    _表示一個字元 
   regexp
5. 邏輯運算子:在多個條件直接可以使用邏輯運算子 and or not
6.any:
只要有一個成立即成立
   all:所有條件成立才成立

1:單條件查詢
    SELECT name FROM employee
        WHERE post='sale';
        
2:多條件查詢
    SELECT name,salary FROM employee
        WHERE post='teacher' AND salary>10000;

3:關鍵字BETWEEN AND
    SELECT name,salary FROM employee 
        WHERE salary BETWEEN 10000 AND 20000;

    SELECT name,salary FROM employee 
        WHERE salary NOT BETWEEN 10000 AND 20000;
    
4:關鍵字IS NULL(判斷某個欄位是否為NULL不能用等號,需要用IS)
    SELECT name,post_comment FROM employee 
        WHERE post_comment IS NULL;

    SELECT name,post_comment FROM employee 
        WHERE post_comment IS NOT NULL;
        
    SELECT name,post_comment FROM employee 
        WHERE post_comment=''; 注意''是空字串,不是null
    ps:
        執行
        update employee set post_comment='' where id=2;
        再用上條檢視,就會有結果了

5:關鍵字IN集合查詢
    SELECT name,salary FROM employee 
        WHERE salary=3000 OR salary=3500 OR salary=4000 OR salary=9000 ;
    
    SELECT name,salary FROM employee 
        WHERE salary IN (3000,3500,4000,9000) ;

    SELECT name,salary FROM employee 
        WHERE salary NOT IN (3000,3500,4000,9000) ;

6:關鍵字LIKE模糊查詢
    萬用字元’%’
    SELECT * FROM employee 
            WHERE name LIKE 'eg%';

    萬用字元’_’
    SELECT * FROM employee 
            WHERE name LIKE 'al__';

分組查詢:GROUP BY  

 一 什麼是分組?為什麼要分組?

1、首先明確一點:分組發生在where之後,即分組是基於where之後得到的記錄而進行的
2、分組指的是:將所有記錄按照某個相同欄位進行歸類,比如針對員工資訊表的職位分組,或者按照性別進行分組等

3、為何要分組呢?
    取每個部門的最高工資
    取每個部門的員工數
    取男人數和女人數

小竅門:‘每’這個字後面的欄位,就是我們分組的依據

4、大前提:
    可以按照任意欄位分組,但是分組完畢後,比如group by post,只能檢視post欄位如果想檢視組內資訊,需要藉助於聚合函式

ONLY_FULL_GROUP_BY
在介紹group by 之前我們先來了解一下sal_mode的這個模式,這個模式在5.6版本中是預設關閉的,在列印結果的時候,會打印出分組後的第一行記錄,5,7版本後面的版本該模式預設是開啟的,在列印的時候不會列印任何內容,並且會進行報錯.                                                             

怎麼修改分組的這種模式:

檢視MySQL 5.7預設的sql_mode如下:
mysql> select @@global.sql_mode;
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

!!!注意
ONLY_FULL_GROUP_BY的語義就是確定select target list中的所有列的值都是明確語義,簡單的說來,在ONLY_FULL_GROUP_BY模式下,target list中的值要麼是來自於聚集函式的結果,要麼是來自於group by list中的表示式的值。


設定sql_mole如下操作(我們可以去掉ONLY_FULL_GROUP_BY模式):
mysql> set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

!!!SQL_MODE設定!!!
elect * from emp group by post;   (按照部門進行分組,然後檢視分組結果)
#由於沒有設定ONLY_FULL_GROUP_BY,於是也可以有結果,預設都是組內的第一條記錄,但其實這是沒有意義的
#設定成功後,一定要退出,然後重新登入方可生效

單獨使用GROUP BY關鍵字分組
    SELECT post FROM employee GROUP BY post;
    注意:我們按照post欄位分組,那麼select查詢的欄位只能是post,想要獲取組內的其他相關資訊,需要藉助函式

GROUP BY關鍵字和GROUP_CONCAT()函式一起使用
    SELECT post,GROUP_CONCAT(name) FROM employee GROUP BY post;#按照崗位分組,並檢視組內成員名
    SELECT post,GROUP_CONCAT(name) as emp_members FROM employee GROUP BY post;

GROUP BY與聚合函式一起使用
    select post,count(id) as count from employee group by post;#按照崗位分組,並檢視每個組有多少人

強調:

如果我們用unique的欄位作為分組的依據,則每一條記錄自成一組,這種分組沒有意義
多條記錄之間的某個欄位值相同,該欄位通常用來作為分組的依據

聚合函式

強調:聚合函式聚合的是組的內容,若是沒有分組,則預設一組

示例:
    SELECT COUNT(*) FROM employee;    查看錶中員工的總個數
    SELECT COUNT(*) FROM employee WHERE depart_id=1;
    SELECT MAX(salary) FROM employee;   檢視最高的工資
    SELECT MIN(salary) FROM employee;    檢視最低的工資
    SELECT AVG(salary) FROM employee;   檢視平均工資
    SELECT SUM(salary) FROM employee;   檢視工資總和
    SELECT SUM(salary) FROM employee WHERE depart_id=3;  

HAVING過濾 

HAVING與WHERE都是對錶進行過濾的,那麼他們之間不一樣的地方在於!!!!!!

!!!執行優先順序從高到低:where > group by > having
1. Where 發生在分組group by之前,因而Where中可以有任意欄位,但是絕對不能使用聚合函式。
2. Having發生在分組group by之後,因而Having中可以使用分組的欄位,無法直接取到其他欄位,可以使用聚合函式

查詢排序:ORDER BY

按單列排序
    SELECT * FROM employee ORDER BY salary;         預設排序排序方式是升序的
    SELECT * FROM employee ORDER BY salary ASC;   也可以自己制定排序方式     
    SELECT * FROM employee ORDER BY salary DESC;    按降序的方式進行排序

按多列排序:先按照age排序,如果年紀相同,則按照薪資排序(適用於按照第一個條件排序時,出現多個數據和第一個條件相等的情況)
    SELECT * from employee
        ORDER BY age,
        salary DESC;

限制查詢的記錄數:LIMIT

示例:
    SELECT * FROM employee ORDER BY salary DESC 
        LIMIT 3;                    #預設初始位置為0 
    
    SELECT * FROM employee ORDER BY salary DESC
        LIMIT 0,5; #從第0開始,即先查詢出第一條,然後包含這一條在內往後查5條

    SELECT * FROM employee ORDER BY salary DESC
        LIMIT 5,5; #從第5開始,即先查詢出第6條,然後包含這一條在內往後查5條

使用正則表示式查詢

在mysql中也可以使用正則表示式來查詢想要的內容,可以有兩種方式
第一種方式關鍵詞是like  因為like支援的方式只有%和 _ 拓展性不好,所以有了regexp
第二種方式的關鍵詞是regexp

SELECT * FROM employee WHERE name REGEXP '^ale';

SELECT * FROM employee WHERE name REGEXP 'on$';

SELECT * FROM employee WHERE name REGEXP 'm{2}';


小結:對字串匹配的方式
WHERE name = 'egon';         精準匹配
WHERE name LIKE 'yua%';  模糊匹配
WHERE name REGEXP 'on$';  使用正則的模糊匹配                                                                         

3、多表查詢

和單表查詢套路一樣,我們還是通過一個例子,來詳細介紹多表查詢的知識,首先準備好表

建表
create table department(
id int,
name varchar(20) 
);

create table employee(
id int primary key auto_increment,
name varchar(20),
sex enum('male','female') not null default 'male',
age int,
dep_id int
);

插入資料
insert into department values
(200,'技術'),
(201,'人力資源'),
(202,'銷售'),
(203,'運營');

insert into employee(name,sex,age,dep_id) values
('egon','male',18,200),
('alex','female',48,201),
('wupeiqi','male',38,201),
('yuanhao','female',28,202),
('liwenzhou','male',18,200),
('jingliyang','female',18,204)
;


查看錶結構和資料
mysql> desc department;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+

mysql> desc employee;
+--------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+-----------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
| sex | enum('male','female') | NO | | male | |
| age | int(11) | YES | | NULL | |
| dep_id | int(11) | YES | | NULL | |
+--------+-----------------------+------+-----+---------+----------------+

mysql> select * from department;
+------+--------------+
| id | name |
+------+--------------+
| 200 | 技術 |
| 201 | 人力資源 |
| 202 | 銷售 |
| 203 | 運營 |
+------+--------------+

mysql> select * from employee;
+----+------------+--------+------+--------+
| id | name | sex | age | dep_id |
+----+------------+--------+------+--------+
| 1 | egon | male | 18 | 200 |
| 2 | alex | female | 48 | 201 |
| 3 | wupeiqi | male | 38 | 201 |
| 4 | yuanhao | female | 28 | 202 |
| 5 | liwenzhou | male | 18 | 200 |
| 6 | jingliyang | female | 18 | 204 |
+----+------------+--------+------+--------+

表department與employee

多表連線查詢

多表連線查詢的語法:
SELECT 欄位列表
    FROM 表1 INNER|LEFT|RIGHT JOIN 表2
    ON 表1.欄位 = 表2.欄位;

笛卡爾積查詢

也稱為交叉連線,不適用於任何匹配條件

語法:
select *from 表1,表n

select * from employee,department;(在多表查詢的例子中生成笛卡爾積的語句)
笛卡爾積其實是將兩張表按照笛卡爾積的方式新建了一張虛擬的表出來,這張表是存在於記憶體中的,當拼接出來之後我們就可以對這張虛擬的表進行相應的操作

查詢結果是
將座標中的每條記錄 與右表中的每條記錄都關聯一遍
因為 他不知道什麼樣的對應關係是正確的 所以它便會每個關係都對應一遍
比如: a表有m條記錄  b表有n條記錄
笛卡爾積結果就會生成m * n 記錄

需要自己篩選出正確的關聯關係
select *from emp,dept where emp.dept_id = dept.id;

內連線查詢

把兩張表有對應關係的記錄連線成一張虛擬表,相當於利用條件從笛卡爾積結果中篩選出了正確的結果

department沒有204這個部門,因而employee表中關於204這條員工資訊沒有匹配出來
mysql> select employee.id,employee.name,employee.age,employee.sex,department.name from employee inner join department on employee.dep_id=department.id; 
+----+-----------+------+--------+--------------+
| id | name      | age  | sex    | name         |
+----+-----------+------+--------+--------------+
|  1 | egon      |   18 | male   | 技術         |
|  2 | alex      |   48 | female | 人力資源     |
|  3 | wupeiqi   |   38 | male   | 人力資源     |
|  4 | yuanhao   |   28 | female | 銷售         |
|  5 | liwenzhou |   18 | male   | 技術         |
+----+-----------+------+--------+--------------+

#上述sql等同於
mysql> select employee.id,employee.name,employee.age,employee.sex,department.name from employee,department where employee.dep_id=department.id;

外連結之左連線:(優先顯示左表全部記錄)

以左表為準,即找出所有員工資訊,當然包括沒有部門的員工

本質就是:在內連線的基礎上增加左邊有右邊沒有的結果

mysql> select employee.id,employee.name,department.name as depart_name from employee left join department on employee.dep_id=department.id;
+----+------------+--------------+
| id | name       | depart_name  |
+----+------------+--------------+
|  1 | egon       | 技術         |
|  5 | liwenzhou  | 技術         |
|  2 | alex       | 人力資源     |
|  3 | wupeiqi    | 人力資源     |
|  4 | yuanhao    | 銷售         |
|  6 | jingliyang | NULL         |
+----+------------+--------------+

外連結之右連線:(優先顯示右表全部記錄)

以右表為準,即找出所有部門資訊,包括沒有員工的部門

本質就是:在內連線的基礎上增加右邊有左邊沒有的結果

mysql> select employee.id,employee.name,department.name as depart_name from employee right join department on employee.dep_id=department.id;
+------+-----------+--------------+
| id   | name      | depart_name  |
+------+-----------+--------------+
|    1 | egon      | 技術         |
|    2 | alex      | 人力資源     |
|    3 | wupeiqi   | 人力資源     |
|    4 | yuanhao   | 銷售         |
|    5 | liwenzhou | 技術         |
| NULL | NULL      | 運營         |
+------+-----------+--------------+

全外連線:(顯示左右兩個表全部記錄)

全外連線:在內連線的基礎上增加左邊有右邊沒有的和右邊有左邊沒有的結果
注意:
mysql不支援全外連線 full JOIN
強調:mysql可以使用此種方式間接實現全外連線

使用全外連線的時候:
union:
合併查詢,查詢的時候會自動去重
union all:合併查詢,查詢的時候不會自動去重
注意 unionunion all的區別:union會去掉相同的紀錄

select * from employee left join department on employee.dep_id = department.id
union
select * from employee right join department on employee.dep_id = department.id
;
#檢視結果
+------+------------+--------+------+--------+------+--------------+
| id   | name       | sex    | age  | dep_id | id   | name         |
+------+------------+--------+------+--------+------+--------------+
|    1 | egon       | male   |   18 |    200 |  200 | 技術         |
|    5 | liwenzhou  | male   |   18 |    200 |  200 | 技術         |
|    2 | alex       | female |   48 |    201 |  201 | 人力資源     |
|    3 | wupeiqi    | male   |   38 |    201 |  201 | 人力資源     |
|    4 | yuanhao    | female |   28 |    202 |  202 | 銷售         |
|    6 | jingliyang | female |   18 |    204 | NULL | NULL         |
| NULL | NULL       | NULL   | NULL |   NULL |  203 | 運營         |
+------+------------+--------+------+--------+------+--------------+

符合條件的連線查詢

示例1是使用內連線的方式實現的,示例2是沒有使用笛卡爾積的虛擬表然後通過條件篩選得出結果的

示例1:以內連線的方式查詢employee和department表,並且employee表中的age欄位值必須大於25,即找出年齡大於25歲的員工以及員工所在的部門
select employee.name,department.name from employee inner join department
    on employee.dep_id = department.id
    where age > 25;

示例2:以內連線的方式查詢employee和department表,並且以age欄位的升序方式顯示
select employee.id,employee.name,employee.age,department.name from employee,department
    where employee.dep_id = department.id
    and age > 25
    order by age asc;

子查詢

1:子查詢是將一個查詢語句巢狀在另一個查詢語句中。
2:內層查詢語句的查詢結果,可以為外層查詢語句提供查詢條件。
3:子查詢中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等關鍵字
4:還可以包含比較運算子:= 、 !=、> 、<等

帶IN關鍵字的子查詢

查詢平均年齡在25歲以上的部門名
select id,name from department
    where id in 
        (select dep_id from employee group by dep_id having avg(age) > 25);

檢視技術部員工姓名
select name from employee
    where dep_id in 
        (select id from department where name='技術');

檢視不足1人的部門名(子查詢得到的是有人的部門id)
select name from department where id not in (select distinct dep_id from employee);

帶比較運算子的子查詢

比較運算子:=、!=、>、>=、<、<=、<>

查詢大於所有人平均年齡的員工名與年齡
mysql> select name,age from emp where age > (select avg(age) from emp);
+---------+------+
| name | age |
+---------+------+
| alex | 48 |
| wupeiqi | 38 |
+---------+------+
rows in set (0.00 sec)


查詢大於部門內平均年齡的員工名、年齡
select t1.name,t1.age from emp t1
inner join 
(select dep_id,avg(age) avg_age from emp group by dep_id) t2
on t1.dep_id = t2.dep_id
where t1.age > t2.avg_age;

 帶EXISTS關鍵字的子查詢

EXISTS關字鍵字表示存在。在使用EXISTS關鍵字時,內層查詢語句不返回查詢的記錄。
而是返回一個真假值。True或False
當返回True時,外層查詢語句將進行查詢;當返回值為False時,外層查詢語句不進行查詢

department表中存在dept_id=203,Ture
mysql> select * from employee
    ->     where exists
    ->         (select id from department where id=200);
+----+------------+--------+------+--------+
| id | name       | sex    | age  | dep_id |
+----+------------+--------+------+--------+
|  1 | egon       | male   |   18 |    200 |
|  2 | alex       | female |   48 |    201 |
|  3 | wupeiqi    | male   |   38 |    201 |
|  4 | yuanhao    | female |   28 |    202 |
|  5 | liwenzhou  | male   |   18 |    200 |
|  6 | jingliyang | female |   18 |    204 |
+----+------------+--------+------+--------+

#department表中存在dept_id=205,False
mysql> select * from employee
    ->     where exists
    ->         (select id from department where id=204);
Empty set (0.00 sec)