Vererbung in OO-Programmierung vs Datenbanken

Status
Für weitere Antworten geschlossen.

jgsedi

Benutzer
Beiträge
9
Hallo Leute,
ich habe eine generelle Frage: Wie implementiert man in Datenbanken Spezialisierungen - nehmen wir als einfachen Fall den Lehrer und den Schüler als Spezialfall einer Person. (siehe Bild Vererbung-OO-Programmierung.jpg).
Vererbung-OO-Programmierung.jpg

Wie setzt man dies nun ganz allgemein in Datenbanken um?

Erstellt man für Lehrer und Schüler eine Tabelle mit allen Feldern (siehe Schueler-Lehrer.jpg) Schueler-Lehrer.jpg
oder erstellt man eine Basistabelle und für die Spezialisierungen eigene Tabellen aber nur die fehlenden Felder (siehe Person-Schueler-Lehrer.jpg)?
Person-Schueler-Lehrer.jpg

Vielen Dank für eure Mühen
 
Werbung:
z.B. kann man das so umsetzen:

Code:
test=# create table person(id serial primary key, nachname text, vorname text);
CREATE TABLE
test=*# create table schueler (klasse text) inherits (person);
CREATE TABLE
test=*# create table lehrer (faecher text[]) inherits (person);
CREATE TABLE
test=*# \d+ person
  Tabelle »public.person«
  Spalte  |  Typ  | Sortierfolge | NULL erlaubt? |  Vorgabewert  | Speicherung | Statistikziel | Beschreibung
----------+---------+--------------+---------------+------------------------------------+-------------+---------------+--------------
 id  | integer |  | not null  | nextval('person_id_seq'::regclass) | plain  |  |
 nachname | text  |  |  |  | extended  |  |
 vorname  | text  |  |  |  | extended  |  |
Indexe:
  "person_pkey" PRIMARY KEY, btree (id)
Kindtabellen: lehrer,
  schueler

test=*# \d+ schueler
  Tabelle »public.schueler«
  Spalte  |  Typ  | Sortierfolge | NULL erlaubt? |  Vorgabewert  | Speicherung | Statistikziel | Beschreibung
----------+---------+--------------+---------------+------------------------------------+-------------+---------------+--------------
 id  | integer |  | not null  | nextval('person_id_seq'::regclass) | plain  |  |
 nachname | text  |  |  |  | extended  |  |
 vorname  | text  |  |  |  | extended  |  |
 klasse  | text  |  |  |  | extended  |  |
Erbt von: person

test=*# \d+ lehrer
  Tabelle »public.lehrer«
  Spalte  |  Typ  | Sortierfolge | NULL erlaubt? |  Vorgabewert  | Speicherung | Statistikziel | Beschreibung
----------+---------+--------------+---------------+------------------------------------+-------------+---------------+--------------
 id  | integer |  | not null  | nextval('person_id_seq'::regclass) | plain  |  |
 nachname | text  |  |  |  | extended  |  |
 vorname  | text  |  |  |  | extended  |  |
 faecher  | text[]  |  |  |  | extended  |  |
Erbt von: person

test=*#
 
Davon abgesehen ist das Modell so nicht gut, ein Lehrer kann ja mehrere Fächer haben / unterrichten. Aber das ist eineandere Baustelle ... (daher auch mein TEXT[] als Datentyp)
 
Danke @akretschmer , aber ist das Standard - SQL? Ttschuldige, aber ich bin kein Datenbänker und meine Kenntnisse beziehen sich auf einen Informatikkurs aus der Vorzeit! Ein Begriff wie "inherits" ist mir aus SQL nicht bekannt!
Zur Erklärung: Bei uns geht es um ein Programmierprojekt. Dabei soll SQLite verwendet werden. Dabei kam eben die Grundsatzfrage auf, wie man die OO-Strukturen in DBs abbildet, also auch Vererbung.
 
Das ist PostgreSQL. Ist eine Object-relationale Datenbank. Eine recht gute übrigens, kann einiges mehr als die sonstigen und üblichen Verdächtigen. Also nein, ist kein Standard-SQL.
 
Ach ja, das mit den Lehrern und den Fächern ist klar @akretschmer - aber Danke für den Hinweis.

Da im Progammierprojekt SQLite erforderlich ist helfen mir Funktionen von spezieller Datenbanksoftware (Postgres, Mysql, ...) nicht. Außerdem geht es hier mehr um das generelle Schema, wie das umsetzbar ist. Mir sind noch ein paar Möglichkeiten eingefallen:

Mögl 1:
Create Table Person(ID, Nachname, Vorname)
Create Table Lehrer(ID {int=Person.ID}, Faecher)
Create Table Schueler(ID {int=Person.ID}, Klasse)

Mögl 2:
Create Table Lehrer(ID, Nachname, Vorname, Faecher)
Create Table Schueler(ID, Nachname, Vorname, Klasse)

Mögl 3:
Create Table Person(ID, Nachname, Vorname)
Create Table Lehrer(ID, Person_ID {Fkey Person.ID}, Nachname, Vorname, Faecher)
Create Table Schueler(ID, Person_ID {Fkey Person.ID}, Nachname, Vorname, Klasse)

oder gar Mögl 4:
Create Table Person(ID, Nachname, Vorname, Faecher, Klasse)

oder vielleicht andere Möglichkeiten?

Wie ihr seht brauch ich wirklich Hilfe ;)
 
Zuletzt bearbeitet:
am nächsten kommst du dem Ziel, denke ich, mit 1. 3 hat redundante Felder, 4 ist IMHO völlig daneben. Und 2 hat exakt NULL Ansatz für Vererbung. Und wenn Du eine DB ohne Arrays etc. hast solltest Du die Zuordnung Lehrer zu Fächern normalisieren.
 
Mögl. 1 war auch mein Favorit!

ABER: Wie ist es dann beim Schreiben der Daten? Gibt es ne Möglichkeit - Trigger - dass man bei der Dateneingabe eine Schülers
INSERT INTO Schueler(1000, "Mustermann", "Max", "11a")

die Daten gleich auf beide relevanten Tabellen verteilt
INSERT INTO Person(1000, "Mustermann", "Max") ++und++ INSERT INTO Schueler(1000, "11a")

Gibt's/geht so was?
 
die Daten gleich auf beide relevanten Tabellen verteilt
INSERT INTO Person(1000, "Mustermann", "Max") ++und++ INSERT INTO Schueler(1000, "11a")

Gibt's/geht so was?

Beim ersten Insert sollte ja eine ID automatisch generiert werde, und diese dann als FK im zweiten Insert verwendet werden. Die gute Nachricht: das geht alles zusammen in EINEM (in Zahlen: 1) SQL-Statement, un dzwar atomar. Die für Dich vielleicht schlechte: das ist eine PostgreSQL-Erweiterung des SQL-Standards. Nennt sich wCTE: writeable Common Table Expressions.

Mit anderen Systemen wirst erst den ersten Insert machen müssen und dann mit last_insert_id() oder was das System halt bietet die neue ID ermitteln, um diese beim zweiten Insert verwenden zu können. Transaktionen können helfen, das 'sicher' zu machen.
 
Offensichtlich ;-)

um es mal kurz zu zeigen, wie es gehen könnte, würdet ihr die beste OpenSource-Datenbank nutzen:

Code:
test=# create table person (id serial primary key, name text);
CREATE TABLE
test=*# create table schueler(person_id int references person, klasse text);
CREATE TABLE
test=*# with new_id as (insert into person (name) values ('person 1') returning id) insert into schueler select id, 'klasse 1' from new_id;
INSERT 0 1
test=*# select * from person;
 id |  name   
----+----------
  1 | person 1
(1 Zeile)

test=*# select * from schueler ;
 person_id |  klasse  
-----------+----------
  1 | klasse 1
(1 Zeile)

test=*#

Der Trick ist in:

Code:
with new_id as (insert into person (name) values ('person 1') returning id) insert into schueler select id, 'klasse 1' from new_id;

hier wird erst in person eingetragen, die id wird zurück geliefert und in einer virtuellen new_id - Tabelle aufgehoben. Das nachfolgende Insert fragt dann diese Tabelle nach der id ab. Das läßt sich beliebig ausbauen. Stelle Dir das mit dem Lehrer vor und einer Zuordnungstabelle Lehrer <-> Fach. Auch diese könnte man so in einem Rutsch befüllen.
 
Moin,
da hier das Stichwort OO Programmierung genannt wird, ist anzumerken, dass das hier eben keine OO Programmierung ist.
Hier wird lediglich (pseudo) Vererbung verwendet, und der Kardinalfehler gemacht Objekt und Persistence zu vermischen.

In der OO Programmierung gibt es Klassen. Klassen haben Konstruktoren, Methoden und Eigenschaften und können Sichtbarkeiten auf diese definieren.
Das ist in diesem Beispiel nicht der Fall, es werden lediglich Eigenschaften vererbt und zwar mit einer öffentlichen Sichtbarkeit - ebenfalls nicht ganz OO konform.
In der besten kommerziellen Datenbank :D wird daher zuerst in PLSQL die Klasse samt Konstruktor, Methoden etc. erzeugt, und anschließend in einer Tabelle eine Spalte von diesem Datentyp angelegt.
Die Persistence ist also getrennt von der eigentlichen Logik innerhalb der Klasse.
 
Werbung:
Sicher.
Code:
CREATE OR REPLACE TYPE PERSON AS OBJECT (
birthday date,
name varchar2(100),
 
CONSTRUCTOR FUNCTION person(p_name VARCHAR2,p_birthday DATE) RETURN SELF AS RESULT,
MEMBER PROCEDURE setName(p_name VARCHAR2),
MEMBER FUNCTION getName RETURN VARCHAR2,
MEMBER PROCEDURE setBirthday(p_birthday DATE),
MEMBER FUNCTION getBirthday RETURN DATE
);

CREATE OR REPLACE TYPE BODY PERSON AS
CONSTRUCTOR FUNCTION person(p_name varchar2,p_birthday DATE) RETURN SELF AS RESULT IS
BEGIN
    setName(p_name);
    setBirthday(p_birthday);
    RETURN;
END;

MEMBER FUNCTION getName RETURN VARCHAR2 IS
BEGIN
   RETURN name;
END;

MEMBER PROCEDURE setName (p_name VARCHAR2) IS
BEGIN
   name:=p_name;
END;

MEMBER PROCEDURE setBirthday(p_birthday DATE)IS
BEGIN
   birthday:=p_birthday;
END;

MEMBER FUNCTION getBirthday RETURN DATE IS
BEGIN
   RETURN birthday;
END;

END;

Eine einfache Personenklasse mit Konstruktor, getter und setter.
Jetzt die Tabelle, die die Objekte speichert:

Code:
create table t (id number primary key, pers person)

Und jetzt ein neues Objekt über den Konstruktoraufruf anlegen.
Code:
insert into t values (1,person('test1', sysdate-2000));

Ich würde jedoch von einer ausgiebigen Nutzung davon abraten, insbesondere wenn fachliche Objekte involviert sind.
Allein die Tatsache, das es für Datenbanken keine IDE vom Kaliber eines Eclipse, IntelliJ oder Visual Studio gibt macht die OO Erweiterungen der Datenbanken eigentlich hinfällig.
In Ausnahmefällen (z.B. wenn man sich selbst Data Cardridge Erweiterungen schreibt) ist es sicherlich sinnvoll, ansonsten sollte man OO bei den darauf spezialisierten Sprachen belassen.
 
Status
Für weitere Antworten geschlossen.
Zurück
Oben