雖然是一個常見的 SQL 報錯缺陷,但是仍要記錄一下解決的過程。
因爲開始實習後好長時間不寫學習記錄了,這篇算是之後的學習記錄再出發的一個起點!
錯誤出現
接口 500,本地 debug 輕鬆地發現了問題:是 Mapper 中的一條 SQL 有問題,Column 'short_name' in order clause is ambiguous
SQL並不複雜,數據庫結構就不贅述了,四張表連接查詢。直接看問題 SQL:
SELECT
p.*,
lt.NAME AS province_name,
t.NAME country_name
FROM
sm_basic_province p
LEFT JOIN sm_basic_country bc ON p.country = bc.country_id
LEFT JOIN sm_basic_lang_tag t ON bc.NAME = t.tag_id
AND t.lang = 1
AND t.tenant_id = 'keycai'
LEFT JOIN sm_basic_lang_tag lt ON p.NAME = lt.tag_id
AND lt.lang = 1
AND lt.tenant_id = 'keycai'
WHERE
p.tenant_id = 'keycai'
AND bc.tenant_id = 'keycai'
ORDER BY CONVERT ( short_name USING gbk ) DESC
問題很明顯、很常見,short_name 字段名不明確。
錯誤解決
常規方法 ORDER BY 之前的查詢結果加括號:
(SELECT
p.*,
lt.NAME AS province_name,
t.NAME country_name
FROM
sm_basic_province p
LEFT JOIN sm_basic_country bc ON p.country = bc.country_id
LEFT JOIN sm_basic_lang_tag t ON bc.NAME = t.tag_id
AND t.lang = 1
AND t.tenant_id = 'keycai'
LEFT JOIN sm_basic_lang_tag lt ON p.NAME = lt.tag_id
AND lt.lang = 1
AND lt.tenant_id = 'keycai'
WHERE
p.tenant_id = 'keycai'
AND bc.tenant_id = 'keycai')
ORDER BY CONVERT ( short_name USING gbk ) DESC
錯誤依舊,SELECT * (…) t 生成中間表 t,再根據 t 表的 short_name 字段進行排序:
SELECT * FROM (SELECT
p.*,
lt.NAME AS province_name,
t.NAME country_name
FROM
sm_basic_province p
LEFT JOIN sm_basic_country bc ON p.country = bc.country_id
LEFT JOIN sm_basic_lang_tag t ON bc.NAME = t.tag_id
AND t.lang = 1
AND t.tenant_id = 'keycai'
LEFT JOIN sm_basic_lang_tag lt ON p.NAME = lt.tag_id
AND lt.lang = 1
AND lt.tenant_id = 'keycai'
WHERE
p.tenant_id = 'keycai'
AND bc.tenant_id = 'keycai') t
ORDER BY CONVERT ( short_name USING gbk ) DESC
至此,問題解決。
錯誤分析
可以去回看一下最開始出問題的 SQL,根據 SELECT 後面的字段,不該出現 short_name 不明確的問題。
雖然在 sm_basic_province
和 sm_basic_country
兩張表中都存在 short_name 字段,但是隻查詢了 sm_basic_province
中的 short_name 字段。
刪掉 CONVERT() 函數,嘗試執行:
SELECT
p.*,
lt.NAME AS province_name,
t.NAME country_name
FROM
sm_basic_province p
LEFT JOIN sm_basic_country bc ON p.country = bc.country_id
LEFT JOIN sm_basic_lang_tag t ON bc.NAME = t.tag_id
AND t.lang = 1
AND t.tenant_id = 'keycai'
LEFT JOIN sm_basic_lang_tag lt ON p.NAME = lt.tag_id
AND lt.lang = 1
AND lt.tenant_id = 'keycai'
WHERE
p.tenant_id = 'keycai'
AND bc.tenant_id = 'keycai'
ORDER BY short_name DESC
正確執行。
正常來講一條 SQL 的執行順序是:
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT
每步執行都會產生一個虛表,作爲當前執行步驟的輸出和下一步執行時的輸入,只有當最後一步執行完後這個虛表纔會作爲這條被執行 SQL 的最終結果。
按照這個執行順序,CONVERT()
執行的時候應該已經沒有重複的列了,說明這個函數是在 SELECT 之前就執行了,因爲這時候的虛表中存在相同名稱的列,所以報了 ambiguous 錯誤。
至此解決了問題,但是原理層面僅停留在猜測,並沒有找到權威的材料證明,也沒有想到什麼好的驗證方法。
如果有知道原因的大神,還望賜教!
本篇未完待續。。。。
菜鳥本菜,不吝賜教,感激不盡!