一.索引
索引是查詢優化最有效和最常用的技術
索引是對數據庫表中一個列或多個列進行排序的結構。
索引是一個單獨的、物理的數據庫結構,它是指向表中某一列或若干列上的指針列表。
mysql中,一個表的物理存儲由兩部分組成,一部分用於存放表的數據,另一部分存放索引,當進行數據搜索時,mysql會首先搜索索引,從中找到所需數據的起始位置的指針,再直接通過指針查找目標數據。
1.創建索引:
CREATE INDEX 索引名 on 表名(要添加索引的列名)
可以給一個表中的多個列添加索引
通過在查詢sql語句前加一句Explain可以分析索引效率,
有這樣一張表:
執行EXPLAIN SELECT*FROM t_product WHERE productName ='電視機'
同一條sql查詢語句,在沒建立索引時,可以看到掃描了9行
CREATE INDEX index_pname ON t_product(productName)
在建立索引之后,再次執行相同查詢語句,發現只掃描了1行便得到了結果
效果如下圖
2.刪除索引:
Alter table 表名 drop index 索引名
如何選擇索引列:
1.Where子句中常出現的列
select id from t_student where sName=’張三’
2.在join子句中常出現的列
select s.*,g.grade from t_student s join t_grade g on s.id=g.studentId
3.頻繁進行排序或者分組的列
select s.*,g.grade from t_student s join t_grade g on s.id=g.studentId group by s.name order by g.grade
like使用索引列,like比較特別,mysql對其索引的情況是:操作數不以通配符開頭。
select*from t_student where sName like ‘j%’ 會使用索引
select*from t_student where sName like ‘%j%’ 不會使用索引,仍會全表查詢
索引的缺點:
- 減慢增刪改數據的速度
- 占用磁盤空間
- 增加查詢優化器負擔
不能簡單的認為“索引越多,性能越高”,不必對每個數據列都進行索引。如果很少使用或從不使用某個索引,建議刪除該索引。
二.視圖
視圖是數據庫中由一個或多個基本表導出的虛擬表。
視圖是指計算機數據庫中的視圖,是一個虛擬表,其內容由查詢定義。
1.創建視圖
CREATE VIEW v_find AS SELECT id,productName,price,imgPath FROM t_product;
2.使用視圖
直接從視圖中查詢
SELECT*FROM v_find;
對於復雜的連表查詢,可以利用創建視圖來使SQL語句變得簡單。
CREATE VIEW v_join AS SELECT r.address,r.price,m.manName FROM test.t_man m,test.t_room r WHERE r.manId=m.id;
SELECT*FROM v_join where manName=’張三
3.刪除視圖
DROP VIEW v_find
注意:視圖只適用於查詢,它只是一張虛擬表!如果需要添加數據,那么必須加在真實表中。
三.SQL語句優化策略
1. 給適當的列加上索引。
2. 盡量避免全表掃描,首先應考慮在where及order by涉及的列上建立索引。
3. 避免select *
從數據庫里讀出越多的數據,那么查詢就會變得越慢
4. 永遠為每張表設置一個ID
使用VARCHAR類型來當主鍵會使得性能下降
5. 使用EMUM而不是VARCHAR
sex ENUM(‘’男,’女’)
如果一個列只含有有限樹木的特定值,比如:性別、狀態等,盡量采用ENUM列舉出所有可能的取值作為數據類型,enum列的值都是以標識數值標識,mysql會處理的更快。
6. 盡量避免在where子句中使用or來連接條件,如果一個字段有索引,一個字段沒有索引,將導致引擎放棄使用索引而進行全表掃描
7. 模糊查詢不要以通配符開始,否則會導致索引失效而進行全表掃描
select *from t_student where sName like ‘a%’
8. 盡量避免在where子句中對字段進行表達式操作,這會導致引擎失效放棄使用索引而進行全表掃描。
select id from t where num/2=100
應該改為 select id from t where num=100*2
9. In和not in要慎用,否則可以導致全表掃描,可以用exists代替In。
Select num from a where num in(select num from b)
用下面的語句替換
Select num from a where exists(select 1 from b where num=a.num)
10.一個表的索引最好不要超過6個,若太多則應考慮刪除一些不常使用的索引
11.盡量避免大事務操作,提高系統並發能力
12.並不是所有索引對查詢都有效,當索引列有大量數據重復時,SQL查詢可能不會去利用索引,如果一表中有字段sex,male,female幾乎各一半,那么即使在sex上建了索引頁會查詢效率起不了太大作用
四、數據庫存儲過程
l 什么是存儲過程?
存儲過程是一段寫好的SQL代碼,它是存在數據庫的目錄中,外部程序可以直接調用數據庫里面定義好的存儲過程。
l 存儲過程的優點
- 性能上的提高,比起通過應用程序發送SQL語句給數據庫執行,讓數據庫自己內部執行存儲過程效率更高、速度更快。
- 減少了應用程序與數據庫信息的交互頻率。在一些業務中,應用程序發送多條SQL指令給服務器。而使用存儲過程則只需要一條調用存儲過程的語句,然后獲取需要的數據就可以了。
- 存儲過程重用性比較高,保存在數據庫里面所以對任何應來說都可以使用。
- 存儲過程是一種安全的做法,數據庫管理員可以對那些沒有權限訪問數據庫中的表格的應用,給他們使用存儲過程的權限來獲得數據服務。
l 存儲過程的缺點
- 存儲過程會使得數據庫占用的系統資源加大。
- 因為存儲過程依舊是sql,沒辦法像編程語言那樣寫出復雜業務邏輯對應的存儲過程。
- 存儲過程不容易進行調試
- 存儲過程是寫及維護難度都比較大。
l 存儲過程語法
#創建存儲過程
DELIMITER//
CREATE PROCEDURE p_find()
BEGIN
SELECT* FROM test.t_man;
END;
//
#使用存儲過程
CALL P_find();
#刪除存儲過程
DROP PROCEDURE p_find;
l 存儲過程參數
#IN表示輸入參數,OUT表示輸出參數
DELIMITER//
CREATE PROCEDURE pro(IN mname VARCHAR(20),OUT jname VARCHAR(20))
BEGIN
SELECT *FROM test.t_man where manName=mname;
END;
//
CALL pro(‘張三’,@j);
SELECT @j;
l 存儲過程定義變量
DECLARE t INT DEFAULT 0;
SET t=10;
SET t=t+1;
注意:存儲過程只能在存儲過程的開始定義
實例:
#在exercise數據庫中有t_man表,包含字段員工名稱,員工職位名稱。輸入一個公民姓名,根據職位名稱,判斷出所屬等級(表中沒有等級)
DELIMITER//
CREATE PROCEDURE p_get(IN mname VARCHAR(20),OUT info VARCHAR(20))
BEGIN
#定義變量
DECLARE mjob VARCHAR(20) DEFAULT '';
#查詢指定姓名的員工的職務,並將職務賦值給mjob變量
SELECT job INTO mjob FROM exercise.t_man WHERE manName=mname;
IF mjob='經理' OR mjob='助理' THEN
SET info='高管';
ELSEIF mjob='會計' OR mjob='文員' THEN
SET info ='行政人員';
ELSE
SET info='辦事人員';
END IF;
END
//
#兩句可以一起執行,但是中間必須用”;”隔開
CALL p_get('張三豐',@m);
SELECT @m;
#動態條件查詢
DELIMITER//
CREATE PROCEDURE p_dyna(IN mname VARCHAR(20),IN startDate DATE,IN endDate DATE)
BEGIN
DECLARE msql VARCHAR(200) DEFAULT 'select *from exercise.t_man where 1=1 ';
IF mname IS NOT NULL AND mname !='' THEN
SET msql=CONCAT(msql,"and manName like '",mname,"%' ");
END IF;
IF startDate IS NOT NULL THEN
SET msql=CONCAT(msql,"and birthday>='",startDate,"' ");
END IF;
IF endDate IS NOT NULL THEN
SET msql=CONCAT(msql,"and birthday<='",endDate,"'");
END IF;
#執行SQL語句,首先建一個全局變量
SET @ms=msql;
PREPARE st FROM @ms;
EXECUTE st;
DEALLOCATE PREPARE st;
END;
//
CALL p_dyna('張','1980-01-01','2016-01-01')