天天看點

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

本節書摘來自異步社群出版社《mysql排錯指南》一書中的第1章,第1.2節,作者:【美】sveta smirnova(斯維特 斯米爾諾娃),更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

這是使用者回報的另一個非常常見的問題,主要的現象有:使用者看不到更新的結果、展示的順序錯誤或者查詢到了非預期的結果。

這個問題主要有兩方面的原因:一方面是你的select查詢有誤;另一方面是資料庫中的資料和你想象的不同。我先介紹第一種情況。

在我規劃本節示例的時候,我考慮要麼使用真實的示例,要麼使用我自己設計的小場景。真實的示例可能占用大量篇幅,但是我自己設計的示例可能對你沒有什麼幫助,因為沒有人會寫出那樣的代碼。是以,我選擇使用典型的真實示例作為示例,隻是大幅簡化了它們。

第一個示例是使用者在大量使用join時的常見錯誤。我們将使用之前介紹的例1-1中的表。這張表包含了mysql中會引起一些常見使用錯誤的特性,這些特性是我在mysql支援團隊中收集的。每個錯誤在items表中都有一行記錄。我還有一張關聯資源資訊的links表。因為條目和關聯資訊之間是多對多的關系,是以我通過items_links關聯表把它們聯系起來。下面是items表和items_links表的定義(在這個示例中不需要links表):

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

我編寫的第一條查詢正常運作,并且似乎傳回了合理的結果:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

……直到我把傳回的數值與關聯總數進行比較的時候,我才發現:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

查詢到的關聯資訊數比關聯表的記錄還多,這怎麼可能?

我們再來檢查一下我特意編寫的這個查詢。它很簡單,僅僅包含兩部分,一個子查詢:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

和一個外部查詢:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

子查詢是開始錯誤排查的好切入點,因為它可以獨立運作。是以,我們可以預期一個完整的結果集:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

令人驚訝的是,我們居然有一個輸入錯誤,事實上items_links表中并沒有id字段,而是iid字段(代表items的id)。如果我們重寫該條查詢,讓它使用正确的辨別符,它便可正常運作:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

我們剛剛學習了一個新的調試技巧。如果一個select查詢沒有按預期工作,可以将其拆分成小段語句,然後分析每一部分直到你找到産生錯誤行為的原因。

提示 提示

如果你通過表名.列名的格式指定完整的列名,那麼你可以從一開始就避免這個錯誤,因為你會立即獲得錯誤:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

mysql指令行用戶端是一個非常好的測試工具,該工具包含在mysql的安裝包中。第6章将讨論這個重要的工具。

然而,為什麼mysql在執行原始查詢語句的時候沒有傳回同樣的錯誤呢?這是因為在items表有一個名為id的列,是以mysql認為我們想要執行一個依賴子查詢,結果實際上從items_links表中查詢了items.id。“依賴子查詢”是指引用外部查詢中字段的查詢。

我們也可以借助explain extends指令,通過show warnings來查找這個錯誤。如果我們用該指令運作原始查詢,會得到:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

explain extended輸出的2.row表明該子查詢實際上是依賴的:select_type是dependent subquery。

在結束這個示例之前,我想再介紹一個可以在請求語句涉及很多表的時候,幫助你避免迷茫的小技巧。要知道當你面對10個甚至更多表的連接配接時,即使你很了解它們應該怎麼連接配接,你也會感到迷茫。

上面示例中一個值得注意的地方是show warnings的輸出資訊。mysql伺服器不是總按照語句輸入的順序執行它,而是調用優化器去構造一個更好的執行計劃,是以使用者通常都會很快得到傳回結果。在explain extended之後,show warnings指令展示的就是優化後的查詢。

在該示例中,show warnings的輸出包含兩個主要資訊。第一個是:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

這條資訊明确指出伺服器是通過items表而不是items_links表解析id的值。

第二條資訊包含了優化過的語句:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

這個輸出資訊也指出伺服器是從items表接受id的值。

現在我們來對比一下正确的查詢和之前列出的錯誤查詢的explain extended的結果:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

這次優化過的語句看起來完全不同了,并且确實像我們預期的那樣比較items.id和items_links.iid的值。

我們剛剛學習了另一教訓:在explain extended指令之後使用show warnings指令檢視查詢是如何優化(與執行)的。

在正确的查詢中,select_type的值仍然是dependent subquery。我們已經通過items_links表來解析字段的名稱了,為什麼結果仍是那樣?答案從show warnings中下面這部分輸出開始:

《MySQL排錯指南》——1.2 SELECT傳回錯誤結果

子查詢仍然顯示是依賴的,因為外部查詢子句中的id需要子查詢去檢查與内部查詢對應的每行裡的iid值。這個問題在mysql社群bug資料庫的12106号報告的讨論中提出。

這個bug報告給我們了另一個重要的教訓:如果你懷疑你的查詢的執行行為,可以通過有效的資源去擷取資訊。社群bug資料庫就是這樣的一種資源。

select查詢運作異常可能有很多不同的原因,但是查找問題的一般方法總是相同的。

将查詢分解成小段,然後依次執行它們直到你發現問題的原因。

使用explain extended,然後使用show warnings指令去獲得查詢執行計刬及其實際運作方式的相關資訊。

如果你不了解mysql伺服器的執行狀況,可以使用網際網路和其他有效的資源去獲得資訊。附錄提供了非常有用的資源清單。