MySQL without GROUP BY directly HAVING returns empty problem analysis

  • 2020-06-01 11:10:44
  • OfStack

There is a table, id is the primary key, so this way you can return a record:


 " SELECT * FROM t HAVING id=MIN(id); " 

But just replace MIN with MAX, so the return is empty:

 " SELECT * FROM t HAVING id=MAX(id); " 

Why is that?
Let's do an experiment to verify this.
This is the table structure, initializes the two records, and then tests them:


root@localhost : plx 10:25:10> show create table t2\G
*************************** 1. row ***************************
       Table: t2
Create Table: CREATE TABLE `t2` (
  `a` int(11) DEFAULT NULL,
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8

root@localhost : plx 10:25:15> select * from t2;
+------+----+
| a    | id |
+------+----+
|    1 |  1 |
|    1 |  3 |
+------+----+
2 rows in set (0.00 sec)

root@localhost : plx 10:25:20> SELECT * FROM t2 HAVING id=MIN(id);
+------+----+
| a    | id |
+------+----+
|    1 |  1 |
+------+----+
1 row in set (0.00 sec)

root@localhost : plx 10:25:30> SELECT * FROM t2 HAVING id=MAX(id);
Empty set (0.00 sec)

At first glance, it seems like it really is. How could it be? I'm going to try 1 again, and I'm going to change the a field to 10, and then I'm going to try the a field:


root@localhost : plx 10:26:58> select * from t2;
+------+----+
| a    | id |
+------+----+
|   10 |  1 |
|    1 |  3 |
+------+----+
2 rows in set (0.00 sec)

root@localhost : plx 10:28:20> SELECT * FROM t2 HAVING a=MAX(a);
+------+----+
| a    | id |
+------+----+
|   10 |  1 |
+------+----+
1 row in set (0.00 sec)

root@localhost : plx 10:28:28> SELECT * FROM t2 HAVING a=MIN(a);
Empty set (0.00 sec)

I'm going to erase, this time MAX will return, MIN will not. Why is that?

narrator
1 generally speaking, the HAVING clause is used in conjunction with GROUP BY. The use of HAVING alone is not in conformity with the specification.
But MySQL will do one rewrite, plus one GROUP BY NULL, "SELECT * FROM HAVING id=MIN(id)" will be rewritten as "SELECT * FROM t GROUP NULL HAVING id=MIN(id)", so that the syntax is compliant.
Continue to...
But what happens to this GROUP BY NULL? After reviewing the code and testing, it can be proved that GROUP BY NULL is equivalent to LIMIT 1:


root@localhost : plx 10:25:48> SELECT * FROM t2 GROUP BY NULL;
+------+----+
| a    | id |
+------+----+
|   10 |  1 |
+------+----+
1 row in set (0.00 sec)

In other words, after GROUP BY NULL, there will only be one group, which is the first row of data.
But if this is the case, MIN and MAX should result in 1, then MAX and MIN1 should not result in 1, and MIN1 should not result in 1. Why is that? Do another test.
Modify the data 1, and then directly view the MIN/MAX values:


root@localhost : plx 10:26:58> select * from t2;
+------+----+
| a    | id |
+------+----+
|   10 |  1 |
|    1 |  3 |
+------+----+
2 rows in set (0.00 sec)

root@localhost : plx 10:27:04> SELECT * FROM t2 GROUP BY NULL;
+------+----+
| a    | id |
+------+----+
|   10 |  1 |
+------+----+
1 row in set (0.00 sec)

root@localhost : plx 10:30:21> SELECT MAX(a),MIN(a),MAX(id),MIN(id) FROM t2 GROUP BY NULL;
+--------+--------+---------+---------+
| MAX(a) | MIN(a) | MAX(id) | MIN(id) |
+--------+--------+---------+---------+
|     10 |      1 |       3 |       1 |
+--------+--------+---------+---------+
1 row in set (0.00 sec)

Did you find a problem?
The value of the MAX/MIN function is global, not within the group LIMIT 1.
Therefore, when GROUP BY NULL, the MAX/MIN function takes the maximum and minimum values of all the data!
So, "SELECT * FROM t HAVING id=MIN(id)" is essentially "SELECT * FROM FROM t HAVING id=1", you can return a record, And "SELECT * FROM t HAVING HAVING id=MAX(id)" is essentially "SELECT * FROM t HAVING id=3", of course there is no record returned, that's the root of the problem.
Test 1: GROUP BY a, that's right, there are only 1 row in each group, so MAX/MIN1 is as large as the maximum and minimum values in the group.


root@localhost : plx 11:29:49> SELECT MAX(a),MIN(a),MAX(id),MIN(id) FROM t2 GROUP BY a;
+--------+--------+---------+---------+
| MAX(a) | MIN(a) | MAX(id) | MIN(id) |
+--------+--------+---------+---------+
|      1 |      1 |       3 |       3 |
|     10 |     10 |       5 |       5 |
+--------+--------+---------+---------+
2 rows in set (0.00 sec)

GROUP BY NULL, MAX/MIN behavior, is the essence of the problem, so try to use standard grammar, before playing with SQL, 1 must make sure that its behavior is consistent with the 1 it understands.


Related articles: