Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Qual é a maneira de projetar um banco de dados relacional simples professor-sujeito-aluno-lote?


Está faltando uma tabela associando disciplinas e alunos (por ponto 2):
// student [student_id] takes subject [subject_id]
takes(student_id, subject_id)

Observe que cada tabela base tem um modelo de instrução associado para instruções sobre a situação do negócio, parametrizado por nomes de coluna -- seu predicado (característico) . As linhas que tornam o predicado verdadeiro vão para a tabela. Observe que a definição da tabela parece um atalho para o 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]

Como você parece confuso sobre isso, vou derivá-lo em termos de como podemos raciocinar para o design de tabela, restrição e consulta. Uma maneira de expressar a restrição desejada parece ser o CHECK comentado acima.

Para expressar qualquer tabela, restrição ou consulta em SQL, primeiro decidimos seu predicado. Então podemos converter o predicado para abreviação. Então podemos converter a abreviação para SQL.

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

Usando predicados da tabela 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 abreviações:
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)

Em um alias FROM cada (possivelmente implícito) representa uma tabela como o nome da tabela base e/ou subconsulta, mas com cada coluna em seu valor e predicado renomeado para alias .coluna .

Obtemos as linhas que satisfazem o AND de dois predicados juntando as tabelas dos predicados em SQL. Se queremos linhas que satisfaçam o AND de uma condição, usamos ON ou WHERE no SQL.

Uma cláusula SELECT retorna linhas onde FOR SOME valores de colunas pontilhadas as colunas retornadas (não pontilhadas) são iguais às funções de colunas pontilhadas que satisfazem o predicado FROM.

SQL:Substitua as instruções por suas tabelas, AND por JOIN ou ON ou WHERE, e FOR SOME &THERE EXISTS 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

A tabela de linhas que satisfaz o OR de dois predicados é a UNION de suas tabelas. Para AND NOT, usamos EXCEPT (também conhecido como MINUS) (ou uma expressão idiomática LEFT JOIN). FOR SOME ou THERE EXISTS em todas as colunas não podem ser consultadas em SQL, mas se quisermos saber se existem linhas que satisfaçam um predicado, podemos usar EXISTS em torno de uma subconsulta com esse predicado.

Suponha que queremos restringir uma tabela base para que cada linha satisfaça um predicado em algumas colunas. Ou seja, PARA TODAS as colunas SE elas satisfazem o predicado base ENTÃO elas satisfazem o predicado da consulta. Ou seja, PARA TODAS as colunas SE a linha que elas formam está na base ENTÃO ela está na consulta. Portanto, exigimos no SQL que NÃO EXISTE (SELECT columns FROM base EXCEPT query). Ou para cada linha na base que exigimos no SQL que EXISTE(consulta).

No SQL padrão, você pode CREATE ASSERTION CHECK(NOT EXISTS (SELECT student_id, batch_id FROM student-batch EXCEPT query)) ou em um CREATE TABLE student-batch você pode CHECK(EXISTS(query)). Infelizmente, eles não são suportados pelo MySQL ou pela maioria dos DBMSs. Se você INSERT no aluno-lote após o lote, pode exigir no gatilho que EXISTS (consulta). Ou você pode adicionar certas colunas e restrições FK compostas (chave estrangeira).

Agora estamos escrevendo uma consulta. Queremos linhas onde:
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)

Isso se parece com o predicado de restrição com diferentes colunas de retorno e linhas adicionadas. O SQL é tão diretamente traduzido. Adicionamos uma junção com o aluno para obter os nomes dos alunos. Adicionamos um join com student-batch porque a restrição não lida com isso; os contextos que usam a consulta de restrição verificam se as sub-linhas student-batch (student_id, batch_id) estão nela.
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

Você pode tentar uma versão 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