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 R(A1,,An)R(A_1, \dots, A_n), em que cada atributo AiA_i tem um domínio implícito e discreto de valores DiD_i, temos que uma relação rr de um schema de relação RR, é o conjunto:

rD1××Dnr \subseteq D_1 \times \dots \times D_n

Todos os elementos trt \in r são tuplos de tamanho nn, na forma a1,,an\lang a_1,\dots,a_n \rang tal que aiDa_i \in D.

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 product\text{product} é o conjunto:

productstring×string×integer×integer\text{product} \subseteq \text{string} \times \text{string} \times \text{integer} \times \text{integer}

Os elementos que pertencem a esta relação são, por exemplo:

product={a1,Bolachas,50,10a2,Napolitanas,20,15a3,Leite,95,2}\begin{aligned} \text{product} = &\{\\ &\lang \text{a1}, \text{Bolachas}, 50, 10 \rang\\ &\lang \text{a2}, \text{Napolitanas}, 20, 15 \rang\\ &\lang \text{a3}, \text{Leite}, 95, 2 \rang\\ &\} \end{aligned}

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:

  • {}\{\}
  • {Bolachas}\{\lang \text{Bolachas} \rang\}
  • {Bolachas,Napolitanas}\{\lang \text{Bolachas} \rang, \lang \text{Napolitanas} \rang\}
  • {Bolachas,50,Napolitanas,20}\{\lang \text{Bolachas}, 50 \rang, \lang \text{Napolitanas}, 20 \rang\}

Os seguintes conjuntos não são relações:

  • {}\{\lang\rang\}
    • Uma relação tem pelo menos um atributo
  • {Bolachas,20}\{\lang \text{Bolachas} \rang, \lang 20 \rang\}
    • Os elementos na mesma posição não são do mesmo tipo (isto é, não pertencem ao mesmo domínio)
  • {Bolachas,50,Napolitanas}\{\lang \text{Bolachas}, 50 \rang, \lang \text{Napolitanas} \rang\}
    • 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:

    {Bolacha,50,Napolitana,20}{Bolacha,50}={Bolacha,50,Napolitana,20}\begin{aligned} &\{\lang \text{Bolacha}, 50\rang, \lang \text{Napolitana}, 20 \rang\} \cup \{\lang \text{Bolacha}, 50 \rang\}\\ =&\{\lang \text{Bolacha}, 50\rang, \lang \text{Napolitana}, 20 \rang\} \end{aligned}
  • Exemplo 2:

    {Bolacha,50,Napolitana,20}{Napolitana,25,Bolacha,50}={Bolacha,50}\begin{aligned} &\{\lang \text{Bolacha}, 50\rang, \lang \text{Napolitana}, 20 \rang\} \cap \{\lang \text{Napolitana}, 25\rang, \lang \text{Bolacha}, 50 \rang\}\\ =&\{\lang \text{Bolacha}, 50\rang\} \end{aligned}

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)