<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于關(guān)系型代數(shù)的 SQL 等價改寫

          共 8801字,需瀏覽 18分鐘

           ·

          2021-09-14 16:16

          點(diǎn)擊藍(lán)色“有關(guān)SQL”關(guān)注我喲

          加個“星標(biāo)”,天天與10000人一起快樂成長


          看過我那篇《SQL 數(shù)據(jù)庫小白,從入門到精通》的朋友,一定不會陌生,SQL 的數(shù)學(xué)原理,就是集合運(yùn)算。

          集合運(yùn)算, 排名第一的交換律,是這樣的:

          交換律(Commutative Laws):

          • A ∪ B = B∪A,
          • A ∩ B = B ∩ A

          數(shù)學(xué)就這么妙!她把復(fù)雜的邏輯,抽象成簡單的符號,收斂住精美。

          當(dāng)然,用純數(shù)學(xué)理論來解釋SQL,我想我會被罵成狗頭。我的目的,是還原精簡的符號,用實(shí)例來演繹背后的邏輯。

          這里的A,B,是集合表達(dá)式??梢钥闯?SQL 的 where 驅(qū)動出的數(shù)據(jù)集。

          比如有同學(xué)表如下:


          CREATE TABLE dbo.STUDENTS(
              STUDENT_ID INT 
          ,   STUDENT_NAME NVARCHAR(256)
          ,   STUDENT_GENDER NVARCHAR(6)
              )

          字段分別代表:

          • STUDENT_ID: 學(xué)號
          • STUDENT_NAME:姓名
          • STUDENT_GENDER:性別

          假使 A 邏輯是 :


          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'男'
           

          B 邏輯是:

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'女' 

          那么

          A ∪ B = B∪A,

          則可以表達(dá)為 :


          --A ∪ B 

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'男' OR STUDENT_GENDER=N'女'
           
           
          --B∪A
          SELECT * 
          FROM dbo.STUDENTS 
          WHERE  STUDENT_GENDER=N'女' OR STUDENT_GENDER = N'男' 

          或者表達(dá)為:



          --A ∪ B 

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'男'
           
          UNION 

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER=N'女'


          --B∪A

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER=N'女'

           
          UNION 

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'男'


           

          再或者:


          --A ∪ B 

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'男'
           
          UNION ALL 

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER=N'女'
           
           
          --B∪A

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER=N'女'

           
          UNION ALL 

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'男'
           
            

          這 3 對(Or,Union, All Union ) 2 組,都是用來抓取全部的同學(xué),那么有什么不一樣嗎?為什么可以有六種寫法


          聰明如你一定能想到,其實(shí)我這么寫出來,肯定是有不一樣的地方。

          本質(zhì)上,這 6 條語句,完成同一件事,但寫法的復(fù)雜度,肉眼可見的遞增。性能,也是逐個漸好。這一點(diǎn),與大多數(shù)初學(xué)者的直覺相反。

          沒錯,這才是本文要講的重點(diǎn),基于關(guān)系型代數(shù)的SQL等價改寫

          我記得,有一次做報表,肯茲肯茲寫了一下午的 SQL ,死摳了各種業(yè)務(wù)細(xì)節(jié),精簡了各類邏輯表達(dá),自認(rèn)為方方面面都考慮周全,無可挑剔。

          雖然用了二十多個 UNION ALL, 代碼長達(dá) 800 多行,但整體代碼排版合理,邏輯清晰可見,一是一,二是二,閱讀體驗(yàn)特別棒。這么完美的一個報表 SQL,自己看著都要給自己磕頭。

          但,就怕人比人!直到我看到另一個同事寫的SQL,區(qū)區(qū)2,30行,結(jié)果居然一樣的。便羞恥得驚掉下巴。怎么會這樣?!

          我忍不住從 Code Repo 里面 Clone 下來,仔細(xì)把玩,哦不,品讀。

          細(xì)看,這段SQL,版面清潔光滑,短小耐看,邏輯還不失完整。我不由得連連佩服,這樣清秀的代碼,簡直把我摁在地上,摩擦了幾條街。

          從此,我便開始注意代碼的凝練,就像寫作般克制。于是就有了那篇《如何寫好 5000 行的 SQL 代碼》。

          總體來說,寫 SQL 或者其他代碼,反復(fù)修改或重構(gòu),是提升自己的不二之法。

          自那以后,我放棄了一遍就寫好代碼的妄想,刻意在每次寫完之后,都反復(fù)修改 2-3 遍,直到自己心里說“ 對了,就是這樣!”,才敢簽入代碼庫。

          就像現(xiàn)在我寫文一樣,越是害怕,越是難以下筆。唯有鼓足勇氣,多讀,多修改,內(nèi)心的糾結(jié)與痛苦,才得以緩解。

          你猜對了,我為最近的難產(chǎn),找到一個好借口!

          如此小心翼翼,卻始終也還擔(dān)心,再次遇到這位朋友,恐怕他的造詣又上升了幾個段位。

          有時,真被自己見賢思齊的心態(tài),折騰得夠嗆。夢回午夜,經(jīng)常感嘆,自己的智商,技藝如此之低,競爭力何在啊。


          扯遠(yuǎn)了,拉回到那 3對2組的 SQL 上來!

          組之間,完成的是 A ∪ B 與 B∪A的 轉(zhuǎn)換。所以他們之間并沒有不同。但“對之間”,差異就很大。

          這就是 SQL 等價改寫的魅力所在!

          運(yùn)行第一對,看其執(zhí)行計劃:


          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'男' OR STUDENT_GENDER=N'女'


          Table Scan 這個物理操作,代表的是訪問表的方式。在這里,Table Scan 執(zhí)行了全表掃描的操作。

          Table Scan 這是一個非常危險的操作,需要優(yōu)化

          運(yùn)行第二對,它的執(zhí)行計劃是這樣:


          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'男'
           
          UNION 

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER=N'女'


          除了第一對里出現(xiàn)的 Table Scan, 這里還出現(xiàn)了 Sort(Distinct Sort) 和 Concatenation 操作符。

          這兩個操作符,是拜 UNION 所賜,UNION 有一層去重的功能。所以,它的這個功能在本次查詢中,是多余的,可去除。

          第三對:



          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'男'
           
          UNION ALL 

          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER=N'女'
           
           

          比起第二對,UNION ALL 去掉了去重的功能,即上下兩個結(jié)果集,如果有同樣的一條記錄,會在最終的結(jié)果集保留下來

          雖然,UNION ALL 會在性能上,優(yōu)于 OR, 需要小心的是,在這里 A 與B 的限制條件互斥,才能改寫,一旦兩者有重合,則會出現(xiàn)重復(fù)記錄,這就與實(shí)際需求不符了。

          比如,往 STUDENTS 表里新建一條未知性別的同學(xué):



          INSERT INTO dbo.STUDENTS(STUDENT_NAME,STUDENT_GENDER) 
          SELECT 'Test Case' , 'UNKWN' AS STUDENT_GENDER 


          再執(zhí)行


          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'男' OR STUDENT_GENDER = N'UNKWN'
          UNION ALL 
          SELECT * 
          FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'女'  OR STUDENT_GENDER = N'UNKWN'
          ORDER BY STUDENT_GENDER

          就能看到有兩條 UNKWN 性別的記錄;

          STUDENT_ID STUDENT_NAME STUDENT_GENDER
          33815 Test Case UNKWN
          33815 Test Case UNKWN

          所以,SQL 轉(zhuǎn)換前提,一定是等價.

          上面的例子,是日常開發(fā)或面試常見操作。底下這例,便是體現(xiàn)優(yōu)化功底的騷操作,不曾用過,就真不知道還能這么干。


          SELECT *
          FROM (

              SELECT A.*
              ,       B.*
              ,       C.*
              ,       D.*
              ,       E.*
              ,       F.*
              FROM A 
                  INNER JOIN B ON B.XXX = A.XXX
                  INNER JOIN C ON C.ZZZ = B.ZZZ 
                  INNER JOIN D ON D.YYY = C.YYY 
                  INNER JOIN E ON E.III = E.III
                  INNER JOIN F ON F.PPP = E.PPP
          ) TMP 
              LEFT JOIN G ON G.WWW = TMP.WWW
          WHERE TMP.FLD1 = 'SAMSUNG'
              AND TMP.FLD2 = 'KOREA'
              AND TMP.FLDX ='XXXX'

          這種多表連接的 SQL,司空見慣??峙逻B接的表,只有更多。

          初學(xué)者,往往能將邏輯理清楚,就已經(jīng)非常吃力了。就像我之前的例子,嘩嘩嘩,一通寫下來,把數(shù)據(jù)找正確,就滿足了。

          但,假如 FLD1, FLD2, FLDX,隸屬于 A,B,C,D,E,F,你是否能看出點(diǎn)什么來?

          沒錯, A ∩ B = B ∩ A 交集等價轉(zhuǎn)換:



          SELECT *
          FROM (

              SELECT A.*
              ,       B.*
                     
                     
                     
                     
              FROM A 
                  INNER JOIN B ON B.XXX = A.XXX
              WHERE A.FLD1 = 'SAMSUNG'
              AND A.FLD2 = 'KOREA'
              AND B.FLDX ='XXXX'
          ) TMP 
              INNER JOIN C ON C.ZZZ = TMP.ZZZ 
              INNER JOIN D ON D.YYY = C.YYY 
              INNER JOIN E ON E.III = E.III
              INNER JOIN F ON F.PPP = E.PPP
              LEFT JOIN G ON G.WWW = TMP.WWW


          前提:FLD1, FLD2, FLDX 隸屬于 A,B 兩表,且不是計算字段

          原先的內(nèi)連接,會拋出一個巨大的矩陣:

          SELECT A.*
              ,       B.*
              ,       C.*
              ,       D.*
              ,       E.*
              ,       F.*
              FROM A 
                  INNER JOIN B ON B.XXX = A.XXX
                  INNER JOIN C ON C.ZZZ = B.ZZZ 
                  INNER JOIN D ON D.YYY = C.YYY 
                  INNER JOIN E ON E.III = E.III
                  INNER JOIN F ON F.PPP = E.PPP

          而事實(shí)上,基于


           WHERE A.FLD1 = 'SAMSUNG'
              AND A.FLD2 = 'KOREA'
              AND B.FLDX ='XXXX'
              

          這樣的條件,只能選出一條或者少量數(shù)據(jù)。那前期做了很多 Join 操作,就變成了無用功,浪費(fèi)了計算資源。

          驅(qū)動表最小化,這是優(yōu)化的一條方法。如果優(yōu)化器,做不到謂詞推進(jìn),那只能人工幫他做選擇。

          什么是“謂詞推進(jìn)”?

          當(dāng)在 STUDENTS 表上加索引后,


          CREATE INDEX IDX_STU_GENDER ON dbo.STUDENTS(STUDENT_GENDER) 

          執(zhí)行查詢:

          SELECT STUDENT_GENDER FROM dbo.STUDENTS 
          WHERE STUDENT_GENDER = N'UNKWN'


          標(biāo)記為紅框的部分,就是謂詞表達(dá)式。只有謂詞靠近原表,才能發(fā)揮減少數(shù)據(jù)訪問量的作用。




          --完--





          往期精彩:


          本號精華合集(三)

          外企一道 SQL 面試題,刷掉 494 名候選人

          我在面試數(shù)據(jù)庫工程師候選人時,常問的一些題

          零基礎(chǔ) SQL 數(shù)據(jù)庫小白,從入門到精通的學(xué)習(xí)路線與書單








          瀏覽 51
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久999 | 亚洲精品国产AV | 欧美成人网站在线 | 黄色录像视频大片 | 欧美久久一区二区三区四区视频 |