MariaDB中的奇怪行为或兰德:单兰特提供超过1个结果

时间:2018-03-17 23:12:56

标签: mysql random

运行以下查询时:

SELECT productid 
FROM product 
WHERE productid=ROUND(RAND()*(SELECT MAX(productid) FROM product));

结果应该是0或1个结果(0表示由于数据缺口,如果找到记录则为1),但是它会导致多次结果很多次(很容易重现,90%的查询超过1结果)。

示例输出:

+-----------+
| productid |
+-----------+
|     11701 |
|     20602 |
|     22029 |
|     24994 |
+-----------+

(DB中的记录数约为30k)。

运行单个SELECT RAND()始终会产生一个结果。

说明:

explain SELECT productid  FROM product  WHERE productid=ROUND(RAND()*(SELECT MAX(productid) FROM product));
+----+-------------+---------+------------+-------+---------------+--------------+---------+------+-------+----------+------------------------------+
| id | select_type | table   | partitions | type  | possible_keys | key          | key_len | ref  | rows  | filtered | Extra                        |
+----+-------------+---------+------------+-------+---------------+--------------+---------+------+-------+----------+------------------------------+
|  1 | PRIMARY     | product | NULL       | index | NULL          | idx_prod_url | 2003    | NULL | 31197 |    10.00 | Using where; Using index     |
|  2 | SUBQUERY    | NULL    | NULL       | NULL  | NULL          | NULL         | NULL    | NULL |  NULL |     NULL | Select tables optimized away |
+----+-------------+---------+------------+-------+---------------+--------------+---------+------+-------+----------+------------------------------+

谁可以解释这种行为?

跟进: 按照Martin的评论重写了以下内容:

SELECT productid FROM product 
WHERE productid=(SELECT ROUND(RAND()*(SELECT MAX(productid) FROM product)));

说明:

explain SELECT productid FROM product WHERE productid=(SELECT ROUND(RAND()*(SELECT MAX(productid) FROM product)));
+----+----------------------+---------+------------+-------+---------------+--------------+---------+------+-------+----------+------------------------------+
| id | select_type          | table   | partitions | type  | possible_keys | key          | key_len | ref  | rows  | filtered | Extra                        |
+----+----------------------+---------+------------+-------+---------------+--------------+---------+------+-------+----------+------------------------------+
|  1 | PRIMARY              | product | NULL       | index | NULL          | idx_prod_url | 2003    | NULL | 31197 |   100.00 | Using where; Using index     |
|  2 | UNCACHEABLE SUBQUERY | NULL    | NULL       | NULL  | NULL          | NULL         | NULL    | NULL |  NULL |     NULL | No tables used               |
|  3 | SUBQUERY             | NULL    | NULL       | NULL  | NULL          | NULL         | NULL    | NULL |  NULL |     NULL | Select tables optimized away |
+----+----------------------+---------+------------+-------+---------------+--------------+---------+------+-------+----------+------------------------------+

然而,尽管计划已经改变,但行为保持不变。

跟进2

使用INNER JOIN,行为消失:

SELECT a.productid  FROM product a 
INNER JOIN (SELECT ROUND(RAND()*(SELECT MAX(productid))) as productid 
   FROM product) b ON a.productid=b.productid;

说明:

explain SELECT a.productid  FROM product a INNER JOIN (SELECT ROUND(RAND()*(SELECT MAX(productid))) as productid FROM product) b ON a.productid=b.productid;
+----+--------------------+------------+------------+--------+---------------+--------------+---------+-------+-------+----------+----------------+
| id | select_type        | table      | partitions | type   | possible_keys | key          | key_len | ref   | rows  | filtered | Extra          |
+----+--------------------+------------+------------+--------+---------------+--------------+---------+-------+-------+----------+----------------+
|  1 | PRIMARY            | <derived2> | NULL       | system | NULL          | NULL         | NULL    | NULL  |     1 |   100.00 | NULL           |
|  1 | PRIMARY            | a          | NULL       | const  | PRIMARY       | PRIMARY      | 4       | const |     1 |   100.00 | Using index    |
|  2 | DERIVED            | product    | NULL       | index  | NULL          | idx_prod_url | 2003    | NULL  | 31197 |   100.00 | Using index    |
|  3 | DEPENDENT SUBQUERY | NULL       | NULL       | NULL   | NULL          | NULL         | NULL    | NULL  |  NULL |     NULL | No tables used |
+----+--------------------+------------+------------+--------+---------------+--------------+---------+-------+-------+----------+----------------+

2 个答案:

答案 0 :(得分:0)

试试这个:

SELECT productid 
FROM product 
ORDER BY rand() LIMIT 1;

有关其他随机选择选项,请参阅MySQL Select Random Records

答案 1 :(得分:0)

说明:

执行原始帖子中的查询时,Maria说:“我需要查看表,产品中的数据,所以让我从第一行开始。然后,“让我检查WHERE子句以查看是否应使用此行。”那时,Maria计算一个随机数,然后将其乘以最大乘积,然后将其舍入为整数。她检查该整数是否等于第一行中的productid。如果不是,那么她会忘记该行。如果是这样,那么她从该行中选择productid并保留它。无论哪种方式,她都将继续前进到第二行。她计算了一个(新的)随机数,将其乘以最大的产品编号,然后检查它是否等于第二行中的产品编号。如果没有,她继续前进。如果是这样,她会将该产品ID添加到她的选定值列表中。她在第三行上做同样的事情,等等。然后,最终输出是一个零到MAX(productid)个productid值的列表,每个值都以1 / MAX(productid)的概率独立选择。

执行来自后续2的查询时,联接的左侧只是一个表。对于连接的右侧,Maria表示:“我需要查看产品表中的数据。由于没有WHERE子句,因此我将查看整个表。哦,我有一个聚合函数MAX( productid),在SELECT子句中。那给了我整个表中的一个值。”她计算一个随机数并将其乘以该值,从而创建一个表b,该表b由单列productid和一行组成。然后,将该表与表product进行内部连接,以查找具有匹配productid的每一行,该行恰好是一行,然后选择该行中的productid。

请注意,如果您要查找的只是productid,而您不需要选定行中的任何其他数据,那么联接的右侧就是您所需要的。

SELECT ROUND(RAND()*(SELECT MAX(productid))) as productid from product;

还请注意,ROUND可能不是您要尝试执行的正确功能。这可能就是您真正想要的:

SELECT FLOOR(1+RAND()*(SELECT MAX(productid))) as productid from product;