Le falta una tabla que asocie asignaturas y alumnos (por punto 2):
// student [student_id] takes subject [subject_id]
takes(student_id, subject_id)
Observe que cada tabla base tiene una plantilla de declaración asociada para declaraciones sobre la situación comercial, parametrizada por nombres de columna:su predicado (característico) . Las filas que hacen que el predicado sea verdadero van en la tabla. Observe que la definición de la tabla parece una abreviatura del predicado.
// 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]
Dado que parece desconcertado acerca de esto, lo derivaré en términos de cómo podemos razonar para el diseño de tablas, restricciones y consultas. Una forma de expresar la restricción que desea parece ser el CHECK comentado arriba.
Para expresar cualquier tabla, restricción o consulta en SQL, primero decidimos su predicado. Entonces podemos convertir el predicado en taquigrafía. Entonces podemos convertir la abreviatura a SQL.
Predicado:
student [student_id] takes the subject that is taught by the teacher of batch [batch_id]
Uso de predicados de tabla 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])
Usando abreviaturas:
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)
En un FROM, cada alias (posiblemente implícito) representa una tabla como el nombre de la tabla base y/o la subconsulta dada, pero con cada columna en su valor y predicado renombrado a alias .columna .
Obtenemos las filas que satisfacen el AND de dos predicados UNIENDO las tablas de los predicados en SQL. Si queremos filas que satisfagan el AND de una condición, entonces usamos ON o WHERE en SQL.
Una cláusula SELECT devuelve filas donde PARA ALGUNOS valores de columnas punteadas, las columnas devueltas (sin puntos) son iguales a funciones de columnas punteadas que satisfacen el predicado FROM.
SQL:reemplace las declaraciones por sus tablas, AND por JOIN o ON o WHERE, y FOR SOME &THERE EXISTS externo por 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 tabla de filas que satisface el OR de dos predicados es la UNIÓN de sus tablas. Para AND NOT usamos EXCEPT (también conocido como MINUS) (o un modismo LEFT JOIN). FOR ALGUNOS o EXISTE sobre todas las columnas no se pueden consultar en SQL, pero si queremos saber si hay filas que satisfacen un predicado, entonces podemos usar EXISTS alrededor de una subconsulta con ese predicado.
Supongamos que queremos restringir una tabla base para que cada fila satisfaga un predicado en algunas columnas. Es decir, PARA TODAS las columnas SI satisfacen el predicado base ENTONCES satisfacen el predicado de consulta. Es decir, PARA TODAS las columnas SI la fila que forman está en la base ENTONCES está en la consulta. Por lo tanto, requerimos en SQL que NO EXISTA (SELECCIONE columnas DESDE la base EXCEPTO la consulta). O para cada fila en la base que requerimos en SQL que EXISTE (consulta).
En SQL estándar, podría CREAR UNA VERIFICACIÓN DE ASERCIÓN (NO EXISTE (SELECCIONE id_estudiante, id_lote DESDE el lote de estudiantes EXCEPTO consulta)) o en CREAR TABLA lote de estudiantes podría COMPROBAR (EXISTE (consulta)). Desafortunadamente, estos no son compatibles con MySQL ni con la mayoría de los DBMS. Si INSERTA en un lote de estudiante tras lote, puede solicitar en el activador que EXISTE (consulta). O puede agregar ciertas columnas y restricciones compuestas de FK (clave externa).
Ahora estamos escribiendo una consulta. Queremos filas donde:
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)
Esto se parece al predicado de restricción con diferentes columnas de retorno y líneas añadidas. El SQL se traduce directamente. Agregamos una unión con el estudiante para obtener los nombres de los estudiantes. Agregamos una combinación con lote de estudiantes porque la restricción no se ocupa de eso; los contextos que usan la consulta de restricción verifican si las subfilas del lote de estudiantes (student_id, batch_id) están en él.
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
Podría probar una versión 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