长大后想做什么?做回小孩!

0%

MySQL报错Column 'xxxx' in xxxx clause is ambiguous

虽然是一个常见的 SQL 报错缺陷,但是仍要记录一下解决的过程。

因为开始实习后好长时间不写学习记录了,这篇算是之后的学习记录再出发的一个起点!

错误出现

接口 500,本地 debug 轻松地发现了问题:是 Mapper 中的一条 SQL 有问题,Column 'short_name' in order clause is ambiguous

SQL并不复杂,数据库结构就不赘述了,四张表连接查询。直接看问题 SQL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 之前的查询结果加括号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(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 字段进行排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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_provincesm_basic_country 两张表中都存在 short_name 字段,但是只查询了 sm_basic_province 中的 short_name 字段。

删掉 CONVERT() 函数,尝试执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 的执行顺序是:

1
2
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT
每步执行都会产生一个虚表,作为当前执行步骤的输出和下一步执行时的输入,只有当最后一步执行完后这个虚表才会作为这条被执行 SQL 的最终结果。

按照这个执行顺序,CONVERT() 执行的时候应该已经没有重复的列了,说明这个函数是在 SELECT 之前就执行了,因为这时候的虚表中存在相同名称的列,所以报了 ambiguous 错误

至此解决了问题,但是原理层面仅停留在猜测,并没有找到权威的材料证明,也没有想到什么好的验证方法。

如果有知道原因的大神,还望赐教!

本篇未完待续。。。。


菜鸟本菜,不吝赐教,感激不尽!

更多题解源码和学习笔记:githubCSDNM1ng