Umfragen und Multiple-Choice-Tests - Teil 3

15.05.2006
Zum Auswerten von Multiple-Choice-Tests benötigen Sie ebenso wie für deren Anzeige neben einigen Abfragen, die nicht ganz unkompliziert sind, wieder etwas VBA. Wie Sie die Auswertung mit Abfragen und VBA optimal gestalten, zeigt Ihnen dieser letzte Teil der Artikelfolge.

Das größte Problem bei der Auswertung des Multiple-Choice-Tests ist, dass es nicht nur falsche und richtige Antworten gibt. Es könnte auch sein, dass der Benutzer eine Frage nicht beantwortet. Generell gibt es also folgende Möglichkeiten, wie eine Benutzerantwort von der korrekten vorgegebenen Antwort abweichen kann.

Bild 1: Aufbau der grundlegenden Abfrage.

Diese drei Abweichungen müssen Sie also erkennen, um eine fehlerhafte Antwort zu finden. Alleine die dazu notwendigen Abfragen sind äußerst komplex und werden daher Schritt für Schritt erläutert.

Richtige Antworten den Benutzerantworten gegenüberstellen

Die Grundlage ist eine Abfrage, die zunächst einmal die definierten Antworten den vom Benutzer gegebenen Antworten gegenüberstellt. Dazu erstellen Sie eine Auswahlabfrage mit den beiden Tabellen tabErgebnisse und tabAntworten. In dieser Abfrage geben Sie die Felder tabAntworten.ID, tabAntworten.Frage, tabAntworten.Antwort, tabAntworten. Wert, tabErgebnisse.Antwort, tabErgebnisse. Benutzer aus. Die Ergebnisse der Abfrage sollten Sie nach den Feldern Frage und Antworten aufsteigend sortieren.

Wichtig sind dann noch das Filterkriterium und die Verknüpfungseigenschaften. Zunächst benötigen Sie dazu eine Beziehung zwischen dem Feld ID der Tabelle tabAntworten und dem Feld Antwort der Tabelle tabErgebnisse. Da in der Tabelle tabErgebnisse nur die Antworten gespeichert sind, die der Benutzer für die Frage aktiviert hat, nicht aber die nicht aktivierten Antworten, müssen Sie nun mit entsprechenden Verknüpfungseigenschaften dafür sorgen, dass alle Datensätze der Tabelle tabAntworten angezeigt werden und nur die Datensätze der Tabelle tabErgebnisse, die diesen zugeordnet werden können. Dazu aktivieren Sie für die Verknüpfungseigenschaften die zweite Option.

Da nur die Datensätze des aktuellen Benutzers ausgewertet werden sollen, müssen Sie nun die Datensätze noch nach Benutzer einschränken. Das erreichen Sie durch den Kriteriumsausdruck CurrentUser() or IstNull im Feld Benutzer. Wichtig ist, dass Sie nicht nur die Datensätze anzeigen lassen, die im Feld Benutzer den aktuellen Benutzername stehen haben, sondern auch die, die hier keinen Wert enthalten. Das sind nämlich die möglichen Antworten, die der Benutzer nicht gewählt hat, und auch darin kann ja ein Fehler liegen.

Benutzerantworten ermitteln

Im nächsten Schritt müssen Sie die gewählten Antworten des Benutzers in einer Form ermitteln, die Sie später auch im Formular darstellen können. Das Problem dabei ist, dass in den Antworten des Benutzers nur die Antwort gespeichert ist, die er aktiviert hat. Stehen für eine Frage also beispielsweise fünf Antworten zur Verfügung und hat der Benutzer davon die ersten beiden aktiviert, erscheinen auch nur diese in der Tabelle tabAntworten. Sie müssen nun ein berechnetes Feld hinzufügen, das für die Antworten des Benutzers in der Tabelle tabAntworten True zurückgibt und für die nicht darin enthaltenen Antworten False. Dazu erweitern Sie die Abfrage um folgende Felddefinition:

Benutzerantwort: Wenn([tabAntworten].[ID]=[tabErgebnisse].[Antwort];Wahr;Falsch)

Bild 2: Die Ergebnisse des Benutzers für die einzelnen Antwortmöglichkeiten.

Damit geben Sie True aus, wenn im Feld Antwort der Tabelle tabErgebnisse die ID der Antwort aus der Tabelle tabAntworten steht. Das Ergebnis ist dann ein berechnetes Feld, in dem die Antworten des Benutzers für jede Frage und jede mögliche Antwort als boolesche Werte stehen (Bild 2).

Vorsicht Falle!

Sollten Sie auf den Gedanken kommen, anstelle des Mittelwertes die Funktion Varianz zu verwenden, geht das daneben. Zwar ist der Gedanke richtig, dass bei richtig beantworteten Fragen jede Antwortmöglichkeit richtig sein muss und damit die Varianz 0 ergibt. Sie ergibt allerdings auch dann 0, wenn der Benutzer alle Antwortmöglichkeiten falsch beantwortet hat.

Falsche und richtige Antworten finden

Als Letztes fehlt noch ein berechnetes Feld, das prüft, ob die Antworten des Benutzers mit denen der Tabelle tabAntworten übereinstimmen. Allerdings dürfen Sie das nicht in die eben erstellte Abfrage einfügen, sondern benötigen eine zusätzliche Abfrage, da sonst nicht alle Antwortmöglichkeiten im Abfrageergebnis enthalten wären. Erstellen Sie dazu eine weitere Auswahlabfrage abfTestergebnisDetails und fügen Sie dort als Datenquelle die Tabelle tabAntworten und die eben erstellte Abfrage ein. In die Abfrage fügen Sie nun die Felder ID, Frage, Antwort und Wert aus der Tabelle ein sowie das Feld Benutzerantwort aus der Abfrage.

Bild 3: Abfrage zur Ermittlung richtiger und falscher Fragen.

Die Beziehung zwischen Tabelle und Abfrage sollte zwischen den ID-Feldern bestehen. Über die Optionen für die Beziehung müssen Sie dafür sorgen, dass alle Datensätze der Tabelle angezeigt werden, und diejenigen der Abfrage, die zugeordnet werden können.

Jetzt fehlt nur noch das berechnete Feld. Sie nutzen dazu wieder die Wenn-Funktion und geben True aus, wenn das Feld Wert und das Feld Benutzerantwort übereinstimmen, andernfalls geben Sie False zurück. Die Felddefinition dazu lautet:

korrekt: Wenn([Benutzerantwort]=[Wert];Wahr;Falsch)

Mit dieser Abfrage haben Sie nun die Möglichkeit, dem Benutzer die richtigen und seine eigenen Antworten anzuzeigen. Mit Hilfe einer weiteren können Sie nun noch prüfen, ob er nicht nur die einzelnen Antwortmöglichkeiten für eine Frage korrekt aktiviert, sondern die ganze Frage damit richtig oder falsch beantwortet hat.

Falsche und richtige Fragen ermitteln

Erstellen Sie dazu eine weitere Abfrage, die als Quelltabelle die eben erstellte Abfrage enthält. Darin lassen Sie dann das Feld Frage und das berechnete Feld korrekt anzeigen. Wichtig ist dabei, dass Sie für das Feld korrigiert als Funktion Mittelwert auswählen und nach dem Feld Frage gruppieren. Eine Frage wurde nämlich nur dann beantwortet, wenn jede mögliche Antwort richtig angekreuzt oder nicht angekreuzt wurde. Das ist nur dann der Fall, wenn für alle Antwortmöglichkeiten das Feld korrekt den Wert True enthält. Der Mittelwert muss für richtig beantwortete Fragen also -1 betragen, für falsch beantwortete Fragen einen anderen Wert.

Die Ergebnisse darstellen

Ein Lerneffekt kann natürlich nur dann eintreten, wenn Sie dem Benutzer auch zeigen, was er falsch gemacht hat und was nicht. Am einfachsten ist das, wenn Sie dem Benutzer ermöglichen, die richtigen Antworten und seine eigenen zu betrachten. Dazu können Sie wieder ein Formular mit Unterformular verwenden (Bild 4). Als Hauptformular können Sie das Formular zur Anzeige des Tests frmTestanzeigen kopieren und wie folgt anpassen. Wichtig ist dabei, dass Sie vor allem den nicht notwendigen Quellcode löschen. Das Klassenmodul des Formulars darf ausschließlich den Code in Listing 1 enthalten. Er sorgt dafür, dass der Benutzer die Testfragen durchblättern kann.

Bild 4: Aufbau des Formulars zur Anzeige der Testergebnisse.

Private Sub bttWeiter_Click()
On Error GoTo Err_bttWeiter_Click
'Nächste Frage anzeigen
DoCmd.GoToRecord , , acNext
Exit_bttWeiter_Click:
Exit Sub
Err_bttWeiter_Click:
DoCmd.GoToRecord , , acFirst
Resume Exit_bttWeiter_Click
End Sub

Fehlerbehandlungsroutine

Darüber hinaus benötigen Sie im Hauptformular nur ein Steuerelement, das an das Feld Frage gebunden ist und die Frage des Tests anzeigt. Die Antworten werden in einem Unterformular frmTestergebnisUfo angezeigt, das an die Abfrage abfTestergebnisDetails gebunden ist.

Ein Problem gibt es allerdings noch. Nach der letzten Frage erscheint eine Fehlermeldung, weil nicht zum nächsten Datensatz gewechselt werden kann. Um diesen Fehler auszumerzen ist es wichtig, dass Sie in der Fehlerbehandlungsroutine nach der Marke Err_bttWeiter_Click die Go-ToRecord-Methode aufrufen und damit zum ersten Datensatz wechseln (Listing 1).

Bild 5: Das Formular zur Laufzeit.

Das Unterformular sollten Sie als Endlosformular gestalten und im Formularkopf die Spaltenüberschriften als Label-Felder einfügen. Damit für die berechneten Felder korrekt und Benutzerantwort ebenfalls Kontrollkästchen angezeigt werden, dürfen Sie aber nicht einfach die Felder aus der Feldliste in das Formular ziehen. Stattdessen sollten Sie die Kontrollkästchen manuell aus der Werkzeugleiste einfügen und über die Eigenschaft Steuerelementinhalt im Eigenschaftenfenster das entsprechende Abfragefeld zuordnen.

Zur Laufzeit werden dann die Antworten zu jeder Frage angezeigt und können vom Benutzer mit seinen eigenen Antworten verglichen werden. In der dritten Spalte sieht er dann gleich, welche Antwortmöglichkeiten er korrekt gewählt hat.

Das Gesamtergebnis anzeigen

Vielfach wird der Benutzer aber zunächst das Gesamtergebnis des Tests ermitteln, d.h. wissen wollen, wie viele Fragen er korrekt beantwortet hat und wie viele falsch waren. Das können Sie wahlweise mit einem Formular oder einem Bericht realisieren. Berichte haben dabei den Nachteil, dass Sie dort keinen Button zu dem zuvor erstellten Formular einfügen können, mit dem der Benutzer dann die Details anzeigen lassen kann.

Bild 6: Das Gesamtergebnis anzeigen

Daher sollten Sie auch für die Gesamtergebnisse ein Formular verwenden. Sie benötigen darin drei Labelfelder zur Beschriftung, drei Eingabefelder für die Ausgabe des Testergebnisses und eine Schaltfläche mit der Aufschrift Details, für die Sie die in Listing 2 gezeigte Ereignisprozedur erstellen beziehungsweise vom Steuerelement-Assistenten erstellen lassen. Nutzen Sie den Assistenten, erstellen Sie damit einfach eine Schaltfläche, die ein anderes Formular öffnet, und wählen Sie das Formular frmTestergebnis aus.

Private Sub bttDetails_Click()
On Error GoTo Err_bttDetails_Click
Dim stDocName As String
Dim stLinkCriteria As String
stDocName = "frmTestergebnis"
DoCmd.OpenForm stDocName, , , stLinkCriteria
Exit_bttDetails_Click:
Exit Sub
Err_bttDetails_Click:
MsgBox Err.Description
Resume Exit_bttDetails_Click
End Sub

Das Formular, das für jede Frage ein Feld mit dem Mittelwert für das berechnete Feld korrekt der Unterabfrage enthält, muss an die Abfrage abfTestergebnisGruppiert gebunden sein. Hat dieses Feld den Wert –1, ist die Frage korrekt beantwortet worden. Damit Sie ermitteln können, wie viele Fragen es gibt, wie viele richtig und wie viele falsch beantwortet wurden, müssen Sie also nur die entsprechenden Datensätze der Abfrage zählen. Am einfachsten geht das, indem Sie als Steuerelementinhalt für die Textfelder die Dom-Anzahl-Funktion mit entsprechenden Parametern angeben. Sie zählt die Datensätze der Abfrage gemäß den Parameterwerten. Um die Gesamtzahl der Fragen im Test zu ermitteln, geben Sie als Steuerelementinhalt die Funktion wie folgt an:

=DomAnzahl("Mittelwertvonkorrekt";"abfTestergebnis-Gruppiert")

Der dritte Parameter, der das Ergebnis der Abfrage abfTestergebnisGruppiert filtert, wird hier weggelassen, da keine Filterung nach falschen und richtigen Fragen notwendig ist. Möchten Sie hingegen die richtig beantworteten Fragen zählen, geben Sie die folgende Formel an:

=DomAnzahl("Mittelwertvonkorrekt";"abfTestergebnis-Gruppiert";"Mittelwertvonkorrekt=-1")

Hier schränken Sie die zu zählenden Datensätze auf diejenigen ein, die im Feld Mittelwertvonkorrekt den Wert -1 haben. Analog dazu verwenden Sie folgende Formel, um die Anzahl der falsch beantworteten Fragen zu ermitteln:

=DomAnzahl("Mittelwertvonkorrekt";"abfTestergebnis-Gruppiert";"Mittelwertvonkorrekt<>-1")

Nur falsch beantwortete Fragen anzeigen

Dient der Test nicht nur zur Kontrolle, sondern soll er es auch ermöglichen, dass die Benutzer die falsch beantworteten Fragen und nur diese üben können, benötigen Sie dazu wieder eine neue Abfrage sowie ein Formular, das auf dieser Abfrage basiert und nur die Fragen anzeigt, die vorher falsch beantwortet wurden. Die Abfrage dazu ist ganz einfach. Sie muss alle Felder der Tabelle tabFragen enthalten, allerdings nur von den Fragen, die zuvor falsch beantwortet wurden. Sie benötigen dazu eine Abfrage abfFalscheFragen, die die Tabelle tabFragen und die Abfrage abfErgebnisGruppiert enthält. Zwischen beiden müssen Sie eine einfache 1:1-Beziehung zwischen den Feldern ID herstellen.

Neben den Feldern der Tabelle tabFragen fügen Sie noch das Feld MittewertvonKorrekt aus der Abfrage ein und definieren für dieses Feld das Kriterium <>-1. In der SQL-Ansicht sollte die Abfrage dann wie in Listing 3 aussehen.

SELECT tabFragen.ID, tabFragen.Frage, tabFragen.Hilfe,
abfTestergebnisGruppiert.Mittelwertvonkorrekt
FROM abfTestergebnisGruppiert
INNER JOIN tabFragen
ON abfTestergebnisGruppiert.Frage = tabFragen.ID
WHERE (((abfTestergebnisGruppiert.Mittelwertvonkorrekt)<>-1));

Nun müssen Sie noch das Formular erstellen. Am einfachsten ist es, dazu das Formular frmTestanzeigen zu kopieren. Nennen Sie die Kopie einfach frmTestUeben. Änderungen müssen Sie jetzt nur noch am Hauptformular und dessen Code vornehmen. Das Unterformular bleibt unangetastet.

Zunächst einmal müssen Sie die Datenquelle für das Formular ändern. Setzen Sie dazu die Eigenschaft Datenherkunft auf abfFalscheFragen.

Nun fehlt noch etwas Code (Listing 4). Sorgen Sie dafür, dass das Formular mit einer entsprechenden Meldung geschlossen wird, wenn der Benutzer zuvor keine Frage falsch beantwortet hat. Fragen Sie hierzu in der Open-Ereignisprozedur des Formulars die RecordCount-Eigenschaft ab. Ist sie gleich 0, gibt es keine anzuzeigende Frage. Der Code gibt eine entsprechende Meldung aus und schließt dann das Formular über den Timer.

Private Sub Form_Open(Cancel As Integer)
If Me.RecordsetClone.RecordCount = 0 Then
MsgBox "Es gibt keine Fragen zum Üben, Sie " & _
"haben vorher alle richtig beantwortet oder " & _
"den Test noch nie vollständig gemacht!", _
vbInformation
boolSchliessen = True
Me.TimerInterval = 500
End If
End Sub
Private Sub bttWeiter_Click()
On Error GoTo Err_bttWeiter_Click
'Ermitteln der Daten im Unterformular
Dim lngMax As Long
Dim objUForm As Form
Dim arrDaten As Variant
Dim lngI As Long
Dim lngZeilen As Long
Set objUForm = Me.frmAntwortenUfo.Form
lngMax = objUForm.Controls.Count \ 3
lngZeilen = 0
For lngI = 0 To lngMax - 1
If (objUForm.Controls("Wert" & lngI + 1).Visible = True) _
And (objUForm.Controls("Wert" & lngI + 1).Value = True) Then
If lngZeilen = 0 Then
ReDim arrDaten(1, 0)
Else
ReDim Preserve arrDaten(1, lngZeilen)
End If
arrDaten(0, lngZeilen) = objUForm.Controls("Wert" & lngI + 1).Value
arrDaten(1, lngZeilen) = objUForm.Controls("ID" & lngI + 1).Value
lngZeilen = lngZeilen + 1
End If
Next lngI
'Speichern der Daten
On Error GoTo 0
Speichern Me.ID.Value, Me.txtBenutzer.Value, arrDaten
On Error GoTo Fehler
'Nächste Frage anzeigen
DoCmd.GoToRecord , , acNext
Exit_bttWeiter_Click:
Exit Sub
Err_bttWeiter_Click:
MsgBox Err.Description
Resume Exit_bttWeiter_Click
Fehler:
MsgBox "Test abgeschlossen!", vbInformation
boolSchliessen = True
Me.TimerInterval = 500
End Sub

Code ändern

Nun müssen Sie gegenüber dem ursprünglichen Formular noch den Code für die Weiter-Schaltfläche ändern. Fügen Sie eine On-Error-Goto-Fehleranweisung und die Sprungmarke Fehler ein. Das ist notwendig, weil durch die Änderung der Datenquelle nun kein leerer Datensatz nach der letzten Frage angezeigt wird. Nur dann sorgt aber die Current-Ereignisprozedur dafür, dass das Formular geschlossen wird, weil der Test beendet ist. Da es hier keinen leeren Datensatz am Ende gibt, erscheint beim Versuch, vom letzten Datensatz zum nächsten zu wechseln, eine Fehlermeldung.

Damit ist das Formular fertig und zeigt nun der Reihe nach alle Fragen an, die zuvor falsch beantwortet wurden.

Zusammenfassung

Die Auswertung des Testergebnisses ist recht komplex. Mit einigen grundlegenden Abfragen können Sie damit aber eine ganze Menge erreichen und verschiedene Formulare und Auswertungen gestalten. Mit nur wenig Mehraufwand wäre es auch möglich, in einer weiteren Tabelle nach jedem Test die Fehleranzahl des Benutzers zu speichern, so dass Sie dem Benutzer anzeigen lassen können, wie sich seine Ergebnisse verändern.

Mögen Sie Farbe?

Optional können Sie die Kontrollkästchen farbig unterlegen. Dazu fügen Sie einfach ein Rechteck aus der Werkzeugleiste ein und kopieren das Kontrollkästchen in das Rechteck, indem Sie dieses markieren, bevor Sie das ausgeschnittene Kontrollkästchen mit [Strg]+[V] einfügen. Das Rechteck können Sie dann mit einer Farbe Ihrer Wahl füllen und in der Höhe über den gesamten Detailbereich ausdehnen. Dann bilden die Rechtecke farbige Streifen.