Mysql 每组选择N个随机记录

Mysql 每组选择N个随机记录,mysql,random,Mysql,Random,大家好,星期天好。 我需要从每组中选择N个随机记录 从查询Quassnoi开始 为了选择X随机记录,我编写了这个存储过程 delimiter // drop procedure if exists casualiPerGruppo // create procedure casualiPerGruppo(in tabella varchar(50),in campo varchar(50),in numPerGruppo int) comment 'Selezione di N record

大家好,星期天好。 我需要从每组中选择N个随机记录

从查询Quassnoi开始

为了选择X随机记录,我编写了这个存储过程

delimiter //
drop procedure if exists casualiPerGruppo //
create procedure casualiPerGruppo(in tabella varchar(50),in campo varchar(50),in numPerGruppo int)
comment 'Selezione di N record casuali per gruppo'
begin
declare elenco_campi varchar(255);
declare valore int;
declare finite int default 0;
declare query1 varchar(250);
declare query2 varchar(250);
declare query3 varchar(250);
declare query4 varchar(250);
declare cur_gruppi cursor for select gruppo from tmp_view;
declare continue handler for not found set finite = 1;

drop table if exists tmp_casuali;
set @query1 = concat('create temporary table tmp_casuali like ', tabella);
prepare stmt from @query1;
execute stmt;
deallocate prepare stmt;

set @query2 = concat('create or replace view tmp_view as select ',campo,' as gruppo from ',tabella,' group by ',campo);
prepare stmt from @query2;
execute stmt;
deallocate prepare stmt;

open cur_gruppi;
mio_loop:loop
fetch cur_gruppi into valore;
    if finite = 1 then
        leave mio_loop;
    end if;

set @query3 = concat("select group_concat(column_name) into @elenco_campi
              from information_schema.columns
                      where table_name = '",tabella,"' and table_schema = database()");
prepare stmt from @query3;
execute stmt;
deallocate prepare stmt;

set @query4 = concat('insert into tmp_casuali select ',
             @elenco_campi,' from (
                     select  @cnt := count(*) + 1,
                     @lim :=', numPerGruppo,
                         ' from ',tabella,
                     ' where ',campo,' = ', valore,
                     ' ) vars
                     straight_join
                    (
                    select  r.*,
                    @lim := @lim - 1
                    from ', tabella, ' r
                    where   (@cnt := @cnt - 1)
                    and rand() < @lim / @cnt and ', campo, ' = ', valore ,
                    ') i');

prepare stmt from @query4;
execute stmt;
deallocate prepare stmt;

end loop;
close cur_gruppi;
select * from tmp_casuali;
end //
delimiter ;
我的问题是,尽管Quassnoi查询的性能非常好,但在一个大的记录上甚至需要1秒。因此,如果我在sp中多次应用它,总时间会增加很多

你能给我建议一个更好的方法来解决我的问题吗? 提前谢谢

编辑

create table `prova` (
  `id` int(11) not null auto_increment,
  `id_gruppo` int(11) default null,
  `prog` int(11) default null,
  primary key (`id`)
) engine=myisam charset=latin1;

delimiter //
drop procedure if exists inserisci //
create procedure inserisci(in quanti int)
begin
declare i int default 0;
while i < quanti do
insert into prova (id_gruppo,prog) values (
                        (floor(1 + (rand() * 100))),
                        (floor(1 + (rand() * 30)))
                       );
set i = i + 1;
end while;
end //

delimiter ;

call inserisci(1000000);

给我200条记录,大约需要23秒。您的存储过程不断给我错误代码:1473对于select来说嵌套级别太高,即使我将varchar值增加到20000。我不知道查询中涉及的工会是否有任何限制

哇。这是一个复杂的方法来做一些非常简单的事情。试试这个:

假设您有顺序ID(否则您将无法获得任何行)

这将给您一个随机行

要获得多行,可以在存储过程或服务器程序中循环,直到获得足够的行,也可以通过编程创建使用union的查询。 例如,这将为您提供3个随机行:

select * from random_prova
union
select * from random_prova
union
select * from random_prova;
请注意,使用RAND(0)而不是RAND()意味着为每次调用获取不同的随机数。RAND()将为one语句中的每个调用提供相同的值(因此将RAND()与union一起使用不会提供多行)

使用union有一些缺点-可能会两次偶然获得同一行。通过编程调用此函数,直到获得足够的行,这样更安全

为了获得更好的性能,可以使用java之类的工具为一个简单的查询随机选择ID,如

select * from prova where id in (...)
让java(或perl或其他什么)用随机id填充列表——这样可以避免每次都必须获取id范围的低效性


如果你的ID不是连续的,那就发帖子吧——这是一种有效的方法,但我认为它的解释很长

我从过程中删除了tabella和campo参数,只是为了更容易理解。我相信你能把它们带回来

delimiter //
drop procedure if exists casualiPerGruppo //
create procedure casualiPerGruppo(in numPerGruppo int)
begin
declare valore int;
declare finite int default 0;
declare query_part varchar(200);
declare query_union varchar(2000);
declare cur_gruppi cursor for select distinct id_gruppo from prova;
declare continue handler for not found set finite = 1;

create temporary table resultset (id int, id_gruppo int, altro varchar(10));

set @query_part = 'select id, id_gruppo, altro from (select id, id_gruppo, altro from prova where id_gruppo = @id_gruppo order by rand() limit @numPerGruppo) ss@id_gruppo';
set @query_part = replace(@query_part, '@numPerGruppo', numPerGruppo);
set @query_union = '';

open cur_gruppi;
mio_loop:loop
fetch cur_gruppi into valore;
    if finite = 1 then
        leave mio_loop;
    end if;

set @query_union = concat(@query_union, concat(' union ', @query_part));
set @query_union = replace(@query_union, '@id_gruppo', valore);

end loop;
close cur_gruppi;

set @query_union = substr(@query_union, 8);
set @query_union = concat('insert into resultset ', @query_union);

prepare stmt from @query_union;
execute stmt;
deallocate prepare stmt;
select * from resultset order by id_gruppo, altro;
drop table resultset;

end //
delimiter ;

您只需在random_prova中将所选行从列表中排除即可。一种方法是将选定的值推送到数组中。排除该数组中的那些。但是也有其他的方法。谢谢你的回复。也许我错了,但在我看来,你不认为我需要不同的记录来自各组。可能会出现ID不连续的情况。我正在寻找一个不涉及任何编程语言的sql解决方案@赛义德。我不知道如何有效地执行你的建议。@nick你试过这个吗?有什么问题吗?嗨,克洛多尔多。首先感谢你为我奉献的时间+我只是为了它。很抱歉,我的回复被耽搁了。我正在一个有100个不同组和100万条记录的表上尝试您的sp。sp给了我这个错误“太高的嵌套级别,无法选择”。我的sp非常慢(大约25秒),但至少返回了正确的结果。正如好奇的是,它是否可以处理数据的子集?你有多少记忆?对于100个组,查询字符串的长度至少为15000个字符。所以这是必要的:声明查询联合varchar(20000).Hi。我已经编辑了我的帖子。对如果我修改sp“inserisci”以使用10个不同的id_gruppo(而不是100个)填充我的表,那么您的sp可以正常工作并正确地为我提供20条记录。它不适用于大量不同的群体。
select * from random_prova
union
select * from random_prova
union
select * from random_prova;
select * from prova where id in (...)
delimiter //
drop procedure if exists casualiPerGruppo //
create procedure casualiPerGruppo(in numPerGruppo int)
begin
declare valore int;
declare finite int default 0;
declare query_part varchar(200);
declare query_union varchar(2000);
declare cur_gruppi cursor for select distinct id_gruppo from prova;
declare continue handler for not found set finite = 1;

create temporary table resultset (id int, id_gruppo int, altro varchar(10));

set @query_part = 'select id, id_gruppo, altro from (select id, id_gruppo, altro from prova where id_gruppo = @id_gruppo order by rand() limit @numPerGruppo) ss@id_gruppo';
set @query_part = replace(@query_part, '@numPerGruppo', numPerGruppo);
set @query_union = '';

open cur_gruppi;
mio_loop:loop
fetch cur_gruppi into valore;
    if finite = 1 then
        leave mio_loop;
    end if;

set @query_union = concat(@query_union, concat(' union ', @query_part));
set @query_union = replace(@query_union, '@id_gruppo', valore);

end loop;
close cur_gruppi;

set @query_union = substr(@query_union, 8);
set @query_union = concat('insert into resultset ', @query_union);

prepare stmt from @query_union;
execute stmt;
deallocate prepare stmt;
select * from resultset order by id_gruppo, altro;
drop table resultset;

end //
delimiter ;