Conversão para o Modelo Relacional
Relações
Antes de entrarmos na conversão do Modelo E-A para o Modelo Relacional, vamos definir o que é uma Relação.
Definição
Considerando um schema de relação , em que cada atributo tem um domínio implícito e discreto de valores , temos que uma relação de um schema de relação , é o conjunto:
Todos os elementos são tuplos de tamanho , na forma tal que .
Por palavras mais simples, uma relação é composta por vários "campos", em que cada um deles tem um domínio. Os elementos que pertencem a essa relação são tuplos com cada um dos valores para os respetivos "campos".
Exemplo
Vejamos o seguinte exemplo:
product(product_code: string, product_name: string, price: integer, stock: integer)
Representação de uma relação
Daqui para a frente, por razões de simplicidade, não se irá representar os domínios de cada atributo.
Temos, então, que a relação é o conjunto:
Os elementos que pertencem a esta relação são, por exemplo:
Para uma relação, podemos determinar:
- O grau de uma relação, que corresponde ao número de atributos (ou se preferirem, campos ou colunas)
- A cardinalidade de uma relação, que equivale ao número de tuplos (isto é, linhas)
Uma relação é um objeto matemático que é representável como uma tabela.
Quais são relações?
Para ajudar a perceber o que é ou não uma relação, vejamos os seguintes exemplos.
Os seguintes conjuntos são relações:
Os seguintes conjuntos não são relações:
-
- Uma relação tem pelo menos um atributo
-
- Os elementos na mesma posição não são do mesmo tipo (isto é, não pertencem ao mesmo domínio)
-
- O número de elementos em cada tuplo é diferente
Propriedades das Relações
Uma relação não tem tuplos duplicados nem colunas duplicadas. Do mesmo modo, a ordenação tanto dos tuplos como dos atributos (colunas) é irrelevante.
Ou seja, a relação product(p_code, p_name)
é equivalente a product(p_name, p_code)
.
Estas propriedades refletem-se diretamente caso queiramos efetuar a união ou conjunção de duas relações:
-
Exemplo 1:
-
Exemplo 2:
Restrições
Tal como no modelo E-A, vamos novamente ter Restrições de Integridade.
As restrições de integridade podem ser aplicadas tanto às relações como à base de dados.
Dependendo do tipo de restrição, deve-se usar diferentes mecanismos no SGBD:
Tipo de Restrição | Mecanismo do SGBD |
---|---|
Domínio | Domínios dos atributos; CHECK |
Chave Primária | PRIMARY KEY |
Unicidade | UNIQUE |
Integridade Referencial | FOREIGN KEY |
Integridade Genérica | Assertions, Stored Procedures e Triggers |
Restrições aplicadas a Relações
Podemos ter três tipos de restrições aplicadas a relações:
- Restrições de Domínio
- Restrições de Unicidade
- Restrições de Chave
Definição
Uma restrição de integridade aplicada a uma relação é uma condição num dos atributos dessa relação, que restringe os dados que podem ser guardados na mesma.
Restrições de Domínio
Uma restrição de domínio, tal como o próprio nome indica, restringe os valores do domínio de um atributo da relação.
É fácil pensar em vários exemplos:
- O preço de um produto tem de ser um inteiro positivo
- O código do produto tem de ter 6 caracteres e começar pela letra 'A'
- A data de nascimento de um utilizador tem de ser anterior a 2002-01-01
Tais restrições podem ser indicadas da seguinte forma:
product(p_code, p_name, price, stock)
- (price > 0): O preço de um produto tem sempre de ser positivo
Restrições de Unicidade
Uma restrição de unicidade indica quais são os atributos, ou conjuntos de atributos, cujos valores não se podem repetir na relação.
Quando temos uma restrição de unicidade num conjunto de atributos, estamos a indicar
que este par de valores não se pode repetir, mas os valores individualmente podem.
No exemplo indicado abaixo, podemos ter dois produtos com o nome "Bolacha" se tiverem
preços diferentes.
product(p_code, p_name, price, stock)
- UNIQUE(p_code)
- UNIQUE(p_name, price)
Com estas restrições:
- não podem existir dois produtos com o mesmo código de barras.
- não podem existir dois produtos com a mesma combinação nome/preço.
Minimal vs Mínimo
É importante perceber a diferença entre um elemento minimal e um elemento mínimo.
- Um elemento minimal é um elemento tal que não existe nenhum elemento menor que ele.
- Um elemento mínimo é um elemento que é menor que todos os outros.
É de notar que podem existir vários elementos minimais, mas apenas um elemento mínimo. Podemos concluir também que um elemento mínimo é sempre minimal.
Só devemos aplicar restrições de unicidade a elementos minimais, isto é, às combinações de atributos de tamanho mínimo necessário para garantir a unicidade. Se é possível identificar um produto apenas pelo seu código de barras, não faz sentido ter uma restrição de unicidade no par chave/nome.
Restrições de Chave
Uma restrição de chave indica qual é o atributo (ou o conjunto de atributos minimal) que
identifica unicamente um tuplo.
Por outras palavras, não existe nenhum subconjunto da chave que pode também identificar
unicamente o conjunto.
Representamos esta restrição através de sublinhado, de forma semelhante ao Modelo E-A.
Tomemos dois exemplos, um com chave de um atributo e outro com chave de dois atributos.
product(p_code, p_name, price, stock)
order(p_code, client_id, quantity, date)
Restrições aplicadas à Base de Dados
As restrições aplicadas à base de dados são aplicadas a conjuntos de relações.
Existem dois tipos:
- Restrições de Integridade Referencial (ou foreign keys)
- Restrições de Integridade Genéricas
Restrições de Integridade Referencial (Foreign Keys)
Restrições deste tipo requerem que exista um valor (ou combinação de valores) correspondente noutra relação. Chama-se a isto uma foreign key (ou chave estrangeira em português).
Se os dados numa das relações forem alterados, é necessário verificar que as relações continuam a ser válidas.
order(p_code, client_id, quantity, date)
- p_code: FK(product.p_code)
Caso o nome dos atributos seja igual em ambas as relações, podemos omitir o nome
do atributo dentro do FK
, isto é, p_code: FK(product)
.
Podemos também incluir foreign keys para atributos da mesma relação.
category(name, parent_category)
- parent_category: FK(category.name)
É de realçar também que se quisermos aplicar uma foreign key a um conjunto de atributos, devemos usar a seguinte notação:
course(course_name, year, degree)
enrollment(student, course_name, year)
- course_name, year: FK(course.course_name, course.year)
Restrições de Integridade Genéricas
Há certas restrições que não se encaixam em mais nenhum tipo de categoria e têm de ser explicitadas textualmente.
degree(degree_id, name)
student(ist_id, name, degree)
- degree: FK(degree.degree_id)
course(course_name, year, degree)
- degree: FK(degree.degree_id)
enrollment(student, course_name, year)
- student: FK(student.ist_id)
- course_name, year: FK(course.course_name, course.year)
- IC-1: Students can only be enrolled in courses belonging to the same degree they signed up for.
Conversão a partir do Modelo E-A
Entidades e Atributos
A conversão de entidade e atributos do Modelo E-A para o Modelo Relacional é bastante simples. As chaves primárias continuam a ser representadas por um sublinhado, e os atributos únicos passam a ter uma restrição de unicidade.
student(st_id, name, birthdate)
- UNIQUE(name)
Associações
Dependendo do tipo de associação, a conversão para o modelo relacional faz-se de forma diferente.
- Many-to-Many e Ternárias: cria-se uma nova relação com as chaves das entidades envolvidas
- One-to-Many: cria-se uma nova relação em que a chave primária é a chave da entidade com multiplicidade 1.
- One-to-One: igual à many-to-many, mas adiciona-se uma restrição de unicidade às chaves das entidades envolvidas.
Existem também casos especiais para quando temos participação obrigatória:
- Many-to-Many com participação obrigatória: não é possível representar diretamente no modelo relacional, pelo que precisamos de uma restrição de integridade.
- One-to-Many com participação obrigatória: deixamos de precisar de uma nova relação, e colocamos os atributos na relação da entidade com multiplicidade 1.
Nos exemplos abaixo, os atributos das entidades nas representações em modelo E-A são omitidos por brevidade.
Many-to-Many
student(ist_id, name)
course(course_id, course_name, department)
enrolls(ist_id, course_id, enrollment_date)
- ist_id: FK(student)
- course_id: FK(course)
One-to-Many
student(ist_id, name)
degree(degree_acronym, degree_name, department)
studies(ist_id, degree_acronym, start_date)
- ist_id: FK(student)
- degree_acronym: FK(degree)
One-to-One
student(ist_id, name)
degree(degree_acronym, degree_name, department)
is_delegate(ist_id, degree_acronym, start_date)
- ist_id: FK(student)
- degree_acronym: FK(degree)
- UNIQUE(ist_id)
- UNIQUE(degree_acronym)
Many-to-Many com Participação Obrigatória
Tal como referido acima, não é possível modelar completamente esta associação sem recorrer a Restrições de Integridade.
teacher(ist_id, name)
course(course_id, course_name, department)
- IC-1: Every course (course_id) must participate in the lectures association
lectures(ist_id, course_id)
- ist_id: FK(teacher)
- course_id: FK(course)
One-to-Many com Participação Obrigatória
Neste caso, não precisamos de uma nova relação, usamos a relação já existente da entidade de multiplicidade 1 e obrigatória.
department(department_acronym, department_name)
teacher(ist_id, name, department_acronym, join_date)
- department_acronym: FK(department)
Generalizações/Especializações
Pegando no exemplo de Pessoa, Professor e Aluno, como podemos converter este diagrama de modelo E-A para o modelo relacional?
Cada uma das especializações vai ser uma relação distinta, partilhando a chave da sua generalização.
person(name, citizen_card, birthday)
- UNIQUE(citizen_card)
teacher(name)
- name: FK(person)
student(name, ingress_date)
- name: FK(person)
Para modelar disjunções e obrigatoriedade, temos de recorrer a restrições de integridade.
Imaginando, agora, os seguintes cenários para a especialização de Pessoa, teríamos
as seguintes restrições de integridade na relação person
:
- Obrigatoriedade:
- (IC-1) name must exist in 'teacher' and/or 'student'
- Disjunção:
- (IC-1) name cannot exist at the same time in 'teacher' and 'student'
- Obrigatoriedade e Disjunção:
- (IC-1) name must exist in 'teacher' or 'student'
- (IC-2) name cannot exist at the same time in 'teacher' and 'student'
Relembremos, agora, o exemplo de membro e sócio
da página anterior, para ilustrarmos a conversão de especializações de vários níveis.
Neste caso, devemos criar uma foreign key com a generalização imediatamente acima, e
não com a generalização no "topo da árvore".
member(name, citizen_card, birthdate)
- UNIQUE(citizen_card)
- IC-1: name must exist in 'regular_member' or 'occasional_member'
- IC-2: name cannot exist at the same time in 'regular_member' and 'occasional_member'
regular_member(name, regularity)
- name: FK(member)
occasional_member(name, last_visit)
- name: FK(member)
manager(name)
- name: FK(member)
associate(name, join_date)
- name: FK(member)
- IC-1: name must exist in 'bronze', 'silver' or 'gold'
- IC-2: name cannot exist at the same time in 'bronze', 'silver' or 'gold'
bronze(name)
- name: FK(associate)
silver(name)
- name: FK(associate)
gold(name)
- name: FK(associate)
Restrições de Integridade: Disjunção e Obrigatoriedade
Na maioria dos SGBDs, não existe um mecanismo nativo e simples para implementar as restrições de integridade relativas à disjunção e à obrigatoriedade. Pode ser necessário usar mecanismos mais avançados do SGBD ou mesmo implementar estas restrições no código da aplicação.
Entidades Fracas
Para convertermos uma entidade fraca (ou um conjunto delas), recorremos praticamente à mesma metodologia que utilizámos para associações one-to-many com participação obrigatória, mas desta vez fazemos com que a chave da entidade forte faça parte da chave da entidade fraca.
Tomemos um exemplo em que temos armazéns que contêm armários que, por si, estão divididos em prateleiras:
Modelar a relação cabinet (armário) é simples, visto que basta fazer com que a chave
de warehouse faça também parte da chave de cabinet.
No entanto, temos de prestar atenção ao modelar uma prateleira, visto que temos
de garantir que tanto a chave de warehouse como de cabinet formam uma entidade válida.
Para isto, utilizamos uma foreign key com múltiplos atributos.
Caso não o fizéssemos, poderíamos ter uma prateleira que estava associada a um armário
e a um armazém que não contém esse armário.
warehouse(address, max_workers)
cabinet(address, cabinet_letter, height, width)
- address: FK(warehouse)
shelf(address, cabinet_letter, shelf_number, height, max_weight)
- address, cabinet_letter: FK(cabinet.address, cabinet.cabinet_letter)
Note-se que, na prateleira, o atributo address é uma foreign key referente ao armário e não ao armazém, visto que a relação da prateleira é exclusivamente com o armário e não com o armazém, apesar de ser este que tem address como atributo "originalmente".
Agregações
Visto que uma agregação é apenas uma associação entre uma entidade e outra associação, quando estamos a converter uma agregação para o modelo relacional podemos ter isso em mente.
Consideremos o seguinte Modelo E-A, em que temos professor, disciplina e curso:
Podemos começar por modelar a associação course/degree, recorrendo às mesmas regras de uma associação many-to-many. Possivelmente, aqui, faria sentido aplicar uma restrição de obrigatoriedade, mas vamos omiti-la por simplicidade, embora não fosse muito complicado aplicá-la.
De seguida, vamos considerar que estamos perante uma associação entre teacher e part of curriculum, voltando a aplicar uma associação many-to-many.
Ficamos então com o seguinte modelo relacional:
degree(degree_acronym, degree_name, department)
course(course_name)
part_of_curriculum(degree_acronym, course_name)
- degree_acronym: FK(degree)
- course_name: FK(course)
teacher(ist_id, name, birthdate)
lectures(ist_id, degree_acronym, course_name, year)
- ist_id: FK(teacher)
- degree_acronym, course_name: FK(part_of_curriculum.degree_acronym, part_of_curriculum.course_name)