2011-02-06 5 views
1

Hallo и хорошее воскресенье для всех. Мне нужно выбрать N случайных записей из каждой группы.Выберите N случайных записей в группе

Начиная с запроса Quassnoi

http://explainextended.com/2009/03/01/selecting-random-rows/

выбрать 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 ; 

, что я помню, таким образом, чтобы дать вам идею:

create table prova (
id int not null auto_increment primary key, 
id_gruppo int, 
altro varchar(10) 
) engine = myisam; 


insert into prova (id_gruppo,altro) values 
(1,'aaa'),(2,'bbb'),(3,'ccc'),(1,'ddd'),(1,'eee'),(2,'fff'), 
(2,'ggg'),(2,'hhh'),(3,'iii'),(3,'jjj'),(3,'kkk'),(1,'lll'),(4,'mmm'); 

call casualiPerGruppo('prova','id_gruppo',2); 

Моя проблема заключается в том, что Quassnoi query, даже t hough очень эффективен, он занимает ровно 1 секунду на большом рекордере. Поэтому, если я применяю его в моем sp несколько раз, общее время увеличивается.

Можете ли вы предложить мне лучший способ решить мою проблему? Заранее благодарен

EDIT.

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); 

@Clodoaldo: Моя хранимая процедура

call casualipergruppo('prova','id_gruppo',2); 

дает мне 200 записей и занимает около 23 секунд. Ваша хранимая процедура продолжает давать мне код ошибки: 1473 Слишком высокий уровень вложенности для выбора, даже если я увеличиваю значение varchar до 20000. Я не знаю, существует ли ограничение на союзы, участвующие в запросе.

ответ

2

Я удалил параметры табеллы и кампуса из процедуры, чтобы упростить ее понимание. Я уверен, что ты сможешь вернуть их.

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) [email protected]_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 ; 
+0

@nick Вы попробовали это? Любая проблема? –

+0

Привет, Clodoaldo. Прежде всего, спасибо за то, что вы посвятили меня. +1 только для этого. Извините за задержку моего ответа. Я пробовал ваш sp на столе с 1 миллионом записей и 100 различными группами. Sp дает мне эту ошибку «Слишком высокий уровень гнездования для выбора» –

+0

My sp очень медленный (около 25 секунд), но, по крайней мере, возвращает мне правильный результат. –

1

Ничего себе. Это сложный способ сделать что-то очень простое. Попробуйте следующее:

Предполагая, что у вас есть последовательные идентификаторы (в противном случае вы не можете получить строки).

create view random_prova as 
select * from prova 
where id = (select min(id) from prova) + 
    floor(RAND(0) * (select max(id) - min(id) from prova)); 

Это даст вам 1 случайный ряд.

Чтобы получить несколько строк, выполните цикл в хранимой процедуре или на серверной программе, пока не получите достаточное количество строк, или программно создайте запрос, который использует объединение. например, это даст вам 3 случайные строки:

select * from random_prova 
union 
select * from random_prova 
union 
select * from random_prova; 

Обратите внимание, что с помощью RAND (0) вместо RAND() означает получение другого случайного числа для каждого вызова. RAND() даст одно и то же значение для каждого вызова в одном из операторов (поэтому использование RAND() с соединением не даст вам нескольких строк).

Есть некоторые недостатки с использованием объединения - можно получить одну и ту же строку дважды случайно. Программно называть это, пока вы не получите достаточное количество строк, безопаснее.

Чтобы дать более высокую производительность, использовать что-то вроде Java, чтобы случайным образом выбирать идентификаторы для простого запроса, как

select * from prova where id in (...) 

и есть Java (или Perl или любой другой), заполнить список со случайными идентификаторами - вы бы избежать неэффективность в том, чтобы каждый раз получать идентификационный диапазон.

Сообщение, если ваши идентификаторы не являются последовательными - есть эффективный способ, но я объясняю его длинным.

+0

Вы можете просто исключить выбранную строку из списка в random_prova. Один из способов сделать это - нажать выбранные значения в массив. Исключить те из этого массива. Но доступны и другие методы. –

+0

Спасибо за ваш ответ. Возможно, я ошибаюсь, но мне кажется, что вы не считаете, что мне нужны N разных записей из каждой группы. Возможно, что идентификаторы не являются последовательными. Я ищу решение sql без участия какого-либо языка программирования. @Syed. Я не знаю, как эффективно использовать ваши советы. –

Смежные вопросы