Die Art, wie LimeSurvey Umfrageantworten speichert, gehört zu den ältesten und fundamentalsten Architekturentscheidungen der Plattform. Sie beeinflusst alles, was darüber liegt: Plugins, Integrationen, Reporting und die Erweiterbarkeit des Systems.
Wer mit LimeSurvey arbeitet, kommt früher oder später mit dieser Struktur in Berührung – spätestens dann, wenn Antwortdaten außerhalb der Plattform ausgewertet oder in eigene Systeme integriert werden sollen.
Survey-spezifische Antworttabellen
Für jede Umfrage legt LimeSurvey eine eigene Datenbanktabelle an. Der Tabellenname folgt dem Schema
survey_<sid>
wobei <sid> der Survey-ID entspricht – zum Beispiel:
survey_112233
Alle Antworten aller Teilnehmer dieser Umfrage werden in dieser einen Tabelle gespeichert.
Das war zu seiner Zeit eine pragmatische Entscheidung. Eine Tabelle pro Umfrage ist leicht zu verstehen, einfach zu debuggen und erlaubt schnelle Abfragen ohne komplexe Joins. Für kleine bis mittelgroße Umfragen funktioniert dieses Modell zuverlässig und performant.
Datenbankseitig entspricht dieses Modell einem Wide-Table-Design: Jede Frage – oder sogar jede Teilfrage – wird zu einer eigenen Spalte. Die Antworten eines Teilnehmers bilden eine einzelne Zeile, die alle Antworten enthält.
Die Kehrseite zeigt sich, sobald man über eine einzelne Umfrage hinausdenkt. Wer mehrere Umfragen zusammenführen, vergleichen oder in ein Data Warehouse überführen möchte, arbeitet nicht mit einer einheitlichen Struktur, sondern mit einer Vielzahl unterschiedlicher Tabellen.
Warum dieses Modell damals sinnvoll war
Aus heutiger Sicht wirkt dieses Wide-Table-Design ungewöhnlich. In der Zeit, in der LimeSurvey entstand, war es jedoch eine sehr pragmatische Entscheidung.
Frühe Web-Stacks bestanden typischerweise aus PHP und MySQL-Versionen, deren Query-Optimizer bei komplexen Joins deutlich schwächer war als heute. Abfragen über viele Tabellen konnten schnell zum Performanceproblem werden.
Das Wide-Table-Modell vermeidet dieses Problem vollständig: Jede Antwort eines Teilnehmers liegt in genau einer Zeile, und viele Auswertungen können ohne Joins erfolgen.
Auch für Datenexporte ist dieses Modell ideal. CSV- oder SPSS-Dateien lassen sich direkt erzeugen, da jede Zeile einem Teilnehmer und jede Spalte einer Frage entspricht.
Für die technischen Rahmenbedingungen der damaligen Zeit war dieses Modell daher nicht nur einfach – sondern auch effizient.
Das Feldnamenschema (SGQA)
Die Spaltennamen innerhalb der Antworttabellen folgen einem internen Schema, das als SGQA bekannt ist:
- Survey ID
- Group ID
- Question ID
- Answer ID
Die Komponenten werden durch X getrennt.
Grundschema:
sidXgidXqid
Beispiel:
112233X12X42
Bei Fragetypen mit Teilfragen oder Antwortoptionen wird ein zusätzlicher Suffix angehängt.
| Fragetyp | Code | Feldname-Schema | Beispiel |
|---|---|---|---|
| Radio List | L | sidXgidXqid | 112233X12X42 |
| Radio List (Other) | L | sidXgidXqidother | 112233X12X42other |
| Multiple Choice | M | sidXgidXqid + aid | 112233X12X42SQ001 |
| Multiple Choice mit Kommentar | P | sidXgidXqid + aid | 112233X12X42SQ001 |
| Kommentar-Feld | P | sidXgidXqid + aid + comment | 112233X12X42SQ001comment |
| Array | F | sidXgidXqid + aid | 112233X12X42SQ001 |
| Array Dual Scale | 1 | sidXgidXqid + aid + #scale | 112233X12X42SQ001#0 |
Die aid entspricht dem Code der Teilfrage oder Antwortoption. Standardmäßig erzeugt LimeSurvey Codes wie:
SQ001
SQ002
SQ003
Das Schema ist intern konsistent, aber ohne Kenntnis der Struktur schwer zu lesen.
Eine kleine historische Kuriosität
Auf den ersten Blick enthält SGQA eine Redundanz: Die Survey-ID (sid) steht sowohl im Tabellennamen als auch im Feldnamen.
Wenn die Tabelle bereits survey_112233 heißt – warum dann noch einmal 112233 im Feldnamen?
Der Grund liegt in der ursprünglichen Architektur des Expression-Systems. SGQA diente dort als globale Referenz für Fragen, zum Beispiel in Relevanzbedingungen oder Validierungsregeln:
{112233X12X42 == "Y"}
Damit konnte das System jede Frage eindeutig identifizieren – unabhängig davon, in welcher Umfrage oder Gruppe sie gerade verarbeitet wurde.
Heute wirkt diese Redundanz ungewohnt, erklärt aber, warum das Schema so lange unverändert geblieben ist.
Datentypen der Antwortspalten
Die tatsächlichen Datentypen der Antwortfelder sind meist relativ klein. Typische Beispiele sind:
| Fragetyp | Datentyp | Inhalt |
|---|---|---|
| Single Choice | VARCHAR(5) |
Antwortcode |
| Matrix / Array | VARCHAR(5) |
Antwortcode |
| Multiple Choice | VARCHAR(1) |
'Y' oder NULL |
| Textfragen | TEXT |
Freitext |
| Numerische Fragen | DECIMAL(30,10) |
numerischer Wert |
Viele Spalten sind damit sehr kompakt. Eine typische Single-Choice-Antwort benötigt zum Beispiel nur wenige Bytes, da lediglich der Antwortcode gespeichert wird.
Die Herausforderung liegt daher meist nicht in der Größe einzelner Spalten, sondern in der Anzahl der Spalten, die große Umfragen erzeugen können.
Die MySQL-Zeilengrößen-Limitierung
MySQL begrenzt die maximale Zeilengröße auf 65.535 Bytes.
Da LimeSurvey alle Antworten eines Teilnehmers in einer einzigen Zeile speichert, wächst die Zeilengröße mit jeder zusätzlichen Frage oder Teilfrage.
Dabei ist wichtig: MySQL berechnet die Zeilengröße nicht aus den tatsächlich gespeicherten Daten, sondern aus der maximal möglichen Länge jeder Spalte.
Eine Spalte
VARCHAR(5) utf8mb4
kann bis zu
5 × 4 = 20 Bytes
belegen, da utf8mb4 bis zu vier Bytes pro Zeichen verwendet.
Die effektive Zeilengröße ergibt sich aus der Summe der maximal möglichen Speicherbedarfe aller Spalten.
Große Umfragen mit vielen Matrixfragen oder umfangreichen Multiple-Choice-Blöcken können dadurch sehr breite Tabellen erzeugen. Wird die maximale Zeilengröße überschritten, verweigert MySQL die Erstellung der Tabelle mit dem Fehler:
Row size too large
Die Umfrage lässt sich dann nicht aktivieren.
In der Praxis kann neben der Zeilengröße auch ein weiteres MySQL-Limit relevant werden: Tabellen dürfen maximal rund 1000 Spalten enthalten.
Konsequenzen für Plugins und Integrationen
Wer Plugins oder externe Integrationen entwickelt, die direkt auf Antworttabellen zugreifen, muss das SGQA-Schema korrekt berücksichtigen.
Änderungen an Fragen oder Gruppen können Feldnamen beeinflussen. Integrationen, die Feldnamen hart kodieren, sind daher fragil.
LimeSurvey stellt mit createFieldMap() eine Hilfsfunktion bereit, die das aktuelle Feldnamenschema einer Umfrage liefert. Diese Funktion sollte bevorzugt verwendet werden.
Ein moderner Alternativansatz
Würde man das Speichermodell heute neu entwerfen, kämen verschiedene alternative Ansätze infrage.
Eine mögliche Variante wäre ein Modell, bei dem jede Frage einer Spalte entspricht und die eigentlichen Antwortdaten als JSON gespeichert werden.
| response_id | Q1 | Q2 | Q3 |
|---|---|---|---|
| 1 | {"value":"A1"} | {"value":"Ja"} | {"value":42} |
| 2 | {"SQ001":"A2","SQ002":"A1"} | {"value":"Nein"} | {"value":7} |
Der Spaltenname entspricht dem Fragecode und ist damit stabil und menschenlesbar.
Das JSON enthält die eigentlichen Antwortdaten – bei einfachen Fragen ein einzelner Wert, bei komplexeren Fragen strukturierte Informationen.
Ein solcher Ansatz reduziert strukturelle Einschränkungen erheblich. TEXT- oder JSON-Spalten zählen in MySQL nur mit wenigen Bytes zur Zeilengröße, sodass die 65-KB-Grenze praktisch keine Rolle mehr spielt.
Der Preis ist jedoch real: Abfragen auf Teilfragenebene erfordern JSON-Extraktion, bestehende Reporting-Tools müssten angepasst werden, und eine Migration vorhandener Daten wäre aufwendig. Was das strukturell bedeutet, haben wir im Artikel Der Rewrite-Reflex ausführlicher beleuchtet.
Wide Tables sind nicht grundsätzlich ein Problem
Ein Wide-Table-Design ist nicht grundsätzlich falsch. In vielen analytischen Szenarien ist es sogar vorteilhaft.
Data-Warehouse-Systeme und analytische Datenbanken arbeiten häufig mit sehr breiten Tabellen, weil viele Abfragen spaltenweise erfolgen. Moderne spaltenorientierte Systeme wie ClickHouse oder BigQuery sind explizit für solche Strukturen optimiert.
Auch in LimeSurvey hat dieses Modell Vorteile: Ein kompletter Datensatz eines Teilnehmers lässt sich sehr einfach exportieren oder analysieren.
Die Herausforderungen entstehen vor allem dann, wenn sehr große Umfragen erstellt werden oder externe Systeme flexibel auf einzelne Fragen zugreifen müssen.
Ausblick: LimeSurvey 7
LimeSurvey adressiert in Version 7 Beta 1 genau diese Punkte. Die Änderungen sind struktureller Natur – keine neuen Features, aber eine grundlegende Bereinigung:
survey_<sid>wird zuresponses_<sid>survey_<sid>_timingswird zutimings_<sid>- Feldnamen folgen einem lesbaren Schema:
Q<questionId>bzw.Q<questionId>_S<subquestionId>
Das ist eine direkte Antwort auf die Schwächen des bisherigen Modells: mehr Lesbarkeit, mehr Konsistenz, eine stabilere Basis für Plugins und Integrationen. Wer eigene Plugins, Themes oder Integrationen betreibt, sollte die Beta im Blick behalten – Anpassungen werden nötig sein.
Wir werden auf Version 7 und die Konsequenzen dieser Änderungen zurückkommen, sobald die finale Version verfügbar ist.
Fazit
Das Speichermodell von LimeSurvey ist ein gutes Beispiel für historisch gewachsene Architekturentscheidungen: pragmatisch und effizient in ihrer Zeit, mit Konsequenzen, die erst sichtbar werden, wenn Systeme über ihre ursprünglichen Anforderungen hinaus genutzt werden.
Dass LimeSurvey diese Struktur in Version 7 selbst überarbeitet, zeigt, dass der Modernisierungsbedarf erkannt wurde – und dass selbst grundlegende Architekturentscheidungen nicht für immer unveränderlich sind.