Mysql
 sql >> Database >  >> RDS >> Mysql

Qual è il modo di progettare un semplice database relazionale insegnante-materia-studente-batch?

Manca una tabella che associa materie e studenti (al punto 2):

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

Si noti che ogni tabella di base ha un modello di istruzione associato per le istruzioni sulla situazione aziendale, parametrizzato dai nomi delle colonne:il suo predicato (caratteristico) . Le righe che rendono vero il predicato vanno nella tabella. Si noti che la definizione della tabella sembra una scorciatoia per il predicato.

// 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]

Dato che sembri perplesso su questo, lo trarrò in termini di come possiamo ragionare per la progettazione di tabelle, vincoli e query. Un modo per esprimere il vincolo desiderato sembra essere il CONTROLLO commentato sopra.

Per esprimere qualsiasi tabella, vincolo o query in SQL, prima decidiamo il suo predicato. Quindi possiamo convertire il predicato in stenografia. Quindi possiamo convertire l'abbreviazione in SQL.

Predicato:

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

Utilizzo dei predicati della tabella di base:

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])

Usare le abbreviazioni:

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 un FROM ogni alias (possibilmente implicito) rappresenta una tabella come il nome della tabella di base specificata e/o la sottoquery ma con ogni colonna nel suo valore e predicato rinominato in alias .colonna .

Otteniamo le righe che soddisfano l'AND di due predicati unendo le tabelle dei predicati in SQL. Se vogliamo che le righe soddisfino l'AND di una condizione, utilizziamo ON o WHERE in SQL.

Una clausola SELECT restituisce righe in cui FOR SOME valori delle colonne tratteggiate le colonne restituite (senza punti) sono uguali alle funzioni delle colonne tratteggiate che soddisfano il predicato FROM.

SQL:sostituisci le istruzioni con le loro tabelle, AND by JOIN o ON o WHERE, e external FOR SOME &THERE EXISTS by 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

La tabella di righe che soddisfa l'OR di due predicati è l'UNIONE delle loro tabelle. Per AND NOT usiamo EXCEPT (aka MINUS) (o un idioma LEFT JOIN). FOR SOME o THERE EXISTS su tutte le colonne non possono essere interrogate in SQL, ma se vogliamo sapere se ci sono righe che soddisfano un predicato, possiamo usare EXISTS attorno a una sottoquery con quel predicato.

Supponiamo di voler vincolare una tabella di base in modo che ogni riga soddisfi un predicato su alcune colonne. Vale a dire PER TUTTE le colonne SE soddisfano il predicato di base ALLORA soddisfano il predicato della query. Vale a dire PER TUTTE le colonne SE la riga che formano è nella base ALLORA è nella query. Quindi richiediamo in SQL che NON ESISTE (SELECT colonne DALLA query di base EXCEPT). Oppure per ogni riga nella base richiediamo in SQL che EXISTS(query).

In SQL standard puoi CREATE ASSETION CHECK (NON EXISTS (SELECT student_id, batch_id FROM student-batch EXCEPT query)) o in un CREATE TABLE student-batch puoi CHECK(EXISTS(query)). Sfortunatamente questi non sono supportati da MySQL o dalla maggior parte dei DBMS. Se INSERISCI al batch studente dopo il batch, puoi richiedere sul trigger che EXISTS (query). Oppure potresti aggiungere determinate colonne e vincoli FK (chiave esterna) compositi.

Ora stiamo scrivendo una query. Vogliamo righe dove:

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)

Sembra il predicato del vincolo con diverse colonne di ritorno e righe aggiunte. L'SQL è altrettanto tradotto direttamente. Aggiungiamo un join con lo studente per ottenere i nomi degli studenti. Aggiungiamo un join con student-batch perché il vincolo non lo gestisce; i contesti che utilizzano la query di vincolo controllano se sono presenti sottobrows student-batch (student_id, batch_id).

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

Potresti provare una versione ON:

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