Álgebra relacional

Operações sobre conjuntos, renomeação, junções, divisão e equivalências.

TLDR

Perguntar sobre esta página

ChatGPTClaudePerplexityGrokGeminiCopiar e abrirDeepSeekCopiar e abrir

Para saber quem comprou em 5 de janeiro, filtramos Encomenda pela data. Depois ligamos o resultado a Cliente e escolhemos os atributos da resposta.

A álgebra relacional compõe essas operações. Cada uma recebe relações e devolve uma relação. Usamos aqui a álgebra clássica, com conjuntos, sem duplicados e sem ordem de tuplos.

Os operadores essenciais

A seleção σp(R)\sigma_p(R) conserva os tuplos de R que satisfazem o predicado p. σprecoCentimos>3000(Produto)\sigma_{precoCentimos>3000}(Produto) conserva Teclado e Monitor, com todos os seus atributos. A seleção não escolhe colunas.

A projeção πX(R)\pi_X(R) conserva os atributos X e elimina tuplos repetidos. Se dois produtos distintos se chamarem Rato, πnome(Produto)\pi_{nome}(Produto) contém Rato uma vez. Uma projeção SQL precisa de DISTINCT para reproduzir essa semântica de conjuntos.

O produto cartesiano R×SR\times S combina cada tuplo de R com cada tuplo de S. Se houver m e n tuplos, o resultado tem mnm n tuplos, assumindo nomes de atributos desambiguados. Se uma relação estiver vazia, o produto está vazio.

A projeção clássica só escolhe atributos existentes. A projeção generalizada admite também expressões calculadas e nomes novos, como no exemplo seguinte:

πnome,precoCentimos/100.0→euros(Produto)\pi_{nome, precoCentimos/100.0\to euros}(Produto)

É a forma algébrica da lista de SELECT em SQL: cada coluna da resposta indica a expressão e o nome.

A renomeação ρ\rho altera o nome de uma relação ou dos seus atributos, conservando os valores. Escrevemos, por exemplo, ρC(idCliente,nome,email)(Cliente)\rho_{C(idCliente,nome,email)}(Cliente).

A ordem indicada corresponde à dos atributos na notação do esquema original. Outras convenções de renomeação são possíveis. Explicita a usada.

Na demonstração, a seleção usa preço ≥ limite, com preços em euros. Acrescentámos um segundo produto chamado Rato, de id 13 e preço 30, para observar duplicados. Com limite 30, vê os ids 10, 12 e 13; baixa para 25 e projeta o nome. Entram quatro produtos, mas a projeção só tem três tuplos porque os dois Ratos dão o mesmo nome.

3 produtos selecionados; 3 tuplos no resultado.

Seleção por preço ≥ limite, seguida da projeção escolhida
idnomepreço
10Teclado45
12Monitor180
13Rato30

No estado inicial, o Rato de 25 euros fica de fora. Com limite 25, entram os quatro produtos; projetar só o nome reúne os dois Ratos num único tuplo. Com limite 185, o resultado fica vazio.

Junções

A teta-junção R⋈pSR\bowtie_p S equivale a σp(R×S)\sigma_p(R\times S). O predicado pode usar igualdade ou outras comparações. Numa equijunção, comparamos atributos por igualdade. Atributos do mesmo nome precisam de qualificação ou renomeação.

A junção natural R⋈SR\bowtie S impõe igualdade em todos os atributos de nome comum e conserva uma cópia de cada atributo comum.

Se não houver nenhum nome comum, equivale ao produto cartesiano. Se houver vários, todos participam, mesmo que não fossem a ligação que tinhas em mente.

Na loja, Cliente(id,nome,email) e Encomenda(id,data,idCliente) têm id em comum, mas um id de cliente não é um id de encomenda. Uma junção natural compararia estes ids erradamente. Usa a condição Cliente.id = Encomenda.idCliente ou renomeia o id do cliente antes da junção natural.

A junção natural de R(A,B) com S(B,C) liga pelo B. A de R(A,B) com T(C,D) produz todas as combinações, porque não há nomes comuns.

Semijunção e agregação

A semijunção R⋉SR\ltimes S conserva apenas os tuplos de R com alguma correspondência em S. Não acrescenta as colunas de S. Na álgebra de conjuntos, podemos escrevê-la como πatributos(R)(R⋈S)\pi_{atributos(R)}(R\bowtie S).

Na loja, a semijunção seguinte dá Ana e Rui, uma vez cada, com os atributos de Cliente:

Cliente⋉Cliente.id=Encomenda.idClienteEncomendaCliente\ltimes_{Cliente.id=Encomenda.idCliente}Encomenda

Mia fica de fora. Ana ter duas encomendas não cria duas cópias da cliente. Em SQL, um EXISTS sobre Cliente exprime este teste; uma junção seguida de projeção sem DISTINCT pode manter repetições.

Contar ou somar exige uma extensão da álgebra clássica. Aqui usamos γ\gamma para agrupamento e agregação. Os slides também apresentam agregações como uma extensão de π\pi; identifica sempre a convenção usada.

γidCliente; count⁡(∗)→n(Encomenda)\gamma_{idCliente;\,\operatorname{count}(*)\to n}(Encomenda)

Agrupamos pelo cliente e chamamos n à contagem:

idClienten
12
21

Duas encomendas para Ana e uma para Rui. Mia não aparece, porque não há tuplos seus em Encomenda. Para mostrar também clientes com zero, é preciso conservar o universo Cliente, como na junção externa em SQL.

União, diferença e interseção

R∪SR\cup S contém os tuplos que estão em pelo menos um lado; R∩SR\cap S, os que estão nos dois; R−SR-S, os que estão em R e não em S. Os esquemas têm de ser compatíveis: mesmos atributos, com domínios correspondentes. Renomeia ou projeta antes da operação se necessário.

A diferença não é comutativa. CompraramTeclado − CompraramRato responde a quem comprou teclado e não rato; trocar os lados muda a pergunta. A condição “há uma compra que não é rato” também é diferente de “não há qualquer compra de rato”.

A interseção pode ser escrita só com diferença: R∩S=R−(R−S)R\cap S=R-(R-S). Estes operadores não somam contagens de duplicados, porque a álgebra clássica usa conjuntos.

Uma pergunta completa

Queremos os nomes dos clientes com encomendas em 5 de janeiro de 2026. Seja C a renomeação de Cliente que muda id para idCliente. Primeiro filtramos Encomenda, depois juntamos pelo atributo comum idCliente e finalmente projetamos nome:

E5=σdata=′2026−01−05′(Encomenda)E_5=\sigma_{data='2026-01-05'}(Encomenda) C=ρC(idCliente,nome,email)(Cliente)C=\rho_{C(idCliente,nome,email)}(Cliente) Resposta=πnome(E5⋈C).Resposta=\pi_{nome}(E_5\bowtie C).

Lê a árvore de baixo para cima. A junção recebe as encomendas filtradas e os clientes com o id renomeado.

As tabelas intermédias, com os dados da loja:

E5E_5 tem uma linha:

iddataidCliente
1002026-01-051

E5⋈CE_5\bowtie C acrescenta os atributos de Cliente, incluindo email:

iddataidClientenomeemail
1002026-01-051Anaana@example.test

A projeção final devolve o conjunto com um tuplo, {Ana}. Se Ana tivesse duas encomendas nesse dia, a projeção continuaria a mostrá-la uma vez. Para identificar pessoas, conserva o id.

As folhas da árvore são relações e cada nó interior é um operador. A seleção e a renomeação recebem uma relação cada; a junção recebe duas. Ao transformar a árvore, conserva todas as entradas e as colunas de que os operadores precisam.

Clientes sem encomendas

Não podemos descobrir uma ausência filtrando apenas encomendas existentes. Precisamos do conjunto de candidatos e de retirar os que aparecem:

Todos=πid(Cliente)Todos=\pi_{id}(Cliente) Compradores=ρCompradores(id)(πidCliente(Encomenda))Compradores=\rho_{Compradores(id)}(\pi_{idCliente}(Encomenda)) SemCompra=Todos−Compradores.SemCompra=Todos-Compradores.

Com os dados da loja, Todos é {1,2,3}\{1,2,3\} e Compradores é {1,2}\{1,2\}; a diferença é {3}\{3\}. Para obter nome e email, juntamos esse resultado com Cliente. A renomeação tornou os esquemas compatíveis.

Divisão: perguntas com todos

Se R(X,Y)R(X,Y) regista pares e S(Y)S(Y) contém os valores exigidos, R÷SR\div S devolve os X que estão ligados a todos os Y de S. O esquema da resposta contém os atributos de R que não estão em S.

Suponhamos Comprou(idCliente,idProduto) com (1,10),(1,11),(1,12),(2,11)(1,10),(1,11),(1,12),(2,11) e Exigido(idProduto) com 10 e 11. O cliente 1 comprou todos os produtos exigidos; o 2 não comprou o 10. A divisão devolve 1.

Pode ser construída com operadores básicos:

Candidatos=πX(R)Candidatos=\pi_X(R) Faltas=(Candidatos×S)−RFaltas=(Candidatos\times S)-R R÷S=Candidatos−πX(Faltas).R\div S=Candidatos-\pi_X(Faltas).

O produto enumera pares que deveriam existir. A diferença encontra os que faltam:

CandidatoProduto exigidoEstá em Comprou?
110sim
111sim
210não, falta
211sim

Só falta (2,10). Projetar o cliente dessa falta dá {2}. Retirá-lo dos candidatos {1,2} deixa {1}, a resposta. Não basta filtrar compras do produto 10 ou 11: isso também aceitaria o cliente 2, que só cumpriu um dos requisitos.

Se S estiver vazia, não falta nenhum requisito e a divisão devolve πX(R)\pi_X(R). Repara no universo dos candidatos: um cliente que não aparece em Comprou não pertence à projeção inicial. Se quisermos todos os clientes, incluindo os sem compras quando não há requisitos, usamos Cliente como universo e a mesma ideia de retirar faltas. A divisão em SQL explicita esta escolha.

Equivalências e execução

Podemos separar uma seleção conjuntiva: σp∧q(R)=σp(σq(R))\sigma_{p\land q}(R)=\sigma_p(\sigma_q(R)). Se p só usa atributos de R, podemos aplicar essa seleção antes de juntar R com S. Isso pode reduzir o número de tuplos intermédios sem mudar a resposta.

A projeção também pode reduzir atributos antes de uma junção, mas tem de conservar os usados pela condição e os necessários ao resultado. Se retirarmos idCliente antes de ligar as encomendas aos clientes, perdemos a ligação.

Por exemplo, para R(A,B)R(A,B) e S(B,C)S(B,C):

πA,C(R⋈σB=1(S))=πA(σB=1(R))×πC(σB=1(S)).\pi_{A,C}(R\bowtie\sigma_{B=1}(S))= \pi_A(\sigma_{B=1}(R))\times\pi_C(\sigma_{B=1}(S)).

À esquerda, a junção obriga também B de R a ser 1. À direita, os dois lados já foram filtrados por esse mesmo valor, por isso todas as combinações concordam em B e podemos retirá-lo antes do produto.

Retirar B de R antes do filtro, usando πA,C(πA(R)×σB=1(S))\pi_{A,C}(\pi_A(R)\times\sigma_{B=1}(S)), perde a obrigação. Com R={(3,4)}R=\{(3,4)\} e S={(1,2)}S=\{(1,2)\}, as expressões corretas devolvem vazio, mas esta última devolve (3,2)(3,2). Já não existe B do lado de R para comparar. Antecipar a projeção exige conferir filtros além das colunas da junção.

Estas equivalências explicam transformações de um otimizador. Não determinam a ordem física exata que o SGBD seguirá. Junções externas e a lógica de NULL exigem cuidados adicionais; não lhes apliques automaticamente todas as identidades da álgebra clássica.

Exercícios

Uma junção pelo atributo errado

Cliente(id,nome) e Encomenda(id,idCliente) são juntadas naturalmente. Que condição é usada? Como obter a ligação pretendida?

Pista

A junção natural usa todos os nomes comuns, sem conhecer o seu significado.

Solução

A condição natural é Cliente.id = Encomenda.id, porque id é o nome comum. Isso compara uma chave de cliente com uma chave de encomenda. Usa uma teta-junção com Cliente.id = Encomenda.idCliente, qualificando os atributos, ou renomeia Cliente.id para idCliente e faz depois a junção natural. A mera existência de nomes comuns não prova a ligação correta.

Confere a tua resposta:

  • Identifiquei a igualdade realmente usada.
  • Propus uma ligação correta.
Voltar à explicação
Divisor vazio e universo de candidatos

Comprou(cliente,produto) contém (1,10),(1,11),(2,11). Exigido(produto) está vazio. Cliente contém 1,2,3. Qual o resultado de Comprou ÷ Exigido e de uma pergunta universal sobre todos os clientes?

Pista

A divisão clássica parte da projeção dos candidatos de Comprou.

Solução

A divisão clássica devolve {1,2}, que é a projeção cliente de Comprou. A pergunta que parte da relação Cliente devolve {1,2,3}, pois não há produto exigido que falte a qualquer cliente. O cliente 3 não entra na primeira porque não aparece no universo inicial, não porque tenha falhado um requisito.

Confere a tua resposta:

  • Distingui os dois conjuntos de candidatos.
  • Expliquei a satisfação universal sem requisitos.
Voltar à explicação
Correspondências, tuplos e grupos

Cliente contém (1,Ana), (2,Rui), (3,Mia). Encomenda contém (100,1), (101,1), (102,2), com atributos idEncomenda e idCliente. Quantos tuplos produz a junção por Cliente.id = Encomenda.idCliente? E a semijunção que conserva apenas Cliente? Indica ainda o resultado de agrupar Encomenda por idCliente e contar os tuplos de cada grupo.

Pista

A junção conserva cada encomenda; a semijunção conserva os clientes com alguma correspondência.

Solução

A junção produz três tuplos, um por encomenda. A semijunção produz dois, (1,Ana) e (2,Rui), sem atributos de Encomenda e sem repetir Ana. O agrupamento de Encomenda produz (1,2) e (2,1). Não produz (3,0), porque não há linhas de Mia nessa relação. Para incluir Mia, temos de partir de Cliente e conservar os clientes sem correspondência.

Confere a tua resposta:

  • Distingui os três tuplos da junção dos dois clientes da semijunção.
  • Obtive as contagens 2 e 1, sem criar um grupo para Mia.

Erros frequentes

Não contes encomendas como clientes distintos. A agregação de uma relação não inventa grupos para valores que não aparecem nela.

Voltar à explicação
Ausência e diferença de conjuntos

Cliente(id,nome) contém (1,Ana), (2,Rui), (3,Mia). Encomenda(id,idCliente) contém (100,1), (101,1), (102,2). Comprou(idCliente,idProduto) contém (1,10), (1,11), (1,12), (2,11).

Escreve expressões de álgebra para os ids de clientes sem encomendas e para os ids de clientes que compraram teclado, id 10, mas nunca rato, id 11. Apresenta os conjuntos intermédios. Filtrar idProduto ≠ 11 responde à segunda pergunta?

Pista
Para encontrar uma ausência, começa pelo universo e retira correspondências.
Mais uma pista
Ana tem uma compra de teclado e outra de rato. A condição sobre uma linha não diz o que acontece nas outras.
Solução

Para os clientes sem encomendas, projeta id de Cliente: {1,2,3}. Projeta idCliente de Encomenda e renomeia-o para id: {1,2}. A diferença dá {3}, Mia.

Para teclado sem rato, usa

πidCliente(σidProduto=10(Comprou))−πidCliente(σidProduto=11(Comprou)).\pi_{idCliente}(\sigma_{idProduto=10}(Comprou))- \pi_{idCliente}(\sigma_{idProduto=11}(Comprou)).

O primeiro conjunto é {1}, o segundo {1,2}. A diferença é vazia. Selecionar compras com idProduto ≠ 11 daria {1} pela compra do teclado e do monitor, apesar de Ana também ter comprado rato.

Confere a tua resposta:

  • Obtive clientes sem encomendas a partir de Cliente.
  • Renomeei os atributos para tornar a diferença compatível.
  • Distingui comprar algo diferente de rato de nunca comprar rato.

Erros frequentes

Não subtraias relações com esquemas diferentes. A diferença compara tuplos completos, não deduz que id e idCliente têm o mesmo significado.
Voltar à explicação
Uma projeção apaga a condição necessária

Considera R(A,B) e S(B,C), com valores inteiros, sem hipóteses de chaves. Compara:

E1=πA,C(R⋈σB=1(S))E_1=\pi_{A,C}(R\bowtie\sigma_{B=1}(S))E2=πA(σB=1(R))×πC(σB=1(S))E_2=\pi_A(\sigma_{B=1}(R))\times\pi_C(\sigma_{B=1}(S))E3=πA,C(πA(R)×σB=1(S)).E_3=\pi_{A,C}(\pi_A(R)\times\sigma_{B=1}(S)).

Quais são equivalentes em todas as instâncias? Constrói um contraexemplo com um tuplo em cada relação e calcula as três respostas. Usa a semântica de conjuntos da álgebra clássica.

Pista
Na junção natural de E1, que valor tem de ter B do lado de R?
Solução

E1 e E2 são equivalentes. Em E1, S foi filtrada por B=1, e a junção obriga o B de R a ser também 1. Todos os tuplos dos dois conjuntos filtrados têm o mesmo B; o produto das projeções A e C dá exatamente os mesmos pares.

E3 não filtra R por B=1. Escolhe R com (3,4) e S com (1,2). E1 dá vazio porque 4 não é 1. E2 dá vazio porque o filtro de R elimina o seu único tuplo. E3 projeta R em {3} e combina-o com o tuplo de S, dando {(3,2)}. A projeção eliminou B antes de exigir a condição.

Confere a tua resposta:

  • Justifiquei a equivalência de E1 e E2 para qualquer instância.
  • Construí e calculei um contraexemplo para E3.
  • Identifiquei o filtro de R que desapareceu.
Voltar à explicação

À tua maneira

Aparência

Modo de cor
Tema
Cor de destaque

Leitura

Fonte de leitura

Álgebra, lógica e uma ideia de cada vez.

Código

Fonte do código

Navegação

Cursor de leitura, sem edição. h, j, k, l movem; / pesquisa nesta página. v seleciona caracteres; V seleciona linhas; y copia. Tab permite destacar ou comentar. Espaço ou Enter abre blocos recolhíveis. Nos cartões, h, j, k, l mudam o foco.

CSS personalizado

Ativa uma sugestão ou guarda o teu CSS. Só muda este navegador.

Usa --page, --text e --accent para acompanhar o tema.

Seletores e recuperação

.site-header, .prose, .course-card, [data-reading-history], [data-playground] e .page-actions.

Abre a aparência com a tecla vírgula ou no rodapé. Se o CSS esconder os controlos, abre esta página sem CSS personalizado.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Copiar fórmula

A cópia automática não está disponível. Copia o LaTeX abaixo.

Pesquisar nesta página

Enter procura · n / N repetem · Esc cancela. Ctrl / Cmd K pesquisa todo o site.

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.