1. 程式人生 > >Python學習--day41-mysql多表查詢

Python學習--day41-mysql多表查詢

目錄

   1.多表查詢 => 轉化為一張聯合大表

   2.視覺化工具
   3.pymysql模組

多表資料

create table dep(
    id int primary key auto_increment,
    name varchar(16),
    work varchar(16)
);
create table emp(
    id int primary key auto_increment,
    name varchar(16),
    salary float,
    dep_id int
);
insert into dep values(
1, '市場部', '銷售'), (2, '教學部', '授課'), (3, '管理部', '開車'); insert into emp(name, salary, dep_id) values('egon', 3.0, 2),('yanghuhu', 2.0, 2),('sanjiang', 10.0, 1),('owen', 88888.0, 2),('liujie', 8.0, 1),('yingjie', 1.2, 0);

 

一、笛卡爾積 (交叉連線)

# 需求: 
# 檢視每位員工的部門的所有資訊
select * from emp;
select * from
dep; ​ # 子查詢, 最終結果只能顯示單表的資訊, 但需求是同時顯示兩張表的資訊 => 先將兩張表合成一張表 select * from emp where dep_id in (select id from dep); ​ 笛卡爾積: 集合 X{a, b} * Y{o, p, q} => Z{{a, o}, {a, p}, {a, q}, {b, o}, {b, p}, {b, q}} 交叉查詢: select * from emp, dep; | select * from emp course join dep; +----+----------+--------+--------+----+-----------+--------+ | id | name | salary | dep_id | id | name | work | +----+----------+--------+--------+----+-----------+--------+ | 1 | egon | 3 | 2 | 1 | 市場部 | 銷售 | | 1 | egon | 3 | 2 | 2 | 教學部 | 授課 | | 1 | egon | 3 | 2 | 3 | 管理部 | 開車 | | 2 | yanghuhu | 2 | 2 | 1 | 市場部 | 銷售 | | 2 | yanghuhu | 2 | 2 | 2 | 教學部 | 授課 | | 2 | yanghuhu | 2 | 2 | 3 | 管理部 | 開車 | | 3 | sanjiang | 10 | 1 | 1 | 市場部 | 銷售 | | 3 | sanjiang | 10 | 1 | 2 | 教學部 | 授課 | | 3 | sanjiang | 10 | 1 | 3 | 管理部 | 開車 | | 4 | owen | 88888 | 2 | 1 | 市場部 | 銷售 | | 4 | owen | 88888 | 2 | 2 | 教學部 | 授課 | | 4 | owen | 88888 | 2 | 3 | 管理部 | 開車 | | 5 | liujie | 8 | 1 | 1 | 市場部 | 銷售 | | 5 | liujie | 8 | 1 | 2 | 教學部 | 授課 | | 5 | liujie | 8 | 1 | 3 | 管理部 | 開車 | | 6 | yingjie | 1.2 | 0 | 1 | 市場部 | 銷售 | | 6 | yingjie | 1.2 | 0 | 2 | 教學部 | 授課 | | 6 | yingjie | 1.2 | 0 | 3 | 管理部 | 開車 | +----+----------+--------+--------+----+-----------+--------+ 做了篩選, 結果
<=完整資料, 非笛卡爾積 select * from emp, dep where db2.emp.dep_id = db2.dep.id; # 同sql語句上表現是從兩張表拿資料 +----+----------+--------+--------+----+-----------+--------+ | id | name | salary | dep_id | id | name | work | +----+----------+--------+--------+----+-----------+--------+ | 1 | egon | 3 | 2 | 2 | 教學部 | 授課 | | 2 | yanghuhu | 2 | 2 | 2 | 教學部 | 授課 | | 3 | sanjiang | 10 | 1 | 1 | 市場部 | 銷售 | | 4 | owen | 88888 | 2 | 2 | 教學部 | 授課 | | 5 | liujie | 8 | 1 | 1 | 市場部 | 銷售 | +----+----------+--------+--------+----+-----------+--------+ # 注意: 同時查詢兩張表形成新的表,可以稱之為虛擬表, 原表與表之間可能存在重複欄位, 同時使用時需要明確所屬表,必要時還需明確所屬資料庫

 

 

二、多表連線(*****) => 虛擬的單表

1、內連線

inner join on
內連線:結果為兩張表有對應關係的資料(emp有dep沒有,emp沒有dep有的記錄均不會被虛擬表展示)
語法:左表 inner join 右表 on 兩表有關聯的欄位的條件, on就是產生對於關係的(連線的依據)
eg:select * from emp inner join dep on emp.dep_id = dep.id;
+----+----------+--------+--------+----+-----------+--------+
| id | name     | salary | dep_id | id | name      | work   |
+----+----------+--------+--------+----+-----------+--------+
|  1 | egon     |      3 |      2 |  2 | 教學部     | 授課    |
|  2 | yanghuhu |      2 |      2 |  2 | 教學部     | 授課    |
|  3 | sanjiang |     10 |      1 |  1 | 市場部     | 銷售    |
|  4 | owen     |  88888 |      2 |  2 | 教學部     | 授課    |
|  5 | liujie   |      8 |      1 |  1 | 市場部     | 銷售    |
+----+----------+--------+--------+----+-----------+--------+

 

 

2、左連線

left join on
左連線:在內連線的基礎上還保留左表特有的記錄
語法:左表 left join 右表 on 兩表有關聯的欄位的條件
​
eg:select emp.name '員工', dep.name '部門', dep.work '職責' from emp left join dep on emp.dep_id = dep.id;
+----------+-----------+--------+
| 員工      | 部門      | 職責    |
+----------+-----------+--------+
| sanjiang | 市場部     | 銷售   |
| liujie   | 市場部     | 銷售   |
| egon     | 教學部     | 授課   |
| yanghuhu | 教學部     | 授課   |
| owen     | 教學部     | 授課   |
| yingjie  | NULL      | NULL   |
+----------+-----------+--------+

 

 

3、右連線

right join on
右連線:在內連線的基礎上還保留右表特有的記錄
語法:左表 right join 右表 on 兩表有關聯的欄位的條件
eg:select * from emp right join dep on emp.dep_id = dep.id;
+------+----------+--------+--------+----+-----------+--------+
| id   | name     | salary | dep_id | id | name      | work   |
+------+----------+--------+--------+----+-----------+--------+
|    1 | egon     |      3 |      2 |  2 | 教學部     | 授課   |
|    2 | yanghuhu |      2 |      2 |  2 | 教學部     | 授課   |
|    3 | sanjiang |     10 |      1 |  1 | 市場部     | 銷售   |
|    4 | owen     |  88888 |      2 |  2 | 教學部     | 授課   |
|    5 | liujie   |      8 |      1 |  1 | 市場部     | 銷售   |
| NULL | NULL     |   NULL |   NULL |  3 | 管理部     | 開車   |
+------+----------+--------+--------+----+-----------+--------+


在連線語法join 前就是左表, 後就是右表
採用的是left關鍵詞就是左連線, right關鍵詞就是右連線, inner關鍵詞就是內連線

 

 

4、全連線

全連線:在內連線的基礎上分別保留這左表及右表特有的記錄
語法:mysql沒有full join on語法,但可以通過去重達到效果
eg:
select * from emp left join dep on emp.dep_id = dep.id
union
select * from emp right join dep on emp.dep_id = dep.id;
+------+----------+--------+--------+------+-----------+--------+
| id   | name     | salary | dep_id | id   | name      | work   |
+------+----------+--------+--------+------+-----------+--------+
|    1 | egon     |      3 |      2 |    2 | 教學部     | 授課    |
|    2 | yanghuhu |      2 |      2 |    2 | 教學部     | 授課    |
|    3 | sanjiang |     10 |      1 |    1 | 市場部     | 銷售    |
|    4 | owen     |  88888 |      2 |    2 | 教學部     | 授課    |
|    5 | liujie   |      8 |      1 |    1 | 市場部     | 銷售    |
| NULL | NULL     |   NULL |   NULL |    3 | 管理部     | 開車    |
|    6 | yingjie  |    1.2 |      0 | NULL | NULL      | NULL   |
+------+----------+--------+--------+------+-----------+--------+

 

 

練習

1.查詢每一位員工對應的工作職責
# 每一位員工 => 左表為emp表, 那麼左表的所有資料均需要被保留, 所有采用左連線
           => 左表為dep表, 那麼右表的所有資料均需要被保留, 所有采用右連線
# select emp.name, dep.work from emp left join dep on emp.dep_id = dep.id;
select emp.name, dep.work from dep right join emp on emp.dep_id = dep.id;
+----------+--------+
| name     | work   |
+----------+--------+
| sanjiang | 銷售   |
| liujie   | 銷售   |
| egon     | 授課   |
| yanghuhu | 授課   |
| owen     | 授課   |
| yingjie  | NULL   |
+----------+--------+2.查詢每一個部門下的員工們及員工職責
# select max(dep.name), max(dep.work), group_concat(emp.name) from emp right join dep on emp.dep_id = dep.id group by dep_id;
+---------------+---------------+------------------------+
| max(dep.name) | max(dep.work) | group_concat(emp.name) |
+---------------+---------------+------------------------+
| 管理部         | 開車          | NULL                   |
| 市場部         | 銷售          | liujie,sanjiang        |
| 教學部         | 授課          | yanghuhu,owen,egon     |
+---------------+---------------+------------------------+# 分析過程
# 每一個部門 => dep的資訊要被全部保留, 需要分組
# 員工職責 => dep.work, 由於分組不能直接被查詢 => 需要用聚合函式處理
# 員工們 => emp.name做拼接 => group_concat(emp.name)
# 分組的欄位 => 部門 => emp.dep_id => emp.dep_id可以直接被查詢,但沒有顯示意義 => dep.name用來顯示 => dep.name需要用聚合函式處理
​
​
select max(dep.name), max(dep.work), group_concat(emp.name) from dep left join emp on  emp.dep_id = dep.id group by emp.dep_id;
​
# 注: on在where條件關鍵詞之左

 

三、pymysql模組

1.基本操作:連線 -> 設定遊標 -> 執行sql -> 提交或獲取結果 -> 關閉遊標與連線
2.遊標設定:pymysql.cursors.DictCursor
3.遊標取值與偏移:fetchone() | fetchmany(n) | scroll(n,'relative|absolute')
4.防注入:cursor.execute('select * from user where usr=%s and pwd=%s', (usr, pwd))
5.提交操作結果:conn.commit()
​
# 模組pymysql
# 按照並匯入pymysql: pip3 insatll pymysql
# 通過pymysql操作資料庫分四步:
1.建立連線
conn = pymysql.connect(host="localhost", port=3306, db='db2', user='root', password='root')
​
2.設定字典型別遊標
cursor = conn.cursor(pymysql.cursors.DictCursor)
​
3.執行sql語句並使用執行結果
​
# 書寫sql語句
sql = 'select * from emp'
# 執行sql語句, 有返回值, 返回值為得到的記錄行數
line = cursor.execute(sql)
print(line)
​
# 使用執行的結果: 
        fetchone())當前遊標往後獲取一行記錄 
        fetchall()當前遊標往後所有的記錄
        scroll(num, mode="relative|absolute")
                relative: 遊標從當前位置往後移動num行
                ablolute: 遊標從頭往後移動num行, 一般可以結合line來使用能定位到任意位置
tag = cursor.fetchone() # 第一條
print(tag)
print(tag['salary'])
tag = cursor.fetchone() # 第二條
print(tag)
cursor.scroll(1, mode='relative') # 偏移第三條
# cursor.scroll(line - 1, mode='absolute') # 指標絕對, 遊標永遠從頭開始偏移
tags = cursor.fetchall() # 第四條到最後
print(tags)
​
4.斷開連線
cursor.close()
conn.close()

 

1、pymysql處理了sql注入

# 什麼是sql注入:
# 通過書寫sql包含(註釋相關的)特殊字元, 讓原有的sql執行順序發生改變, 從而改變執行得到的sql
# 目的:
# 繞過原有的sql安全認證, 達到對資料庫攻擊的目的
# 沒有處理sql注入的寫法
sql = 'select * from user where usr="%s" and pwd="%s"' % (usr, pwd)
res = cursor.execute(sql)
​
# sql注入
# 1.知道使用者名稱:  abc" -- hehe | ooo
# select * from user where usr="abc" -- hehe" and pwd="ooo"
# 2.不知道使用者名稱 aaa" or 1=1 -- hehe | 000
# select * from user where usr="aaa" or 1=1 -- hehe" and pwd="000"
# 處理sql注入
sql = 'select * from user where usr=%s and pwd=%s'
res = cursor.execute(sql, (usr, pwd))

 

 

2、pymysql中增刪改

#
# 增sql語句
sql1 = 'insert into user(usr, pwd) values (%s, %s)'
# 在記憶體中一次插入一條
cursor.execute(sql1, ("opq", "123"))
# 在記憶體中一次插入多條
cursor.executemany(sql1, [("aaa", "000"), ("bbb", "111")])
# 將記憶體中的資料提交到硬碟中
conn.commit()