mysql六:數據備份、pymysql模塊html
生產環境仍是推薦使用mysql命令行,但爲了方便咱們測試,可使用IDE工具java
下載連接:https://pan.baidu.com/s/1bpo5mqjpython
掌握: #1. 測試+連接數據庫 #2. 新建庫 #3. 新建表,新增字段+類型+約束 #4. 設計表:外鍵 #5. 新建查詢 #6. 備份庫/表 #注意: 批量加註釋:ctrl+?鍵 批量去註釋:ctrl+shift+?鍵
#1. 物理備份: 直接複製數據庫文件,適用於大型數據庫環境。但不能恢復到異構系統中如Windows。 #2. 邏輯備份: 備份的是建表、建庫、插入等操做所執行SQL語句,適用於中小型數據庫,效率相對較低。 #3. 導出表: 將表導入到文本文件中。
1、使用mysqldump實現邏輯備份mysql
#語法: # mysqldump -h 服務器 -u用戶名 -p密碼 數據庫名 > 備份文件.sql #示例: #單庫備份 mysqldump -uroot -p123 db1 > db1.sql mysqldump -uroot -p123 db1 table1 table2 > db1-table1-table2.sql #多庫備份 mysqldump -uroot -p123 --databases db1 db2 mysql db3 > db1_db2_mysql_db3.sql #備份全部庫 mysqldump -uroot -p123 --all-databases > all.sql
2、恢復邏輯備份linux
#方法一: [root@egon backup]# mysql -uroot -p123 < /backup/all.sql #方法二: mysql> use db1; mysql> SET SQL_LOG_BIN=0; mysql> source /root/db1.sql #注:若是備份/恢復單個庫時,能夠修改sql文件 DROP database if exists school; create database school; use school;
3、備份/恢復案例程序員
#數據庫備份/恢復實驗一:數據庫損壞 備份: 1. # mysqldump -uroot -p123 --all-databases > /backup/`date +%F`_all.sql 2. # mysql -uroot -p123 -e 'flush logs' //截斷併產生新的binlog 3. 插入數據 //模擬服務器正常運行 4. mysql> set sql_log_bin=0; //模擬服務器損壞 mysql> drop database db; 恢復: 1. # mysqlbinlog 最後一個binlog > /backup/last_bin.log 2. mysql> set sql_log_bin=0; mysql> source /backup/2014-02-13_all.sql //恢復最近一次徹底備份 mysql> source /backup/last_bin.log //恢復最後個binlog文件 #數據庫備份/恢復實驗二:若是有誤刪除 備份: 1. mysqldump -uroot -p123 --all-databases > /backup/`date +%F`_all.sql 2. mysql -uroot -p123 -e 'flush logs' //截斷併產生新的binlog 3. 插入數據 //模擬服務器正常運行 4. drop table db1.t1 //模擬誤刪除 5. 插入數據 //模擬服務器正常運行 恢復: 1. # mysqlbinlog 最後一個binlog --stop-position=260 > /tmp/1.sql # mysqlbinlog 最後一個binlog --start-position=900 > /tmp/2.sql 2. mysql> set sql_log_bin=0; mysql> source /backup/2014-02-13_all.sql //恢復最近一次徹底備份 mysql> source /tmp/1.log //恢復最後個binlog文件 mysql> source /tmp/2.log //恢復最後個binlog文件 注意事項: 1. 徹底恢復到一個乾淨的環境(例如新的數據庫或刪除原有的數據庫) 2. 恢復期間全部SQL語句不該該記錄到binlog中
4、實現自動化備份web
備份計劃: 1. 什麼時間 2:00 2. 對哪些數據庫備份 3. 備份文件放的位置 備份腳本: [root@egon ~]# vim /mysql_back.sql #!/bin/bash back_dir=/backup back_file=`date +%F`_all.sql user=root pass=123 if [ ! -d /backup ];then mkdir -p /backup fi # 備份並截斷日誌 mysqldump -u${user} -p${pass} --events --all-databases > ${back_dir}/${back_file} mysql -u${user} -p${pass} -e 'flush logs' # 只保留最近一週的備份 cd $back_dir find . -mtime +7 -exec rm -rf {} \; 手動測試: [root@egon ~]# chmod a+x /mysql_back.sql [root@egon ~]# chattr +i /mysql_back.sql [root@egon ~]# /mysql_back.sql 配置cron: [root@egon ~]# crontab -l 2 * * * /mysql_back.sql
5、表的導出和導入算法
SELECT... INTO OUTFILE 導出文本文件 示例: mysql> SELECT * FROM school.student1 INTO OUTFILE 'student1.txt' FIELDS TERMINATED BY ',' //定義字段分隔符 OPTIONALLY ENCLOSED BY '」' //定義字符串使用什麼符號括起來 LINES TERMINATED BY '\n' ; //定義換行符 mysql 命令導出文本文件 示例: # mysql -u root -p123 -e 'select * from student1.school' > /tmp/student1.txt # mysql -u root -p123 --xml -e 'select * from student1.school' > /tmp/student1.xml # mysql -u root -p123 --html -e 'select * from student1.school' > /tmp/student1.html LOAD DATA INFILE 導入文本文件 mysql> DELETE FROM student1; mysql> LOAD DATA INFILE '/tmp/student1.txt' INTO TABLE school.student1 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '」' LINES TERMINATED BY '\n';
報錯:Variable 'secure_file_priv' is a read onlysql
#可能會報錯 mysql> select * from db1.emp into outfile 'C:\\db1.emp.txt' fields terminated by ',' lines terminated by '\r\n'; ERROR 1238 (HY000): Variable 'secure_file_priv' is a read only variable #數據庫最關鍵的是數據,一旦數據庫權限泄露,那麼經過上述語句就能夠輕鬆將數據導出到文件中而後下載拿走,於是mysql對此做了限制,只能將文件導出到指定目錄 在配置文件中 [mysqld] secure_file_priv='C:\\' #只能將數據導出到C:\\下 重啓mysql 從新執行上述語句
6、數據庫遷移數據庫
務必保證在相同版本之間遷移 # mysqldump -h 源IP -uroot -p123 --databases db1 | mysql -h 目標IP -uroot -p456
#安裝 pip3 install pymysql
一 連接、執行sql、關閉(遊標)
import pymysql user=input('用戶名: ').strip() pwd=input('密碼: ').strip() #連接 conn=pymysql.connect(host='localhost',user='root',password='123',database='egon',charset='utf8') #遊標 cursor=conn.cursor() #執行完畢返回的結果集默認以元組顯示 #cursor=conn.cursor(cursor=pymysql.cursors.DictCursor) #執行sql語句 sql='select * from userinfo where name="%s" and password="%s"' %(user,pwd) #注意%s須要加引號 print(sql) res=cursor.execute(sql) #執行sql語句,返回sql查詢成功的記錄數目 print(res) cursor.close() conn.close() if res: print('登陸成功') else: print('登陸失敗')
二 execute()之sql注入
注意:符號--會註釋掉它以後的sql,正確的語法:--後至少有一個任意字符
根本原理:就根據程序的字符串拼接name='%s',咱們輸入一個xxx' -- haha,用咱們輸入的xxx加'在程序中拼接成一個判斷條件name='xxx' -- haha'
最後那一個空格,在一條sql語句中若是遇到select * from t1 where id > 3 -- and name='egon';則--以後的條件被註釋掉了 #一、sql注入之:用戶存在,繞過密碼 egon' -- 任意字符 #二、sql注入之:用戶不存在,繞過用戶與密碼 xxx' or 1=1 -- 任意字符
解決方法:
# 原來是咱們對sql進行字符串拼接 # sql="select * from userinfo where name='%s' and password='%s'" %(user,pwd) # print(sql) # res=cursor.execute(sql) #改寫爲(execute幫咱們作字符串拼接,咱們無需且必定不能再爲%s加引號了) sql="select * from userinfo where name=%s and password=%s" #!!!注意%s須要去掉引號,由於pymysql會自動爲咱們加上 res=cursor.execute(sql,[user,pwd]) #pymysql模塊自動幫咱們解決sql注入的問題,只要咱們按照pymysql的規矩來。
三 增、刪、改:conn.commit()
import pymysql #連接 conn=pymysql.connect(host='localhost',user='root',password='123',database='egon') #遊標 cursor=conn.cursor() #執行sql語句 #part1 # sql='insert into userinfo(name,password) values("root","123456");' # res=cursor.execute(sql) #執行sql語句,返回sql影響成功的行數 # print(res) #part2 # sql='insert into userinfo(name,password) values(%s,%s);' # res=cursor.execute(sql,("root","123456")) #執行sql語句,返回sql影響成功的行數 # print(res) #part3 sql='insert into userinfo(name,password) values(%s,%s);' res=cursor.executemany(sql,[("root","123456"),("lhf","12356"),("eee","156")]) #執行sql語句,返回sql影響成功的行數 print(res) conn.commit() #提交後才發現表中插入記錄成功 cursor.close() conn.close()
四 查:fetchone,fetchmany,fetchall
import pymysql #連接 conn=pymysql.connect(host='localhost',user='root',password='123',database='egon') #遊標 cursor=conn.cursor() #執行sql語句 sql='select * from userinfo;' rows=cursor.execute(sql) #執行sql語句,返回sql影響成功的行數rows,將結果放入一個集合,等待被查詢 # cursor.scroll(3,mode='absolute') # 相對絕對位置移動 # cursor.scroll(3,mode='relative') # 相對當前位置移動 res1=cursor.fetchone() res2=cursor.fetchone() res3=cursor.fetchone() res4=cursor.fetchmany(2) res5=cursor.fetchall() print(res1) print(res2) print(res3) print(res4) print(res5) print('%s rows in set (0.00 sec)' %rows) conn.commit() #提交後才發現表中插入記錄成功 cursor.close() conn.close() ''' (1, 'root', '123456') (2, 'root', '123456') (3, 'root', '123456') ((4, 'root', '123456'), (5, 'root', '123456')) ((6, 'root', '123456'), (7, 'lhf', '12356'), (8, 'eee', '156')) rows in set (0.00 sec) '''
五 獲取插入的最後一條數據的自增ID
import pymysql conn=pymysql.connect(host='localhost',user='root',password='123',database='egon') cursor=conn.cursor() sql='insert into userinfo(name,password) values("xxx","123");' rows=cursor.execute(sql) print(cursor.lastrowid) #在插入語句後查看 conn.commit() cursor.close() conn.close()
mysql七:視圖、觸發器、事物、存儲過程、函數
視圖是一個虛擬表(非真實存在),其本質是【根據SQL語句獲取動態的數據集,併爲其命名】,用戶使用時只需使用【名稱】便可獲取結果集,能夠將該結果集當作表來使用。
使用視圖咱們能夠把查詢過程當中的臨時表摘出來,用視圖去實現,這樣之後再想操做該臨時表的數據時就無需重寫複雜的sql了,直接去視圖中查找便可,但視圖有明顯地效率問題,而且視圖是存放在數據庫中的,若是咱們程序中使用的sql過度依賴數據庫中的視圖,即強耦合,那就意味着擴展sql極爲不便,所以並不推薦使用
臨時表建立舉例:
#兩張有關係的表 mysql> select * from course; +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 體育 | 3 | | 4 | 美術 | 2 | +-----+--------+------------+ 4 rows in set (0.00 sec) mysql> select * from teacher; +-----+-----------------+ | tid | tname | +-----+-----------------+ | 1 | 張磊老師 | | 2 | 李平老師 | | 3 | 劉海燕老師 | | 4 | 朱雲海老師 | | 5 | 李傑老師 | +-----+-----------------+ 5 rows in set (0.00 sec) #查詢李平老師教授的課程名 mysql> select cname from course where teacher_id = (select tid from teacher where tname='李平老師'); +--------+ | cname | +--------+ | 物理 | | 美術 | +--------+ 2 rows in set (0.00 sec) #子查詢出臨時表,做爲teacher_id等判斷依據 select tid from teacher where tname='李平老師'
一 建立視圖
#語法:CREATE VIEW 視圖名稱 AS SQL語句 create view teacher_view as select tid from teacher where tname='李平老師'; #因而查詢李平老師教授的課程名的sql能夠改寫爲 mysql> select cname from course where teacher_id = (select tid from teacher_view); +--------+ | cname | +--------+ | 物理 | | 美術 | +--------+ 2 rows in set (0.00 sec) #!!!注意注意注意: #1. 使用視圖之後就無需每次都重寫子查詢的sql,可是這麼效率並不高,還不如咱們寫子查詢的效率高 #2. 並且有一個致命的問題:視圖是存放到數據庫裏的,若是咱們程序中的sql過度依賴於數據庫中存放的視圖,那麼意味着,一旦sql須要修改且涉及到視圖的部分,則必須去數據庫中進行修改,而一般在公司中數據庫有專門的DBA負責,你要想完成修改,必須付出大量的溝通成本DBA可能纔會幫你完成修改,極其地不方便
二 使用視圖
#修改視圖,原始表也跟着改 mysql> select * from course; +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 體育 | 3 | | 4 | 美術 | 2 | +-----+--------+------------+ 4 rows in set (0.00 sec) mysql> create view course_view as select * from course; #建立表course的視圖 Query OK, 0 rows affected (0.52 sec) mysql> select * from course_view; +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 體育 | 3 | | 4 | 美術 | 2 | +-----+--------+------------+ 4 rows in set (0.00 sec) mysql> update course_view set cname='xxx'; #更新視圖中的數據 Query OK, 4 rows affected (0.04 sec) Rows matched: 4 Changed: 4 Warnings: 0 mysql> insert into course_view values(5,'yyy',2); #往視圖中插入數據 Query OK, 1 row affected (0.03 sec) mysql> select * from course; #發現原始表的記錄也跟着修改了 +-----+-------+------------+ | cid | cname | teacher_id | +-----+-------+------------+ | 1 | xxx | 1 | | 2 | xxx | 2 | | 3 | xxx | 3 | | 4 | xxx | 2 | | 5 | yyy | 2 | +-----+-------+------------+ 5 rows in set (0.00 sec)
咱們不該該修改視圖中的記錄,並且在涉及多個表的狀況下是根本沒法修改視圖中的記錄的,以下圖
三 修改視圖
語法:ALTER VIEW 視圖名稱 AS SQL語句 mysql> alter view teacher_view as select * from course where cid>3; Query OK, 0 rows affected (0.04 sec) mysql> select * from teacher_view; +-----+-------+------------+ | cid | cname | teacher_id | +-----+-------+------------+ | 4 | xxx | 2 | | 5 | yyy | 2 | +-----+-------+------------+ 2 rows in set (0.00 sec)
四 刪除視圖
語法:DROP VIEW 視圖名稱 DROP VIEW teacher_view
使用觸發器能夠定製用戶對錶進行【增、刪、改】操做時先後的行爲,注意:沒有查詢
一 建立觸發器
# 插入前 CREATE TRIGGER tri_before_insert_tb1 BEFORE INSERT ON tb1 FOR EACH ROW BEGIN ... END # 插入後 CREATE TRIGGER tri_after_insert_tb1 AFTER INSERT ON tb1 FOR EACH ROW BEGIN ... END # 刪除前 CREATE TRIGGER tri_before_delete_tb1 BEFORE DELETE ON tb1 FOR EACH ROW BEGIN ... END # 刪除後 CREATE TRIGGER tri_after_delete_tb1 AFTER DELETE ON tb1 FOR EACH ROW BEGIN ... END # 更新前 CREATE TRIGGER tri_before_update_tb1 BEFORE UPDATE ON tb1 FOR EACH ROW BEGIN ... END # 更新後 CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW BEGIN ... END
#準備表 CREATE TABLE cmd ( id INT PRIMARY KEY auto_increment, USER CHAR (32), priv CHAR (10), cmd CHAR (64), sub_time datetime, #提交時間 success enum ('yes', 'no') #0表明執行失敗 ); CREATE TABLE errlog ( id INT PRIMARY KEY auto_increment, err_cmd CHAR (64), err_time datetime ); #建立觸發器 delimiter // CREATE TRIGGER tri_after_insert_cmd AFTER INSERT ON cmd FOR EACH ROW BEGIN IF NEW.success = 'no' THEN #等值判斷只有一個等號 INSERT INTO errlog(err_cmd, err_time) VALUES(NEW.cmd, NEW.sub_time) ; #必須加分號 END IF ; #必須加分號 END// delimiter ; #往表cmd中插入記錄,觸發觸發器,根據IF的條件決定是否插入錯誤日誌 INSERT INTO cmd ( USER, priv, cmd, sub_time, success ) VALUES ('egon','0755','ls -l /etc',NOW(),'yes'), ('egon','0755','cat /etc/passwd',NOW(),'no'), ('egon','0755','useradd xxx',NOW(),'no'), ('egon','0755','ps aux',NOW(),'yes'); #查詢錯誤日誌,發現有兩條 mysql> select * from errlog; +----+-----------------+---------------------+ | id | err_cmd | err_time | +----+-----------------+---------------------+ | 1 | cat /etc/passwd | 2017-09-14 22:18:48 | | 2 | useradd xxx | 2017-09-14 22:18:48 | +----+-----------------+---------------------+ 2 rows in set (0.00 sec)
特別的:NEW表示即將插入的數據行,OLD表示即將刪除的數據行。
二 使用觸發器
觸發器沒法由用戶直接調用,而知因爲對錶的【增/刪/改】操做被動引起的。
三 刪除觸發器
drop trigger tri_after_insert_cmd;
事務用於將某些操做的多個SQL做爲原子性操做,一旦有某一個出現錯誤,便可回滾到原來的狀態,從而保證數據庫數據完整性。
create table user( id int primary key auto_increment, name char(32), balance int ); insert into user(name,balance) values ('wsb',1000), ('egon',1000), ('ysb',1000); #原子操做 start transaction; update user set balance=900 where name='wsb'; #買支付100元 update user set balance=1010 where name='egon'; #中介拿走10元 update user set balance=1090 where name='ysb'; #賣家拿到90元 commit; #出現異常,回滾到初始狀態 start transaction; update user set balance=900 where name='wsb'; #買支付100元 update user set balance=1010 where name='egon'; #中介拿走10元 uppdate user set balance=1090 where name='ysb'; #賣家拿到90元,出現異常沒有拿到 rollback; commit; mysql> select * from user; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | wsb | 1000 | | 2 | egon | 1000 | | 3 | ysb | 1000 | +----+------+---------+ 3 rows in set (0.00 sec)
一 介紹
存儲過程包含了一系列可執行的sql語句,存儲過程存放於MySQL中,經過調用它的名字能夠執行其內部的一堆sql
使用存儲過程的優勢:
#1. 用於替代程序寫的SQL語句,實現程序與sql解耦 #2. 基於網絡傳輸,傳別名的數據量小,而直接傳sql數據量大
使用存儲過程的缺點:
#1. 程序員擴展功能不方便
補充:程序與數據庫結合使用的三種方式
#方式一: MySQL:存儲過程 程序:調用存儲過程 #方式二: MySQL: 程序:純SQL語句 #方式三: MySQL: 程序:類和對象,即ORM(本質仍是純SQL語句)
二 建立簡單存儲過程(無參)
delimiter // create procedure p1() BEGIN select * from blog; INSERT into blog(name,sub_time) values("xxx",now()); END // delimiter ; #在mysql中調用 call p1() #在python中基於pymysql調用 cursor.callproc('p1') print(cursor.fetchall())
三 建立存儲過程(有參)
對於存儲過程,能夠接收參數,其參數有三類: #in 僅用於傳入參數用 #out 僅用於返回值用 #inout 既能夠傳入又能夠看成返回值
in:傳入參數
delimiter // create procedure p2( in n1 int, in n2 int ) BEGIN select * from blog where id > n1; END // delimiter ; #在mysql中調用 call p2(3,2) #在python中基於pymysql調用 cursor.callproc('p2',(3,2)) print(cursor.fetchall())
out:返回值
delimiter // create procedure p3( in n1 int, out res int ) BEGIN select * from blog where id > n1; set res = 1; END // delimiter ; #在mysql中調用 set @res=0; #0表明假(執行失敗),1表明真(執行成功) call p3(3,@res); select @res; #在python中基於pymysql調用 cursor.callproc('p3',(3,0)) #0至關於set @res=0 print(cursor.fetchall()) #查詢select的查詢結果 cursor.execute('select @_p3_0,@_p3_1;') #@p3_0表明第一個參數,@p3_1表明第二個參數,即返回值 print(cursor.fetchall())
inout:既能夠傳入又能夠返回
delimiter // create procedure p4( inout n1 int ) BEGIN select * from blog where id > n1; set n1 = 1; END // delimiter ; #在mysql中調用 set @x=3; call p4(@x); select @x; #在python中基於pymysql調用 cursor.callproc('p4',(3,)) print(cursor.fetchall()) #查詢select的查詢結果 cursor.execute('select @_p4_0;') print(cursor.fetchall())
事務
#介紹 delimiter // create procedure p4( out status int ) BEGIN 1. 聲明若是出現異常則執行{ set status = 1; rollback; } 開始事務 -- 由秦兵帳戶減去100 -- 方少偉帳戶加90 -- 張根帳戶加10 commit; 結束 set status = 2; END // delimiter ; #實現 delimiter // create PROCEDURE p5( OUT p_return_code tinyint ) BEGIN DECLARE exit handler for sqlexception BEGIN -- ERROR set p_return_code = 1; rollback; END; DECLARE exit handler for sqlwarning BEGIN -- WARNING set p_return_code = 2; rollback; END; START TRANSACTION; DELETE from tb1; #執行失敗 insert into blog(name,sub_time) values('yyy',now()); COMMIT; -- SUCCESS set p_return_code = 0; #0表明執行成功 END // delimiter ; #在mysql中調用存儲過程 set @res=123; call p5(@res); select @res; #在python中基於pymysql調用存儲過程 cursor.callproc('p5',(123,)) print(cursor.fetchall()) #查詢select的查詢結果 cursor.execute('select @_p5_0;') print(cursor.fetchall())
四 執行存儲過程
在MySQL中執行存儲過程
-- 無參數 call proc_name() -- 有參數,全in call proc_name(1,2) -- 有參數,有in,out,inout set @t1=0; set @t2=3; call proc_name(1,2,@t1,@t2) 執行存儲過程
在python中基於pymysql執行存儲過程
#!/usr/bin/env python # -*- coding:utf-8 -*- import pymysql conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 執行存儲過程 cursor.callproc('p1', args=(1, 22, 3, 4)) # 獲取執行完存儲的參數 cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3") result = cursor.fetchall() conn.commit() cursor.close() conn.close() print(result)
五 刪除存儲過程
drop procedure proc_name;
MySQL中提供了許多內置函數,例如:
1、數學函數 ROUND(x,y) 返回參數x的四捨五入的有y位小數的值 RAND() 返回0到1內的隨機值,能夠經過提供一個參數(種子)使RAND()隨機數生成器生成一個指定的值。 2、聚合函數(經常使用於GROUP BY從句的SELECT查詢中) AVG(col)返回指定列的平均值 COUNT(col)返回指定列中非NULL值的個數 MIN(col)返回指定列的最小值 MAX(col)返回指定列的最大值 SUM(col)返回指定列的全部值之和 GROUP_CONCAT(col) 返回由屬於一組的列值鏈接組合而成的結果 3、字符串函數 CHAR_LENGTH(str) 返回值爲字符串str 的長度,長度的單位爲字符。一個多字節字符算做一個單字符。 CONCAT(str1,str2,...) 字符串拼接 若有任何一個參數爲NULL ,則返回值爲 NULL。 CONCAT_WS(separator,str1,str2,...) 字符串拼接(自定義鏈接符) CONCAT_WS()不會忽略任何空字符串。 (然而會忽略全部的 NULL)。 CONV(N,from_base,to_base) 進制轉換 例如: SELECT CONV('a',16,2); 表示將 a 由16進制轉換爲2進制字符串表示 FORMAT(X,D) 將數字X 的格式寫爲'#,###,###.##',以四捨五入的方式保留小數點後 D 位, 並將結果以字符串的形式返回。若 D 爲 0, 則返回結果不帶有小數點,或不含小數部分。 例如: SELECT FORMAT(12332.1,4); 結果爲: '12,332.1000' INSERT(str,pos,len,newstr) 在str的指定位置插入字符串 pos:要替換位置其實位置 len:替換的長度 newstr:新字符串 特別的: 若是pos超過原字符串長度,則返回原字符串 若是len超過原字符串長度,則由新字符串徹底替換 INSTR(str,substr) 返回字符串 str 中子字符串的第一個出現位置。 LEFT(str,len) 返回字符串str 從開始的len位置的子序列字符。 LOWER(str) 變小寫 UPPER(str) 變大寫 REVERSE(str) 返回字符串 str ,順序和字符順序相反。 SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len) 不帶有len 參數的格式從字符串str返回一個子字符串,起始於位置 pos。帶有len參數的格式從字符串str返回一個長度同len字符相同的子字符串,起始於位置 pos。 使用 FROM的格式爲標準 SQL 語法。也可能對pos使用一個負值。倘若這樣,則子字符串的位置起始於字符串結尾的pos 字符,而不是字符串的開頭位置。在如下格式的函數中能夠對pos 使用一個負值。 mysql> SELECT SUBSTRING('Quadratically',5); -> 'ratically' mysql> SELECT SUBSTRING('foobarbar' FROM 4); -> 'barbar' mysql> SELECT SUBSTRING('Quadratically',5,6); -> 'ratica' mysql> SELECT SUBSTRING('Sakila', -3); -> 'ila' mysql> SELECT SUBSTRING('Sakila', -5, 3); -> 'aki' mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2); -> 'ki' 4、日期和時間函數 CURDATE()或CURRENT_DATE() 返回當前的日期 CURTIME()或CURRENT_TIME() 返回當前的時間 DAYOFWEEK(date) 返回date所表明的一星期中的第幾天(1~7) DAYOFMONTH(date) 返回date是一個月的第幾天(1~31) DAYOFYEAR(date) 返回date是一年的第幾天(1~366) DAYNAME(date) 返回date的星期名,如:SELECT DAYNAME(CURRENT_DATE); FROM_UNIXTIME(ts,fmt) 根據指定的fmt格式,格式化UNIX時間戳ts HOUR(time) 返回time的小時值(0~23) MINUTE(time) 返回time的分鐘值(0~59) MONTH(date) 返回date的月份值(1~12) MONTHNAME(date) 返回date的月份名,如:SELECT MONTHNAME(CURRENT_DATE); NOW() 返回當前的日期和時間 QUARTER(date) 返回date在一年中的季度(1~4),如SELECT QUARTER(CURRENT_DATE); WEEK(date) 返回日期date爲一年中第幾周(0~53) YEAR(date) 返回日期date的年份(1000~9999) 重點: DATE_FORMAT(date,format) 根據format字符串格式化date值 mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y'); -> 'Sunday October 2009' mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s'); -> '22:23:00' mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00', -> '%D %y %a %d %m %b %j'); -> '4th 00 Thu 04 10 Oct 277' mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00', -> '%H %k %I %r %T %S %w'); -> '22 22 10 10:23:00 PM 22:23:00 00 6' mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V'); -> '1998 52' mysql> SELECT DATE_FORMAT('2006-06-00', '%d'); -> '00' 5、加密函數 MD5() 計算字符串str的MD5校驗和 PASSWORD(str) 返回字符串str的加密版本,這個加密過程是不可逆轉的,和UNIX密碼加密過程使用不一樣的算法。 6、控制流函數 CASE WHEN[test1] THEN [result1]...ELSE [default] END 若是testN是真,則返回resultN,不然返回default CASE [test] WHEN[val1] THEN [result]...ELSE [default]END 若是test和valN相等,則返回resultN,不然返回default IF(test,t,f) 若是test是真,返回t;不然返回f IFNULL(arg1,arg2) 若是arg1不是空,返回arg1,不然返回arg2 NULLIF(arg1,arg2) 若是arg1=arg2返回NULL;不然返回arg1 7、控制流函數小練習 #7.一、準備表 /* Navicat MySQL Data Transfer Source Server : localhost_3306 Source Server Version : 50720 Source Host : localhost:3306 Source Database : student Target Server Type : MYSQL Target Server Version : 50720 File Encoding : 65001 Date: 2018-01-02 12:05:30 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for course -- ---------------------------- DROP TABLE IF EXISTS `course`; CREATE TABLE `course` ( `c_id` int(11) NOT NULL, `c_name` varchar(255) DEFAULT NULL, `t_id` int(11) DEFAULT NULL, PRIMARY KEY (`c_id`), KEY `t_id` (`t_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of course -- ---------------------------- INSERT INTO `course` VALUES ('1', 'python', '1'); INSERT INTO `course` VALUES ('2', 'java', '2'); INSERT INTO `course` VALUES ('3', 'linux', '3'); INSERT INTO `course` VALUES ('4', 'web', '2'); -- ---------------------------- -- Table structure for score -- ---------------------------- DROP TABLE IF EXISTS `score`; CREATE TABLE `score` ( `id` int(11) NOT NULL AUTO_INCREMENT, `s_id` int(10) DEFAULT NULL, `c_id` int(11) DEFAULT NULL, `num` double DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of score -- ---------------------------- INSERT INTO `score` VALUES ('1', '1', '1', '79'); INSERT INTO `score` VALUES ('2', '1', '2', '78'); INSERT INTO `score` VALUES ('3', '1', '3', '35'); INSERT INTO `score` VALUES ('4', '2', '2', '32'); INSERT INTO `score` VALUES ('5', '3', '1', '66'); INSERT INTO `score` VALUES ('6', '4', '2', '77'); INSERT INTO `score` VALUES ('7', '4', '1', '68'); INSERT INTO `score` VALUES ('8', '5', '1', '66'); INSERT INTO `score` VALUES ('9', '2', '1', '69'); INSERT INTO `score` VALUES ('10', '4', '4', '75'); INSERT INTO `score` VALUES ('11', '5', '4', '66.7'); -- ---------------------------- -- Table structure for student -- ---------------------------- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `s_id` varchar(20) NOT NULL, `s_name` varchar(255) DEFAULT NULL, `s_age` int(10) DEFAULT NULL, `s_sex` char(1) DEFAULT NULL, PRIMARY KEY (`s_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of student -- ---------------------------- INSERT INTO `student` VALUES ('1', '魯班', '12', '男'); INSERT INTO `student` VALUES ('2', '貂蟬', '20', '女'); INSERT INTO `student` VALUES ('3', '劉備', '35', '男'); INSERT INTO `student` VALUES ('4', '關羽', '34', '男'); INSERT INTO `student` VALUES ('5', '張飛', '33', '女'); -- ---------------------------- -- Table structure for teacher -- ---------------------------- DROP TABLE IF EXISTS `teacher`; CREATE TABLE `teacher` ( `t_id` int(10) NOT NULL, `t_name` varchar(50) DEFAULT NULL, PRIMARY KEY (`t_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of teacher -- ---------------------------- INSERT INTO `teacher` VALUES ('1', '大王'); INSERT INTO `teacher` VALUES ('2', 'alex'); INSERT INTO `teacher` VALUES ('3', 'egon'); INSERT INTO `teacher` VALUES ('4', 'peiqi'); #7.二、統計各科各分數段人數.顯示格式:課程ID,課程名稱,[100-85],[85-70],[70-60],[ <60] select score.c_id, course.c_name, sum(CASE WHEN num BETWEEN 85 and 100 THEN 1 ELSE 0 END) as '[100-85]', sum(CASE WHEN num BETWEEN 70 and 85 THEN 1 ELSE 0 END) as '[85-70]', sum(CASE WHEN num BETWEEN 60 and 70 THEN 1 ELSE 0 END) as '[70-60]', sum(CASE WHEN num < 60 THEN 1 ELSE 0 END) as '[ <60]' from score,course where score.c_id=course.c_id GROUP BY score.c_id;
須要掌握函數:date_format
#1 基本使用 mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y'); -> 'Sunday October 2009' mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s'); -> '22:23:00' mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00', -> '%D %y %a %d %m %b %j'); -> '4th 00 Thu 04 10 Oct 277' mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00', -> '%H %k %I %r %T %S %w'); -> '22 22 10 10:23:00 PM 22:23:00 00 6' mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V'); -> '1998 52' mysql> SELECT DATE_FORMAT('2006-06-00', '%d'); -> '00' #2 準備表和記錄 CREATE TABLE blog ( id INT PRIMARY KEY auto_increment, NAME CHAR (32), sub_time datetime ); INSERT INTO blog (NAME, sub_time) VALUES ('第1篇','2015-03-01 11:31:21'), ('第2篇','2015-03-11 16:31:21'), ('第3篇','2016-07-01 10:21:31'), ('第4篇','2016-07-22 09:23:21'), ('第5篇','2016-07-23 10:11:11'), ('第6篇','2016-07-25 11:21:31'), ('第7篇','2017-03-01 15:33:21'), ('第8篇','2017-03-01 17:32:21'), ('第9篇','2017-03-01 18:31:21'); #3. 提取sub_time字段的值,按照格式後的結果即"年月"來分組 SELECT DATE_FORMAT(sub_time,'%Y-%m'),COUNT(1) FROM blog GROUP BY DATE_FORMAT(sub_time,'%Y-%m'); #結果 +-------------------------------+----------+ | DATE_FORMAT(sub_time,'%Y-%m') | COUNT(1) | +-------------------------------+----------+ | 2015-03 | 2 | | 2016-07 | 4 | | 2017-03 | 3 | +-------------------------------+----------+ 3 rows in set (0.00 sec)
一 自定義函數
#!!!注意!!! #函數中不要寫sql語句(不然會報錯),函數僅僅只是一個功能,是一個在sql中被應用的功能 #若要想在begin...end...中寫sql,請用存儲過程
delimiter // create function f1( i1 int, i2 int) returns int BEGIN declare num int; set num = i1 + i2; return(num); END // delimiter ;
delimiter // create function f5( i int ) returns int begin declare res int default 0; if i = 10 then set res=100; elseif i = 20 then set res=200; elseif i = 30 then set res=300; else set res=400; end if; return res; end // delimiter ;
二 刪除函數
drop function func_name;
三 執行函數
# 獲取返回值 select UPPER('egon') into @res; SELECT @res; # 在查詢中使用 select f1(11,nid) ,name from tb2;
一 條件語句
delimiter // CREATE PROCEDURE proc_if () BEGIN declare i int default 0; if i = 1 THEN SELECT 1; ELSEIF i = 2 THEN SELECT 2; ELSE SELECT 7; END IF; END // delimiter ;
二 循環語句
while循環
delimiter // CREATE PROCEDURE proc_while () BEGIN DECLARE num INT ; SET num = 0 ; WHILE num < 10 DO SELECT num ; SET num = num + 1 ; END WHILE ; END // delimiter ;
repeat循環
delimiter // CREATE PROCEDURE proc_repeat () BEGIN DECLARE i INT ; SET i = 0 ; repeat select i; set i = i + 1; until i >= 5 end repeat; END // delimiter ;
loop
BEGIN declare i int default 0; loop_label: loop set i=i+1; if i<8 then iterate loop_label; end if; if i>=10 then leave loop_label; end if; select i; end loop loop_label; END
mysql八:ORM框架SQLAchemy
SQLAlchemy是Python編程語言下的一款ORM框架,該框架創建在數據庫API之上,使用關係對象映射進行數據庫操做,簡言之即是:將對象轉換成SQL,而後使用數據API執行SQL並獲取執行結果。
一、安裝
pip3 install sqlalchemy
二、架構與流程
#一、使用者經過ORM對象提交命令 #二、將命令交給SQLAlchemy Core(Schema/Types SQL Expression Language)轉換成SQL #三、使用 Engine/ConnectionPooling/Dialect 進行數據庫操做 #3.一、匹配使用者事先配置好的egine #3.二、egine從鏈接池中取出一個連接 #3.三、基於該連接經過Dialect調用DB API,將SQL轉交給它去執行
!!!上述流程分析,能夠大體分爲兩個階段!!!:
#第一個階段(流程1-2):將SQLAlchemy的對象換成可執行的sql語句 #第二個階段(流程3):將sql語句交給數據庫執行
若是咱們不依賴於SQLAlchemy的轉換而本身寫好sql語句,那是否是意味着能夠直接從第二個階段開始執行了,事實上正是如此,咱們徹底能夠只用SQLAlchemy執行純sql語句,以下
from sqlalchemy import create_engine #1 準備 # 須要事先安裝好pymysql # 須要事先建立好數據庫:create database db1 charset utf8; #2 建立引擎 egine=create_engine('mysql+pymysql://root@127.0.0.1/db1?charset=utf8') #3 執行sql # egine.execute('create table if not EXISTS t1(id int PRIMARY KEY auto_increment,name char(32));') # cur=egine.execute('insert into t1 values(%s,%s);',[(1,"egon1"),(2,"egon2"),(3,"egon3")]) #按位置傳值 # cur=egine.execute('insert into t1 values(%(id)s,%(name)s);',name='egon4',id=4) #按關鍵字傳值 #4 新插入行的自增id # print(cur.lastrowid) #5 查詢 cur=egine.execute('select * from t1') cur.fetchone() #獲取一行 cur.fetchmany(2) #獲取多行 cur.fetchall() #獲取全部行
三、DB API
SQLAlchemy自己沒法操做數據庫,其必須以來pymsql等第三方插件,Dialect用於和數據API進行交流,根據配置文件的不一樣調用不一樣的數據庫API,從而實現對數據庫的操做,如:
#一、MySQL-Python mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> #二、pymysql mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>] #三、MySQL-Connector mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname> #四、cx_Oracle oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
更多詳見:http://docs.sqlalchemy.org/en/latest/dialects/index.html
ORM中:
#類===>表 #對象==>表中的一行記錄
四張表:業務線,服務,用戶,角色,利用ORM建立出它們,並創建好它們直接的關係
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,DateTime,Enum,ForeignKey,UniqueConstraint,ForeignKeyConstraint,Index from sqlalchemy.orm import sessionmaker egine=create_engine('mysql+pymysql://root@127.0.0.1:3306/db1?charset=utf8',max_overflow=5) Base=declarative_base() #建立單表:業務線 class Business(Base): __tablename__='business' id=Column(Integer,primary_key=True,autoincrement=True) bname=Column(String(32),nullable=False,index=True) #多對一:多個服務能夠屬於一個業務線,多個業務線不能包含同一個服務 class Service(Base): __tablename__='service' id=Column(Integer,primary_key=True,autoincrement=True) sname=Column(String(32),nullable=False,index=True) ip=Column(String(15),nullable=False) port=Column(Integer,nullable=False) business_id=Column(Integer,ForeignKey('business.id')) __table_args__=( UniqueConstraint(ip,port,name='uix_ip_port'), Index('ix_id_sname',id,sname) ) #一對一:一種角色只能管理一條業務線,一條業務線只能被一種角色管理 class Role(Base): __tablename__='role' id=Column(Integer,primary_key=True,autoincrement=True) rname=Column(String(32),nullable=False,index=True) priv=Column(String(64),nullable=False) business_id=Column(Integer,ForeignKey('business.id'),unique=True) #多對多:多個用戶能夠是同一個role,多個role能夠包含同一個用戶 class Users(Base): __tablename__='users' id=Column(Integer,primary_key=True,autoincrement=True) uname=Column(String(32),nullable=False,index=True) class Users2Role(Base): __tablename__='users2role' id=Column(Integer,primary_key=True,autoincrement=True) uid=Column(Integer,ForeignKey('users.id')) rid=Column(Integer,ForeignKey('role.id')) __table_args__=( UniqueConstraint(uid,rid,name='uix_uid_rid'), ) def init_db(): Base.metadata.create_all(egine) def drop_db(): Base.metadata.drop_all(egine) if __name__ == '__main__': init_db()
注:設置外鍵的另外一種方式 ForeignKeyConstraint(['other_id'], ['othertable.other_id'])
表結構
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,ForeignKey from sqlalchemy.orm import sessionmaker egine=create_engine('mysql+pymysql://root@127.0.0.1:3306/db1?charset=utf8',max_overflow=5) Base=declarative_base() #多對一:假設多個員工能夠屬於一個部門,而多個部門不能有同一個員工(只有建立公司才把員工當駱駝用,一個員工身兼數職) class Dep(Base): __tablename__='dep' id=Column(Integer,primary_key=True,autoincrement=True) dname=Column(String(64),nullable=False,index=True) class Emp(Base): __tablename__='emp' id=Column(Integer,primary_key=True,autoincrement=True) ename=Column(String(32),nullable=False,index=True) dep_id=Column(Integer,ForeignKey('dep.id')) def init_db(): Base.metadata.create_all(egine) def drop_db(): Base.metadata.drop_all(egine) drop_db() init_db() Session=sessionmaker(bind=egine) session=Session()
增
#增 row_obj=Dep(dname='銷售') #按關鍵字傳參,無需指定id,因其是自增加的 session.add(row_obj) session.add_all([ Dep(dname='技術'), Dep(dname='運營'), Dep(dname='人事'), ]) session.commit()
刪
session.query(Dep).filter(Dep.id > 3).delete() session.commit()
改
#改 session.query(Dep).filter(Dep.id > 0).update({'dname':'哇哈哈'}) session.query(Dep).filter(Dep.id > 0).update({'dname':Dep.dname+'_SB'},synchronize_session=False) session.query(Dep).filter(Dep.id > 0).update({'id':Dep.id*100},synchronize_session='evaluate') session.commit()
查
#查全部,取全部字段 res=session.query(Dep).all() #for row in res:print(row.id,row.dname) #查全部,取指定字段 res=session.query(Dep.dname).order_by(Dep.id).all() #for row in res:print(row.dname) res=session.query(Dep.dname).first() print(res) # ('哇哈哈_SB',) #過濾查 res=session.query(Dep).filter(Dep.id > 1,Dep.id <1000) #逗號分隔,默認爲and print([(row.id,row.dname) for row in res])
一 準備表和數據
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,ForeignKey from sqlalchemy.orm import sessionmaker egine=create_engine('mysql+pymysql://root@127.0.0.1:3306/db1?charset=utf8',max_overflow=5) Base=declarative_base() #多對一:假設多個員工能夠屬於一個部門,而多個部門不能有同一個員工(只有建立公司才把員工當駱駝用,一個員工身兼數職) class Dep(Base): __tablename__='dep' id=Column(Integer,primary_key=True,autoincrement=True) dname=Column(String(64),nullable=False,index=True) class Emp(Base): __tablename__='emp' id=Column(Integer,primary_key=True,autoincrement=True) ename=Column(String(32),nullable=False,index=True) dep_id=Column(Integer,ForeignKey('dep.id')) def init_db(): Base.metadata.create_all(egine) def drop_db(): Base.metadata.drop_all(egine) drop_db() init_db() Session=sessionmaker(bind=egine) session=Session() # 準備數據 session.add_all([ Dep(dname='技術'), Dep(dname='銷售'), Dep(dname='運營'), Dep(dname='人事'), ]) session.add_all([ Emp(ename='林海峯',dep_id=1), Emp(ename='李傑',dep_id=1), Emp(ename='武配齊',dep_id=1), Emp(ename='元昊',dep_id=2), Emp(ename='李鋼彈',dep_id=3), Emp(ename='張二丫',dep_id=4), Emp(ename='李坦克',dep_id=2), Emp(ename='王大炮',dep_id=4), Emp(ename='牛榴彈',dep_id=3) ]) session.commit()
二 條件、通配符、limit、排序、分組、連表、組合
#1、條件 sql=session.query(Emp).filter_by(ename='林海峯') #filter_by只能傳參數:什麼等於什麼 res=sql.all() #sql語句的執行結果 res=session.query(Emp).filter(Emp.id>0,Emp.ename == '林海峯').all() #filter內傳的是表達式,逗號分隔,默認爲and, res=session.query(Emp).filter(Emp.id.between(1,3),Emp.ename == '林海峯').all() res=session.query(Emp).filter(Emp.id.in_([1,3,99,101]),Emp.ename == '林海峯').all() res=session.query(Emp).filter(~Emp.id.in_([1,3,99,101]),Emp.ename == '林海峯') #~表明取反,轉換成sql就是關鍵字not from sqlalchemy import and_,or_ res=session.query(Emp).filter(and_(Emp.id > 0,Emp.ename=='林海峯')).all() res=session.query(Emp).filter(or_(Emp.id < 2,Emp.ename=='功夫熊貓')).all() res=session.query(Emp).filter( or_( Emp.dep_id == 3, and_(Emp.id > 1,Emp.ename=='功夫熊貓'), Emp.ename != '' ) ).all() #2、通配符 res=session.query(Emp).filter(Emp.ename.like('%海_%')).all() res=session.query(Emp).filter(~Emp.ename.like('%海_%')).all() #3、limit res=session.query(Emp)[0:5:2] #4、排序 res=session.query(Emp).order_by(Emp.dep_id.desc()).all() res=session.query(Emp).order_by(Emp.dep_id.desc(),Emp.id.asc()).all() #5、分組 from sqlalchemy.sql import func res=session.query(Emp.dep_id).group_by(Emp.dep_id).all() res=session.query( func.max(Emp.dep_id), func.min(Emp.dep_id), func.sum(Emp.dep_id), func.avg(Emp.dep_id), func.count(Emp.dep_id), ).group_by(Emp.dep_id).all() res=session.query( Emp.dep_id, func.count(1), ).group_by(Emp.dep_id).having(func.count(1) > 2).all() #6、連表 #笛卡爾積 res=session.query(Emp,Dep).all() #select * from emp,dep; #where條件 res=session.query(Emp,Dep).filter(Emp.dep_id==Dep.id).all() # for row in res: # emp_tb=row[0] # dep_tb=row[1] # print(emp_tb.id,emp_tb.ename,dep_tb.id,dep_tb.dname) #內鏈接 res=session.query(Emp).join(Dep) #join默認爲內鏈接,SQLAlchemy會自動幫咱們經過foreign key字段去找關聯關係 #可是上述查詢的結果均爲Emp表的字段,這樣鏈表還有毛線意義,因而咱們修改成 res=session.query(Emp.id,Emp.ename,Emp.dep_id,Dep.dname).join(Dep).all() #左鏈接:isouter=True res=session.query(Emp.id,Emp.ename,Emp.dep_id,Dep.dname).join(Dep,isouter=True).all() #右鏈接:同左鏈接,只是把兩個表的位置換一下 #7、組合 q1=session.query(Emp.id,Emp.ename).filter(Emp.id > 0,Emp.id < 5) q2=session.query(Emp.id,Emp.ename).filter( or_( Emp.ename.like('%海%'), Emp.ename.like('%昊%'), ) ) res1=q1.union(q2) #組合+去重 res2=q1.union_all(q2) #組合,不去重 print([i.ename for i in q1.all()]) #['林海峯', '李傑', '武配齊', '元昊'] print([i.ename for i in q2.all()]) #['林海峯', '元昊'] print([i.ename for i in res1.all()]) #['林海峯', '李傑', '武配齊', '元昊'] print([i.ename for i in res2.all()]) #['林海峯', '李傑', '武配齊', '元昊', '元昊', '林海峯']
三 子查詢
有三種形式的子查詢,注意:子查詢的sql必須用括號包起來,尤爲在形式三中須要注意這一點
形式一:子查詢當作一張表來用,調用subquery()
#示例:查出id大於2的員工,當作子查詢的表使用 #原生SQL: # select * from (select * from emp where id > 2); #ORM: res=session.query( session.query(Emp).filter(Emp.id > 8).subquery() ).all()
形式二:子查詢當作in的範圍用,調用in_
#示例:#查出銷售部門的員工姓名 #原生SQL: # select ename from emp where dep_id in (select id from dep where dname='銷售'); #ORM: res=session.query(Emp.ename).filter(Emp.dep_id.in_( session.query(Dep.id).filter_by(dname='銷售'), #傳的是參數 # session.query(Dep.id).filter(Dep.dname=='銷售') #傳的是表達式 )).all()
形式三:子查詢當作select後的字段,調用as_scalar()
#示例:查詢全部的員工姓名與部門名 #原生SQL: # select ename as 員工姓名,(select dname from dep where id = emp.dep_id) as 部門名 from emp; #ORM: sub_sql=session.query(Dep.dname).filter(Dep.id==Emp.dep_id) #SELECT dep.dname FROM dep, emp WHERE dep.id = emp.dep_id sub_sql.as_scalar() #as_scalar的功能就是把上面的sub_sql加上了括號 res=session.query(Emp.ename,sub_sql.as_scalar()).all()
一 表修改
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,ForeignKey from sqlalchemy.orm import sessionmaker,relationship egine=create_engine('mysql+pymysql://root@127.0.0.1:3306/db1?charset=utf8',max_overflow=5) Base=declarative_base() class Dep(Base): __tablename__='dep' id=Column(Integer,primary_key=True,autoincrement=True) dname=Column(String(64),nullable=False,index=True) class Emp(Base): __tablename__='emp' id=Column(Integer,primary_key=True,autoincrement=True) ename=Column(String(32),nullable=False,index=True) dep_id=Column(Integer,ForeignKey('dep.id')) #在ForeignKey所在的類內添加relationship的字段,注意: #1:Dep是類名 #2:depart字段不會再數據庫表中生成字段 #3:depart用於Emp表查詢Dep表(正向查詢),而xxoo用於Dep表查詢Emp表(反向查詢), depart=relationship('Dep',backref='xxoo') def init_db(): Base.metadata.create_all(egine) def drop_db(): Base.metadata.drop_all(egine) drop_db() init_db() Session=sessionmaker(bind=egine) session=Session() # 準備數據 session.add_all([ Dep(dname='技術'), Dep(dname='銷售'), Dep(dname='運營'), Dep(dname='人事'), ]) session.add_all([ Emp(ename='林海峯',dep_id=1), Emp(ename='李傑',dep_id=1), Emp(ename='武配齊',dep_id=1), Emp(ename='元昊',dep_id=2), Emp(ename='李鋼彈',dep_id=3), Emp(ename='張二丫',dep_id=4), Emp(ename='李坦克',dep_id=2), Emp(ename='王大炮',dep_id=4), Emp(ename='牛榴彈',dep_id=3) ]) session.commit()
二 標準連表查詢
# 示例:查詢員工名與其部門名 res=session.query(Emp.ename,Dep.dname).join(Dep) #迭代器 for row in res: print(row[0],row[1]) #等同於print(row.ename,row.dname)
三 基於relationship的正查、反查
#SQLAlchemy的relationship在內部幫咱們作好表的連接 #查詢員工名與其部門名(正向查) res=session.query(Emp) for row in res: print(row.ename,row.id,row.depart.dname) #查詢部門名以及該部門下的員工(反向查) res=session.query(Dep) for row in res: # print(row.dname,row.xxoo) print(row.dname,[r.ename for r in row.xxoo])