在MySQL临时自动递增列的奇怪行为对HAVING子句

问题描述 投票:1回答:1

假设我们有一个名为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。

mysql auto-increment temp-tables having
1个回答
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成为一个关键词,你不应该使用它的情况下,你是否打算升级的别名。

© www.soinside.com 2019 - 2024. All rights reserved.