IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Requêtes et SQL. Discussion :

Extractions aléatoires multiples


Sujet :

Requêtes et SQL.

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    Contrôleur
    Inscrit en
    Juin 2017
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Contrôleur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2017
    Messages : 5
    Points : 1
    Points
    1
    Par défaut Extractions aléatoires multiples
    Bonjour à tous,

    Je suis débutant sur Access et je me bats avec mes requêtes actuelles. Je vous expose le problème:
    J'ai une table avec, pour chaque ligne, un domaine, une question, diverses réponses.
    Je souhaite extraire de cette table x questions aléatoires parmi celles du domaine D1 et y questions aléatoires parmi celles du domaine D2.
    Je souhaite compiler tout ça sous la forme d'un questionnaire de ces x+y questions.

    Je parviens à créer une nouvelle table avec les x questions aléatoires parmi celles du domaine D1 avec la requête ajout suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    INSERT INTO
    SELECT TOP x 
    FROM 
    WHERE Domaine=D1
    ORDER BY Rnd([Numéro]);
    Je parviens à créer une nouvelle table avec les y questions aléatoires parmi celles du domaine D2 avec une autre requête similaire.

    J'ai tenté une requête union mais là il ne ressort que les x premières du domaine D1et les y premières du domaine D2 car je n'arrive pas à utiliser alors l'instruction Rnd.

    Je suis donc à la recherche d'une piste pour compiler les résultats de mes requêtes. SQL? VBA? J'en appelle à vos lumières!

    Merci d'avance à tous.

  2. #2
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2017
    Messages
    114
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2017
    Messages : 114
    Points : 148
    Points
    148
    Par défaut
    Le fait d'écrire "ORDER BY Rnd([Numéro])" va te trier tes questions dans un ordre semi-aléatoire. C'est à dire que tu vas normalement avoir la même séquence de questions si tu relance la même requête. Pour éviter ça, il faudrait écrire quelque chose comme "Rnd(-[Numéro]*Timer())"

    Imaginons qu'il y ai 100 lignes dans ta table (50 du domaine D1 et 50 du domaine D2).
    Si on écris la requête de la même manière que toi, tu risques de te retrouver avec un chiffre au hasard entre 1 et 100, qui ne sera pas forcément avec le bon domaine.

    Donc pour moi il faut passer par une sous-requête.
    Je suis pas sûr que ma requête fonctionne, essaye plutôt de voir l'idée que de recopier ^^

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    INSERT INTO
    SELECT TOP x
    FROM (SELECT * FROM --- WHERE Domaine = D1)
    ORDER BY Rnd(-[COUNT([Numéro])*Timer())

  3. #3
    Nouveau Candidat au Club
    Homme Profil pro
    Contrôleur
    Inscrit en
    Juin 2017
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Contrôleur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2017
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Merci pour la réponse. L'idée de la sous-requête semble bonne. Je voulais sans doute aller trop vite.
    En effet, après plusieurs essais, lors d'une requête simple (extraire 50 questions ayant pour paramètre D1 parmi les 100 qui ont le paramètres D1 dans la liste de toutes les questions), l'instruction Rnd me renvoie une séquence différente à chaque fois (toujours avec D1).
    Avec cela, je crée une table concernant D1, puis je recommence avec le domaine D2. J'ai donc deux requêtes Ajout qui vont constituer mes deux tables de travail. J'empile ensuite ces deux tables (extraitD1 et extraitD2) avec une requête UNION (ça fonctionne!).

    J'ai ma base. J'imagine qu'après, je vais pouvoir enchaîner les requêtes via une macro, non?

    Imaginons ensuite que j'empile les différentes requêtes Union (avec des domaines D3, D4...), il va donc falloir créer autant de sous-requêtes et donc autant de tables que de variations de paramètres, non?

  4. #4
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2017
    Messages
    114
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2017
    Messages : 114
    Points : 148
    Points
    148
    Par défaut
    Si tu créé une table à chaque fois c'est trop gourmand, imagine tu as 5000 personnes qui utilise cette fonctionnalité en même temps. On a donc 5000 tables par domaines...
    Et en fonction de ce que tu stockes dans cette nouvelle table, tu peux avoir des données en double (voire beaucoup plus si tu as pleins d'utilisateurs en même temps)

    Ce que tu peux faire par exemple, c'est avoir une seule table qui va lier les questions à l'utilisateur (Genre un champ IDLien, un champ IDUtilisateur et un champ IDQuestion)

    Ensuite, tu pourrais faire ça en une seule requête avec du VBA:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    'On va dire que tu as 5 domaines et que tu veux 10 questions par domaines, mais tu peux changer ces chiffres
     
    query = "INSERT INTO "
     
    i = 1
    While i <= 5
       If i <> 1 Then
          query = query & "UNION "
       End If
       query = query & "(SELECT TOP 10 " & _
          "FROM (SELECT * FROM --- WHERE Domaine = D" & i & ") " & _
          "ORDER BY Rnd(-[COUNT([Numéro])*Timer()) "
       i = i + 1
    Wend
     
    'Et là tu exécute le query
    La requête est longue à mettre en place, mais au moins tu ne fais qu'un appel à la base

  5. #5
    Nouveau Candidat au Club
    Homme Profil pro
    Contrôleur
    Inscrit en
    Juin 2017
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Contrôleur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2017
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Merci Syrald. Il est certain que ce n'est pas élégant d'avoir autant de tables (d'après mon calcul, on se limiterait à une quinzaine) mais cela a l'avantage d'être simple, d'autant qu'on sera peu nombreux à manipuler le logiciel.
    Je vais également tenter ta solution en VBA (qui va me donner un peu plus de fil à retordre). Dans ce cas, si je veux créer plusieurs types de tests, il me suffit de créer une table de base (la liste de questions), et une table pour chaque test dans laquelle la macro (une par type de test) viendra écrire le résultat. Ai-je bien compris?
    On arriverait à moins d'une dizaine de table.

  6. #6
    Invité
    Invité(e)
    Par défaut
    Bonjour

    concernant la clause
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ORDER BY Rnd(-[COUNT([Numéro])*Timer())
    Plusieurs erreurs :
    1) La fonction d'agrégat Count() ne peux pas être utilisée dans cette requete qui n'a pas de clause Group By : Pas de regroupement donc pas d'agrégat
    2) Aucun intérêt de mettre un signe - puisque dans la formule vous utilisez timer() qui change la valeur passée à Rnd à chaque appel. Passer une valeur négative à Rnd() a justement l'usage inverse qui est de fournir des valeurs aléatoires fixes en fonction de la valeur négative passée. C'est donc antinomique (ne pas confondre avec antimonique )
    3) si [Numéro] est la clef primaire de la table, ORDER BY Rnd([Numéro]) est suffisant pour générer des valeurs aléatoires différentes pour la même ligne à chaque appel de la requete.
    4) Rnd fournit des nombres pseudo-aléatoire et n'est donc pas d'une qualité cryptographique mais suffisant je pense dans votre cas

    Pour finir, si la table s'appelle tQuestion et possède au moins ces 3 colonnes : Numéro (type numauto, clef primaire), Question (type texte), Domaine (type texte), ...
    la requête pour insérer 3 questions aléatoires pour le domaine "1" et 2 questions aléatoires pour le domaine "2" dans la nouvelle table tQuestionExtrait est
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    SELECT Question, Domaine
    INTO tQuestionExtrait FROM
    (
    SELECT TOP 3 Question, Domaine FROM tQuestion WHERE Domaine="1" ORDER BY Rnd([Numéro]) 
    UNION
    SELECT Question, Domaine FROM (SELECT TOP 2 Question, Domaine FROM tQuestion WHERE Domaine="2" ORDER BY Rnd([Numéro]) ) As R2 
    ) AS R
    La requête créé la table (ou demande à la supprimer si elle existe déjà) et peuple la table avec 5 lignes aléatoires (top 3 + top 2)
    On est obligé d'ajouter une sous-requete pour la sélection du domaine 2 car access n'accepte pas un order by sur le deuxième SELECT de l'union

  7. #7
    Nouveau Candidat au Club
    Homme Profil pro
    Contrôleur
    Inscrit en
    Juin 2017
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Contrôleur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2017
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Bonjour Galoir,

    Je vous remercie pour votre piste et votre aide. J'ai essayé votre code adapté à mes besoins:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef
    INTO tESSAI_QuestionSOL FROM
    (SELECT TOP 20 Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine="SOL" ORDER BY Rnd([Numéro]) 
    UNION
    SELECT Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM (SELECT TOP 10 Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine="SOL_INDIC" ORDER BY Rnd([Numéro]) ) As R2 
    )  AS R;
    La table est créée ou nettoyée à la demande, c'est parfait, mais la clause aléatoire ne semble pas fonctionner sur la requête concernant le domaine SOL INDIC.

  8. #8
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Désolé, je ne savais pas qu'il y avait autant de colonnes à retourner, sinon la requête peut être simplifiée grâce a * :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    SELECT * INTO tESSAI_QuestionSOL
    FROM (SELECT TOP 20 Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine="SOL" ORDER BY Rnd([Numéro]) 
    UNION
    SELECT * FROM (SELECT TOP 10 Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine="SOL_INDIC" ORDER BY Rnd([Numéro]) )
    )  AS R;
    De plus, si vous retournez TOUTES les colonnes de la table dans la nouvelle table, on peut simplifier encore plus :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    SELECT * INTO tESSAI_QuestionSOL
    FROM (SELECT TOP 20 * FROM LISTE_QUESTIONS WHERE Domaine="SOL" ORDER BY Rnd([Numéro]) 
    UNION
    SELECT * FROM (SELECT TOP 10 * FROM LISTE_QUESTIONS WHERE Domaine="SOL_INDIC" ORDER BY Rnd([Numéro]) )
    )  AS R;
    J'ai fais aussi une fonction VBA qui permet de créer une table avec le nom passé en paramètre et de choisir les domaines et le TOP de chacun.
    Exemple d'utilisation : CreerTableQuestionnaireDomaines("tESSAI_QuestionSOL", "SOL", 20, "SOL_INDIC", 10)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
     
    Public Function CreerTableQuestionnaireDomaines(ByVal tNomTableDestination As String, ParamArray DomainesEtNbQuestions()) As Boolean
    On Error GoTo MyErr
    Const cTableDest As String = "MA_TABLE", cDomaine As String = "MON_DOMAINE", cTop As String = "MON_TOP"
    Const cSQLDebut As String = "SELECT * INTO MA_TABLE FROM ("
    Const cSQLSelectFrom As String = "SELECT TOP " & cTop & " Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, " & _
                                     "BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine=""" & cDomaine & """ ORDER BY Rnd(Numéro)"
    Const cSQLSelectUnion As String = " UNION SELECT * FROM (" & cSQLSelectFrom & ")"
    Const cSQLFin As String = ") As R;"
    Dim Sql As String, s As String, i As Long
    Randomize 'Réinit le générateur de nombres aléatoires
     
    DoCmd.SetWarnings False
    If UBound(DomainesEtNbQuestions) > -1 And (UBound(DomainesEtNbQuestions) And 1) = 1 Then 'Si array non vide et longueur impaire
        Sql = Replace(cSQLDebut, cTableDest, tNomTableDestination, , , vbBinaryCompare)
        For i = 0 To UBound(DomainesEtNbQuestions) Step 2
            If i > 0 Then s = cSQLSelectUnion Else: s = cSQLSelectFrom
            s = Replace(s, cDomaine, DomainesEtNbQuestions(i), , , vbBinaryCompare)
            Sql = Sql & Replace(s, cTop, DomainesEtNbQuestions(i + 1), , , vbBinaryCompare)
        Next i
        DoCmd.RunSQL Sql & cSQLFin
        CreerTableQuestionnaireDomaines = True
    End If
    Fin:
    DoCmd.SetWarnings True
    Exit Function
    MyErr:
        MsgBox "Erreur n°" & Err.Number & vbCrLf & Err.Description & vbCrLf & "SQL :" & vbCrLf & Sql, vbCritical, "CreerTableQuestionnaireDomaines()"
        Resume Fin
    End Function
    Cette fonction utilise la fonction Randomize qui force la réinitialisation du générateur de nombres aléatoires Rnd() au cas où...

    Concernant votre interrogation au sujet de l'aléatoire de SOL_INDIC, il faudrait vérifier que le pool de questions du domaine est supérieur au TOP demandé ?

    De plus le top sur ce domaine est de 10, si vous avez 11 questions dans le pool, vous avez >38% de chance que les mêmes questions sortent deux fois de suite !
    Si pool de 12 et Top 10 : ~16% de chance

    Ci-dessous, la même requête qui retourne en plus la colonne Alea qui affiche la valeur de Rnd(Numéro).
    Relever les valeurs d'aléa pour deux ou trois Numéros et relancer la requête pour vérifier que la valeur Aléa est bien différente :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT Rnd(Numéro) AS Alea, * INTO tESSAI_QuestionSOL
    FROM (SELECT TOP 20 Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine="SOL" ORDER BY Rnd([Numéro]) 
    UNION
    SELECT * FROM (SELECT TOP 10 Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine="SOL_INDIC" ORDER BY Rnd([Numéro]) )
    )  AS R;

  9. #9
    Nouveau Candidat au Club
    Homme Profil pro
    Contrôleur
    Inscrit en
    Juin 2017
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Contrôleur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Juin 2017
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Merci beaucoup.

    Après plusieurs essais, les versions simplifiées ne fonctionnaient pas car je n'avais aucun retour dans la table créée des questions du second domaine. Je ne comprends pas pourquoi.
    J'ai tatonné et je ne sais si Access a corrigé quelque chose automatiquement mais voici le code fonctionnel:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    SELECT R.Numéro, R.Question, R.Réponse1, R.Réponse2, R.Réponse3, R.Réponse4, R.Domaine, R.BonneRéponse, R.DocRef INTO tESSAI_SOL
    FROM
    (SELECT TOP 20 Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine="SOL" ORDER BY Rnd([Numéro]) 
    UNION
    SELECT Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM (SELECT TOP 8 Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine="SOL INDIC" ORDER BY Rnd([Numéro]) ) As R2 
    )  AS R;
    J'ai bien l'aléatoire, j'ai bien les deux domaines, j'ai la table désirée et complète. Cet inattendu R. s'est glissé là tout seul ai-je l'impression. En prolongeant la logique, j'ai écrit ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    SELECT R.Numéro, R.Question, R.Réponse1, R.Réponse2, R.Réponse3, R.Réponse4, R.Domaine, R.BonneRéponse, R.DocRef
    INTO tESSAI_LOC
    FROM
    (SELECT TOP 20 Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine="SOL" ORDER BY Rnd([Numéro]) 
    UNION
    SELECT Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM (SELECT TOP 20 Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine="LOC" ORDER BY Rnd([Numéro]) ) As R2
    UNION
    SELECT Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM (SELECT TOP 6 Numéro, Question, Réponse1, Réponse2, Réponse3, Réponse4, Domaine, BonneRéponse, DocRef FROM LISTE_QUESTIONS WHERE Domaine="LOC INDIC" ORDER BY Rnd([Numéro]) ) As R3 
    )  AS R;
    Cela semble fonctionner. Je cherche le code le plus simple possible pour que les utilisateurs n'aient pas à s'y plonger trop difficilement. Reste à travailler la mise en forme des résultats.

  10. #10
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Le plus simple serait pour moi de passer par la fonction vba qui évite de mettre les mains dans la requête...

    Résolu alors ?

Discussions similaires

  1. Réponses: 4
    Dernier message: 22/07/2016, 13h11
  2. [XL-2010] Extraction de multiples chaines de caractères de tailles variables
    Par Thierry360 dans le forum Macros et VBA Excel
    Réponses: 9
    Dernier message: 22/06/2016, 16h39
  3. Extraction aléatoire en SQL
    Par pat84 dans le forum MS SQL Server
    Réponses: 5
    Dernier message: 11/03/2008, 19h03
  4. Réponses: 15
    Dernier message: 26/07/2007, 13h52
  5. Extraction sql multiples
    Par Mengué georges dans le forum JDBC
    Réponses: 3
    Dernier message: 27/02/2007, 19h57

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo