第八章| 3. MyAQL資料庫|Navicat工具與pymysql模組
1、Navicat工具與pymysql模組
在生產環境中操作MySQL資料庫還是推薦使用命令列工具mysql,但在我們自己開發測試時,可以使用視覺化工具Navicat,以圖形介面的形式操作MySQL資料庫
掌握: #1. 測試+連結資料庫 #2. 新建庫 #3. 新建表,新增欄位+型別+約束 #4. 設計表:外來鍵 #5. 新建查詢 #6. 備份庫/表 #注意: 批量加註釋:ctrl+?鍵 批量去註釋:ctrl+shift+?鍵
之前我們都是通過MySQL自帶的命令列客戶端工具mysql來操作資料庫,那如何在python程式中操作資料庫呢?這就用到了pymysql模組,該模組本質就是一個套接字客戶端軟體,使用前需要事先安裝 pip3 install pymysql
import pymysql user=input('user>>: ').strip() pwd=input('password>>: ').strip() # 建立連結 conn=pymysql.connect( host='192.168.1.123', port=3306, user='root', password='123', db='db10', charset='utf8' ) # 拿到遊標 cursor=conn.cursor() # 執行sql語句 # sql='select * from userinfo where user = "%s" and pwd="%s"' %(user,pwd)View Code# print(sql) sql='select * from userinfo where user = %s and pwd=%s' rows=cursor.execute(sql,(user,pwd)) #提交給遊標執行 execute這個介面拿到的是2 rows in set (0.00 sec) 2那個行數,如果值不為0說明就輸對了 cursor.close() conn.close() # 進行判斷 if rows: print('登入成功') else: print('登入失敗')
mysql語句中 -- xfjl ,-- + 空格後邊的都給你註釋掉了
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('登入失敗') 列印: #兩種執行的錯誤 user>>: egon" -- xxxhhhh password>>: select * from userinfo where user = "egon" -- xxxhhhh" and pwd="" 登入成功 user>>: xxxx" or 1=1 -- hhhhhaaa password>>: select * from userinfo where user = "xxxx" or 1=1 -- hhhhhaaa" and pwd="" 登入成功View Code
改正:
import pymysql user=input('user>>: ').strip() pwd=input('password>>: ').strip() # 建立連結 conn=pymysql.connect( host='192.168.1.123', port=3306, user='root', password='123', db='db10', charset='utf8' ) # 拿到遊標 cursor=conn.cursor() # 執行sql語句 # sql='select * from userinfo where user = "%s" and pwd="%s"' %(user,pwd) # print(sql) # rows=cursor.execute(sql) sql='select * from userinfo where user = %s and pwd=%s' #由execute作為拼接,不用你自己去拼接了,在拼接過程中給你過濾掉這種非法操作 rows=cursor.execute(sql,(user,pwd)) #提交給遊標執行 execute這個介面拿到的是2 rows in set (0.00 sec) 2那個行數,如果值不為0說明就輸對了 cursor.close() conn.close() # 進行判斷 if rows: print('登入成功') else: print('登入失敗')View Code
增加
import pymysql # 建立連結 conn=pymysql.connect( host='192.168.1.123', port=3306, user='root', password='123', db='db10', charset='utf8' ) # 拿遊標 cursor=conn.cursor() # 執行sql # 增、刪、改 對資料的變動 sql='insert into userinfo(user,pwd) values(%s,%s)' rows=cursor.execute(sql,('wxx','123')) print(rows) conn.commit() #必須加上這個 # 關閉 cursor.close() conn.close() 列印:View Code
#1、增刪改 import pymysql # 建立連結 conn=pymysql.connect( host='192.168.1.123', port=3306, user='root', password='123', db='db10', charset='utf8' ) # 拿遊標 cursor=conn.cursor() # 執行sql # 增、刪、改 對資料的變動 sql='insert into userinfo(user,pwd) values(%s,%s)' # rows=cursor.execute(sql,('wxx','123')) # print(rows) rows=cursor.executemany(sql,[('yxx','123'),('egon1','111'),('egon2','2222')]) #可以插入多條 print(rows) conn.commit() #必須加上這個 # 關閉 cursor.close() conn.close() 列印View Code
#2、查詢 import pymysql # # 建立連結 conn=pymysql.connect( host='192.168.1.123', port=3306, user='root', password='123', db='db10', charset='utf8' ) # 拿遊標 cursor=conn.cursor(pymysql.cursors.DictCursor) #基於字典形式的遊標,不加括號內的是以元組形式 # 執行sql # 查詢 rows=cursor.execute('select * from userinfo;') #把字串send給服務端,在服務端把這個sql語句執行下,然後把結果丟給客戶端 print(rows) # print(cursor.fetchone()) #代表取一行 # print(cursor.fetchone()) # print(cursor.fetchone()) # print(cursor.fetchone()) # print(cursor.fetchone()) # print(cursor.fetchone()) # print(cursor.fetchone()) #print(cursor.fetchmany(2)) #指定取的個數,以列表的形式 #print(cursor.fetchall()) #拿出所有,列表的形式 # print(cursor.fetchall()) cursor.scroll(3,mode='absolute') #移動游標 相對絕對位置移動,從頭數3個開始取出來1個,結果是第4個 #print(cursor.fetchone()) cursor.scroll(2,mode='relative') # 相對當前位置移動 往後跳2位 print(cursor.fetchone()) # 關閉 cursor.close() conn.close() 列印: {'id': 6, 'user': 'egon2', 'pwd': '2222'}View Code
import pymysql # # # 建立連結 conn=pymysql.connect( host='192.168.1.123', port=3306, user='root', password='123', db='db10', charset='utf8' ) # # # 拿遊標 cursor=conn.cursor() sql='insert into userinfo(user,pwd) values(%s,%s)' rows=cursor.executemany(sql,[('egon3','123'),('egon4','111'),('egon5','2222')]) print(cursor.lastrowid) #插入之前游標走到哪裡了 # conn.commit() #必須加上這個 # 關閉 cursor.close() conn.close() 列印:View Code
涉及資料庫的操作的,先要編寫好資料,然後基於pymysql模組幫我把sql語句提交給mysql服戶端,執行完之後把結果再返回到應用程式中再做進一步的處理;將應用程式的開發與資料庫的開發結合
2、mysql內建函式功能
檢視是一個虛擬表(非真實存在),其本質是【根據SQL語句獲取動態的資料集,併為其命名】,使用者使用時只需使用【名稱】即可獲取結果集,可以將該結果集當做表來使用。
使用檢視我們可以把查詢過程中的臨時表摘出來,用檢視去實現,這樣以後再想操作該臨時表的資料時就無需重寫複雜的sql了,直接去檢視中查詢即可,但檢視有明顯地效率問題,並且檢視是存放在資料庫中的,如果我們程式中使用的sql過分依賴資料庫中的檢視,即強耦合,那就意味著擴充套件sql極為不便,因此並不推薦使用
檢視 只有表結構frm,沒有資料,因為是查出來的虛擬表;不用重複寫,但是不建議使用,如果有好多個檢視,要找到所有的檢視給它修改很麻煩;
mysql> use db7; Database changed mysql> select * from course; +-----+--------+------------+ | cid | cname | teacher_id | +-----+--------+------------+ | 1 | 生物 | 1 | | 2 | 物理 | 2 | | 3 | 體育 | 3 | | 4 | 美術 | 2 | +-----+--------+------------+ rows in set (0.00 sec) mysql> select * from teacher; +-----+-----------------+ | tid | tname | +-----+-----------------+ | 1 | 張磊老師 | | 2 | 李平老師 | | 3 | 劉海燕老師 | | 4 | 朱雲海老師 | | 5 | alex | +-----+-----------------+ rows in set (0.00 sec) mysql> select * from course inner join teacher on course.teacher_id=teacher.tid; +-----+--------+------------+-----+-----------------+ | cid | cname | teacher_id | tid | tname | +-----+--------+------------+-----+-----------------+ | 1 | 生物 | 1 | 1 | 張磊老師 | | 2 | 物理 | 2 | 2 | 李平老師 | | 4 | 美術 | 2 | 2 | 李平老師 | | 3 | 體育 | 3 | 3 | 劉海燕老師 | +-----+--------+------------+-----+-----------------+ rows in set (0.10 sec) mysql> create view course2teacher as select * from course inner join teacher on course.teacher_id=teacher.tid; Query OK, 0 rows affected (0.20 sec) mysql> show tables; +----------------+ | Tables_in_db7 | +----------------+ | course | | course2teacher | | teacher | +----------------+ rows in set (0.00 sec) mysql>View Code
觸發器
使用觸發器可以定製使用者對錶進行【增、刪、改】操作時前後的行為,注意:沒有查詢
mysql> create database db11; Query OK, 1 row affected (0.00 sec) mysql> use db11; Database changed mysql> 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代表執行失敗 -> ); Query OK, 0 rows affected (0.77 sec) mysql> mysql> CREATE TABLE errlog ( -> id INT PRIMARY KEY auto_increment, -> err_cmd CHAR (64), -> err_time datetime -> ); Query OK, 0 rows affected (0.68 sec) #建立觸發器 mysql> delimiter // mysql> 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// Query OK, 0 rows affected (0.34 sec) mysql> delimiter ; mysql> mysql> mysql>#往cmd中插入記錄,觸動觸發器,根據IF條件決定是否插入錯誤日誌 mysql> 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'); Query OK, 4 rows affected (0.43 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> select * from cmd; +----+------+------+-----------------+---------------------+---------+ | id | USER | priv | cmd | sub_time | success | +----+------+------+-----------------+---------------------+---------+ | 1 | egon | 0755 | ls -l /etc | 2018-04-28 18:26:33 | yes | | 2 | egon | 0755 | cat /etc/passwd | 2018-04-28 18:26:33 | no | | 3 | egon | 0755 | useradd xxx | 2018-04-28 18:26:33 | no | | 4 | egon | 0755 | ps aux | 2018-04-28 18:26:33 | yes | +----+------+------+-----------------+---------------------+---------+ rows in set (0.00 sec) mysql> select * from errlog; #查詢錯誤日誌發現有兩條 +----+-----------------+---------------------+ | id | err_cmd | err_time | +----+-----------------+---------------------+ | 1 | cat /etc/passwd | 2018-04-28 18:26:33 | | 2 | useradd xxx | 2018-04-28 18:26:33 | +----+-----------------+---------------------+ rows in set (0.00 sec)View Code
儲存過程
把mysql處理好的資料給封裝好,一個介面名,應用程式可以直接呼叫介面,這個介面就叫儲存過程。是mysql內建功能的一系列總和;
儲存過程包含了一系列可執行的sql語句,儲存過程存放於MySQL中,通過呼叫它的名字可以執行其內部的一堆sql
使用儲存過程的優點:
1. 用於替代程式寫的SQL語句,實現程式與sql解耦
2. 基於網路傳輸,傳別名的資料量小,而直接傳sql資料量大
使用儲存過程的缺點:
1. 程式設計師擴充套件功能不方便
程式與資料庫結合使用的三種方式:
建立簡單的儲存過程:
#1、無參儲存過程 delimiter // create procedure p1() BEGIN select * from db7.teacher; END // delimiter ;
mysql> use db7; Database changed mysql> delimiter // mysql> create procedure p1() -> BEGIN -> select * from db7.teacher; -> END // Query OK, 0 rows affected (0.21 sec) mysql> delimiter ; mysql> mysql> mysql> show create procedure p1; +-----------+--------------------------------------------+-----------------------------------------------------------------------------------------+----------------------+----------------------+------ --------------+ | Procedure | sql_mode | Create Procedure | character_set_client | collation_connection | Datab ase Collation | +-----------+--------------------------------------------+-----------------------------------------------------------------------------------------+----------------------+----------------------+------ --------------+ | p1 | STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION | CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`() BEGIN select * from db7.teacher; END | utf8 | utf8_general_ci | utf8_general_ci | +-----------+--------------------------------------------+-----------------------------------------------------------------------------------------+----------------------+----------------------+------ --------------+ row in set (0.00 sec)View Code
# MySQL中呼叫 call p1();
mysql> call p1(); +-----+-----------------+ | tid | tname | +-----+-----------------+ | 1 | 張磊老師 | | 2 | 李平老師 | | 3 | 劉海燕老師 | | 4 | 朱雲海老師 | | 5 | alex | +-----+-----------------+ rows in set (0.00 sec) Query OK, 0 rows affected (0.01 sec)View Code
# Python中呼叫
import pymysql # 建立連結 conn=pymysql.connect( host='192.168.1.123', port=3306, user='root', password='123', db='db7', charset='utf8' ) # 拿遊標 cursor=conn.cursor() # 執行sql cursor.callproc('p1') #只是執行 print(cursor.fetchall()) 列印 ((1, '張磊老師'), (2, '李平老師'), (3, '劉海燕老師'), (4, '朱雲海老師'), (5, 'alex'))View Code
#2、有參儲存過程 #在mysql中引數必須指定型別,是用來接收值的還是返回值的
對於儲存過程,可以接收引數,其引數有三類: #in 僅用於傳入引數用 #out 僅用於返回值用 #inout 既可以傳入又可以當作返回值
delimiter // create procedure p2(in n1 int,in n2 int,out res int) #當儲存過程p1執行完了,就把res當做返回值返回了這就是out的作用,只有out的值才能被返回 BEGIN select * from db7.teacher where tid > n1 and tid < n2; set res = 1; END // delimiter ; # MySQL中呼叫 set @x=0 #初始值等於零 call p2(2,4,@x); select @x;檢視結果 # Python中呼叫 cursor.callproc('p2',(2,4,0))# @_p2_0=2,@_p2_1=4,@_p2_2=0 cursor.execute('select @_p3_2') cursor.fetchone()
mysql> delimiter // mysql> create procedure p2(in n1 int, in n2 int, out res int) -> BEGIN -> select * from db7.teacher where tid > n1 and tid < n2; -> set res = 1; -> END // Query OK, 0 rows affected (0.06 sec) mysql> delimiter ; mysql> set @x=0; Query OK, 0 rows affected (0.00 sec) mysql> call p2(2,4,@x); +-----+-----------------+ | tid | tname | +-----+-----------------+ | 3 | 劉海燕老師 | +-----+-----------------+ row in set (0.00 sec) Query OK, 0 rows affected (0.01 sec) mysql> select @x; +------+ | @x | +------+ | 1 | +------+ row in set (0.00 sec)View Code
import pymysql # 建立連結 conn=pymysql.connect( host='192.168.1.123', port=3306, user='root', password='123', db='db7', charset='utf8' ) # 拿遊標 cursor=conn.cursor() # cursor.execute('select @_p2_2') print(cursor.fetchone()) # 關閉 cursor.close() conn.close() 列印 (1, )View Code
應用程式與資料庫結合使用 方式一: (很少用,部門之間溝通效率不高;優點很好解開了耦合,效率最高) Python:呼叫儲存過程 MySQL:編寫儲存過程 方式二: Python:編寫純生SQL (可維護性好,都是開發人員寫的,) MySQL: 方式三: Python:ORM->純生SQL (開發效率高,可維護性高,用類,ORM框架) MySQL:
事務
同時成功同時失敗;
事務用於將某些操作的多個SQL作為原子性操作,一旦有某一個出現錯誤,即可回滾到原來的狀態,從而保證資料庫資料完整性。
mysql> create table user( -> id int primary key auto_increment, -> name char(32), -> balance int -> ); Query OK, 0 rows affected (1.98 sec) mysql> mysql> insert into user(name,balance) -> values -> ('wsb',1000), -> ('egon',1000), -> ('ysb',1000); Query OK, 3 rows affected (0.50 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> select * from user; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | wsb | 1000 | | 2 | egon | 1000 | | 3 | ysb | 1000 | +----+------+---------+ rows in set (0.00 sec) #原子操作 mysql> start transaction; Query OK, 0 rows affected (0.05 sec) mysql> update user set balance=900 where name='wsb'; #買支付100元 Query OK, 1 row affected (0.11 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> update user set balance=1010 where name='egon'; #中介拿走10元 Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> update user set balance=1090 where name='ysb'; #賣家拿到90元 Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from user; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | wsb | 900 | | 2 | egon | 1010 | | 3 | ysb | 1090 | +----+------+---------+ rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.16 sec) mysql> select * from user; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | wsb | 900 | | 2 | egon | 1010 | | 3 | ysb | 1090 | +----+------+---------+ rows in set (0.00 sec) mysql> rollback; #出現異常就回滾到初始狀態 Query OK, 0 rows affected (0.00 sec) mysql> select * from user; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | wsb | 900 | | 2 | egon | 1010 | | 3 | ysb | 1090 | +----+------+---------+ rows in set (0.00 sec)View Code
函式與流程控制
函式
一、數學函式 ROUND(x,y) 返回引數x的四捨五入的有y位小數的值 RAND() 返回0到1內的隨機值,可以通過提供一個引數(種子)使RAND()隨機數生成器生成一個指定的值。 二、聚合函式(常用於GROUP BY從句的SELECT查詢中) AVG(col)返回指定列的平均值 COUNT(col)返回指定列中非NULL值的個數 MIN(col)返回指定列的最小值 MAX(col)返回指定列的最大值 SUM(col)返回指定列的所有值之和 GROUP_CONCAT(col) 返回由屬於一組的列值連線組合而成的結果 三、字串函式 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' 四、日期和時間函式 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' 五、加密函式 MD5() 計算字串str的MD5校驗和 PASSWORD(str) 返回字串str的加密版本,這個加密過程是不可逆轉的,和UNIX密碼加密過程使用不同的演算法。 六、控制流函式 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;否則返回arg1View Code
mysql> CREATE TABLE blog ( -> id INT PRIMARY KEY auto_increment, -> NAME CHAR (32), -> sub_time datetime -> ); Query OK, 0 rows affected (0.78 sec) mysql> 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'); Query OK, 9 rows affected (0.14 sec) Records: 9 Duplicates: 0 Warnings: 0 mysql> 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 | +-------------------------------+----------+ rows in set (0.22 sec) mysql>View Code
流程控制
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 ; if條件語句View Code
3、索引原理
索引在MySQL中也叫做“鍵”,是儲存引擎用於快速找到記錄的一種資料結構。
索引相當於字典的音序表,如果要查某個字,如果不使用音序表,則需要從幾百頁中逐頁去查。
若索引太多,應用程式的效能可能會受到影響。而索引太少,對查詢效能又會產生影響,要找到一個平衡點,這對應用程式的效能至關重要。
索引的目的在於提高查詢效率,與我們查閱圖書所用的目錄是一個道理:先定位到章,然後定位到該章下的一個小節,然後找到頁數。相似的例子還有:查字典,查火車車次,飛機航班等
本質都是:通過不斷地縮小想要獲取資料的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是說,有了這種索引機制,我們可以總是用同一種查詢方式來鎖定資料。
資料庫複雜的多,因為不僅面臨著等值查詢,還有範圍查詢(>、<、between、in)、模糊查詢(like)、並集查詢(or)等等。資料庫應該選擇怎麼樣的方式來應對所有的問題呢?我們回想字典的例子,能不能把資料分成段,然後分段查詢呢?最簡單的如果1000條資料,1到100分成第一段,101到200分成第二段,201到300分成第三段......這樣查第250條資料,只要找第三段就可以了,一下子去除了90%的無效資料。但如果是1千萬的記錄呢,分成幾段比較好?稍有演算法基礎的同學會想到搜尋樹,其平均複雜度是lgN,具有不錯的查詢效能。但這裡我們忽略了一個關鍵的問題,複雜度模型是基於每次相同的操作成本來考慮的。而資料庫實現比較複雜,一方面資料是儲存在磁碟上的,另外一方面為了提高效能,每次又可以把部分資料讀入記憶體來計算,因為我們知道訪問磁碟的成本大概是訪問記憶體的十萬倍左右,所以簡單的搜尋樹難以滿足複雜的應用場景。
當一次IO時,不光把當前磁碟地址的資料,而是把相鄰的資料也都讀取到記憶體緩衝區內,因為區域性預讀性原理告訴我們,當計算機訪問一個地址的資料的時候,與其相鄰的資料也會很快被訪問到。每一次IO讀取的資料我們稱之為一頁(page)。
每次查詢資料時把磁碟IO次數控制在一個很小的數量級,最好是常數數量級。那麼我們就想到如果一個高度可控的多路搜尋樹是否能滿足需求呢?就這樣,b+樹應運而生(B+樹是通過二叉查詢樹,再由平衡二叉樹,B樹演化而來)。
b+樹性質 1.索引欄位要儘量的小;2.索引的最左匹配特性
這種資料結構減少I/O次數
真實資料只存在葉子節點的磁碟塊的資料項;樹杈節點的資料象就是為了建資料結構而虛擬出的;
I/O固定在固定範圍,3次,它的高度決定的。
InnoDB儲存引擎表示索引組織表,即表中資料按照主鍵順序存放,建表的時候一定要建個主鍵,它會在你這個表裡邊找主鍵;
聚集索引與輔助索引
資料庫中的B+樹索引可以分為聚集索引(clustered index)和輔助索引(secondary index),
聚集索引與輔助索引相同的是:不管是聚集索引還是輔助索引,其內部都是B+樹的形式,即高度是平衡的,葉子結點存放著所有的資料。
聚集索引與輔助索引不同的是:葉子結點存放的是否是一整行的資訊