Mysql
 sql >> Database >  >> RDS >> Mysql

Come fare eco a righe casuali dal database?

Due soluzioni qui presentate. Entrambe queste soluzioni proposte sono solo MySQL e possono essere utilizzate da qualsiasi linguaggio di programmazione come consumatore. PHP sarebbe decisamente troppo lento per questo, ma potrebbe esserne il consumatore.

Soluzione più rapida :Posso portare 1000 righe casuali da una tabella di 19 milioni di righe in circa 2 decimi di secondo con tecniche di programmazione più avanzate.

Soluzione più lenta :Ci vogliono circa 15 secondi con tecniche di programmazione senza alimentazione.

A proposito, entrambi utilizzano la generazione di dati vista QUI che ho scritto. Quindi questo è il mio piccolo schema. Lo uso, continua con DUE più auto-inserimenti visti laggiù, finché non ho 19 milioni di righe. Quindi non lo mostrerò di nuovo. Ma per ottenere quelle 19 milioni di righe, vai a vederlo e fai altri 2 di quegli inserti e avrai 19 milioni di righe.

Prima la versione più lenta

Primo, il metodo più lento.

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

Ciò restituisce 1000 righe in 15 secondi.

Soluzione più rapida

Questo è un po' più complicato da descrivere. Il succo è che pre-calcoli i tuoi numeri casuali e generi una in clause fine di numeri casuali, separati da virgole e racchiusi tra una coppia di parentesi.

Sembrerà (1,2,3,4) ma conterrà 1000 numeri.

E li conservi e li usi una volta. Come un time pad per la crittografia. Ok, non è una grande analogia, ma spero che tu riesca a capire.

Pensalo come un finale per un in clausola e archiviato in una colonna TEXT (come un BLOB).

Perché mai uno dovrebbe voler fare questo? Perché RNG (generatori di numeri casuali) sono proibitivamente lenti. Ma generarli con poche macchine potrebbe essere in grado di sfornare migliaia in tempi relativamente brevi. A proposito (e lo vedrai nella struttura delle mie cosiddette appendici, catturo quanto tempo ci vuole per generare una riga. Circa 1 secondo con mysql. Ma C#, PHP, Java, qualsiasi cosa può metterlo insieme. Il punto non è come lo metti insieme, piuttosto, che lo hai quando lo vuoi.

Questa strategia, lunga e corta, quando è combinata con il recupero di una riga che non è stata utilizzata come elenco casuale, contrassegnandola come utilizzata ed emettendo una chiamata come

select id,thing from ratings where id in (a,b,c,d,e, ... )

e la clausola in contiene 1000 numeri, i risultati sono disponibili in meno di mezzo secondo. Efficace impiegando il mysql CBO (ottimizzatore basato sui costi) che lo tratta come un join su un indice PK.

Lo lascio in forma riassuntiva, perché in pratica è un po' complicato, ma include potenzialmente le seguenti particelle

  • una tabella contenente i numeri casuali precalcolati (Appendice A)
  • una strategia per l'evento mysql create (Appendice B)
  • una procedura memorizzata che utilizza una dichiarazione preparata (appendice C)
  • un proc archiviato solo in mysql per dimostrare RNG in clausola per i calci (Appendice D)

Appendice A

Una tabella contenente i numeri casuali precalcolati

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

Appendice B

Nell'interesse di non trasformarlo in un libro, vedere la mia risposta QUI per un meccanismo per l'esecuzione di un evento mysql ricorrente. Guiderà il mantenimento del tavolo visto nell'Appendice A usando le tecniche viste nell'Appendice D e altri pensieri che vuoi inventare. Come il riutilizzo di righe, l'archiviazione, l'eliminazione, qualsiasi cosa.

Appendice C

stored procedure per ottenere semplicemente 1000 righe casuali.

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 ;

Appendice D

Può essere intrecciato con il concetto dell'Appendice B. Comunque tu voglia farlo. Ma ti lascia qualcosa per vedere come mysql potrebbe fare tutto da solo sul lato RNG delle cose. A proposito, poiché i parametri 1 e 2 sono rispettivamente 1000 e 19M, ci vogliono 800 ms sulla mia macchina.

Questa routine può essere scritta in qualsiasi lingua, come menzionato all'inizio.

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 ;

Come chiamare il processo memorizzato sopra:

call createARandomInString(1000,18000000);

Ciò genera e salva 1 riga, di 1000 numeri avvolti come descritto sopra. Grandi numeri, da 1 a 18 milioni

Come una rapida illustrazione, se si dovesse modificare il proc archiviato, annullare la riga vicino al fondo che dice "usato per il debug" e averlo come ultima riga, nel proc archiviato che viene eseguito, ed eseguire questo:

call createARandomInString(4,18000000);

... per generare 4 numeri casuali fino a 18 milioni, i risultati potrebbero assomigliare a

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

Appendice E

Controllo di realtà. Queste sono tecniche alquanto avanzate e non posso fare da tutor a nessuno su di esse. Ma volevo comunque condividerli. Ma non posso insegnarlo. Avanti e indietro.