1. 程式人生 > >資料庫之多表查詢、視覺化工具、pymysql模組

資料庫之多表查詢、視覺化工具、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;
+----+----------+--------+--------+
| id | name     | salary | dep_id |
+----+----------+--------+--------+
|  1 | egon     |      3 |      2 |
|  2 | yanghuhu |      2 |      2 |
|  3 | sanjiang |     10 |      1 |
|  4 | owen     |  88888 |      2 |
|  5 | liujie   |      8 |      1 |
|  6 | yingjie  |    1.2 |      0 |
+----+----------+--------+--------+

select 
* from dep; +----+-----------+--------+ | id | name | work | +----+-----------+--------+ | 1 | 市場部 | 銷售 | | 2 | 教學部 | 授課 | | 3 | 管理部 | 開車 | +----+-----------+--------+ 子查詢,最終結果只能顯示單表的資訊,但需求是同時能夠顯示兩張表的資訊==>先將兩張表合成一張表 select * from emp where dep_id in (select id from dep);
+----+----------+--------+--------+ | id | name | salary | dep_id | +----+----------+--------+--------+ | 1 | egon | 3 | 2 | | 2 | yanghuhu | 2 | 2 | | 3 | sanjiang | 10 | 1 | | 4 | owen | 88888 | 2 | | 5 | liujie | 8 | 1 | +----+----------+--------+--------+ 笛卡爾積:集合 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; ''' 做了篩選,結果<==完整資料,非笛卡爾積 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 | 市場部 | 銷售 | +----+----------+--------+--------+----+-----------+--------+ 注意:同時查詢兩張表形成新的表,可以稱之為虛擬表,原表與表之間可能存在重複欄位,同時使用時需要明確所屬表,必要時還需明確所需資料庫 '''

二:多表連線=>虛擬的單表

2.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.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   |
+----------+-----------+--------+

左表:                                                                                             右表:

                      

2.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關鍵詞就是內連線

 2.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   |
+------+----------+--------+--------+------+-----------+--------+
|    3 | sanjiang |     10 |      1 |    1 | 市場部    | 銷售   |
|    5 | liujie   |      8 |      1 |    1 | 市場部    | 銷售   |
|    1 | egon     |      3 |      2 |    2 | 教學部    | 授課   |
|    2 | yanghuhu |      2 |      2 |    2 | 教學部    | 授課   |
|    4 | owen     |  88888 |      2 |    2 | 教學部    | 授課   |
|    6 | yingjie  |    1.2 |      0 | NULL | NULL      | NULL   |
| NULL | NULL     |   NULL |   NULL |    3 | 管理部    | 開車   |
+------+----------+--------+--------+------+-----------+--------+

'''
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;

# 分析過程
# 每一個部門 => 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條件關鍵詞之左
'''
練習

三:Navicat

1.安裝Navicat
2.連線資料庫,並建庫
3.建立表、設定欄位、插入資料
4.新建查詢

四:Python使用mysql

  模組pymysql

  按照並匯入pymysql:pip3 install 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. 建立連線
2. 設定一個可以執行sql語句的物件 - 遊標
3. 通過該物件執行sql語句得到執行結果
4. 埠連線

'''
import pymysql
# 1. 建立連線
conn = pymysql.connect(host="localhost", port=3306, db='db2', user='root', password='root')
# 2. 設定遊標
cursor = conn.cursor(pymysql.cursors.DictCursor)  # 設定遊標的具體型別, DictCursor拿到欄位名
# 3. 執行sql語句
sql = 'select * from emp'

res = cursor.execute(sql)  # 結果的行數
print(res)

# 需求: 具體的一條條記錄
tag = cursor.fetchone()
print(tag)
print(tag['salary'])
tag = cursor.fetchone()
print(tag)

# cursor.scroll(1, mode='relative') # 指標相對於上一次位置往後偏移1條記錄
cursor.scroll(res - 1, mode='absolute') # 指標絕對, 遊標永遠從頭開始偏移

tags = cursor.fetchall()
print(tags)


cursor.close()
conn.close()
連線資料庫

五: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))
'''
sql語法中
註釋: /**/  | --  | #

什麼是sql注入:
通過書寫sql包含(註釋相關的)特殊字元, 讓原有的sql執行順序發生改變, 從而改變執行得到的sql

目的:
繞過原有的sql安全認證, 達到對資料庫攻擊的目的
'''
import pymysql
conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='db2')
cursor = conn.cursor(pymysql.cursors.DictCursor)

# 登入

# 得到使用者輸入的賬戶密碼
usr = input("請輸入使用者名稱: ") #abc
pwd = input("請輸入密碼: ") #123

# 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"' % (usr, pwd)
# select * from user where usr="abc" and pwd="123"
# res = cursor.execute(sql)

# pymysql已經處理了sql注入
sql = 'select * from user where usr=%s and pwd=%s'
res = cursor.execute(sql, (usr, pwd))

# print(res)
if res:
    print('login success')
else:
    print('login failed')

cursor.close()
conn.close()
sql注入問題

六:增、刪、改

增:
增加sql的語句:
sql1 = 'insert into user(usr, pwd) values (%s, %s)'

在記憶體中一次插入一條:
cursor.execute(sql1, ("opq", "123"))

在記憶體中插入多條
cursor.executemany(sql1, [("aaa", "000"), ("bbb", "111")])

將記憶體中的資料提交到硬碟中:
conn.commit()


刪:
sql2 = 'delete from user where usr=%s'
cursor.execute(sql2, ("aaa"))
conn.commit()


改:
sql3 = 'update user set pwd=%s where usr=%s'
res = cursor.execute(sql3, ("222", "bbb"))
conn.commit()
import pymysql
conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='db2')
cursor = conn.cursor(pymysql.cursors.DictCursor)

#
# sql1 = 'insert into user(usr, pwd) values (%s, %s)'
# cursor執行sql語句,在記憶體中完成了對資料的插入, 但不能將資料存放到硬碟
# 會將id完成自增
# 插入單條
# res = cursor.execute(sql1, ("opq", "123"))
# 插入多條
#res = cursor.executemany(sql1, [("aaa", "000"), ("bbb", "111")])
# print(res)
# 將記憶體中的資料提交給硬碟, 完成真實意義上的資料儲存
# conn.commit()

#
# sql2 = 'delete from user where usr=%s'
# res = cursor.execute(sql2, ("aaa"))
# print(res)
# conn.commit()

#
sql3 = 'update user set pwd=%s where usr=%s'
res = cursor.execute(sql3, ("222", "bbb"))
conn.commit()

cursor.close()
conn.close()
pymysql中的增刪改