sql >> Databasteknik >  >> RDS >> Mysql

Hur ekar man slumpmässiga rader från databasen?

Två lösningar presenteras här. Båda dessa föreslagna lösningar är endast för mysql och kan användas av vilket programmeringsspråk som helst som konsument. PHP skulle vara alldeles för långsamt för detta, men det kan vara konsumenten av det.

Snabbare lösning :Jag kan hämta 1000 slumpmässiga rader från en tabell med 19 miljoner rader på cirka 2 tiondelar av en sekund med mer avancerade programmeringstekniker.

Långsammare lösning :Det tar cirka 15 sekunder med programmeringstekniker utan effekt.

Båda använder förresten datagenereringen som ses HÄR som jag skrev. Så det är mitt lilla schema. Jag använder det, fortsätt med TVÅ fler självinsatser sett där borta, tills jag har 19 miljoner rader. Så det tänker jag inte visa igen. Men för att få de 19 miljoner raderna, gå och se det och gör ytterligare 2 av dessa insatser, och du har 19 miljoner rader.

Långsammare version först

Först den långsammare metoden.

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

Det returnerar 1000 rader på 15 sekunder.

Snabbare lösning

Det här är lite mer komplicerat att beskriva. Kontentan av det är att du förberäknar dina slumptal och genererar en in clause slutar av slumpmässiga tal, separerade med kommatecken och lindade med ett par parenteser.

Det kommer att se ut som (1,2,3,4) men det kommer att ha 1000 nummer i sig.

Och du lagrar dem och använder dem en gång. Som en engångsplatta för kryptografi. Ok, ingen bra liknelse, men du förstår poängen hoppas jag.

Se det som ett slut på en in sats och lagras i en TEXT-kolumn (som en blob).

Varför i hela friden skulle man vilja göra detta? Eftersom RNG (slumptalsgeneratorer) är oöverkomligt långsamma. Men att generera dem med ett fåtal maskiner kanske kan veva ut tusentals relativt snabbt. Förresten (och du kommer att se detta i strukturen av mina så kallade bilagor, jag fångar hur lång tid det tar att generera en rad. Cirka 1 sekund med mysql. Men C#, PHP, Java, vad som helst kan sätta ihop det. Poängen är inte hur du sätter ihop det, snarare att du har det när du vill ha det.

Den här strategin, den långa och korta av den är, när den kombineras med att hämta en rad som inte har använts som en slumpmässig lista, markera den som använd och skicka ett anrop som t.ex.

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

och in-satsen har 1000 nummer i sig, resultaten är tillgängliga på mindre än en halv sekund. Effektiv användning av mysql CBO (kostnadsbaserad optimerare) än behandlar den som en sammanfogning på ett PK-index.

Jag lämnar detta i sammanfattande form, eftersom det är lite komplicerat i praktiken, men inkluderar följande partiklar potentiellt

  • en tabell som innehåller de förberäknade slumptalen (bilaga A)
  • en mysql skapa händelsestrategi (bilaga B)
  • en lagrad procedur som arbetar med ett förberett uttalande (bilaga C)
  • en mysql-enbart lagrad proc för att demonstrera RNG in klausul för sparkar (bilaga D)

Bilaga A

En tabell som innehåller de förberäknade slumptalen

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

Bilaga B

För att inte förvandla detta till en bok, se mitt svar HÄR för en mekanism för att köra en återkommande mysql-händelse. Det kommer att driva underhållet av tabellen som visas i bilaga A med hjälp av tekniker som ses i bilaga D och andra tankar du vill tänka dig. Såsom återanvändning av rader, arkivering, radering, vad som helst.

Bilaga C

lagrad procedur för att helt enkelt få mig 1000 slumpmässiga rader.

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 ;

Bilaga D

Kan sammanflätas med Appendix B koncept. Hur du än vill göra det. Men det lämnar dig med något att se hur mysql kunde göra det helt själv på RNG-sidan. Förresten, för parametrarna 1 och 2 som är 1000 respektive 19M, tar det 800 ms på min maskin.

Denna rutin kan skrivas på vilket språk som helst som nämndes i början.

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 ;

Så här anropar du ovanstående lagrade proc:

call createARandomInString(1000,18000000);

Det genererar och sparar 1 rad med 1000 nummer inslagna enligt beskrivningen ovan. Stora siffror, 1 till 18 miljoner

Som en snabb illustration, om man skulle modifiera den lagrade proc, ta bort rem raden nära botten som säger "används för felsökning", och ha det som sista raden, i den lagrade proc som körs, och kör detta:

call createARandomInString(4,18000000);

... för att generera fyra slumpmässiga tal upp till 18M, kan resultaten se ut som

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

Bilaga E

Verklighetskoll. Det här är något avancerade tekniker och jag kan inte lära någon i dem. Men jag ville dela med mig av dem ändå. Men jag kan inte lära ut det. Över och ut.



  1. Filtrera på Output-sats sql

  2. SQLite kan inte öppna databasfil (kod 14) vid frekvent SELECT-fråga

  3. MySQL:unikt fält måste vara ett index?

  4. Välj de sista N raderna från MySQL