假设我们有一个名为Test与单个列COL1表(VARCHAR(10))。对于这个例子的目的,假设它具有以下数据:
col1
a
b
c
d
现在,我要选择在COL1的数据,并添加临时自动增量列,我们将其称为等级。下面的查询这项工作:
SELECT
(@cnt := @cnt + 1) AS Rank, Test.col1
FROM Test
CROSS JOIN (SELECT @cnt := 0) AS tmp;
其结果是
Rank col1
1 a
2 b
3 c
4 d
没有问题为止。现在假设我们需要选择与排名的行大于1。我做到以下几点:
SELECT
(@cnt := @cnt + 1) AS Rank, Test.col1
FROM Test
CROSS JOIN (SELECT @cnt := 0) AS tmp
having Rank > 1;
结果就变成
Rank col1
3 b
5 c
7 d
请注意,在排名列第一项是3而不是2作为一个自然希望。可有人请指出这个可能的原因是什么?
为什么排名(临时自动递增)跳形式2至3?
MySQL的版本是5.6.32-78.1。
有很多意想不到的事情,当你使用变量,可以发生。其中一些列于the manual:
在SELECT语句中,当发送到客户机的每个选择表达式仅被评估。这意味着,在一个HAVING,GROUP BY,或ORDER BY子句,指的是在选择表达式列表不工作分配的值的变量如预期
具体细节有所不同,但在你的情况,你的增量计算两次(一次是在select
,一次having
),因此,它基本上表现得像
SELECT (@cnt := @cnt + 1) AS Rank, Test.col1
FROM Test
CROSS JOIN (SELECT @cnt := 0) AS tmp
having (@cnt := @cnt + 1) > 1;
解决此问题的办法是强制MySQL事先计算表达式。
正确的解决方案(尽可能使用变量时)大概是:
select (
SELECT (@cnt := @cnt + 1) AS Rank, Test.col1
FROM Test
CROSS JOIN (SELECT @cnt := 0) AS tmp
) x
where Rank > 1;
where Rank > 1
(无子查询)可能是你原本打算无论如何做。
但你也可以通过其他方式做到这一点。在你的榜样,你应该能够使用
SELECT (@cnt := @cnt + 1) AS Rank, Test.col1
FROM Test
CROSS JOIN (SELECT @cnt := 0) AS tmp
group by col1
having Rank > 1;
除非col1
是主键候选者(例如唯一不为空),MySQL需要实际计算表达式做group by
。在另一方面,如果是col1
例如主键,MySQL能够优化group by
路程,就回到了原来的状况 - 如果它不这样做可能取决于你的MySQL版本,但据我所知的MySQL 5.6应该这样做。
附注:由于rank
在MySQL 8成为一个关键词,你不应该使用它的情况下,你是否打算升级的别名。