1. 程式人生 > >【Mysql知識補充】

【Mysql知識補充】

問題: 無需 mysql 數據結構 目的 opened 建表 觸發器 年齡

一、子查詢

1.定義

子查詢是將一個查詢語句嵌套在另一個查詢語句中。內層查詢語句的查詢結果,可以為外層查詢語句提供查詢條件。子查詢中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等關鍵字,還可以包含比較運算符:= 、 !=、> 、<等。

2.舉例

  • 帶IN關鍵字的子查詢
#查看技術部員工姓名
select name from employee
    where dep_id in 
        (select id from department where name=技術);

  • 帶比較運算符的子查詢
#比較運算符:=
!=>>=<<=<> #查詢大於所有人平均年齡的員工名與年齡

select name,age from emp where age > (select avg(age) from emp);

  • 帶EXISTS關鍵字的子查詢

EXISTS關字鍵字表示存在。在使用EXISTS關鍵字時,內層查詢語句不返回查詢的記錄,而是返回一個真假值:True或False;

當返回True時,外層查詢語句將進行查詢;當返回值為False時,外層查詢語句不進行查詢。

#department表中存在dept_id=205,False
mysql> select
* from employee -> where exists -> (select id from department where id=204);

查詢每個部門最新入職的那位員工
技術分享圖片
company.employee
    員工id      id                  int             
    姓名        emp_name            varchar
    性別        sex                 enum
    年齡        age                 
int 入職日期 hire_date date 崗位 post varchar 職位描述 post_comment varchar 薪水 salary double 辦公室 office int 部門編號 depart_id int #創建表 create table employee( id int not null unique auto_increment, name varchar(20) not null, sex enum(male,female) not null default male, #大部分是男的 age int(3) unsigned not null default 28, hire_date date not null, post varchar(50), post_comment varchar(100), salary double(15,2), office int, #一個部門一個屋子 depart_id int ); #查看表結構 mysql> desc employee; +--------------+-----------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------------+-----------------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(20) | NO | | NULL | | | sex | enum(male,female) | NO | | male | | | age | int(3) unsigned | NO | | 28 | | | hire_date | date | NO | | NULL | | | post | varchar(50) | YES | | NULL | | | post_comment | varchar(100) | YES | | NULL | | | salary | double(15,2) | YES | | NULL | | | office | int(11) | YES | | NULL | | | depart_id | int(11) | YES | | NULL | | +--------------+-----------------------+------+-----+---------+----------------+ #插入記錄 #三個部門:教學,銷售,運營 insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values (egon,male,18,20170301,老男孩駐沙河辦事處外交大使,7300.33,401,1), #以下是教學部 (alex,male,78,20150302,teacher,1000000.31,401,1), (wupeiqi,male,81,20130305,teacher,8300,401,1), (yuanhao,male,73,20140701,teacher,3500,401,1), (liwenzhou,male,28,20121101,teacher,2100,401,1), (jingliyang,female,18,20110211,teacher,9000,401,1), (jinxin,male,18,19000301,teacher,30000,401,1), (成龍,male,48,20101111,teacher,10000,401,1), (歪歪,female,48,20150311,sale,3000.13,402,2),#以下是銷售部門 (丫丫,female,38,20101101,sale,2000.35,402,2), (丁丁,female,18,20110312,sale,1000.37,402,2), (星星,female,18,20160513,sale,3000.29,402,2), (格格,female,28,20170127,sale,4000.33,402,2), (張野,male,28,20160311,operation,10000.13,403,3), #以下是運營部門 (程咬金,male,18,19970312,operation,20000,403,3), (程咬銀,female,18,20130311,operation,19000,403,3), (程咬銅,male,18,20150411,operation,18000,403,3), (程咬鐵,female,18,20140512,operation,17000,403,3) ;
創建表 技術分享圖片
alter table employee rename emp;#修改表名

SELECT
    *
FROM
    emp AS t1
INNER JOIN (
    SELECT
        post,
        max(hire_date) max_date
    FROM
        emp
    GROUP BY
        post
) AS t2 ON t1.post = t2.post
WHERE
    t1.hire_date = t2.max_date;
聯表查詢

補充:

SELECT語句關鍵字的執行順序

(7)     SELECT 
(8)     DISTINCT <select_list>
(1)     FROM <left_table>
(3)     <join_type> JOIN <right_table>
(2)     ON <join_condition>
(4)     WHERE <where_condition>
(5)     GROUP BY <group_by_list>
(6)     HAVING <having_condition>
(9)     ORDER BY <order_by_condition>
(10)    LIMIT <limit_number>

二、索引

1.定義

索引在MySQL中也叫做“鍵”,是存儲引擎用於快速找到記錄的一種數據結構。

索引相當於字典的音序表,如果要查某個字,如果不使用音序表,則需要從幾百頁中逐頁去查。索引優化應該是對查詢性能優化最有效的手段了。索引能夠輕易將查詢性能提高好幾個數量級。

2.原理

索引的目的在於提高查詢效率,與我們查閱圖書所用的目錄是一個道理:先定位到章,然後定位到該章下的一個小節,然後找到頁數。相似的例子還有:查字典,查火車車次,飛機航班等。

本質都是:通過不斷地縮小想要獲取數據的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是說,有了這種索引機制,我們可以總是用同一種查找方式來鎖定數據。

3.索引的數據結構

  • B+樹

技術分享圖片

如上圖,是一顆b+樹,關於b+樹的定義可以參見B+樹,這裏只說一些重點,淺藍色的塊我們稱之為一個磁盤塊,可以看到每個磁盤塊包含幾個數據項(深藍色所示)和指針(黃色所示),如磁盤塊1包含數據項17和35,包含指針P1、P2、P3,P1表示小於17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大於35的磁盤塊。真實的數據存在於葉子節點即3、591013152829366075799099。非葉子節點不存儲真實的數據,只存儲指引搜索方向的數據項,如17、35並不真實存在於數據表中。

  • b+樹的查找過程
如圖所示,如果要查找數據項29,那麽首先會把磁盤塊1由磁盤加載到內存,此時發生一次IO,在內存中用二分查找確定29在17和35之間,鎖定磁盤塊1的P2指針,內存時間因為非常短(相比磁盤的IO)可以忽略不計,通過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內存,發生第二次IO,29在26和30之間,鎖定磁盤塊3的P2指針,通過指針加載磁盤塊8到內存,發生第三次IO,同時內存中做二分查找找到29,結束查詢,總計三次IO。真實的情況是,3層的b+樹可以表示上百萬的數據,如果上百萬的數據查找只需要三次IO,性能提高將是巨大的,如果沒有索引,每個數據項都要發生一次IO,那麽總共需要百萬次的IO,顯然成本非常非常高。

三、視圖

1、定義

視圖是一個虛擬表(非真實存在),其本質是【根據SQL語句獲取動態的數據集,並為其命名】,用戶使用時只需使用【名稱】即可獲取結果集,可以將該結果集當做表來使用。

使用視圖我們可以把查詢過程中的臨時表摘出來,用視圖去實現,這樣以後再想操作該臨時表的數據時就無需重寫復雜的sql了,直接去視圖中查找即可,但視圖有明顯地效率問題,並且視圖是存放在數據庫中的,如果我們程序中使用的sql過分依賴數據庫中的視圖,即強耦合,那就意味著擴展sql極為不便,因此並不推薦使用

2、創建視圖

#語法: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  |
+--------+
| 物理   |
| 美術   |
+--------+
rows in set (0.00 sec)

註意:
#1. 使用視圖以後就無需每次都重寫子查詢的sql,但是這麽效率並不高,還不如我們寫子查詢的效率高

#2. 而且有一個致命的問題:視圖是存放到數據庫裏的,如果我們程序中的sql過分依賴於數據庫中存放的視圖,那麽意味著,一旦sql需要修改且涉及到視圖的部分,則必須去數據庫中進行修改,而通常在公司中數據庫有專門的DBA負責,你要想完成修改,必須付出大量的溝通成本DBA可能才會幫你完成修改,極其地不方便

3、修改視圖

語法:ALTER VIEW 視圖名稱 AS SQL語句
mysql> alter view teacher_view as select * from course where cid>3;

4、刪除視圖

語法:DROP VIEW 視圖名稱

DROP VIEW teacher_view

四、觸發器

使用觸發器可以定制用戶對表進行【增、刪、改】操作時前後的行為,註意:沒有查詢

1、創建觸發器

技術分享圖片
# 插入前
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
創建觸發器

NEW表示即將插入的數據行,OLD表示即將刪除的數據行。

#準備表
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 | +----+-----------------+---------------------+ rows in set (0.00 sec)

2、刪除觸發器

drop trigger tri_after_insert_cmd;

五、事務

事務用於將某些操作的多個SQL作為原子性操作,一旦有某一個出現錯誤,即可回滾到原來的狀態,從而保證數據庫數據完整性。

START TRANSACTION或BEGIN語句可以開始一項新的事務。

COMMIT可以提交當前事務,是變更成為永久變更。

ROLLBACK可以 回滾當前事務,取消其變更。

SET AUTOCOMMIT語句可以禁用或啟用默認的autocommit模式,用於當前連接。

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 |
+----+------+---------+
rows in set (0.00 sec)

六、存儲過程

存儲過程包含了一系列可執行的sql語句,存儲過程存放於MySQL中,通過調用它的名字可以執行其內部的一堆sql

使用存儲過程的優點:

#1. 用於替代程序寫的SQL語句,實現程序與sql解耦

#2. 基於網絡傳輸,傳別名的數據量小,而直接傳sql數據量大

使用存儲過程的缺點:

#1. 程序員擴展功能不方便

  • 創建存儲過程
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())

七、自定義函數

函數中不要寫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 ;

  • 執行函數
# 獲取返回值
select UPPER(egon) into @res;
SELECT @res;


# 在查詢中使用
select f1(11,nid) ,name from tb2;

八、流程控制

1、條件語句

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 ;


2、循環語句

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 ;


【Mysql知識補充】