Relação "muitos-para-muitos" é quando um registro em uma tabela pode estar ligado a vários registros em outra tabela, e vice-versa. Por exemplo: - Um estudante (da tabela students) pode estar matriculado em vários cursos (da tabela courses). - Um curso pode ser frequentado por vários estudantes.
O problema é que guardar esse tipo de relação direto nas tabelas é bem chato. Por isso, a gente usa uma tabela intermediária, que armazena essas ligações.
Exemplo da vida real
Imagina que você criou uma tabela de estudantes e uma de cursos. Se tentar colocar em cada tabela uma coluna pra guardar todos os dados relacionados, vai virar bagunça:
- Na tabela
studentsvocê teria que guardar uma lista de todos os cursos em que o estudante está matriculado. Mas como guardar essa lista? Separado por vírgula? Em um array? Isso complica muito na hora de fazer query. - Na tabela
coursesteria que guardar a lista de estudantes, aí já viu, dor de cabeça total.
Então o jeito certo é criar uma terceira tabela, que vai guardar as ligações entre estudantes e cursos.
Tabela intermediária: nossa salvação!
A tabela intermediária (às vezes chamada de tabela de ligação) resolve tudo. Ela tem dois campos de referência externa:
- Chave estrangeira pra tabela
students. - Chave estrangeira pra tabela
courses.
Cada registro nessa tabela cria uma ligação entre um estudante específico e um curso específico.
Criando tabelas pra relação "muitos-para-muitos"
Bora praticar! Olha só como a gente pode criar as tabelas pra ligar estudantes e cursos:
Passo 1: Criando a tabela students
Aqui está nossa tabela de estudantes. Guardamos os IDs únicos dos estudantes e os nomes deles.
CREATE TABLE students (
student_id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
student_id— é o identificador único do estudante (auto-incrementado, valeuSERIAL!).name— nome do estudante.
Passo 2: Criando a tabela courses
Agora vamos criar a tabela de cursos. Aqui guardamos os IDs únicos dos cursos e os nomes deles.
CREATE TABLE courses (
course_id SERIAL PRIMARY KEY,
title TEXT NOT NULL
);
course_id— identificador único do curso.title— nome do curso.
Passo 3: Criando a tabela intermediária enrollments
Agora vamos criar nossa tabela mágica de ligação. Ela tem duas colunas, cada uma sendo uma chave estrangeira apontando pras tabelas certas.
CREATE TABLE enrollments (
student_id INT REFERENCES students(student_id),
course_id INT REFERENCES courses(course_id),
PRIMARY KEY (student_id, course_id)
);
Bora entender a estrutura:
student_id— chave estrangeira apontando prostudent_idda tabelastudents.course_id— chave estrangeira apontando procourse_idda tabelacourses.PRIMARY KEY (student_id, course_id)— a chave primária é a combinação das duas chaves estrangeiras. Isso garante que cada ligação é única.
Inserindo dados
Bora colocar uns dados pra ver como funciona.
Passo 1: Adicionando estudantes
INSERT INTO students (name) VALUES
('Alice'),
('Bob'),
('Charlie');
Resultado:
| student_id | name |
|---|---|
| 1 | Alice |
| 2 | Bob |
| 3 | Charlie |
Passo 2: Adicionando cursos
INSERT INTO courses (title) VALUES
('Mathematics'),
('History'),
('Biology');
Resultado:
| course_id | title |
|---|---|
| 1 | Mathematics |
| 2 | History |
| 3 | Biology |
Passo 3: Adicionando registros em enrollments
Agora vamos matricular os estudantes nos cursos. Por exemplo:
Aliceestá matriculada emMathematicseHistory.Bobestá só emBiology.Charlieestá em todos os três cursos.
INSERT INTO enrollments (student_id, course_id) VALUES
(1, 1), -- Alice em Mathematics
(1, 2), -- Alice em History
(2, 3), -- Bob em Biology
(3, 1), -- Charlie em Mathematics
(3, 2), -- Charlie em History
(3, 3); -- Charlie em Biology
Resultado:
| student_id | course_id |
|---|---|
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
Queries pra relação "muitos-para-muitos"
Agora que temos dados, bora tirar proveito disso!
Como achar todos os cursos em que um estudante está matriculado?
Por exemplo, pra saber em quais cursos a Alice (ID = 1) está matriculada, faz assim:
SELECT c.title
FROM courses c
JOIN enrollments e ON c.course_id = e.course_id
WHERE e.student_id = 1;
Resultado:
| title |
|---|
| Mathematics |
| History |
Como achar todos os estudantes matriculados em um curso específico?
Tipo, queremos saber quem está matriculado no curso Mathematics (ID = 1):
SELECT s.name
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
WHERE e.course_id = 1;
Resultado:
| name |
|---|
| Alice |
| Charlie |
Como achar estudantes e seus cursos?
Pra ver o panorama geral de quem está em qual curso, faz essa query:
SELECT s.name AS student, c.title AS course
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
JOIN courses c ON e.course_id = c.course_id;
Resultado:
| student | course |
|---|---|
| Alice | Mathematics |
| Alice | History |
| Bob | Biology |
| Charlie | Mathematics |
| Charlie | History |
| Charlie | Biology |
A tabela enrollments deixa nosso esquema super flexível — dá pra adicionar ou remover ligações entre estudantes e cursos de boa, sem mexer nas tabelas principais. Com JOINs fica fácil achar o que quiser, tipo quem está em qual curso. E as chaves estrangeiras já garantem que não rola erro — tipo, ninguém vai conseguir matricular um estudante num curso que nem existe.
Erros comuns em relações "muitos-para-muitos"
Falta de restrição única: Se não colocar PRIMARY KEY, pode acabar adicionando a mesma ligação mais de uma vez sem querer.
Quebra de integridade dos dados: Tentar inserir um registro com student_id ou course_id que não existe vai dar erro.
Ordem errada ao deletar dados: Se deletar um curso da tabela courses antes, os registros em enrollments vão ficar "órfãos". Pra evitar isso, usa ON DELETE CASCADE na definição das chaves estrangeiras.
GO TO FULL VERSION