從 LeetCode 的題目再看 MySQL Explain

原文鏈接:https://mp.weixin.qq.com/s/d0fdqAzCE9FopNWBcMSYnw

Hello 大家好,我是阿粉,作爲 Java 工程師,數據庫用得最多的肯定是 MySQL,而對於 MySQL 公號前面也發過很多文章,感興趣的可以去翻翻。今天阿粉主要是想通過 LeetCode 上面的一個題目來再帶大家看看 MySQL 的變量使用以及通過 Explain 的解析看看SQL 的執行過程。雖然平時在工作中對於 MySQL 使用得很多,但是相對於 MySQL 的變量使用相對還是較少的,所以阿粉在剛看到的時候還是有點懵的,不過我相信大家肯定不會像阿粉一樣,畢竟能關注我們公衆號的讀者都是優秀的。

題目

題目描述:編寫一個 SQL 查詢,查找所有至少連續出現三次的數字。並且給了一個示例,阿粉按照題目給的示例在本地創建了 Logs 表格插入相應的數據,如下:

我們可以看到在給定上面的 Logs 表中, 1 是唯一連續出現至少三次的數字,所以最後輸出的結果是 1。

原始題目:LeetCode 180

剛看到題目的時候,阿粉一瞬間還是沒反應過來,不知道該如何着手進行,思索了一下考慮是否可以用自連接來實現呢?然後根據題目的意思就寫出瞭如下的 SQL。

SELECTDISTINCTl1.numFROM`Logs`l1,`Logs`l2,`Logs`l3WHEREl1.num = l2.numANDl2.num = l3.numANDl1.id = l2.id -1ANDl2.id = l3.id -1

寫完過後阿粉第一次提交,提示下面錯誤,可以看到是最後沒有將返回重命名,調整了一下 SQL,就l1.num 改成l1.num as ConsecutiveNums 再次提交,得到的第二張通過的圖。

看開始看到通過,阿粉還在想這道題也沒什麼啊,還是 so easy 的嘛。但是突然阿粉轉念一想,這個題目說的是連續出現,並沒有說 ID 是連續的啊,如果 ID 不連續的話,這種就不對了,還有就是如果需要連續 4 次出現的,5 次出現的數字呢?總不能一直自連接下去吧。如果寫成這樣那整個 SQL 就太不靈活了。

隨後阿粉就看了一下官方解答以及相關評論,果不其然雖然官方給出的解答跟阿粉的一致,但是下面的評論卻有很多小夥伴都在說這個 ID 不連續的問題。

既然反饋這種做法有問題,那自然就會有好事之者會想到解決辦法,果然評論區的一個大佬給出了下面的這種解法

剛看到這個解法的時候,阿粉一下子沒有看懂,把這個代碼進行了提交,果然也是正常地通過了。而且這種解法不會被出現幾次的條件給限制。抱着學習的心態,阿粉準備研究一下這條 SQL 裏面的內容。

SQL 拆解

首先這條 SQL 裏面有這麼幾個地方讓阿粉迷惑,第一個是@ 符號,然後是:= 然後還有個 case when then 語法,平日裏在 CRUD 的時候沒遇到過這種寫法,不過不知道沒關係,Google 一下就好了。網上查了下,@prev 表示的是聲明變量,:=操作是 MySQL 的賦值操作,case when then when 後面接的是判斷條件,條件成立則會返回then 後面的結果,需要注意的是 case 只會返回第一個符合條件的結果,剩下將會被忽略。

簡單的瞭解了上面幾個知識點過後,我們就可以對下面這條 SQL 進行拆解了。

selectdistinctNumasConsecutiveNumsfrom(selectNum,casewhen@currnet =Numthen@count:= @count+1when(@currnet :=Num)isnotnullthen@count:=1endasCNTfromLogs, (select@currnet :=null,@count:=0)ast)astempwheretemp.CNT >=3

最外層的 select distinct Num as ConsecutiveNums from () as temp where temp.CNT >= 3 ; 我們可以看到中間的小括號裏面被派生成了一個臨時表,表名叫做 temp,並且 temp 表中有兩個字段分別是Num,CNT。其實Num 則是表Logs 裏面的數字,CNT 則是連續出現的累積次數,最後的where temp.CNT >= 3 則是在根據要求連續出現的次數進行查詢。

派生語句SELECT Num,CASE WHEN @currnet=Num THEN @count:=@count+1 WHEN (@currnet:=Num) IS NOT NULL THEN @count:=1 END AS CNT FROM LOGS,(SELECT @currnet:=NULL,@count:=NULL) AS t 包含兩個部分,一個是Select 中的case when then 另一個是from 中的 (select @currnet:= null,@count := null) as t 其中select @currnet:= null,@count := null 也是一個派生表,這裏通過聲明兩個變量@currnet, @count 並賦值爲null 。

中間派生的表 temp 的內容如下,通過生成記錄每個數字出現的次數的臨時表來查詢數據。

下面我們通過explain 命令看下整個 SQL 的執行過程,:

從select_type中我們可以看到總共派生了兩個表,跟我們上面分析的一致;

ID 爲 3 的派生表的內容是select @current := null,@count := 0 定義兩個變量並賦值,並且 id 越大越先執行;

case 語句中第一個when 中判斷當前掃描到的 num 值與定義的變量是否一致,如果一致則 count 加一,不一致則進行下一個when 條件判斷,並將count 賦值爲 1 返回;

經過全表掃描過後,就得到了上面的中間表 temp 的內容;

不得不說,上面的方案是很完美的,不存在 ID 是否連續的問題,也不會多層自連接,而且也可以根據要求找出連續出現的次數,相對靈活。剛開始看到這個 SQL 的時候,阿粉並不清楚整個執行的過程,然後通過 explain 才漸漸明白整個執行過程, 而且對於在 SQL 中使用變量也有了一定的瞭解。

關於 explain 的詳解,感興趣的朋友可以去看公號之前發的文章MySQL 之 Explain 輸出分析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章