Drei Pfund Gehacktes vom Winzen (Daten-Skelettierung Pt. III)

Ein paar Werktage Pause muss auch mal sein, aber jetzt geht’s weiter mit der Sektion des Winzen-Projekt von @derguenther. Die Diskussionen dazu sind ziemlich spannend und interessanterweise ist dadurch meine Webseite so häufig besucht wie nie in ihren 10 Jahren Bestehen. Es scheint also so eine Art Interesse zu geben.

Damit wir jetzt ein bisschen voran kommen, habe ich einmal eine komplette Liste aller Felder auf dem Stammdatenblatt gezogen und transponiert (um 90° gedreht, für alle, „die-wo-nicht-Statistiker-sein-tun“). Ich habe dabei der Ordnung halber zunächst die eindeutigen Werte, die man gemeinhin Personendaten nennt, nach oben gezogen, so dass wir in der Struktur die Felder zur Person dicht beieinander haben:

ID (Nachname, Vorname)
Geschlecht
Geburtsdatum
Geburtsdatum det.
Geburtsort
Religion
Mädchenname

Das sind alles typische Felder in Personendatensätzen und wir können sie so direkt stehen lassen. Mit kleineren Einschränkungen: Dass wir die Spalte ID zerlegen sollten müssen, hatte ich erklärt. Ob Geschlecht Entität oder Konstrukt ist, wird ja gerne gestritten. Für unsere Datenbank ist es Ersteres, setze ich mal fest. „Geburtsdatum“ und „Geburtsdatum det.“ zeigt uns ein Dilemma der historischen Forschung: Jeder Mensch hat zwar ein Geburtsdatum, aber manche Menschen wissen ihres nicht, und als Historiker kann man eher öfter als selten auf das genaue Datum nicht zugreifen. Wir hätten jetzt mehrere Möglichkeiten, damit umzugehen: Wir machen zwei Spalten – einmal das Geburtsjahr, was fast immer (näherungsweise) aufzuspüren ist und einmal ein Datum, falls wir es exakt wissen. Das hat den Vorteil, dass wir die Daten in ihrem „Typ“ speichern und keine Unschärfen anlegen: Das Datum ist ein Datum, das Jahr eine Zahl, es gibt keinen Wert wie 0.0.1910, der bedeutet: „Weiß ich nicht genau“. Ich würde eine Spalte vom Typ „Boolean“ ergänzen: „isExactYear“, damit wir „1910“ (YearOfBirth = 1910 AND isExactYear = TRUE ) von „ca. 1910“ (YearOfBirth = 1910 AND isExactYear = FALSE). Das Feld „Mädchenname“ würde ich neutral „Geburtsname“ nennen, denn manchmal erhalten auch Männer neue Dokumente und neue Namen.

„Religion“ ist ein typisches Feld, was uns den Unterschied zwischen dem Finanzamt und Historikern klar macht: Religion ist für das Finanzamt ein Enum (erinnert ihr Euch?), das ausdrückt: kassieren wir Kirchensteuer und wer bekommt die Kohle, wenn? Historiker hingegen betrachten nicht selten lebende oder verstorbene Personen in den Zeugnissen, die es über sie gibt. Für die Winzen-Daten ist das eigentlich sekundär, aber wie sieht es mit Josef Kasel aus? Der ist „glaubenlos, früher kath.“ – er ist damit der einzige Datensatz und wir können diese Polyvalenz zugunsten der Datenreinheit auflösen. Wir sollten aber hier definierte Werte einhalten, damit wir die Konfession bzw. den Glauben filtern können, um z.B. zu fragen, ob gläubige Christen in Haft und Verhör sich anders verhalten als Personen, die keine religiöse Einstellung haben – wer weiß, vielleicht macht das einen Unterschied?

Wir kommen zu:

Beruf

Ein typischer Sekundärschlüssel, bei dem hier aber tolerierbar ist, wenn wir ihn nicht abstrahieren. Eigentlich sollte hier nun eine zweite Tabelle entstehen mit einer Spalte BerufId (Integer(16), Auto_Increment) und einer Spalte „Berufsbezeichnung (Varchar(255))“. Auf dem Personendatensatz wird lediglich ein Integer-Wert abgelegt, nämlich die zugehörige BerufId. Warum? Weil in der Tabelle z.B. mehrere Verkäufer/innen vorkommen. Wir können also den Beruf „abstrahieren“ und durch eine BerufId ersetzen, und darauf die Bezeichnung aus der zweiten Tabelle laden. Das nennt man one-to-many oder 1:n-Relation: Ein Wert (Beruf) hat viele Personen, die ausüben.

Wenn jetzt jemand spitzfindig sein will: Ein echter Sekundärschlüssel ist der „ausgeübte Beruf“, und zwar nur zum Zeitpunkt X. Eigentlich müssten wir also eine „Junction“ bauen, und jetzt wird’s echt nerdig. Aber wer scharf nachdenkt, wird darauf kommen, wie es geht:

Wir haben die Person (PersonId, Vorname, Nachname). Und wir haben einen Beruf (BerufId, Berufsbezeichnung). Was uns fehlt, ist der Zeitpunkt, wer wann welchen Beruf ausübt. Und deswegen machen wir das große Fass auf und machen eine „Many-to-Many-Tabelle“ auf (mm-Relation oder n:n). Eine n:n-Relation sähe hier vielleicht so aus: Tabelle AusgeuebteBerufe (AusgeuebterBerufId, StartDate, EndDate, PersonId, BerufId). Wir können jetzt hier jetzt noch ein wenig Feinheiten reinbringen: Ich empfehle uns die Flags (Markierungen) „isKnown“ und „isExact“ für StartDate und EndDate, damit wir markieren können, ob wir ein exaktes Datum oder nur ein vages Jahr speichern, oder gar das Ablaufdatum eines vorangegangenen Datensatzes als Näherungswert für den Beginn eines neuen Datensatzes nehmen müssen.

Ich habe jetzt ein paar Konventionen verletzt, und ein paar andere will ich erklären:

Bool-Felder (Boolean), deren Werte WAHR oder FALSCH (TRUE/FALSE) ausdrücken, haben immer einen Namen, der mit einem Verb wie haben, sein, kann, darf, etc. beginnt. Idealerweise beschränkt man sich auf to have (has) und to be (is) und benennt die Prüfung, auf die der Wert antwortet (isRed, hasChildren). Alle Felder benennt man in derselben Sprache (urks, knapp daneben) und alle Felder, die Schlüssel sind, tragen in ihrem Namen einen Hinweis darauf, was sie beschreiben (PersonId, BerufId). Auf der Person wird es nämlich mit zunehmender Normalisierung immer mehr Id-Felder geben (Sekundärschlüssel für abstrahierte Daten). Und dann ist es gut, auf den ersten Blick zu sehen, dass es sich um einen Schlüssel handelt und was dieser Schlüssel beschreibt.

Schule
Wohnort und Geocode

Auch Schulbildung, Arbeitgeber, Wohnsitz sind solche n:n-Relationen, bei beidem kennen wir das auch als Erzählungen unserer Eltern („hier habe ich als Kind gewohnt“, „auf diesem Stuhl saß ich als Erstklässler auch“) und wissen, dass wir z.B. eine Wohnung vor uns haben, einen Menschen, der diese Wohnung für eine gewisse Zeit gemietet hat, und Mietverträge, die technisch die n:n-Relation zwischen beiden herstellen.

Das interessante ist: Jede n:n-Relation ist zwar riesig aufwendig herzustellen, aber sie hat einen besonderen Charme: Ich erlege uns auf, als letzte Spalte jeder n:n-Tabelle einen Wert „ReferenceId“ oder „BelegstellenId“ zu führen. Und hierauf mappen wir eine Tabelle „References“ (ReferenceId, SourceId, LocationPage, LocationOther, FootnoteText, Note). Die SourceId kann nun entweder beeinhalten den Referenz-Code unserer externen Litataturverwaltung, oder auf eine Zotero-Ressource verweisen, die wir z.B. über die Zotero-API in unser Projekt einlesen. Oder eben banal die nächste Tabelle, die Sources heißt und mit dem Feld SourceId beginnt.

Mit diesen Grundlagen ist es nun einfach:

Eine Reihe Bool-Felder

Im festen kern? Widerstand? - polyvalent, besser je ein Feld für "isInnerCircle" und "isResistanceMember"
Staatsfeindliche Gespräche 
Mitwisser - Winzengruppe 
Briefkontakt mit Kasel 
Stellt Kontakt zu Winzen her

Die meisten anderen Felder sind verschiedene Arten von Relation (1:n und n:n)

Deckname (der kann wechseln, wenn er "verbrannt" ist, aber es tut nicht weh, ihn nicht zu abstrahieren)
NS Organisationen (da steckt vieles in einem, daher sollte hier eine Tabelle her, die NS-Organisationsmitgliedschaften beschreibt)
DAF
RAD
RAB
Wehrmacht
Freidenker (wir verfahren wie oben mit allen nicht-NS-Organisationen und Gruppen)
Freier Wanderbund
Winzengruppe
Andere Nicht NS-Organisation

1:1 Relationen, die wir aber vielleicht abstrahieren wollen, um eine Belegstelle anzuhängen. Hier wäre aber auch denkbar, ein Feld je Erstkontakt und ein Feld je Beleg auf den Personendatensatz zu machen. Ist nicht sauber, aber akzeptabel:

Erstkontakt mit Winzen
Ernstkontakt mit Kasel

m:m-Relationen:

Steht in Beziehung zu - PersonHasRelationToPerson (PersonId1, PersonId2)
Kennt: - PersonKnowsPerson (PersonId1, PersonId2)
Verheiratet mit - Marriages (HusbandId, WifeId, DateMarried, DateDivorced, MarriageSourceId, DivortionSourceId)
Kinder - n:n-Relation (FatherId, MotherId, ChildId)
Familienstand - kann man errechnen aus den Heiratsdaten, schadet aber nicht, ein Enum zu setzen
Vater - bildet man aus n:n-ParentChild-Tabelle ab.
Mutter - dto.

Die übrigen Felder sind etwas schwieriger und man muss sie eher aus dem Dokumentarischen als aus der Datenbank-Gesundheit her betrachten. Ich würde eine weitere Tabelle mit „Haftzeiten“, „Verhören“, „Verfahren“, „Verfahrensbeteiligten“, „Urteilen“ machen und einige dieser Daten in diese Tabellen extrahieren.

 
Geschäft I Winzen - Dortmund-Evening
Geschäft II Winzen 1934-1938 - Uhlandstraße
Wohnung Winzen
Wohnung Lampe
Wohnung Jungmann Ende 1936-38
Wohnung Becker 1939
Festnahme
Einlieferung ins Polizeigefängnis
Freigelassen
Festnahme wg.
Vorwürfe
Untersuchungshaft
Urteil
Urteil durch
Gefängnis etc?
Urteilsspruch
Todesdatum
Bemerkung
Bemerkung II
Quelle

Zum Schluss hin war ich jetzt nicht mehr allzu detailliert, aber die grundsätzliche Technik sollte klar geworden sein. Der nächste Schritt ist die Ausformung als SQL-Datei und die Zerlegung der bestehenden Daten. Mit den beiden Rohdaten könnt ihr Euch dann auf Eurem Testsystem Eure persönliche Winzengruppe aufbauen 😉

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*