MySQL優(yōu)化筆記(二)--查找優(yōu)化(2)(外連接、多表聯(lián)合查詢以及查詢注意點)

繼續(xù)這一系列,上篇的簡單查詢優(yōu)化并沒講完,第二點還有查詢注意點以及多表查詢優(yōu)化呢??!

文章結(jié)構(gòu):(1)多表查詢步步優(yōu)化;(2)查詢編寫的注意點。

本系列demo下載

(一)MySQL優(yōu)化筆記(一)--庫與表基本操作以及數(shù)據(jù)增刪改

(二)MySQL優(yōu)化筆記(二)--查找優(yōu)化(1)(非索引設(shè)計)

(三)MySQL優(yōu)化筆記(二)--查找優(yōu)化(2)(外連接、多表聯(lián)合查詢以及查詢注意點)

(四) MySQL優(yōu)化筆記(三)--索引的使用、原理和設(shè)計優(yōu)化

(五) MySQL優(yōu)化筆記(四)--表的設(shè)計與優(yōu)化(單表、多表)

(六)MySQL優(yōu)化筆記(五)--數(shù)據(jù)庫存儲引擎


文章目錄:

(1)多表查詢步步優(yōu)化

  • 基本連接方法(內(nèi)連接、外連接以及交叉連接)
    - 內(nèi)連接:用比較運算符根據(jù)每個表共有的列的值匹配兩個表中的行(=或>、<)
    - 外連接之左連接
    - 外連接之右連接
    - 外連接之全外連接
    - 交叉連接

  • 超大型數(shù)據(jù)盡可能盡力不要寫子查詢,使用連接(JOIN)去替換它(基礎(chǔ)講完,講優(yōu)化)

  • (3)使用聯(lián)合(UNION)來代替手動創(chuàng)建的臨時表

  • 建立索引(下一篇將詳講)

(2)查詢編寫的注意點

大概有9點,詳情見下文。


一、多表查詢步步優(yōu)化:(使用還是之前的數(shù)據(jù)庫的表,商品分類表以及商品詳情表)

這里寫圖片描述

給出的數(shù)據(jù)庫有基本的數(shù)據(jù)框架,剩下的幾個假數(shù)據(jù)我們就自己創(chuàng)建吧。注意此兩表是有外鍵約束的。

這里寫圖片描述

(1)基本連接方法(內(nèi)連接、外連接以及交叉連接):

一)內(nèi)連接:用比較運算符根據(jù)每個表共有的列的值匹配兩個表中的行(=或>、<)

//意思是:檢索商品分類表和商品表“分類描述”相同的行
select
        d.Good_ID ,
        d.Classify_ID,
        d.Good_Name
        from
        Commodity_list d
        inner join commodity_classification c
        on d.Classify_Description=c.Good_kinds_Name
             
得到的滿足某一條件的是A,B內(nèi)部的數(shù)據(jù);正因為得到的是內(nèi)部共有數(shù)據(jù),所以連接方式稱為內(nèi)連接。
這里寫圖片描述

很容器看出是兩者都滿足才查出

這里寫圖片描述

二)外連接之左連接

//意思:查得商品分類表的所有數(shù)據(jù),以及滿足條件的商品詳情表的數(shù)據(jù)
select
        *
        from
        commodity_classification c
        left join commodity_list d
        on d.Classify_Description=c.Good_kinds_Name
這里寫圖片描述

可以看到,首先是左表數(shù)據(jù)全部羅列,然后有滿足條件的右表數(shù)據(jù)都會全部羅列出。若兩條右表數(shù)據(jù)對左表一條數(shù)據(jù),則會用對應(yīng)好的左表數(shù)據(jù)補足作為一條記錄。

這里寫圖片描述

左連接升級:

[left join 或者left outer join(等同于left join)] + [where B.column is null]

//就是只查分類表數(shù)據(jù),但是減去跟商品詳情表有聯(lián)系的數(shù)據(jù)。
select
        *
        from
        commodity_classification c
        left join commodity_list d
        on d.Classify_Description=c.Good_kinds_Name
        where d.Classify_Description is null
       
這里寫圖片描述

這里寫圖片描述

三)外連接之右連接

//意思是查得商品詳情表的所有數(shù)據(jù)以及在分類描述相同條件下的商品分類表數(shù)據(jù)
select
        *
        from
        commodity_classification c
        right join commodity_list d
        on d.Classify_Description=c.Good_kinds_Name

這里寫圖片描述

與左連恰恰相反,首先是右表數(shù)據(jù)全部羅列,然后有滿足條件的左表數(shù)據(jù)都會全部羅列出。若兩條左表數(shù)據(jù)對右表一條數(shù)據(jù),則會用對應(yīng)好的右表數(shù)據(jù)補足作為一條記錄。

這里寫圖片描述

右連接升級:

//意思:查詢商品詳情表的所有數(shù)據(jù),但是要減去和商品分類表有聯(lián)系的數(shù)據(jù)
select
        *
        from
        commodity_classification c
        right join commodity_list d
        on d.Classify_Description=c.Good_kinds_Name
        where c.Good_kinds_Name is null  
       
這里寫圖片描述

四)外連接之全外連接:

full join (mysql不支持,但是可以用 left join union right join代替)

select
        *
        from
        commodity_classification c
        left join commodity_list d
        on d.Classify_Description=c.Good_kinds_Name
        union
select
        *
        from
        commodity_classification c
        right join commodity_list d
        on d.Classify_Description=c.Good_kinds_Name

這種場景下得到的是滿足某一條件的公共記錄,和獨有的記錄

這里寫圖片描述

全外連接升級:

select
        *
        from
        commodity_classification c
        left join commodity_list d
        on d.Classify_Description=c.Good_kinds_Name
        where d.Classify_Description is null
        union
select
        *
        from
        commodity_classification c
        right join commodity_list d
        on d.Classify_Description=c.Good_kinds_Name
         where c.Good_kinds_Name is null

這種場景下得到的是A,B中不滿足某一條件的記錄之和.

這里寫圖片描述

全部一起列出:消除重復(fù)項

這里寫圖片描述

五)交叉連接:

交叉聯(lián)接返回左表中的所有行,左表中的每一行與右表中的所有行組合。交叉聯(lián)接也稱作笛卡爾積。 FROM 子句中的表或視圖可通過內(nèi)聯(lián)接或完整外部聯(lián)接按任意順序指定;但是,用左或右向外聯(lián)接指定表或視圖時,表或視圖的順序很重要。有關(guān)使用左或右向外聯(lián)接排列表的更多信息,請參見使用外聯(lián)接。

有兩種情況,顯式的和隱式的,不帶ON子句,返回的是兩表的乘積,也叫笛卡爾積。

1. 第一種方式(顯式的交叉連接):A,B表記錄的排列組合,即笛卡兒積。

//可以看到
select
        *
        from
        commodity_classification c
       cross join commodity_list d 
這里寫圖片描述

補充:cross join可指定條件 (where)

select
        *
        from
        commodity_classification c
       cross join commodity_list d 
       where c.Good_kinds_Name=d.Classify_Description

相當(dāng)于實現(xiàn)內(nèi)連接功能了。

這里寫圖片描述

2. 第二種方式:(隱式的交叉連接,沒有CROSS JOIN)

就跟正上面的效果一樣的語句啦!

select
        *
        from
        commodity_classification c,
        commodity_list d 
       where c.Good_kinds_Name=d.Classify_Description

交叉連接存在的問題遺漏: 參考此博主此博文

mysql對sql語句的容錯問題,即在sql語句不完全符合書寫建議的情況,mysql會允許這種情況,盡可能解釋它:

1)一般cross join后面加上where條件,但是用cross join+on也是被解釋為cross join+where;

2)一般內(nèi)連接都需要加上on限定條件,如上面場景一;如果不加會被解釋為交叉連接;

3)如果連接表格使用的是逗號,會被解釋為交叉連接;

注:sql標(biāo)準(zhǔn)中還有union join和natural inner join,mysql不支持,而且本身也沒有多大意義,其結(jié)果可以用上面的幾種連接方式得到。


(2)超大型數(shù)據(jù)盡可能盡力不要寫子查詢,使用連接(JOIN)去替換它(基礎(chǔ)講完,講優(yōu)化):

子查詢:

定義:在一個表表達(dá)中可以調(diào)用另一個表表達(dá)式,這個被調(diào)用的表表達(dá)式叫做子查詢(subquery),也稱作子選擇(subselect)或內(nèi)嵌選擇(inner select)。子查詢的結(jié)果傳遞給調(diào)用它的表表達(dá)式繼續(xù)處理。
//很簡單的意思:就是根據(jù)商品id查商品詳情表,然后用查出來的ID去查商品分類表。
select
       *
       from
       commodity_classification c  
       where Classify_ID  IN(select Classify_ID from commodity_list where Good_ID='tb10025584930')

使用JOIN進行優(yōu)化:

select
       *
       from
       commodity_classification c  
       left join commodity_list d on d.Classify_ID=c.Classify_ID
       where d.Good_ID='tb10025584930'
       

(3)使用聯(lián)合(UNION)來代替手動創(chuàng)建的臨時表

UNION是會把結(jié)果排序的?。?!

union查詢,它可以把需要使用臨時表的兩條或更多的select查詢合并的一個查詢中(即把兩次或多次查詢結(jié)果合并起來。)。在客戶端的查詢會話結(jié)束的時候,臨時表會被自動刪除,從而保證數(shù)據(jù)庫整齊、高效。使用union來創(chuàng)建查詢的時候,我們只需要用UNION作為關(guān)鍵字把多個select語句連接起來就可以了,要注意的是所有select語句中的字段數(shù)目要想同。

要求:兩次查詢的列數(shù)必須一致

推薦:列的類型可以不一樣,但推薦查詢的每一列,想對應(yīng)的類型以一樣

可以來自多張表的數(shù)據(jù):多次sql語句取出的列名可以不一致,此時以第一個sql語句的列名為準(zhǔn)。

如果不同的語句中取出的行,有完全相同(這里表示的是每個列的值都相同),那么union會將相同的行合并,最終只保留一行。也可以這樣理解,union會去掉重復(fù)的行。

如果不想去掉重復(fù)的行,可以使用union all。

如果子句中有order by,limit,需用括號()包起來。推薦放到所有子句之后,即對最終合并的結(jié)果來排序或篩選。

select
       Classify_ID,Good_kinds_Name
       from
       commodity_classification 
       union
select 
        Classify_ID,Classify_Description
        from 
        commodity_list

//加條件
(select
       Classify_ID,Good_kinds_Name
       from
       commodity_classification 
       order by Classify_ID)
       union
(select 
        Classify_ID,Classify_Description
        from 
        commodity_list
        order by Good_ID)
 
//經(jīng)常操作的含義:列出所有在中國和美國的不同的雇員名
SELECT E_Name FROM Employees_China
UNION
SELECT E_Name FROM Employees_USA
       

注意:

1、UNION 結(jié)果集中的列名總是等于第一個 SELECT 語句中的列名

2、UNION 內(nèi)部的 SELECT 語句必須擁有相同數(shù)量的列。列也必須擁有相似的數(shù)據(jù)類型。同時,每條 SELECT 語句中的列的順序必須相同

UNION ALL的作用和語法:

默認(rèn)地,UNION 操作符選取不同的值。如果允許重復(fù)的值,請使用 UNION ALL。當(dāng) ALL 隨 UNION 一起使用時(即 UNION ALL),不消除重復(fù)行。

select
       Classify_ID,Good_kinds_Name
       from
       commodity_classification 
       union ALL
select 
        Classify_ID,Classify_Description
        from 
        commodity_list

不刪除重復(fù)數(shù)據(jù)

這里寫圖片描述

(4)建立索引(下一篇將詳講)


二、查詢編寫的注意點:

(1)對查詢進行優(yōu)化,要盡量避免全表掃描,首先應(yīng)考慮在 where 及 order by 涉及的列上建立索引。(索引的注意點在下篇將詳講)

(2)應(yīng)盡量避免在 where 子句中對字段進行 null 值判斷,否則將導(dǎo)致引擎放棄使用索引而進行全表掃描,如:

//最好不要給數(shù)據(jù)庫留NULL,盡可能的使用 NOT NULL填充數(shù)據(jù)庫.
select id from t where num is null

備注、描述、評論之類的可以設(shè)置為 NULL,其他最好不要使用NULL。

不要以為 NULL 不需要空間,比如:char(100) 型,在字段建立時,空間就固定了, 不管是否插入值(NULL也包含在內(nèi)),都是占用 100個字符的空間的,如果是varchar這樣的變長字段, null 不占用空間。

可以在num上設(shè)置默認(rèn)值0,確保表中num列沒有null值,然后這樣查詢:

select id from t where num = 0

(3)in 和 not in 也要慎用,否則會導(dǎo)致全表掃描,如:

select id from t where num in(1,2,3)

對于連續(xù)的數(shù)值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3

很多時候用 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)

(4)下面的查詢也將導(dǎo)致全表掃描:

select id from t where name like ‘%abc%’

若要提高效率,可以考慮全文檢索。

(5)盡量使用數(shù)字型字段,若只含數(shù)值信息的字段盡量不要設(shè)計為字符型,這會降低查詢和連接的性能,并會增加存儲開銷。這是因為引擎在處理查詢和連 接時會逐個比較字符串中每一個字符,而對于數(shù)字型而言只需要比較一次就夠了。

(6)任何地方都不要使用 select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段。

(7)盡量使用表變量來代替臨時表。如果表變量包含大量數(shù)據(jù),請注意索引非常有限(只有主鍵索引)。

(8)在Join表的時候使用相當(dāng)類型的例,并將其索引

如果你的應(yīng)用程序有很多 JOIN 查詢,你應(yīng)該確認(rèn)兩個表中Join的字段是被建過索引的。這樣,MySQL內(nèi)部會啟動為你優(yōu)化Join的SQL語句的機制。

而且,這些被用來Join的字段,應(yīng)該是相同的類型的。例如:如果你要把 DECIMAL 字段和一個 INT 字段Join在一起,MySQL就無法使用它們的索引。對于那些STRING類型,還需要有相同的字符集才行。(兩個表的字符集有可能不一樣)

//在state中查找company
SELECT company_name FROM users
    LEFT JOIN companies ON (users.state = companies.state)
    WHERE users.id = $user_id"
    //兩個 state 字段應(yīng)該是被建過索引的,而且應(yīng)該是相當(dāng)?shù)念愋?,相同的字符?

(9)查詢索引中的sql語句有很多講究,在下篇文章我們將詳細(xì)討論。


源碼下載:數(shù)據(jù)庫文件下載

好了,MySQL優(yōu)化筆記(二)--查找優(yōu)化(2)(外連接、多表聯(lián)合查詢以及查詢注意點)講完了,下一篇就是我們的索引詳講了,很難又很重要關(guān)鍵,性能的關(guān)鍵點,我會繼續(xù)出這個系列文章,分享經(jīng)驗給大家。歡迎在下面指出錯誤,共同學(xué)習(xí)??!你的點贊是對我最好的支持??!

更多內(nèi)容,可以訪問JackFrost的博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容