sql >> Databáze >  >> RDS >> Mysql

Jak echo náhodné řádky z databáze?

Zde jsou uvedena dvě řešení. Obě tato navrhovaná řešení jsou pouze mysql a jako spotřebitel je může používat jakýkoli programovací jazyk. PHP by na to bylo příliš pomalé, ale mohl by být jeho spotřebitelem.

Rychlejší řešení :Mohu přinést 1000 náhodných řádků z tabulky 19 milionů řádků za přibližně 2 desetiny sekundy pomocí pokročilejších programovacích technik.

Pomalší řešení :U technik programování bez napájení to trvá asi 15 sekund.

Mimochodem oba používají generování dat, které najdete ZDE že jsem napsal. Takže to je moje malé schéma. Používám to, pokračujte DVOU tam je vidět více samovložek, dokud nebudu mít 19 milionů řádků. Takže to nebudu znovu ukazovat. Ale chcete-li získat těch 19 milionů řádků, podívejte se na to a udělejte 2 další takové vložky a máte 19 milionů řádků.

Nejprve pomalejší verze

Za prvé, pomalejší metoda.

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

To vrátí 1000 řádků za 15 sekund.

Rychlejší řešení

Tohle je trochu složitější na popis. Podstatou toho je, že předpočítáte náhodná čísla a vygenerujete in clause končí náhodných čísel oddělených čárkami a uzavřených párem závorek.

Bude to vypadat jako (1,2,3,4) ale bude mít 1000 čísel.

A uložíte je a jednou použijete. Jako jednorázový blok pro kryptografii. Ok, není to skvělá analogie, ale doufám, že rozumíte.

Představte si to jako konec pro in klauzule a uloženy ve sloupci TEXT (jako blob).

Proč by to proboha někdo chtěl dělat? Protože RNG (generátory náhodných čísel) jsou neúměrně pomalé. Ale jejich generování s několika stroji může být schopno vyřadit tisíce relativně rychle. Mimochodem (a uvidíte to ve struktuře mých tzv. příloh, zachycuji, jak dlouho trvá vygenerování jednoho řádku. Asi 1 sekunda s mysql. Ale C#, PHP, Java, cokoli, to může dát dohromady. Pointa není to, jak to poskládáte, ale že to máte, když to chcete.

Tato strategie, dlouhá i krátká, je, když je kombinována s načtením řádku, který nebyl použit jako náhodný seznam, jeho označením jako použitý a vydáním volání, jako je

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

a klauzule in obsahuje 1000 čísel, výsledky jsou k dispozici za méně než půl sekundy. Efektivní využití mysql CBO (nákladově založeného optimalizátoru) než s ním zachází jako se spojením s indexem PK.

Nechám to v souhrnné podobě, protože je to v praxi trochu komplikované, ale potenciálně zahrnuje následující částice

  • tabulku obsahující předem vypočítaná náhodná čísla (příloha A)
  • strategii vytvoření události mysql (příloha B)
  • uložená procedura, která zaměstnává připravené prohlášení (příloha C)
  • proc uložený pouze v mysql k demonstraci RNG in doložka pro kopy (příloha D)

Příloha A

Tabulka obsahující předem vypočítaná náhodná čísla

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

Příloha B

Abychom z toho nedělali knihu, podívejte se na mou odpověď ZDE pro mechanismus pro spouštění opakující se události mysql. Bude to řídit údržbu tabulky uvedené v příloze A pomocí technik uvedených v příloze D a dalších myšlenek, které si chcete vysnít. Jako je opětovné použití řádků, archivace, mazání, cokoliv.

Příloha C

uložená procedura, aby mi jednoduše získal 1000 náhodných řádků.

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 ;

Příloha D

Lze propojit s konceptem Appendix B. Jakkoli to chcete udělat. Ale zanechá vám to něco, abyste viděli, jak by to mysql mohl udělat sám na straně RNG. Mimochodem, když jsou parametry 1 a 2 1000 a 19M, trvá to na mém počítači 800 ms.

Tato rutina může být napsána v jakémkoli jazyce, jak bylo zmíněno na začátku.

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 ;

Jak volat výše uložený proces:

call createARandomInString(1000,18000000);

To vygeneruje a uloží 1 řádek z 1000 čísel zabalených, jak je popsáno výše. Velká čísla, 1 až 18M

Pro rychlou ilustraci, pokud byste chtěli upravit uložený proces, zrušte řádek v dolní části, který říká „použito pro ladění“, a uveďte jej jako poslední řádek v uloženém procesu, který běží, a spusťte toto:

call createARandomInString(4,18000000);

... pro vygenerování 4 náhodných čísel do 18M mohou výsledky vypadat takto

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

Příloha E

Kontrola reality. Jsou to poněkud pokročilé techniky a nemohu je nikoho doučovat. Ale stejně jsem se o ně chtěl podělit. Ale nemůžu to naučit. Znovu a znovu.



  1. Vnořené sady MySQL – Jak najít rodiče uzlu?

  2. Parametrizované dotazy pomocí psycopg2 / Python DB-API a PostgreSQL

  3. Maximální délka MySQL a GROUP_CONCAT().

  4. Zobrazení zprávy, když nebyly nalezeny žádné výsledky vyhledávání PHP MySQL