mariadb create函数返回随机ID

bvjveswy  于 2023-06-22  发布在  其他
关注(0)|答案(2)|浏览(162)

我想创建一个数据库函数,从数据库表中返回随机行。
我有下表

create table category
(
    id    bigint       not null primary key,
    color varchar(255) null
);

我创建了一个函数:

DELIMITER //
create or replace function random_category() returns int
begin
    return (select cc.id from category cc order by rand() limit 1);
end //
DELIMITER ;

当调用select random_category();时,我总是得到一个结果。
但是打电话的时候

select * from category c where id = random_category();

我收到空结果、多行结果和单行结果。
我用10.11.3-MariaDB-1:10.11.3+maria~ubu2204

0tdrvxhp

0tdrvxhp1#

这不是一个bug,这是预期的行为。
让我们把你的函数移到一个子查询中,让我们使用一个有几个值的序列:

# Attempt 1
select seq from seq_1_to_3 where seq=(select seq from seq_1_to_3 order by rand() limit 1);
Empty set (0,001 sec)

# Attempt 2
select seq from seq_1_to_3 where seq=(select seq from seq_1_to_3 order by rand() limit 1);
+-----+
| seq |
+-----+
|   3 |
+-----+
1 row in set (0,002 sec)

因为我们使用子查询而不是函数,所以EXPLAIN会更详细一些:

explain select seq from seq_1_to_3 where seq=(select seq from seq_1_to_3 order by rand() limit 1);
+------+----------------------+------------+-------+---------------+---------+---------+-------+------+----------------------------------------------+
| id   | select_type          | table      | type  | possible_keys | key     | key_len | ref   | rows | Extra                                        |
+------+----------------------+------------+-------+---------------+---------+---------+-------+------+----------------------------------------------+
|    1 | PRIMARY              | seq_1_to_3 | const | PRIMARY       | PRIMARY | 8       | const | 1    | Using where; Using index                     |
|    2 | UNCACHEABLE SUBQUERY | seq_1_to_3 | index | NULL          | PRIMARY | 8       | NULL  | 3    | Using index; Using temporary; Using filesort |
+------+----------------------+------------+-------+---------------+---------+---------+-------+------+----------------------------------------------+

UNCACHEABLE SUBQUERY意味着子查询的结果不能存储在子查询缓存中,并且必须在每次比较时执行。
让我们假设在第一次尝试中,子查询返回3、1和1,在第二次尝试中,它返回2、1和3。在第一次尝试中没有匹配(% 1!= 3,2!= 1和3!= 1),而在第二次尝试中3匹配3。
相关子查询(英语:Correlated Subqueries)(维基百科)
为了避免这种情况,您可以将SQL语句更改为

SELECT * from category ORDER BY RAND() LIMIT 1

但是ORDER BY RAND()非常慢,我建议你阅读Rick James的优秀文章“Fetching random rows from a table”。

krugob8w

krugob8w2#

将为每个单独的行单独调用函数。每个调用生成新的单个id值。因此输出行的数量会有所不同。
必须调用该函数一次。例如,与

select category.* 
from category 
JOIN (SELECT random_category() AS id) AS criteria USING (id);

你也可以尝试将你的函数定义为DETERMINISTIC:

create or replace function random_category() returns int DETERMINISTIC
begin
    return (select cc.id from category cc order by rand() limit 1);
end

AFAIR在这种情况下,函数输出被视为常量(它没有参数),并且它应该被调用一次。但我不确定

相关问题