Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Wie kann eine einfache relationale Lehrer-Fach-Schüler-Stapel-Datenbank entworfen werden?

Ihnen fehlt eine Zuordnungstabelle zu Fächern und Studierenden (zu Punkt 2):

// student [student_id] takes subject [subject_id]
takes(student_id, subject_id)

Beachten Sie, dass jede Basistabelle eine zugeordnete Anweisungsvorlage für Aussagen über die Geschäftslage hat, die durch Spaltennamen parametrisiert ist – ihr (charakteristisches) Prädikat . Die Zeilen, die das Prädikat wahr machen, gehen in die Tabelle. Beachten Sie, dass die Tabellendefinition wie eine Abkürzung für das Prädikat aussieht.

// teacher [id] named [name] with email [email] teaches subject [subject_id]
teacher(id, name, email, subject_id)

// subject [id] named [name] is [description]
subject(id, name, description)

// student [id] named [name] lives at [location])
student(id, name, location)

// batch [id] at venue [venue] was taught by teacher [teacher_id] on date [date]
batch(id, venue, teacher_id, date)

// student-batch [id] reports student [student_id] being in batch [batch_id]
student-batch(id, student_id, batch_id)
// CHECK student [student_id] takes the subject that is taught by the teacher of batch [batch_id]

Da Sie darüber ratlos zu sein scheinen, werde ich es in Bezug darauf ableiten, wie wir Tabellen-, Constraint- und Abfragedesign begründen können. Eine Möglichkeit, die gewünschte Einschränkung auszudrücken, scheint das kommentierte CHECK oben zu sein.

Um eine Tabelle, Einschränkung oder Abfrage in SQL auszudrücken, entscheiden wir uns zuerst für ihr Prädikat. Dann können wir das Prädikat in Kurzschrift umwandeln. Dann können wir die Kurzschrift in SQL umwandeln.

Prädikat:

student [student_id] takes the subject that is taught by the teacher of batch [batch_id]

Basistabellenprädikate verwenden:

FOR SOME k.*, t.*, b.* (
    student_id = k.student_id AND batch_id = b.bid
AND student [k.student_id] takes subject [k.subject_id]
AND teacher [t.id] named [t.name] with email [t.email] teaches subject [t.subject_id] 
AND batch [b.id] at venue [b.venue] was taught by teacher [b.teacher_id] on date [b.date]
AND [k.subject_id] = [t.subject_id]
AND [t.id] = [b.teacher_id])

Verwendung von Abkürzungen:

FOR SOME k.*, t.*, b.* (
    student_id = k.student_id AND batch_id = b.bid
AND takes(k.student_id, k.subject_id)
AND teacher(t.id, t.name, t.email, t.subject_id)
AND batch(b.id, b.venue, b.teacher_id, date)
AND k.subject_id = t.subject_id
AND t.id = b.teacher_id)

In einem FROM stellt jeder (möglicherweise implizite) Alias ​​eine Tabelle wie der angegebene Basistabellenname und/oder die Unterabfrage dar, wobei jedoch jede Spalte in ihrem Wert und Prädikat in alias umbenannt wird .Spalte .

Wir erhalten die Zeilen, die das UND zweier Prädikate erfüllen, indem wir die Tabellen der Prädikate in SQL JOINen. Wenn wir Zeilen wollen, die das UND einer Bedingung erfüllen, verwenden wir ON oder WHERE in SQL.

Eine SELECT-Klausel gibt Zeilen zurück, in denen FOR SOME-Werte gepunkteter Spalten die zurückgegebenen (nicht gepunkteten) Spalten Funktionen von gepunkteten Spalten entsprechen, die das FROM-Prädikat erfüllen.

SQL:Ersetzen Sie Anweisungen durch ihre Tabellen, AND durch JOIN oder ON oder WHERE und äußere FOR SOME &THERE EXISTS durch SELECT:

SELECT t.student_id AS student_id, b.bid AS batch_id
FROM takes k JOIN teacher t JOIN batch b
WHERE k.subject_id = t.subject_id
AND t.id = b.teacher_id
AND student_id = t.student_id
AND batch_id = b.id

Die Tabelle der Zeilen, die das ODER zweier Prädikate erfüllen, ist die UNION ihrer Tabellen. Für AND NOT verwenden wir EXCEPT (auch bekannt als MINUS) (oder ein LEFT JOIN-Idiom). FOR SOME oder THERE EXISTS über alle Spalten können in SQL nicht abgefragt werden, aber wenn wir wissen wollen, ob es Zeilen gibt, die ein Prädikat erfüllen, können wir EXISTS um eine Unterabfrage mit diesem Prädikat herum verwenden.

Angenommen, wir möchten eine Basistabelle so einschränken, dass jede Zeile ein Prädikat für einige Spalten erfüllt. Dh FOR ALL-Spalten, WENN sie das Basisprädikat erfüllen, DANN erfüllen sie das Abfrageprädikat. Dh FÜR ALLE Spalten, WENN die Zeile, die sie bilden, in der Basis ist, DANN ist sie in der Abfrage. Daher benötigen wir in SQL, dass NICHT EXISTIERT (SELECT-Spalten FROM base EXCEPT-Abfrage). Oder für jede Zeile in der Basis benötigen wir in SQL EXISTS(query).

In Standard-SQL könnten Sie CREATE ASSERTION CHECK(NOT EXISTS (SELECT student_id, batch_id FROM student-batch EXCEPT query)) oder in einem CREATE TABLE student-batch könnten Sie CHECK(EXISTS(query)) verwenden. Leider werden diese von MySQL oder den meisten DBMSs nicht unterstützt. Wenn Sie INSERT in einen Studentenstapel nach dem anderen einfügen, können Sie beim Auslösen verlangen, dass EXISTS (Abfrage) vorliegt. Oder Sie könnten bestimmte Spalten und zusammengesetzte FK-Einschränkungen (Fremdschlüssel) hinzufügen.

Jetzt schreiben wir eine Abfrage. Wir wollen Zeilen, in denen:

FOR k.*, t.*, b.*, s.*, sb.* (
    batch = b.id AND teacher = t.name AND student = s.name
AND takes(k.student_id, k.subject_id)
AND teacher(t.id, t.name, t.email, t.subject_id)
AND batch(b.id, b.venue, b.teacher_id, b.date)
AND student(s.id, s.name, s.location)
AND student-batch(sb.id, sb.student_id, sb.batch_id)
AND k.subject_id = t.subject_id
AND t.id = b.teacher_id
AND s.id = k.student_id
AND sb.student_id = k.student_id
AND sb.batch_id = b.id
AND @date = b.date)

Dies sieht aus wie das Constraint-Prädikat mit unterschiedlichen Rückgabespalten und hinzugefügten Zeilen. Das SQL wird genauso direkt übersetzt. Wir fügen einen Join mit Student hinzu, um Studentennamen zu erhalten. Wir fügen einen Join mit student-batch hinzu, weil die Einschränkung sich nicht damit befasst; Die Kontexte, die die Einschränkungsabfrage verwenden, prüfen, ob student-batch (student_id, batch_id) Unterzeilen darin enthalten sind.

SELECT b.id AS batch, t.name AS teacher, s.name AS student
FROM takes k JOIN teacher t JOIN batch b JOIN student s JOIN student-batch sb
WHERE ... AND @date = date

Sie könnten eine ON-Version ausprobieren:

SELECT b.id AS Batch, t.name AS Teacher, s.name AS Student
FROM takes k
JOIN teacher t ON k.subject_id = t.subject_id
JOIN batch b ON t.id = b.teacher_id
JOIN ...
WHERE @date = b.date