2011-02-06 7 views
1

皆様に良いと良い日曜日。 各グループからN個のランダムレコードを選択する必要があります。グループごとにN個のランダムレコードを選択してください。

私はあなたのアイデアを与えるために、このようにリコールこの店舗の手順

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 ; 

書いたXランダムレコードを選択するためにQuassnoi

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

のクエリ最低料金:

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のクエリ、さらにはtハフはとてもパフォーマンスが良いです。大きなレコーセットでは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秒かかります。あなたのストアドプロシージャは私にError Codeを与え続けます:1473 varcharの値を20000に増やしても、selectのためにネストするレベルが高すぎます。クエリに含まれる共用体に制限があるかどうかはわかりません。

答えて

2

私は、理解しやすくするために、手順から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) [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

こんにちはクロドアルド。まず私はあなたが私を捧げた時代に感謝します。 +1だけです。私の返事が遅れて申し訳ありません.1百万のレコードと100の異なるグループを持つテーブルであなたのSPを試していました。 spは私にこのエラー "選択のためのネストのレベルが高すぎる" –

+0

私のspは非常に遅いです(約25秒)が、少なくとも私に正しい結果を返します。 –

1

ワウ。それは非常に単純なことをする複雑な方法です。これを試してください:

逐次IDがあると仮定します(それ以外の場合は行が得られません)。

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つのランダムな行を与えます。

複数の行を取得するには、十分な行が得られるまでストアドプロシージャまたはサーバープログラムでループするか、unionを使用するクエリをプログラムで作成します。 RANDを使用する(0)の代わりにRAND()の呼び出しごとに異なる乱数を得ることを意味していること

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

注: 例えば、これはあなたに3つのランダムな行を与えるだろう。 RAND()は1つのステートメント内の各呼び出しに同じ値を与えます(したがって、RAND()を使用すると、複数の行は得られません)。

ユニオンの使用にはいくつかの欠点があります。同じ行を偶然2回得ることは可能です。あなたが十分な行を得るまでこれをプログラム的に呼び出すことはより安全です。

、より良い性能を与えるランダム

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

のような単純なクエリのIDを選択し、Java(またはPerlや何でも)を持っているJavaのようなものを使用し、ランダムなIDを持つリストを埋めるために - あなたは避けるだろうIDの範囲を毎回取得する必要があるという非効率性です。

あなたのIDが連続していない場合 - 効率的な方法がありますが、その説明は長くなります。

+0

random_provaのリストから選択した行を単純に除外できます。これを行う1つの方法は、選択した値を配列にプッシュすることです。その配列内のものを除外します。しかし、他の方法も利用可能です。返信いただきありがとうございます。 –

+0

たぶん私は間違っていますが、私は各グループからN種類のレコードが必要であるとは考えていないようです。 IDが連続しないことがあります。私はプログラミング言語に関わらずSQLソリューションを探しています。 @Syed。私はあなたのアドバイスを効率的に実装する方法を知らない。 –