資料庫之多表查詢、視覺化工具、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中的增刪改