Mysql优化之基础回顾篇
mysql查詢指令執行順序
where->group-by>having->order by->limit
更新時間2016年7月24日 20:47:28
階段一
進入數據庫???? mysql –h服務器名稱–u用戶名稱 –p密碼;
? ? ? ? ?[示例]mysql -hlocalhost -uroot -p123456;
? ? ?? ? ? ? ?顯示數據庫????
? ? ? ? ?? ? ? ? ?[示例]show databases;
? ? ? ?? ? ? ? ? ?->進入某個數據庫??? use? 數據庫名;
? ? ?? ? ? ? ?? ?[示例]use shop;
? ? ? ? ??? ? ? ? ? ? ? ? ?->查看數據庫里面的表單列表
? ? ? ? ?? ? ?? ? ? ? ?? ?[示例]show tables;
列類型的概念
數值型
? ??整型 tinyint ? ? smallint ? ?mediumint ? ?int ? ? bigint
? ? unsigned?
? ? ? ? ? 無符號,因為計算機記錄數字是以補碼(詳見《計算機組成原理》)的形式保存的
? ??所以當聲明了無符號的時候,該數字記錄的范圍可以多一位了
? ? 比如,聲明一個tinyint類型,它可記錄一個字節,即,可以記錄八位二進制數據
? ? 在未聲明無符號的時候,它會使用第一位作為符號,范圍就在-128~127之間
? ??聲明無符號位的時候,的范圍就變為了0~255
? ? zerofill?
? ? ? ? ?零填充,M寬度
? ? 浮點型/定點型
? ? ? ? ?M為總位數,d代表小數位個數
? ? ? ? ?float(M,d)與decimal(M,d)
? ? ? ? ?無符號的時候decimal比float更精確,常用商城價格
字符型
? ? char(M)與varchar(M)的區別
? ? ? ? ?char實際占了M個字節(定長),存儲的不夠M則向右側補空格,取出時取出右側空格,限制 M<=255
? ? ? ? ? varchar有1~2個字節來標記真實的長度(變長),限制 M<=65535
日期
? ? 常用TIMESTAMP current_timestamp直接記錄時間
? ? 在開發中常用int類型來時間戳秒數,方便計算,顯示時間的時候也方便格式化為不同的顯示樣式
階段二
where
//取出account_log>4的數據?四個運算符? > = < !=
SELECT??* ?FROM?account_log?where?log_id?>4;
//取出4到8直接的數據
SELECT??* ?FROM?account_log?where?log_id?between?4?and?8;
(注意這里可以用??wherelog_id>=4 and log_id <=8來同樣表達)
//取出4和8中的數據
SELECT??* ?FROM?account_log?where?log_id?in (4,8);
//邏輯詞匯三個 not and or,下面以not的用法為例子
SELECT??* ?FROM?account_log?where?log_id not?in (4,8);
group by
通常與統計函數avg(),sum(),count(),min(),max()一起使用
【注意】當使用group的時候,count(*)函數得出的數是指的每個分組內的成員個數,
比如select count(*) as times from table1 group by id,
比如? id=1的個數就是times的值
把每一列都當作一個變量看,運算結果通過as作為別名輸出
//查出一個用戶所購買的所有東西的總價格
SELECT??* sum( money * num) as total?FROM?account_log?group by user_id;
where與having的區別
where查表中的原始數據有作用,having對表中沒有的但對查詢邏輯中有結果有效
//查詢總成績大于380的學生名字,與其對應的總分
SELECT name, (Chinese+Math+English)as score
FROM student
HAVING score>380
ORDER BYscore DESC;
//查詢用戶中,消費超過1000元的人中消費最低的兩個人
SELECTuser_od,sum(user_money) as total
FROMaccount_log
Group BYuser_id
HAVINGtotal>1000
ORDER BY user_idASC
LIMIT 2;
?
//計算掛科兩門及以上的學生的平均分,從高到低每頁展示20個人的信息 column,name,score
SELECT avg(score) as avg_score,sum(score<60) as times
FROMstudent_score
GROUP BY name
HAVINGtimes>=2
ORDER BYavg_score DESC
LIMIT 20;
?
【注意】count()里面只取的是行數,給什么條件都沒用,
sum()?如果里面是判斷類型,結果就為真值的總個數;
如果是普通運算,結果就為運算的結果
?
order by
它可以多字段排序 orderby 字段1,字段2 asc
[示例]order by cat_idasc,shop_price desc;
//通過goods表建立臨時表g2并把goods表中的數據傳給g2,傳入前,
//①將goods表的數據通過cat_id與shop_price排序(但在平時的操作中,
//②我們不用臨時表,我們用子查詢(查詢結果當作表))
CREATE TABLE g2 LIKE goods;
INSERT INTO g2 SELECT * FROM goods ORDER BY cat_id asc,shop_price desc;
?
Where ?From ?Exists子查詢
?>Where是把內部的查詢結果,給外部查詢使用
? ? ??[常用場景=>查詢最大商品、查詢最貴商品][最新,即自增id為最大值的時候]
SELECT name?FROM goods?
WHERE name in(
? ??? ??SELECT name?
? ??? ??FROM goods?
? ??? ??ORDER BY user_id
);
>From查詢結果當成一個臨時表(表必須取一個別名),再讓外部進行一次sql查詢使用
????? [常用場景=>查詢每個欄目下的最新/最貴商品]
SELECT name?FROM(
?? ??? ??SELECT name?
? ??? ??FROM goods?
? ??? ??ORDER BY user_id
) as temp_table;
>Exists 把外層的查詢結果拿到內層,看內部的查詢結果是否成立
????? [常用場景=>查詢有商品的欄目]
SELECT?*?FROM?comment?
WHERE?EXISTS(?
? ??? ??SELECT?*?FROM?comment_inner?
? ??? ??WHERE?comment_inner.article_id=comment.article_id?
);
//計算掛科兩門及以上的學生的平均分 column,name,score
思路:分三次。第一次查詢
①??? 篩選符合條件的人=>這時候會有一個人的名字多次出現的情形【WHERE子查詢】
SELECT name,count(*) as result
FROM stu
WHERE score <60
GROUP BY name having result>1;
②??? 合并重復出現的名字【普通查詢】
SELECT name
FROM temp
GROUP BY name;
③??? 計算這些人的平均成績【FROM子查詢】
SELECT name,avg(score) as avg_score
FROM stu where name in②中的結果;
最終拼寫出語句
SELECT name,avg(score) as avg_score
FROM stu where name in
? ? (SELECT name
? ??FROM (
? ??? ??SELECT name,count(*) as result
? ??? ??FROM stu
?? ?? ??WHERE score <60
? ?? ???GROUP BY name having result>1
? ?? ???)as temp
GROUP BY name
);
更新時間2016年7月26日23:26:48
階段三
union
>把2次或者多次查詢結果合成一張表,要求其列數要一致
可以來源于多張表,可以進行多次sql語句查詢,取出的列名可以不一致,此時以第一個sql的列名為準
?
1)如果不同的語句中取出的行,有完全相同(每個列的值都相同),
那么相同的行將會合并(去重復)
SELECTS * FROM ta
UNION
SELECT * FROM tb;
2)如果不去掉重復項,可以通過加all來搞定
SELECTS * FROM ta
UNION ALL
SELECT * FROM tb;
3)如果語句中有oder by,limit(在子句中order by配合limit才有意義,否則語法分析器,會在分析語法的時候,把order by去掉)
則需要將每個查詢子句都包起來(但平日多用在總的結果后 排序)
//取第三個欄目前三高、第四個欄目前兩高的商品,用union實現
(SELECT goods_id,cat_id,goods_name,shop_price
FROM goods
WHERE cat_id = 3
ORDER BY?shop_price desc
LIMIT 3)
UNION
(SELECT goods_id,cat_id,goods_name,shop_price
FROM goods
WHERE cat_id =4
ORDER BY shop_price desc
LIMIT 2);
join
1) 左連接【這里以tb為基準 [意指它每項得應完全對齊] :交集部分先拿出來。另外,若自己有沒對齊的行,則把這些行的數據也拿出來。因為沒對齊,所以這幾行只有自身的數據,其他數據為NULL】
SELECT tb.level,tb.assignment,ta.name
FROM tb
LEFT JOIN ta
ON ta.level=tb.level
2) 右連接【這里以ta為基準,邏輯過程與左聯接同理】
SELECT tb.level,tb.assignment,ta.name
FROM tb
RIGHT JOIN ta
ON ta.level=tb.level
3)內連接【查詢左右表都有的數據,即左右鏈接的交集】
SELECT tb.level,tb.assignment,ta.name
FROM tb
INNER JOIN ta
ON ta.level=tb.level
//查出左右連接的并集,可以用UNION?
//查出下面2016-08-08之后的所有比賽,并以形如下面的方式輸出:
? ??? ??? ??? ??重慶力帆 ?0:0?上海申花 2016-08-09
SELECT a.matchResult,a.matchTime ,b.teamName as hName,c.teamName as gName
FROM `match` as a
LEFT JOIN `team` as b
On b.teamID=a.hostTeamID
LEFT JOIN `team` as c
On c.teamID=a.guestTeamID
WHERE a.matchTime>'2016-08-08'
更新時間2016年7月28日 10:27:44
view
? >視圖是由查詢結果形成的一張虛擬表
視圖的用處
>簡化查詢
>把表的權限封閉,但開放相應的視圖權限,視圖里只開放部分數據
>大數據分表可以用到.
視圖的創建
create view 視圖名 as select語句
視圖的刪除
drop view 視圖名
視圖的修改
Alter view as select xxx;
視圖與表的關系
視圖是表的查詢結果,自然表的數據改變了,會影響視圖的結果
視圖改變了呢?
1:視圖增刪改也會影響表的嗎
? ? >視圖的數據與表的數據 一一對應時,可以修改
2:視圖總能增刪改嗎
? ? >對于視圖的insert還應該注意,視圖必須包含表中沒有默認值的列
大數據分表查詢
當表超過200萬行的時候,查詢速度就會變慢,這時候可以通過分表查詢的方法
? ? [示例] 現在我把表通過 模4 的方法,把表分成4張視圖
CREATE view g1 as select * from goods where goods_id % 4=0;
CREATE view g2 as select * from goods where goods_id % 4=1;
CREATE view g3 as select * from goods where goods_id % 4=2;
CREATE view g4 as select * from goods where goods_id % 4=3
以php查詢為例
$tableFlag=$_GET['id']%4;
$tablename="g".$tableFlag;
$result=$pdo->("select * from $tablename");
此外,我們可以把多張表通過union合成一張新的視圖
CREATE newTable as select * from t1 unionselect * from t2 ....;
視圖的Algorithm
algorithm = merge / Temptable?/ undefined
Merge當引用視圖的時候,視圖語句與定義視圖的語句合并只是為了形成一條select語句
Temptable 當引用視圖時,根據視圖的創建語句建立一個臨時表多用于做子查詢的結果,出現非常頻繁的時候
Undefined未定義,讓系統幫你選
而temptable是根據創建語句瞬間創建一張臨時表,
然后查詢視圖的語句從該臨時表查數據
create algorithm=Temptable?view g2 as?
select goods_id,cat_id,goods_name,shop_price from goods order by cat_idasc,shop_price desc;
查詢語句為
select * from g2 group by cat_id; //最后執行的這個查詢語句
然后取出結果,并放在臨時表上
字符集與校對集
字符集
>數據庫默認字符集>表默認字符集
>列字符集
>如果某一個級別沒有指定字符集,則繼承上一級
>最上一級是服務器,它是一定有字符集定義的(因為要負責頁面的輸出),
1.告訴服務器,我給你發送的數據是什么編碼 Character_set_client
? ? ? ? ?[示例]set character_set_client=utf8;
2.告訴轉換器,轉換成什么編碼?Character_set_connection
? ? ? ? ?[示例]set character_set_connection=utf8;
3.查詢的結果是什么編碼?Character_set_results
? ? ? ? ?[示例]set character_set_results=utf8;
若三者統一編碼,則可寫為set names 字符集名
? ? ? ? ?[示例]set names utf8;
校對集
>值字符集的排序規則[一個字符集可以有多種排序規則,如圖所示]
? ??>常用字符集為utf8_general_ci
聲明校對規則
? ? ? ? ?[示例]create table(xxxxx) Charset utf8 collate?utf8_general_ci;
更新時間2016年7月29日 01:55:46
階段四
觸發器
四要素
監視地點 監視事件 觸發事件 觸發事件
MySQL包含對觸發器的支持。觸發器是一種與表操作有關的數據庫對象,當觸發器所在表上出現指定事件時,將調用該對象,即表的操作事件觸發表上的觸發器的執行。
創建觸發器
在MySQL中,創建觸發器語法如下:
CREATE TRIGGER trigger_name
trigger_time trigger_event ON tbl_name
FOR EACH ROW
trigger_stmt
其中:
trigger_name:標識觸發器名稱,用戶自行指定;
trigger_time:標識觸發時機,取值為 BEFORE 或 AFTER;
trigger_event:標識觸發事件,取值為 INSERT、UPDATE 或 DELETE;
tbl_name:標識建立觸發器的表名,即在哪張表上建立觸發器;
trigger_stmt:觸發器程序體,可以是一句SQL語句,或者用 BEGIN 和 END 包含的多條語句。
由此可見,可以建立6種觸發器,即:BEFORE INSERT、BEFORE UPDATE、BEFORE DELETE、AFTER INSERT、AFTER UPDATE、AFTER DELETE。
另外有一個限制是不能同時在一個表上建立2個相同類型的觸發器,因此在一個表上最多建立6個觸發器。
trigger_event 詳解
MySQL 除了對 INSERT、UPDATE、DELETE 基本操作進行定義外,還定義了 LOAD DATA 和 REPLACE 語句,這兩種語句也能引起上述6中類型的觸發器的觸發。
LOAD DATA 語句用于將一個文件裝入到一個數據表中,相當與一系列的 INSERT 操作。
REPLACE 語句一般來說和 INSERT 語句很像,只是在表中有 primary key 或 unique 索引時,如果插入的數據和原來 primary key 或 unique 索引一致時,會先刪除原來的數據,然后增加一條新數據,也就是說,一條 REPLACE 語句有時候等價于一條。
INSERT 語句,有時候等價于一條 DELETE 語句加上一條 INSERT 語句。
INSERT 型觸發器:插入某一行時激活觸發器,可能通過 INSERT、LOAD DATA、REPLACE 語句觸發;
UPDATE 型觸發器:更改某一行時激活觸發器,可能通過 UPDATE 語句觸發;
DELETE 型觸發器:刪除某一行時激活觸發器,可能通過 DELETE、REPLACE 語句觸發。
BEGIN … END 詳解
在MySQL中,BEGIN … END 語句的語法為:
BEGIN
[statement_list]
END
其中,statement_list代表一個或多個語句的列表,列表內的每條語句都必須用分號(;)來結尾。
而在MySQL中,分號是語句結束的標識符,遇到分號表示該段語句已經結束,MySQL可以開始執行了。因此,解釋器遇到statement_list 中的分號后就開始執行,然后會報出錯誤,因為沒有找到和 BEGIN 匹配的 END。
這時就會用到 DELIMITER 命令(DELIMITER 是定界符,分隔符的意思),它是一條命令,不需要語句結束標識,語法為:
DELIMITER new_delemiter
new_delemiter 可以設為1個或多個長度的符號,默認的是分號(;),我們可以把它修改為其他符號,如$:
DELIMITER $
在這之后的語句,以分號結束,解釋器不會有什么反應,只有遇到了$,才認為是語句結束。注意,使用完之后,我們還應該記得把它給修改回來。
一個完整的創建觸發器示例
假設系統中有兩個表:
班級表 class(班級號 classID, 班內學生數 stuCount)
學生表 student(學號 stuID, 所屬班級號 classID)
要創建觸發器來使班級表中的班內學生數隨著學生的添加自動更新,代碼如下:
DELIMITER $
create trigger tri_stuInsert after insert
on student for each row
begin
declare c int;
set c = (select stuCount from class where classID=new.classID);
update class set stuCount = c + 1 where classID = new.classID;
end$
DELIMITER ;
變量詳解
MySQL 中使用 DECLARE 來定義一局部變量,該變量只能在 BEGIN … END 復合語句中使用,并且應該定義在復合語句的開頭,
即其它語句之前,語法如下:
DECLARE var_name[,...] type [DEFAULT value]
其中:
var_name 為變量名稱,同 SQL 語句一樣,變量名不區分大小寫;type 為 MySQL 支持的任何數據類型;可以同時定義多個同類型的變量,用逗號隔開;變量初始值為 NULL,如果需要,可以使用 DEFAULT 子句提供默認值,值可以被指定為一個表達式。
對變量賦值采用 SET 語句,語法為:
SET var_name = expr [,var_name = expr] ...
Before 與 After的區別
After ? ??是先完成數據的增刪改,再觸發,觸發的語句晚于監視的增刪改,無法影響前面的增刪改動作
Before 是先完成觸發,再增刪改
修改一次觸發器,就得把之前的觸發器給刪了,不然原來的觸發器還會繼續生效
delimiter $
create trigger t1
before insert on indent
for each row
begin
if new. buy_num > 5 then
??? set new.buy_num= 5;
end if;
update indent_details set num = num – buy_num where id =new.indent_id;
end$
delimiter ;
NEW 與 OLD 詳解
上述示例中使用了NEW關鍵字,和 MS SQL Server 中的 INSERTED 和 DELETED 類似,MySQL 中定義了 NEW 和 OLD,用來表示
觸發器的所在表中,觸發了觸發器的那一行數據。
具體地:
在 INSERT 型觸發器中,NEW 用來表示將要(BEFORE)或已經(AFTER)插入的新數據;
在 UPDATE 型觸發器中,OLD 用來表示將要或已經被修改的原數據,NEW 用來表示將要或已經修改為的新數據;
在 DELETE 型觸發器中,OLD 用來表示將要或已經被刪除的原數據;
使用方法:NEW.columnName (columnName 為相應數據表某一列名)
另外,OLD 是只讀的,而 NEW 則可以在觸發器中使用 SET 賦值,這樣不會再次觸發觸發器,造成循環調用(如每插入一個學生前,都在其學號前加“2013”)。
查看觸發器
和查看數據庫(show databases;)查看表格(show tables;)一樣,查看觸發器的語法如下:
SHOW TRIGGERS [FROM schema_name];
其中,schema_name即 Schema 的名稱,在 MySQL 中 Schema 和 Database 是一樣的,也就是說,可以指定數據庫名,這樣就
不必先“USE database_name;”了。
刪除觸發器
和刪除數據庫、刪除表格一樣,刪除觸發器的語法如下:
DROP TRIGGER [IF EXISTS] [schema_name.]trigger_name
觸發器的執行順序
我們建立的數據庫一般都是 InnoDB 數據庫,其上建立的表是事務性表,也就是事務安全的。這時,若SQL語句或觸發器執行失敗,MySQL 會回滾事務,有:
①如果 BEFORE 觸發器執行失敗,SQL 無法正確執行。
②SQL 執行失敗時,AFTER 型觸發器不會觸發。
③AFTER類型的觸發器執行失敗,SQL 會回滾。
?
預計更新時間2016年7月29日晚
事務
>對一組操作,要么全部執行,要么全部取消,只要沒完成此次事務,其結果不會被看到ACID特性
原子性:數據庫事務不可再分的原則一致性:要么一起執行,要么一起取消
隔離型:每個事務對其他事務是不可見的
持久性:當事務完成后,其影響會被保留,不能撤銷
用法
start transaction;
sql語句1;
sql語句2;
...
若成功,則提交commit;
若失敗,則回滾rollback;
? ? ?[示例] ?HLZ給Leo轉賬,并成功start transaction;
update bank ?set ?money= money -100 where name='HLZ';
update bank ?set ?money= money+100 ?where name='Leo';
commit;
索引
——這部分是簡單引入,下一篇為優化的正文,我將進一步談談各種索引 索引是針對數據所建立的目錄 優點:可以加快查詢速度 缺點:降低了增刪改的速度 原理:類似新華字典,這里就以新華字典為例把。 ? ? ? ? ? ?新華字典根據 拼音或者筆畫 可以搜索到對應的字的位置 ? ? ? ? ? ?在對應字的位置也有對應的拼音與筆畫 ? ? ? 引入索引之前我們先對比一下MyIsam與InnoDB存儲引擎的各自特點, ? ? ? ?因為一會兒我們會用到各自的優點,如圖所示現在我們來稍稍談談個別索引的內部算法 ? (為方便書寫對數表達式,我在這里用 ?lb ?表示以2為底的對數) 設有N條隨機記錄,如果不用索引,平均要用查找 ? N/2? ? 次。 由《數據結構》可知
如果用btree(二叉樹)索引 ? ? ? ? ??lb(N) ? ?次
如果用hash(哈希)散列索引 ? ? ? 1?? ? ? ?次
一般有索引的數據都比較大,所以,換服務器的時候,記得先把索引去掉,導入數據庫后再統一設置索引
索引類型 普通索引:index 只是加快了查詢速度 唯一索引:unique index 行上的值不能重復,一個表中,可以有多個 主鍵索引:primary key 不能重復,這具有唯一索引的功能,但主鍵索引一個表中只能有一個 全文索引:fulltext index 不能重復
這里引入模糊查詢與索引的關系(模糊查詢有點像正則表達式查詢,具體是怎樣的,本篇文章就不講了,請自行百度) 模糊查詢的時候 "%輸入的內容%"不能使用索引, ? "輸入的內容%"可以用到索引 索引創建原則 1.不要過度索引 2.在where條件最頻繁的列上加 3.盡量索引散列值,過于集中的值索引意義不大
查看一張表上的所有索引
Show index from 表名
建立索引
Alter table 表名 add index / unique / fulltext 索引名 (列名) ? ? [示例] alter table indent add unique ?name (name) Alter table 表名 add primary key (列名) ? ? ##這里不加索引名是因為主鍵只有一個 刪除索引 Alter table 表名 drop index 索引名 Alter table 表名 drop primary key
全文索引 >用法 ? ?Match(全文索引名) against ('輸入的內容'); >停止詞 ? ?全文索引不針對非常頻繁的詞做索引,例如this,is,you,my等等 >全文索引與中文搜索 ? ?全文索引在默認情況下,有中文無法做到索引,因為英文有空格或者標點符號來拆成單詞,進而對單詞進行索引整理 但是對中文語句,進行分詞太難了,mysql無法對中文語句進行分詞,如果非得實現,得用中文詞庫處理。(用sphinx處理,以后的文章我會說說怎么用它)
什么情況下使用索引
表的主關鍵字 表的字段唯一約束 直接條件查詢的字段 查詢中與其它表關聯的字段 查詢中排序的字段 查詢中統計或分組統計的字段
什么情況下應不建或少建索引
表記錄太少
經常插入、刪除、修改的表
存儲過程
>類似函數,進行封裝、調用。(相當于sql編程) >要點:封裝sql、參數、控制結構、循環查看現有的存儲過程 Show procedure status
刪除存儲過程 Drop procedure status
調用存儲過程 Call 存儲過程的名字();
mysql里面只能用set來賦值 ? ? [示例] delimiter $ create procedure test1(n smallint) begin ? ? declare i int; ? ? declare s int; ? ? set i ?= 1; ? ? set s = 0; ? ? while i <= n do ? ? ? ? set s= s + i; ? ? ? ? set i = i ?+ 1;
? ?end while; ? ?select s; end$ delimiter ;
其中 ??select s; ? ?是為顯示計算結果
與函數的區別 ①定義時候的方法 若是定義函數 create function xxx(); ②存儲過程沒有返回值,但是函數可以有
——下篇將正式開始講述優化方面的東西
總結
以上是生活随笔為你收集整理的Mysql优化之基础回顾篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 心得体悟帖---18、时间
- 下一篇: Mysql优化之开山篇