Álgebra Relacional
O que é?
Quando efetuamos uma SQL query numa base de dados, o SGBD converte essa query numa expressão algébrica. O que é para nós uma linguagem facilmente compreensível (SQL) é na verdade uma simplificação das expressões algébricas aplicadas a relações.
Uma expressão relacional (algébrica) recebe uma ou mais relações e retorna apenas uma relação.
Vejamos agora que literais/operadores existem em álgebra relacional, isto é, qual a sua gramática formal:
Nome | Representação |
---|---|
Relação vazia | |
Literal de Relação | |
Nome de Relação/relval | |
Seleção | |
Projeção | |
Projeção Generalizada | |
Renomeação | |
União | |
Diferença | |
Interseção | |
Produto Cartesiano | |
Divisão | |
Atribuição | |
Natural Join | |
Agregação |
Aprofundemos, de seguida, alguns destes operadores.
Seleção
A seleção funciona de forma semelhante à cláusula WHERE
do SQL,
em que se restringe uma relação de acordo com uma condição/predicado.
Relembremos a sintaxe . Aqui, temos que:
- é uma condição/predicado, podendo ser simples ou composta
- é a relação a que queremos aplicar esta condição
Assim, iremos obter uma nova relação, definida por:
Na seleção, a condição irá ser avaliada por cada tuplo . Uma condição pode conter os operadores de comparação , , , , , etc, assim como os operadores lógicos , e .
Exemplo
Considerando a relação abaixo, correspondente ao exemplo da loja,
product(product_code, product_name, price, stock)
com os seguintes valores:
product_code |
product_name |
price |
stock |
---|---|---|---|
111111 | Bolachas | 50 | 10 |
222222 | Napolitanas | 25 | 15 |
333333 | Leite com Chocolate | 100 | 3 |
-
Selecionar o produto com o identificador
222222
product_code
product_name
price
stock
222222 Napolitanas 25 15 -
Selecionar os produtos com preço igual ou inferior a 50 cêntimos e stock superior a 5
product_code
product_name
price
stock
111111 Bolachas 50 10 222222 Napolitanas 25 15
Projeção
A projeção funciona de forma semelhante à cláusula SELECT
do SQL,
na medida em que seleciona certas colunas/atributos de uma relação.
Relembremos a sintaxe . Aqui, temos que:
- é a relação que queremos projetar
- é uma coluna/atributo da relação
Assim, iremos obter uma nova relação, definida por:
Na projeção, obtemos a mesma relação dada, mas sem as colunas não especificadas. Todas as colunas têm de pertencer à relação .
Exemplo
Considerando novamente a relação abaixo, correspondente ao exemplo da loja,
product(product_code, product_name, price, stock)
com os seguintes valores:
product_code |
product_name |
price |
stock |
---|---|---|---|
111111 | Bolachas | 50 | 10 |
222222 | Napolitanas | 25 | 15 |
333333 | Leite com Chocolate | 100 | 3 |
-
Projetar os atributos
product_name
estock
product_name
stock
Bolachas 10 Napolitanas 15 Leite com Chocolate 3
Tuplos duplicados
Visto que as relações não contêm tuplos duplicados (isto é, são sets e não listas), ao remover certas colunas poderemos diminuir o número de tuplos na relação.
Por exemplo, na relação
student(ist_id, name, birthday)
com os seguintes valores
ist_id |
name |
birthday |
---|---|---|
ist1123456 | Diogo | 2002-03-06 |
ist1123234 | Rafa | 2002-08-05 |
ist1124453 | Tomás | 2002-12-18 |
ist1123534 | Diogo | 2001-09-16 |
ist1123532 | Diogo | 2002-04-27 |
se efetuarmos a projeção apenas do atributo name
, vamos obter
apenas 3 linhas:
name |
---|
Diogo |
Rafa |
Tomás |
Projeção Generalizada
A projeção generalizada, tal como o nome indica, é uma generalização da projeção apresentada acima, permitindo, além de referenciar atributos de uma relação, a utilização de expressões sobre os atributos da relação.
Relembremos a sintaxe . Aqui, temos que:
- é a relação que queremos projetar
- é uma função/expressão sobre os atributos de
Assim, iremos obter uma nova relação, definida por:
As expressões podem efetuar aritmética (somas, subtrações, multiplicações, etc) entre valores dos atributos de ou até mesmo com literais (i.e. somar a todos os valores de um atributo).
Exemplo
Considerando outra vez a relação abaixo, correspondente ao exemplo da loja,
product(product_code, product_name, price, stock)
com os seguintes valores:
product_code |
product_name |
price |
stock |
---|---|---|---|
111111 | Bolachas | 50 | 10 |
222222 | Napolitanas | 25 | 15 |
333333 | Leite com Chocolate | 100 | 3 |
-
Projetar o atributo
product_name
e a expressãoprice * stock
product_name
price * stock
Bolachas 500 Napolitanas 375 Leite com Chocolate 300 -
Projetar o atributo
product_name
e o valor do IVA (expressãoprice * 0.23
)product_name
price * 0.23
Bolachas 11.5 Napolitanas 5.75 Leite com Chocolate 23
Renomeação
A renomeação funciona de forma semelhante à cláusula AS
do SQL,
na medida em que altera o nome de certas colunas/atributos de uma relação.
Relembremos a sintaxe . Aqui, temos que:
- é a relação cujas colunas queremos renomear
- é o nome original (ou posição) de uma coluna da relação
- é o nome para o qual queremos alterar o nome de
Assim, iremos obter uma nova relação, definida por:
Na renomeação, iremos obter os mesmos tuplos, apenas com nomes de colunas diferentes.
Exemplo
Considerando mais uma vez a relação abaixo, correspondente ao exemplo da loja,
product(product_code, product_name, price, stock)
com os seguintes valores:
product_code |
product_name |
price |
stock |
---|---|---|---|
111111 | Bolachas | 50 | 10 |
222222 | Napolitanas | 25 | 15 |
333333 | Leite com Chocolate | 100 | 3 |
-
Renomear o atributo
product_code
paracode
e o atributoproduct_name
paraname
code
name
price
stock
111111 Bolachas 50 10 222222 Napolitanas 25 15 333333 Leite com Chocolate 100 3
União
A união em álgebra relacional funciona de forma semelhante, se não idêntica, à união que já conhecemos da teoria de conjuntos. A união de conjuntos consiste em obter um novo conjunto com os valores de ambos os conjuntos, eliminando valores duplicados. Do mesmo modo, a união de relações consiste em obter os tuplos de ambas as relações, eliminando tuplos duplicados.
Relembremos a sintaxe , em que e são as duas relações a unir.
Para isto, temos primeiro de obedecer a duas condições: tanto como têm de ter o mesmo número de atributos, e os tipos (isto é, os domínios) dos atributos na i-ésima posição de cada uma das relações têm de corresponder.
Assim, iremos obter uma nova relação, definida por:
É normalmente útil utilizar uma projeção ou renomeação para garantir o mesmo número e tipo de atributos.
Exemplo
Considerando duas relações, student
e teacher
, muito simples,
student(name)
teacher(name)
com os seguintes valores:
name (student) |
---|
Diogo |
Tomás |
Rafa |
João |
name (teacher) |
---|
João |
André |
A união destas duas relações seria a seguinte:
name |
---|
Diogo |
Tomás |
Rafa |
João |
André |
Ou seja, obtivemos todos os alunos e professores.
Diferença
A diferença entre duas relações funciona também da mesma forma que a diferença entre conjuntos (isto é, ). Tal como na união, as relações têm de ter o mesmo número de atributos e estes têm de ter domínios compatíveis.
Relembremos a sintaxe , em que e são as duas relações em que se aplica a diferença.
Assim, iremos obter uma nova relação, definida por:
Exemplo
Considerando novamente duas relações, student
e teacher
,
student(name)
teacher(name)
com os seguintes valores:
name (student) |
---|
Diogo |
Tomás |
Rafa |
João |
name (teacher) |
---|
João |
André |
A diferença destas duas relações seria a seguinte:
name |
---|
Diogo |
Tomás |
Rafa |
Ou seja, obtivemos todos os alunos que não são também professores.
Interseção
A interseção entre duas relações funciona também da mesma forma que a interseção entre conjuntos (isto é, ). Tal como na união, as relações têm de ter o mesmo número de atributos e estes têm de ter domínios compatíveis.
Relembremos a sintaxe , em que e são as duas relações a intersetar.
É também de realçar que a interseção é nada mais nada menos que duas diferenças,
isto é:
Assim, iremos obter uma nova relação, definida por:
Exemplo
Considerando novamente duas relações, student
e teacher
,
student(name)
teacher(name)
com os seguintes valores:
name (student) |
---|
Diogo |
Tomás |
Rafa |
João |
name (teacher) |
---|
João |
André |
A interseção destas duas relações seria a seguinte:
name |
---|
João |
Ou seja, obtivemos todos os alunos que são também professores.
Produto Cartesiano
O produto cartesiano entre duas relações associa cada valor da primeira relação a todos os valores da segunda. Isto significa que, se a primeira relação tiver tuplos e a segunda tiver tuplos, a relação obtida pelo produto cartesiano entre estas duas vai ter tuplos.
Para efetuar o produto cartesiano, o nome dos atributos das duas relações têm de ser diferentes, isto é, não podem ter nomes de atributos em comum. Caso seja este o caso, é necessário utilizar uma renomeação.
Relembremos a sintaxe , em que e são as duas relações entre as quais queremos efetuar o produto cartesiano.
Assim, iremos obter uma nova relação, definida por:
Exemplo
Considerando duas relações, uma contendo 3 tuplos e outra contendo 2 tuplos, sabemos, pelas propriedades do produto cartesiano, que vamos obter uma nova relação com 6 tuplos.
A |
---|
1 |
2 |
3 |
B |
C |
---|---|
a | X |
b | Y |
A |
B |
C |
---|---|---|
1 | a | X |
1 | b | Y |
2 | a | X |
2 | b | Y |
3 | a | X |
3 | b | Y |
Divisão
A divisão entre duas relações é algo relativamente complicado de perceber. Esta consiste em determinar o subconjunto dos tuplos de que cobrem todos os tuplos de . Pode-se considerar como a operação inversa do produto cartesiano, como se pode ver nos exemplos abaixo.
Relembremos a sintaxe , em que e são as duas relações em que queremos efetuar a divisão. Devemos também considerar e , que correspondem às schemas, isto é, aos atributos de e , respetivamente.
Assim, iremos obter uma nova relação, definida por:
Exemplo
Considerando o exemplo do produto cartesiano, podemos verificar que a divisão é efetivamente a operação inversa:
A |
B |
C |
---|---|---|
1 | a | X |
1 | b | Y |
2 | a | X |
2 | b | Y |
3 | a | X |
3 | b | Y |
B |
C |
---|---|
a | X |
b | Y |
A |
---|
1 |
2 |
3 |
Como podemos observar, obtivemos todos os valores originais da relação com o atributo A
,
visto que todas as ocorrências deste atributo apresentavam todos os valores para B, C
.
Se retirarmos um tuplo, fazendo com que haja apenas um tuplo com A = 3
, vamos obter
um resultado diferente, que indica que os valores A = 3
já não cobrem todos os valores
para B, C
.
A |
B |
C |
---|---|---|
1 | a | X |
1 | b | Y |
2 | a | X |
2 | b | Y |
3 | b | Y |
B |
C |
---|---|
a | X |
b | Y |
A |
---|
1 |
2 |
Composição de Operações
Como seria de esperar, podemos encadear várias destas operações, visto que cada uma destas "retorna" uma nova relação.
Por exemplo, podemos efetuar uma projeção após efetuarmos uma seleção:
Atribuição
Além de encadearmos operações, o que se pode revelar muito verboso e até confuso, podemos atribuir resultados de operações a novas relações, como se estivéssemos a definir uma "variável".
Natural Join
O natural join entre duas relações efetua a junção das mesmas, juntando os tuplos que têm valores iguais para atributos com o mesmo nome. É de realçar que se tivermos dois atributos não relacionados nas duas relações que estamos a juntar, deveremos usar a operação de renomeação numa delas para evitar resultados indesejados.
Relembremos a sintaxe , em que e são as duas relações a juntar. Devemos também considerar e , que correspondem às schemas, isto é, aos atributos de e , respetivamente.
Assim, iremos obter uma nova relação, definida por:
Se repararmos, um natural join é uma especialização do produto cartesiano. Não é nada mais nada menos que um produto cartesiano seguido de uma seleção.
Exemplo
Podemos verificar que com um natural join podemos efetuar associações do tipo one-to-one, one-to-many e até many-to-many.
-
One-to-one
A
B
1 a 2 b 3 c B
C
a X b Y c Z A
B
C
1 a X 2 b Y 3 c Z -
One-to-many
A
B
1 a 2 b 3 b B
C
a X b Y c Z A
B
C
1 a X 2 b Y 3 b Y -
Many-to-many
A
B
1 a 2 b 3 b B
C
a X b Y b Z A
B
C
1 a X 2 b Y 2 b Z 3 b Y 3 b Z
Relembra-se também que pode haver mais que uma coluna em comum, e só se junta os tuplos caso os valores sejam iguais em todas as colunas.
A |
B |
C |
---|---|---|
1 | a | 9 |
2 | b | 8 |
3 | b | 7 |
B |
C |
D |
---|---|---|
a | 9 | X |
b | 7 | Y |
b | 3 | Z |
A |
B |
C |
D |
---|---|---|---|
1 | a | 9 | X |
3 | b | 7 | Y |
Agregação
Antes de olharmos para a operação de agregação em si, temos primeiro de definir o que são funções de agregação. Uma função de agregação pega num conjunto de valores e efetua-lhes uma operação, de forma a obter um único valor, como, por exemplo, o máximo/mínimo de um conjunto, a soma, a contagem e até a média.
Existem assim, em álgebra relacional, cinco funções lecionadas em aula:
min
, max
, sum
, count
e avg
, que em princípio são explícitas no seu
comportamento. Todas estas funções necessitam de um argumento a indicar qual o atributo
sobre o qual efetuam os cálculos, à exceção do count
, que não aceita argumentos.
O operador de agregação usa estas funções e aplica-as sobre grupos de tuplos, grupos estes que serão gerados através de um conjunto de atributos.
Relembremos a sintaxe . Aqui, temos que:
- é a relação onde queremos aplicar a agregação
- é um atributo de . Os tuplos serão agrupados pelos valores dos atributos , isto é, juntando os tuplos que partilham os mesmos valores para os atributos . Caso , isto é, não seja dado nenhum atributo por onde agrupar, considera-se a relação na sua totalidade.
- é uma função de agregação a aplicar em cada um dos grupos. Podemos, por conveniência, renomear logo o atributo resultante da função para algo mais ilustrativo, utilizando o operador ou :
Assim, iremos obter uma nova relação, definida por:
Exemplo
Considerando novamente o exemplo da loja, tomemos uma
simplificação da relação purchase
.
purchase(purchase_id, customer_name, product_name, quantity)
purchase_id |
customer_name |
product_name |
quantity |
---|---|---|---|
1 | Diogo | Napolitana | 2 |
2 | Rafa | Napolitana | 4 |
3 | Rafa | Napolitana | 2 |
4 | Tomás | Bolachas | 100 |
5 | Luís | Chá | 1 |
6 | Diogo | Bolachas | 2 |
-
Determinar quantas compras foram feitas no total
count()
6 -
Determinar quantas unidades foram vendidas no total
sum(quantity)
111 -
Determinar quantas unidades foram vendidas por produto
product_name
sum(quantity)
Napolitana 8 Bolachas 102 Chá 1 -
Determinar quantas unidades foram vendidas por produto e cliente
customer_name
product_name
sum(quantity)
Diogo Napolitana 2 Rafa Napolitana 6 Tomás Bolachas 100 Luís Chá 1 Diogo Bolachas 2 -
Determinar o máximo de unidades vendidas numa só compra, por produto
product_name
max(quantity)
Napolitana 4 Bolachas 100 Chá 1