sql >> データベース >  >> RDS >> Mysql

データベースからランダムな行をエコーする方法は?

    ここでは2つのソリューションを紹介します。これらの提案されたソリューションは両方ともmysqlのみであり、コンシューマーとして任意のプログラミング言語で使用できます。 PHPはこれには非常に遅すぎますが、消費者である可能性があります。

    より迅速なソリューション :より高度なプログラミング手法を使用すると、約2/10秒で1900万行のテーブルから1000行のランダム行を取得できます。

    遅いソリューション :非電力プログラミング手法では約15秒かかります。

    ちなみに、どちらもこちら で見られるデータ生成を使用しています 私が書いたこと。これが私の小さなスキーマです。私はそれを使用し、 TWOを続行します 私が1900万行になるまで、あそこにもっと多くの自己挿入が見られます。だから私はそれを再び示すつもりはありません。しかし、これらの19M行を取得するには、それを確認し、さらに2つの挿入を行うと、19M行になります。

    最初に遅いバージョン

    まず、遅い方法です。

    select id,thing from ratings order by rand() limit 1000;
    

    15秒で1000行を返します。

    より高速なソリューション

    これは説明するのが少し複雑です。その要点は、ランダムな数値を事前に計算し、in clauseを生成することです。 終了 ランダムな数字で、コンマで区切られ、括弧で囲まれています。

    (1,2,3,4)のようになります ただし、1000個の数字が含まれます。

    そして、それらを保存し、一度使用します。暗号化のためのワンタイムパッドのように。わかりました。良い例えではありませんが、私が望むポイントが得られます。

    inの終わりと考えてください 句、およびTEXT列に格納されます(blobなど)。

    なぜ世界でこれをしたいのですか? RNG (乱数ジェネレーター)は非常に遅いです。しかし、数台のマシンでそれらを生成することで、数千台を比較的迅速にクランクアウトできる可能性があります。ちなみに(これはいわゆる付録の構造でわかりますが、1つの行を生成するのにかかる時間をキャプチャします。mysqlでは約1秒です。しかし、C#、PHP、Java、何でもそれをまとめることができます。要点まとめる方法ではなく、必要なときに手に入れることができます。

    この戦略は、長短を問わず、ランダムリストとして使用されていない行をフェッチし、使用済みとしてマークし、

    などの呼び出しを発行することと組み合わされた場合です。
    select id,thing from ratings where id in (a,b,c,d,e, ... )
    

    in句には1000個の数値が含まれているため、結果は0.5秒未満で利用できます。 mysql CBO(コストベースのオプティマイザー)を効果的に使用することは、PKインデックスの結合のように扱うよりも効果的です。

    実際には少し複雑なので、これは要約形式のままにしておきますが、次の粒子が含まれている可能性があります

    • 事前に計算されたランダムな数値を保持するテーブル(付録A)
    • mysql create eventstrategy(付録B)
    • プリペアドステートメントを使用するストアドプロシージャ(付録C)
    • RNG inを示すmysqlのみのストアドプロシージャ キックの条項(付録D)

    付録A

    事前に計算されたランダムな数値を保持するテーブル

    create table randomsToUse
    (   -- create a table of 1000 random numbers to use
        -- format will be like a long "(a,b,c,d,e, ...)" string
    
        -- pre-computed random numbers, fetched upon needed for use
    
        id int auto_increment primary key,
        used int not null,  -- 0 = not used yet, 1= used
        dtStartCreate datetime not null, -- next two lines to eyeball time spent generating this row
        dtEndCreate datetime not null,
        dtUsed datetime null, -- when was it used
        txtInString text not null -- here is your in clause ending like (a,b,c,d,e, ... )
        -- this may only have about 5000 rows and garbage cleaned
        -- so maybe choose one or two more indexes, such as composites
    );
    

    付録B

    これを本にしないために、私の答えを参照してくださいこちら 繰り返し発生するmysqlイベントを実行するためのメカニズム。それは、付録Dに見られる技術とあなたが夢見たい他の考えを使用して、付録Aに見られるテーブルのメンテナンスを推進します。行の再利用、アーカイブ、削除など。

    付録C

    1000のランダムな行を取得するためのストアドプロシージャ。

    DROP PROCEDURE if exists showARandomChunk;
    DELIMITER $$
    CREATE PROCEDURE showARandomChunk
    (
    )
    BEGIN
      DECLARE i int;
      DECLARE txtInClause text;
    
      -- select now() into dtBegin;
    
      select id,txtInString into i,txtInClause from randomsToUse where used=0 order by id limit 1;
      -- select txtInClause as sOut; -- used for debugging
    
      -- if I run this following statement, it is 19.9 seconds on my Dell laptop
      -- with 19M rows
      -- select * from ratings order by rand() limit 1000; -- 19 seconds
    
      -- however, if I run the following "Prepared Statement", if takes 2 tenths of a second
      -- for 1000 rows
    
      set @s1=concat("select * from ratings where id in ",txtInClause);
    
      PREPARE stmt1 FROM @s1;
      EXECUTE stmt1; -- execute the puppy and give me 1000 rows
      DEALLOCATE PREPARE stmt1;
    END
    $$
    DELIMITER ;
    

    付録D

    付録Bの概念と絡み合うことができます。しかし、あなたはそれをやりたいのです。しかし、それはmysqlがRNG側でそれをすべて単独で実行する方法を確認するための何かをあなたに残します。ちなみに、パラメータ1と2がそれぞれ1000と19Mの場合、私のマシンでは800ミリ秒かかります。

    このルーチンは、冒頭で述べたように、どの言語でも書くことができます。

    drop procedure if exists createARandomInString;
    DELIMITER $$
    create procedure createARandomInString
    (   nHowMany int, -- how many numbers to you want
        nMaxNum int -- max of any one number
    )
    BEGIN
        DECLARE dtBegin datetime;
        DECLARE dtEnd datetime;
        DECLARE i int;
        DECLARE txtInClause text;
        select now() into dtBegin;
    
        set i=1;
        set txtInClause="(";
        WHILE i<nHowMany DO
            set txtInClause=concat(txtInClause,floor(rand()*nMaxNum)+1,", "); -- extra space good due to viewing in text editor
            set i=i+1;
        END WHILE;
        set txtInClause=concat(txtInClause,floor(rand()*nMaxNum)+1,")");
        -- select txtInClause as myOutput; -- used for debugging
        select now() into dtEnd;
    
        -- insert a row, that has not been used yet
        insert randomsToUse(used,dtStartCreate,dtEndCreate,dtUsed,txtInString) values 
           (0,dtBegin,dtEnd,null,txtInClause);
    END
    $$
    DELIMITER ;
    

    上記のストアドプロシージャを呼び出す方法:

    call createARandomInString(1000,18000000);
    

    これにより、上記のようにラップされた1000個の数値の1行が生成されて保存されます。大きな数、1〜18M

    簡単な例として、ストアドプロシージャを変更する場合は、下部にある「デバッグに使用」という行の削除を解除し、実行するストアドプロシージャの最後の行として、次のように実行します。

    call createARandomInString(4,18000000);
    

    ...最大18Mの4つのランダムな数値を生成するには、結果は次のようになります

    +-------------------------------------+
    | myOutput                            |
    +-------------------------------------+
    | (2857561,5076608,16810360,14821977) |
    +-------------------------------------+
    

    付録E

    リアリティチェック。これらはやや高度なテクニックであり、私はそれらについて誰も指導することはできません。しかし、とにかくそれらを共有したかった。しかし、私はそれを教えることはできません。何度も。



    1. Oracle Cloud Infrastructureでのイベントの操作パート1:サービスの基本

    2. mysqlbinlogを使用してMySQLバイナリログファイル(BinLog)を読み取る方法

    3. SQLServerはグループから最初の行を選択します

    4. Oracle-SQL開発者からスクリプトを生成する方法