Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment faire écho à des lignes aléatoires de la base de données ?

Deux solutions présentées ici. Ces deux solutions proposées sont mysql uniquement et peuvent être utilisées par n'importe quel langage de programmation en tant que consommateur. PHP serait beaucoup trop lent pour cela, mais il pourrait en être le consommateur.

Solution plus rapide :Je peux apporter 1000 lignes aléatoires d'une table de 19 millions de lignes en environ 2 dixièmes de seconde avec des techniques de programmation plus avancées.

Solution plus lente  :Cela prend environ 15 secondes avec des techniques de programmation sans puissance.

Au fait, les deux utilisent la génération de données vue ICI que j'ai écrit. Voilà mon petit schéma. J'utilise ça, continuez avec DEUX plus d'auto-inserts vus là-bas, jusqu'à ce que j'aie 19 millions de lignes. Je ne le montrerai donc plus. Mais pour obtenir ces 19 millions de lignes, allez voir cela et faites 2 autres de ces insertions, et vous avez 19 millions de lignes.

Version plus lente en premier

Tout d'abord, la méthode la plus lente.

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

Cela renvoie 1000 lignes en 15 secondes.

Solution plus rapide

C'est un peu plus compliqué à décrire. L'essentiel est que vous pré-calculez vos nombres aléatoires et générez une clause in clause fin de nombres aléatoires, séparés par des virgules et entourés d'une paire de parenthèses.

Il ressemblera à (1,2,3,4) mais il contiendra 1000 numéros.

Et vous les stockez et les utilisez une fois. Comme un tampon unique pour la cryptographie. Ok, ce n'est pas une grande analogie, mais vous avez compris, j'espère.

Considérez-le comme une fin pour un in clause clause, et stocké dans une colonne TEXT (comme un blob).

Pourquoi diable voudrait-on faire ça ? Parce que RNG (générateurs de nombres aléatoires) sont d'une lenteur prohibitive. Mais les générer avec quelques machines peut permettre d'en produire des milliers assez rapidement. Au fait (et vous le verrez dans la structure de mes soi-disant annexes, je capture le temps qu'il faut pour générer une ligne. Environ 1 seconde avec mysql. Mais C#, PHP, Java, tout peut mettre cela ensemble. Le point ce n'est pas la façon dont vous l'assemblez, mais plutôt que vous l'avez quand vous le voulez.

Cette stratégie, en bref, lorsqu'elle est combinée avec la récupération d'une ligne qui n'a pas été utilisée comme liste aléatoire, la marque comme utilisée et émet un appel tel que

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

et que la clause in contient 1000 chiffres, les résultats sont disponibles en moins d'une demi-seconde. Efficace en utilisant mysql CBO (optimiseur basé sur les coûts) que le traite comme une jointure sur un index PK.

Je laisse cela sous forme de résumé, car c'est un peu compliqué en pratique, mais inclut potentiellement les particules suivantes

  • un tableau contenant les nombres aléatoires précalculés (annexe A)
  • une stratégie d'événement mysql create (annexe B)
  • une procédure stockée qui utilise une déclaration préparée (annexe C)
  • un proc stocké mysql uniquement pour démontrer RNG in clause clause pour les coups de pied (Annexe D)

Annexe A

Une table contenant les nombres aléatoires précalculés

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

Annexe B

Dans l'intérêt de ne pas en faire un livre, voir ma réponse ICI pour un mécanisme d'exécution d'un événement mysql récurrent. Il pilotera la maintenance du tableau vu à l'annexe A en utilisant les techniques vues à l'annexe D et d'autres réflexions que vous souhaitez imaginer. Comme la réutilisation de lignes, l'archivage, la suppression, peu importe.

Annexe C

procédure stockée pour obtenir simplement 1000 lignes aléatoires.

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 ;

Annexe D

Peut être lié au concept de l'annexe B. Cependant vous voulez le faire. Mais cela vous laisse quelque chose pour voir comment mysql pourrait tout faire tout seul du côté RNG des choses. Au fait, pour les paramètres 1 et 2 étant respectivement de 1000 et 19M, cela prend 800 ms sur ma machine.

Cette routine peut être écrite dans n'importe quel langage comme mentionné au début.

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 ;

Comment appeler la procédure stockée ci-dessus :

call createARandomInString(1000,18000000);

Cela génère et enregistre 1 ligne de 1000 numéros enveloppés comme décrit ci-dessus. Grands nombres, 1 à 18M

À titre d'illustration rapide, si vous deviez modifier la procédure stockée, supprimez la ligne vers le bas qui indique "utilisé pour le débogage", et ayez-la comme dernière ligne, dans la procédure stockée qui s'exécute, et exécutez ceci :

call createARandomInString(4,18000000);

... pour générer 4 nombres aléatoires jusqu'à 18M, les résultats pourraient ressembler à

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

Annexe E

Vérification de la réalité. Ce sont des techniques quelque peu avancées et je ne peux enseigner à personne. Mais je voulais quand même les partager. Mais je ne peux pas l'enseigner. Plus et plus.