Otimização de Consultas - INF-Unioeste
Otimização de Consultas - INF-Unioeste
Otimização de Consultas - INF-Unioeste
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
Banco <strong>de</strong> Dados I<br />
2007<br />
Módulo VI: Processamento e<br />
<strong>Otimização</strong> <strong>de</strong> <strong>Consultas</strong><br />
(Aulas 1-5)<br />
Clodis Boscarioli
Agenda:<br />
O Processador <strong>de</strong> <strong>Consultas</strong>:<br />
Conceitos Principais.<br />
Algoritmos usados para implementar operações<br />
algébricas;<br />
<strong>Otimização</strong> Baseada em Custo;<br />
<strong>Otimização</strong> Heurística;<br />
Comentários sobre otimização no PostgreSQL.
Visão Geral<br />
<strong>de</strong> um SGBD<br />
Usuários<br />
navegantes<br />
Interface com<br />
aplicações<br />
Programadores<br />
<strong>de</strong> aplicações<br />
Programas <strong>de</strong><br />
aplicações<br />
Usuários<br />
sofisticados<br />
<strong>Consultas</strong><br />
(queries)<br />
Administradores<br />
<strong>de</strong> BD<br />
Esquema <strong>de</strong><br />
Banco <strong>de</strong> Dados<br />
Usuários<br />
Processador<br />
<strong>de</strong> consultas<br />
Programas <strong>de</strong><br />
aplicações em<br />
código objeto<br />
Pré-compilador<br />
<strong>de</strong> comandos<br />
DML<br />
Compilador<br />
DML<br />
Interpretador<br />
DDL<br />
SGBD<br />
Componentes <strong>de</strong> execução<br />
<strong>de</strong> consultas<br />
Gerenciador<br />
<strong>de</strong> memória<br />
Gerenciador<br />
<strong>de</strong> transações<br />
Gerenciador<br />
<strong>de</strong> buffer<br />
Gerenciador<br />
<strong>de</strong> arquivos<br />
Armazenamento<br />
em disco<br />
Arquivos <strong>de</strong><br />
dados<br />
Índices<br />
Dados<br />
estatísticos<br />
Dicionário<br />
<strong>de</strong> dados<br />
BD
Processamento <strong>de</strong> <strong>Consultas</strong><br />
Processar consultas envolve:<br />
Traduzir consultas expressas em linguagens<br />
<strong>de</strong> alto nível (como SQL) em expressões que<br />
po<strong>de</strong>m ser implementadas no nível físico do<br />
sistema <strong>de</strong> banco <strong>de</strong> dados (nível <strong>de</strong> tabelas);<br />
Otimizar a expressão <strong>de</strong>stas consultas;<br />
Avaliar a base <strong>de</strong> dados <strong>de</strong> acordo com as<br />
diretrizes da consulta, para fornecer o<br />
resultado.
Processamento <strong>de</strong> <strong>Consultas</strong><br />
<br />
<br />
Consulta SQL<br />
É a<strong>de</strong>quada para uso humano;<br />
Não a<strong>de</strong>quada ao processamento pelo SGBD:<br />
Não <strong>de</strong>screve uma seqüência <strong>de</strong> passos<br />
(procedimento) a ser seguida;<br />
Não <strong>de</strong>screve uma estratégia eficiente para a<br />
implementação <strong>de</strong> cada passo no que tange o<br />
acesso em nível físico (arquivos do BD).<br />
Cabe ao SGBD <strong>de</strong>ve se preocupar com este<br />
processamento módulo Processador <strong>de</strong> <strong>Consultas</strong>.
Módulo Processador <strong>de</strong> <strong>Consultas</strong><br />
<br />
<br />
Objetivo: <strong>Otimização</strong> do processamento <strong>de</strong> uma<br />
consulta<br />
Tradução, transformação e geração <strong>de</strong> uma estratégia<br />
(plano) <strong>de</strong> execução;<br />
Estratégia <strong>de</strong> acesso:<br />
Consi<strong>de</strong>ra algoritmos pre<strong>de</strong>finidos para implementação <strong>de</strong><br />
passos do processamento e estimativas sobre os dados.<br />
O esforço é valido, pois quase sempre<br />
T x
Passos no Processamento <strong>de</strong> <strong>Consultas</strong><br />
Consulta<br />
Analisador<br />
sintático<br />
e tradutor<br />
Expressão<br />
algébrica<br />
relacional<br />
Otimizador<br />
Saída da<br />
consulta<br />
Avaliador<br />
Plano <strong>de</strong><br />
execução<br />
Dados<br />
Metadados
Passos no Processamento <strong>de</strong> <strong>Consultas</strong><br />
Consulta<br />
Analisador<br />
sintático<br />
e tradutor<br />
Expressão<br />
algébrica<br />
relacional<br />
Otimizador<br />
• Análise léxica<br />
- cláusulas<br />
Saída<br />
SQL<br />
da<br />
e nomes válidos.<br />
Avaliador<br />
• Análise sintática consulta<br />
- validação da gramática.<br />
• Análise semântica<br />
- nomes usados <strong>de</strong> acordo com a estrutura<br />
do esquema.<br />
Dados<br />
• Conversão para uma árvore algébrica<br />
da consulta<br />
Plano <strong>de</strong><br />
execução<br />
Metadados
Passos no Processamento <strong>de</strong> <strong>Consultas</strong><br />
Consulta<br />
Analisador<br />
sintático<br />
e tradutor<br />
Expressão<br />
algébrica<br />
relacional<br />
Otimizador<br />
• Definição <strong>de</strong> uma árvore <strong>de</strong><br />
consulta equivalente<br />
Saída da<br />
consulta<br />
Avaliador<br />
- chega ao mesmo resultado<br />
- processa <strong>de</strong> forma mais<br />
eficiente<br />
• Fase chamada <strong>de</strong><br />
<strong>Otimização</strong> Algébrica<br />
Dados<br />
Plano <strong>de</strong><br />
execução<br />
Metadados
Passos no Processamento <strong>de</strong> <strong>Consultas</strong><br />
Análise <strong>de</strong> alternativas <strong>de</strong> <strong>de</strong>finição <strong>de</strong><br />
estratégias <strong>de</strong> acesso<br />
Consulta<br />
- escolha <strong>de</strong> algoritmos para<br />
Analisador<br />
sintático<br />
e tradutor<br />
implementação <strong>de</strong> operações<br />
- existência <strong>de</strong> índices<br />
- estimativas sobre os dados<br />
(tamanho <strong>de</strong> tabelas, seletivida<strong>de</strong>, ...)<br />
Expressão<br />
algébrica<br />
relacional<br />
Otimizador<br />
Saída da<br />
consulta<br />
Avaliador<br />
Plano <strong>de</strong><br />
execução<br />
Dados<br />
Metadados
Passos no Processamento <strong>de</strong> <strong>Consultas</strong><br />
Consulta<br />
Analisador<br />
sintático<br />
e tradutor<br />
Expressão<br />
algébrica<br />
relacional<br />
FOCO:<br />
OTIMIZADOR DE<br />
CONSULTA<br />
Otimizador<br />
Saída da<br />
consulta<br />
Avaliador<br />
Plano <strong>de</strong><br />
execução<br />
Dados<br />
Metadados
Exemplo Introdutório<br />
<br />
Suponha a consulta:<br />
select saldo<br />
from conta<br />
where saldo < 2500;<br />
<br />
Esta po<strong>de</strong> ser traduzida nas duas expressões algébricas<br />
relacionais diferentes:<br />
σ<br />
saldo < 2500 (π saldo (conta))<br />
π saldo (σ saldo < 2500(conta))
Exemplo Introdutório<br />
<br />
<br />
Além <strong>de</strong>sta variação, é possível executar cada operação<br />
algébrica relacional usando um entre diversos<br />
algoritmos diferentes. Por exemplo:<br />
Para executar a seleção, po<strong>de</strong>mos procurar em todas<br />
as tuplas <strong>de</strong> conta a fim <strong>de</strong> encontrar as tuplas com<br />
saldo menor 2.500.<br />
Se um índice árvore-B+ estiver disponível no atributo<br />
saldo, po<strong>de</strong>mos usar o índice em vez <strong>de</strong> localizar as<br />
tuplas.<br />
É necessário prover as expressões algébricas <strong>de</strong><br />
anotações que permitam especificar como serão<br />
avaliadas.
Exemplo Introdutório<br />
Uma operação algébrica relacional anotada com<br />
instruções sobre como ser avaliada é chamada<br />
<strong>de</strong> avaliação primitiva.<br />
Vária avaliações primitivas po<strong>de</strong>m ser<br />
agrupadas em pipeline, e executadas em<br />
paralelo.<br />
Uma seqüência <strong>de</strong> operações primitivas é um<br />
plano <strong>de</strong> execução <strong>de</strong> consulta ou plano <strong>de</strong><br />
avaliação <strong>de</strong> consulta.
Exemplo Introdutório<br />
π saldo<br />
σ saldo < 2500 (use índice 1)<br />
conta<br />
<br />
Uma vez escolhido o plano <strong>de</strong> consulta, a consulta é<br />
avaliada com aquele plano e o resultado da consulta é<br />
produzido
<strong>Otimização</strong> <strong>de</strong> <strong>Consultas</strong><br />
<br />
Existem 2 técnicas básicas para otimização <strong>de</strong><br />
consultas:<br />
As baseadas em heurísticas para a or<strong>de</strong>nação <strong>de</strong><br />
acesso ao banco <strong>de</strong> dados, que participarão da<br />
estratégia <strong>de</strong> acesso;<br />
e as que estimam sistematicamente o custo <strong>de</strong><br />
estratégias <strong>de</strong> execução diferentes e escolhem o<br />
plano <strong>de</strong> execução com o menor custo estimado.
Catálogo <strong>de</strong> Informações para Estimativa <strong>de</strong><br />
Custo<br />
n r : é o número <strong>de</strong> tuplas na relação r;<br />
b r : é o número <strong>de</strong> blocos que contêm tuplas da relação r;<br />
s r : é o tamanho em bytes <strong>de</strong> uma tupla da relação r;<br />
<br />
<br />
f r : é o fator <strong>de</strong> bloco da relação r, ou seja, o número <strong>de</strong><br />
tuplas da relação r que cabe em um bloco;<br />
V(A,r): é o número <strong>de</strong> valores distintos que aparecem na<br />
relação r para o atributo A. Esse valor é igual ao<br />
tamanho (em número <strong>de</strong> tuplas) <strong>de</strong> π A (r). Se A é uma<br />
chave para a relação r, V(A,r) é n r .
Catálogo <strong>de</strong> Informações para Estimativa <strong>de</strong><br />
Custo<br />
<br />
SC(A,r): é a cardinalida<strong>de</strong> <strong>de</strong> seleção (seletivida<strong>de</strong>) do atributo A da<br />
relação r.<br />
Dados uma relação r e um atributo A da relação, SC(A,r) é o<br />
número médio <strong>de</strong> registros que satisfazem uma condição <strong>de</strong><br />
igualda<strong>de</strong> no atributo A, dado que pelo menos um registro<br />
satisfaz a condição <strong>de</strong> igualda<strong>de</strong>.<br />
Exemplo:<br />
SC(A,r) = 1 se A é um atributo-chave <strong>de</strong> r;<br />
Para um atributo que não é chave, estimamos que os<br />
valores distintos <strong>de</strong> V(A,r) são distribuídos uniformemente<br />
entre as tuplas, produzindo SC(A,r) = (n r / V(A,r))
Catálogo <strong>de</strong> Informações para Estimativa <strong>de</strong><br />
Custo<br />
As duas últimas estatísticas po<strong>de</strong>m ser<br />
estendidas <strong>de</strong> forma a valer para um conjunto<br />
<strong>de</strong> atributos, ao invés <strong>de</strong> valer para apenas um<br />
atributo.<br />
Se as tuplas da relação r estiverem<br />
armazenadas fisicamente juntas em um arquivo,<br />
a seguinte equação é válida:<br />
B r = [n r , f r ]
Catálogo <strong>de</strong> Informações para Estimativa <strong>de</strong><br />
Custo<br />
<br />
Informações sobre índices:<br />
f i : é o fan-out (número <strong>de</strong> ponteiros) médio dos nós<br />
internos do índice i para índices estruturados em<br />
árvore, como árvores B + ;<br />
HT i : é o número <strong>de</strong> níveis no índice i, ou seja, a altura<br />
do índice i.<br />
LB i : é o número <strong>de</strong> blocos <strong>de</strong> índice <strong>de</strong> nível mais<br />
baixo no índice i, ou seja, o número <strong>de</strong> blocos no<br />
nível <strong>de</strong> folha do índice (o número <strong>de</strong> blocos que<br />
contém os registros folha <strong>de</strong> um índice).
Catálogo <strong>de</strong> Informações para Estimativa <strong>de</strong><br />
Custo<br />
<br />
As variáveis estatísticas são usadas para estimar o tamanho<br />
do resultado e o custo para várias operações e algoritmos.<br />
A estimativa <strong>de</strong> custo do algoritmo A é E A .<br />
<br />
<br />
Para manter as estatísticas precisas, toda vez que uma<br />
relação for modificada tem-se que atualizar as estatísticas.<br />
Contudo, a maioria do sistema não atualiza as estatísticas em<br />
todas as modificações. Atualiza-as periodicamente.<br />
Quanto mais informações forem utilizadas para estimar o<br />
custo da consulta e quanto mais precisas forem essas<br />
informações, melhores serão as estimativas <strong>de</strong> custo.
Medidas do Custo <strong>de</strong> uma Consulta<br />
<br />
O custo <strong>de</strong> uma consulta po<strong>de</strong> ser estimado <strong>de</strong> diversas formas:<br />
Por acessos a disco;<br />
Por tempo <strong>de</strong> uso da CPU;<br />
Pelo tempo <strong>de</strong> comunicação nos BD paralelos<br />
e/ou distribuídos;<br />
<br />
O tempo <strong>de</strong> execução <strong>de</strong> um plano po<strong>de</strong>ria ser usado para<br />
estimar o custo da consulta, contudo em gran<strong>de</strong>s sistemas <strong>de</strong><br />
BD, utiliza-se o número <strong>de</strong> acessos a disco, porque estes<br />
estabelecem o tempo crítico <strong>de</strong> execução do plano (já que são<br />
lentos quando comparados às operações realizadas em<br />
memória).
Medidas do Custo <strong>de</strong> uma Consulta<br />
<br />
Para simplificar nossos cálculos assumiremos que todas as<br />
transferências <strong>de</strong> blocos (do disco para memória) têm o<br />
mesmo custo. Desconsi<strong>de</strong>raremos o tempo <strong>de</strong> latência e o<br />
tempo <strong>de</strong> busca. Também <strong>de</strong>sconsi<strong>de</strong>ramos o custo <strong>de</strong><br />
escrever o resultado final <strong>de</strong> uma operação <strong>de</strong> volta para o<br />
disco.<br />
<br />
Os custos dos algoritmos <strong>de</strong>pen<strong>de</strong>m significativamente do<br />
tamanho do buffer na memória principal. No melhor caso,<br />
todos os dados po<strong>de</strong>m ser lidos para o buffer e o disco não<br />
precisa ser acessado novamente. No pior caso, supomos<br />
que o buffer po<strong>de</strong> manter apenas alguns blocos <strong>de</strong> dados<br />
– aproximadamente um bloco por relação. Geralmente<br />
faremos a suposição do pior caso.
Operação <strong>de</strong> Seleção<br />
<br />
É a varredura <strong>de</strong> arquivos: o operador <strong>de</strong> mais baixo<br />
nível para se ter acesso aos dados.<br />
<br />
São algoritmos <strong>de</strong> procura que localizam e recuperam<br />
os registros que estão <strong>de</strong> acordo com uma condição <strong>de</strong><br />
seleção.<br />
<br />
Tem-se vários algoritmos diferentes, que variam <strong>de</strong><br />
acordo com a complexida<strong>de</strong> da seleção e o uso ou não<br />
<strong>de</strong> índices.
Operação <strong>de</strong> Seleção<br />
<br />
Exemplo <strong>de</strong> algoritmos usados na implementação do<br />
operador select:<br />
Busca Linear (ou força bruta);<br />
Busca Binária;<br />
Utilização <strong>de</strong> índice primário (atributo chave);<br />
Utilização <strong>de</strong> índice primário para recuperar múltiplos<br />
registros (atributo chave);<br />
Utilização <strong>de</strong> um índice cluster para recuperar<br />
múltiplos registros (atributo não chave);<br />
Utilização <strong>de</strong> um índice secundário (Árvore B+) em<br />
uma comparação <strong>de</strong> igualda<strong>de</strong>;<br />
Busca para seleções complexas
Operação <strong>de</strong> Seleção<br />
<br />
Busca para seleções complexas:<br />
Se uma condição <strong>de</strong> uma instrução select é uma<br />
condição conjuntiva – ou seja, é formada por diversas<br />
condições simples conectadas pelo conectivo lógico<br />
AND, o SGBD po<strong>de</strong> usar os seguintes métodos:<br />
Seleção conjuntiva utilizando um índice individual;<br />
Seleção conjuntiva utilizando um índice composto;<br />
Seleção conjuntiva por meio da interseção <strong>de</strong> registros.
Operação <strong>de</strong> Seleção<br />
<br />
Busca para seleções complexas:<br />
Se uma condição <strong>de</strong> uma instrução select é uma<br />
condição disjuntiva – ou seja, é formada por diversas<br />
condições simples conectadas pelo conectivo lógico<br />
OR, a otimização é mais simples.<br />
Pouca otimização po<strong>de</strong> ser feita, pois os registros<br />
que satisfazem a condição disjuntiva são a união dos<br />
registros que satisfazem as condições individuais.
Operação <strong>de</strong> Seleção<br />
Veremos dois <strong>de</strong>les (os básicos):<br />
Aquele que envolve uma Busca Linear;<br />
Aquele que envolve uma Busca Binária.<br />
Consi<strong>de</strong>re uma operação <strong>de</strong> seleção em uma<br />
relação cujas tuplas são armazenadas juntas<br />
em um único arquivo.
Seleção por Busca Linear – A1<br />
<br />
Em uma busca linear, cada bloco <strong>de</strong> arquivo é varrido e<br />
todos os registros são testados para verificar se<br />
satisfazem a condição <strong>de</strong> seleção.<br />
Como todos os blocos precisam ser lidos, E A1 = b r .<br />
<br />
No caso da seleção ser aplicada em um atributo-chave,<br />
po<strong>de</strong>mos supor que a meta<strong>de</strong> dos blocos é varrida antes<br />
<strong>de</strong> o registro ser encontrado, ponto no qual a varredura<br />
termina. A estimativa então será E A1 = (b r /2).
Seleção por Busca Binária – A2<br />
<br />
Se o arquivo é or<strong>de</strong>nado em um atributo e a condição <strong>de</strong> seleção é<br />
uma comparação <strong>de</strong> igualda<strong>de</strong> neste atributo, po<strong>de</strong>mos usar uma<br />
busca binária para localizar os registros que satisfazem a seleção.<br />
Neste caso, a estimativa é:<br />
E A2 = [log 2 (b r )] + [SC(A,r)/f r ] -1<br />
O primeiro termo [log 2 (b r )] contabiliza o custo para localizar a<br />
primeira tupla por meio da busca binária nos blocos;<br />
O número total <strong>de</strong> registros que satisfarão a seleção é SC(A,r), e<br />
esses registros ocuparão [SC(A,r)/f r ] blocos, dos quais um já<br />
havia sido recuperado (por isso o -1).<br />
Se a condição <strong>de</strong> igualda<strong>de</strong> estiver em um atributo-chave, então<br />
SC(A,r) = 1, e a estimativa se reduz a E A2 = [log(b r )].
Cálculo do Custo da Busca Binária<br />
<br />
Acesso aos blocos:<br />
Primeiro acesso (ao bloco central) não encontro o registro<br />
procurado;<br />
Segundo acesso (ao bloco central do lado esquerdo ou direito)<br />
....<br />
Até o pior caso (nono acesso), o registro é encontrando na<br />
última divisão disponível (ou não é encontrado).<br />
<br />
Para 500 blocos:<br />
500 250 125 62,5 31,25 15,62 7,8 3,9 <br />
1,9 (nove divisões)<br />
<br />
Cálculo: log 2 (500) = 9 2 9 = 516 (=~ 500)
Exemplo <strong>de</strong> Seleção por Busca Binária<br />
<br />
Suponha as seguintes informações estatísticas para uma relação<br />
conta:<br />
f conta = 20 (ou seja, 20 tuplas <strong>de</strong> conta cabem em um único<br />
bloco);<br />
V(nome_agência, conta) = 50 (ou seja, existem 50 agências<br />
com nomes diferentes);<br />
V(saldo, conta) = 500 (ou seja, existe 500 valores diferentes <strong>de</strong><br />
saldos nesta relação);<br />
n conta = 10.000 (ou seja, a relação conta possui 10.000 tuplas).<br />
<br />
Consi<strong>de</strong>re a consulta:<br />
σ nome_agência = Perryridge (conta)
Exemplo <strong>de</strong> Seleção por Busca Binária<br />
<br />
<br />
<br />
Como a relação tem 10.000 tuplas, e cada bloco mantém 20 tuplas, o<br />
número <strong>de</strong> blocos é b conta = 500 (10.000/20);<br />
Uma varredura <strong>de</strong> arquivo simples faria 500 acessos a blocos, supondo<br />
que o atributo da condição não fosse atributo-chave. Senão, seriam em<br />
média 250 acessos;<br />
Suponha que conta esteja or<strong>de</strong>nado por nome_agência.<br />
Como V(nome_agência, conta) = 50, esperamos que 10.000/50=200<br />
tuplas da relação conta pertençam à agência Perryridge;<br />
<br />
Essas tuplas caberiam em 200/20 = 10 blocos;<br />
Uma busca binária para encontra o primeiro registro [log 2 (500)] = 9;<br />
<br />
Assim o custo total seria: 9 + 10 – 1 = 18 acessos a bloco.
Operação <strong>de</strong> Seleção<br />
<br />
<br />
<br />
<br />
A otimização <strong>de</strong> consulta para uma operação SELECT é<br />
necessária principalmente em condições <strong>de</strong> seleção<br />
conjuntiva, sempre que mais <strong>de</strong> um dos atributos<br />
envolvidos nas condições possuírem um caminho <strong>de</strong><br />
acesso.<br />
O otimizador <strong>de</strong>ve escolher o caminho <strong>de</strong> acesso que<br />
recupera o menor número <strong>de</strong> registros (gera blocos <strong>de</strong><br />
respostas menores), <strong>de</strong> maneira mais eficiente.<br />
As seleções que separam o menor número <strong>de</strong> tuplas<br />
<strong>de</strong>vem ser realizadas primeiro.<br />
Na escolha entre múltiplas opções o otimizador<br />
consi<strong>de</strong>ra também a seletivida<strong>de</strong> <strong>de</strong> cada condição.
Classificação<br />
<br />
A or<strong>de</strong>nação é bastante importante, uma vez que o<br />
algoritmo é utilizado:<br />
Na implementação do or<strong>de</strong>r by.<br />
Como um componente-chave nos algoritmos <strong>de</strong> sortmerge<br />
usado no join, union e intersection e em<br />
algoritmos <strong>de</strong> eliminação <strong>de</strong> duplicatas para a<br />
operação project.<br />
<br />
A or<strong>de</strong>nação po<strong>de</strong> ser evitada se um índice apropriado<br />
existir <strong>de</strong> forma a permitir o acesso or<strong>de</strong>nado aos<br />
registros.
Classificação<br />
Formas <strong>de</strong> or<strong>de</strong>nação:<br />
Lógica: construção <strong>de</strong> um índice na chave <strong>de</strong><br />
classificação, o qual será usado para ler a<br />
relação na or<strong>de</strong>m <strong>de</strong> classificação.<br />
A leitura <strong>de</strong> tuplas na or<strong>de</strong>m <strong>de</strong> classificação po<strong>de</strong><br />
conduzir a um acesso <strong>de</strong> disco para cada tupla.<br />
Física: as tuplas são gravadas <strong>de</strong> forma<br />
or<strong>de</strong>nada no disco.
Classificação<br />
<br />
O problema <strong>de</strong> classificação po<strong>de</strong> ser tratado sob duas<br />
condições:<br />
Quando a relação cabe completamente na memória<br />
principal:<br />
Técnicas padrões <strong>de</strong> classificação (quicksort entre outras)<br />
po<strong>de</strong>m ser usadas.<br />
Quando a relação é maior que a memória principal <br />
classificação externa:<br />
Algoritmo comum: sort-merge externo<br />
<br />
Para entendê-lo, consi<strong>de</strong>re M o número <strong>de</strong> frames <strong>de</strong> páginas<br />
no buffer da memória principal ( o número <strong>de</strong> blocos <strong>de</strong> disco<br />
cujos conteúdos po<strong>de</strong>m ser colocados no buffer da memória<br />
principal).
Or<strong>de</strong>nação Externa<br />
<br />
A or<strong>de</strong>nação externa é a<strong>de</strong>quada para manipular<br />
arquivos <strong>de</strong> registros gran<strong>de</strong>s, que são armazenados<br />
em disco e que não cabem inteiramente na memória<br />
principal.<br />
A or<strong>de</strong>nação nesse algoritmo é feita por partes –<br />
estratégia merge-sort.<br />
<br />
Fases:<br />
Fase <strong>de</strong> or<strong>de</strong>nação;<br />
Fase <strong>de</strong> fusão.
Inicialização:<br />
i 1;<br />
j b; (tamanho do arquivo em blocos)<br />
k n 0 ; (tamanho do buffer em blocos)<br />
m ⎡ (j/k) ⎤ (maior inteiro)<br />
Fase <strong>de</strong> or<strong>de</strong>nação<br />
Se no buffer cabem 3 blocos,<br />
e o arquivo possui 11 blocos,<br />
será preciso 4 iterações da<br />
fase <strong>de</strong> or<strong>de</strong>nação. As 3<br />
primeiras or<strong>de</strong>narão 9 blocos<br />
e a última or<strong>de</strong>nará 2 blocos.<br />
Enquanto (i
Fase <strong>de</strong> fusão: fundir os subarquivos até que reste apenas 1<br />
Inicialização<br />
Temos 4 subarquivos or<strong>de</strong>nados (m = 4 e k = 3).<br />
i 1;<br />
p ⎡ log k-1 m ⎤; (p é o número <strong>de</strong> passagens da fase <strong>de</strong> fusão)<br />
j m;<br />
p = 2<br />
enquanto (i
Exemplo no Navathe<br />
Se o número <strong>de</strong> blocos do arquivo = 1024<br />
Se o tamanho do buffer = 5 blocos<br />
Na fase <strong>de</strong> or<strong>de</strong>nação serão criados 205 subarquivos<br />
204 com 5 blocos e 1 com 4 blocos<br />
<br />
Na fase <strong>de</strong> fusão, em cada uma das 4 passagens, serão gravados,<br />
respectivamente:<br />
52 subarquivos<br />
13 subarquivos<br />
04 arquivos<br />
01 arquivo<br />
Número <strong>de</strong> subarquivos / tamanho do buffer -1 bloco<br />
Por que -1?<br />
Porque um bloco <strong>de</strong> buffer fica reservado<br />
para armazenar um bloco resultado da fusão.
Sort-merge Externo (Korth)<br />
1. Várias classificações temporárias são executadas:<br />
i = 0;<br />
repeat<br />
leia M blocos da relação, ou o resto da relação,<br />
aquilo que for menor;<br />
or<strong>de</strong>ne a parte da relação que está na memória;<br />
escreva os dados or<strong>de</strong>nados no arquivo temporário<br />
Ri;<br />
i = i + 1;<br />
until o fim da relação
Sort-merge Externo<br />
2. Faz-se o merge nos arquivos temporários. Suponha, por enquanto, que<br />
o número total <strong>de</strong> temporários, N, seja menor do que M, <strong>de</strong> forma que se<br />
consiga alocar um frame <strong>de</strong> página para um bloco <strong>de</strong> cada arquivo<br />
temporário e há espaço para manter uma página <strong>de</strong> resultado.<br />
leia um bloco <strong>de</strong> cada um dos N arquivos Ri, para uma página <strong>de</strong> buffer<br />
na memória;<br />
repeat<br />
escolha a primeira tupla (na or<strong>de</strong>m <strong>de</strong> classificação) entre todas as<br />
páginas do buffer;<br />
escreva a tupla no resultado e apague-a da página <strong>de</strong> buffer;<br />
if a página <strong>de</strong> buffer <strong>de</strong> qualquer temporário Ri está vazia and not fim<br />
<strong>de</strong> arquivo (Ri) then leia o próximo bloco <strong>de</strong> Ri na página <strong>de</strong> buffer;<br />
until todas as páginas <strong>de</strong> buffer estarem vazias;
Consi<strong>de</strong>rações<br />
<br />
<br />
<br />
<br />
<br />
Geralmente, se a relação é muito maior que a memória, po<strong>de</strong> haver<br />
M ou mais temporários gerados na primeira fase, e não será<br />
possível alocar um frame <strong>de</strong> página para cada temporário durante a<br />
fase <strong>de</strong> merge.<br />
Neste caso, faz-se a operação <strong>de</strong> merge em múltiplos passos.<br />
Como há memória suficiente para M-1 páginas <strong>de</strong> buffer <strong>de</strong> entrada,<br />
cada merge terá M-1 temporários como entrada.<br />
Funcionamento próximo sli<strong>de</strong>.<br />
Exemplo: Suponha agora que apenas um tupla caiba em um bloco<br />
(f = 1), e suponha que a memória mantém três frames <strong>de</strong> página no<br />
máximo. Durante os estágios <strong>de</strong> merge, dois frames <strong>de</strong> página são<br />
usados para entrada e um para o resultado.
Funcionamento<br />
<br />
<br />
<br />
<br />
<br />
<br />
Faz-se o merge sobre os primeiros M-1 temporários (conforme <strong>de</strong>scrito<br />
anteriormente) para obter um único temporário para o próximo passo;<br />
Faz-se o merge dos próximos M-1 temporários <strong>de</strong> forma semelhante, e<br />
assim por diante, até que todos os temporários iniciais tenham sido<br />
processados;<br />
Nesse ponto, o número <strong>de</strong> temporários foi reduzido a um fator <strong>de</strong> M–1;<br />
Se esse número reduzido <strong>de</strong> temporários ainda é maior ou igual a M, outro<br />
passo é dado, usando os temporários criados pelo passo anterior;<br />
Esses passos são repetidos tantas vezes quantas forem necessárias, até<br />
que o número <strong>de</strong> temporários seja menor que M;<br />
Então, um passo final gera o resultado classificado.
Exemplo<br />
g<br />
a<br />
d<br />
c<br />
b<br />
e<br />
r<br />
d<br />
m<br />
p<br />
d<br />
24<br />
19<br />
31<br />
33<br />
14<br />
16<br />
16<br />
21<br />
3<br />
2<br />
7<br />
a 14<br />
Relação<br />
inicial<br />
Criar<br />
temporários<br />
a 19<br />
d 31<br />
g 24<br />
b 14<br />
c 33<br />
e 16<br />
d 21<br />
m 3<br />
r 16<br />
a 14<br />
d 7<br />
p 2<br />
Temporários<br />
Passo 1:<br />
<strong>de</strong> merge<br />
a 19<br />
b 14<br />
c 33<br />
d 31<br />
e 16<br />
g 24<br />
a 14<br />
d 7<br />
d 21<br />
m 3<br />
p 2<br />
r 16<br />
Temporários<br />
Passo 2:<br />
<strong>de</strong> merge<br />
a 14<br />
a 19<br />
b 14<br />
c 33<br />
d 7<br />
d 21<br />
d 31<br />
e 16<br />
g 24<br />
m 3<br />
p 2<br />
r 16<br />
Resultado<br />
classificado
Número <strong>de</strong> Acessos a Disco<br />
<br />
Fase <strong>de</strong> or<strong>de</strong>nação:<br />
2 * b, on<strong>de</strong> b é o número <strong>de</strong> blocos do arquivo que está sendo<br />
or<strong>de</strong>nado<br />
Cada bloco b será acessado duas vezes, uma vez para leitura e<br />
outra vez para escrita<br />
<br />
Fase <strong>de</strong> fusão:<br />
2 * (b * log Dm nr), on<strong>de</strong> Dm é o número <strong>de</strong> subarquivos fundidos<br />
em cada fusão e nr é número <strong>de</strong> subarquivos.<br />
O 2 se dá por conta da leitura e escrita <strong>de</strong> cada bloco<br />
O termo interno ao parênteses conta quantas vezes cada bloco<br />
será analisado (lido e escrito)
Operação <strong>de</strong> Junção<br />
<br />
equi_join: <strong>de</strong>signação para uma junção da forma r |X| r.A=s.B<br />
s, em que A e B<br />
são atributos ou conjuntos <strong>de</strong> atributos das relações r e s, respectivamente.<br />
<br />
O exemplo usado será:<br />
<strong>de</strong>positante |X| cliente<br />
<br />
Suponha as seguintes informações <strong>de</strong> catálogo:<br />
n cliente<br />
= 10.000<br />
f cliente<br />
= 25, o que implica b cliente<br />
= 10.000/25 = 400<br />
n <strong>de</strong>positante<br />
= 5.000<br />
f <strong>de</strong>positante<br />
= 50, o que implica b <strong>de</strong>positante<br />
= 5.000/50 = 100<br />
V(nome_cliente, <strong>de</strong>positante) = 2.500, o que implica que, em média,<br />
cada cliente tem duas contas<br />
<br />
Suponha ainda que nome-cliente em <strong>de</strong>positante seja uma chave<br />
estrangeira vinda <strong>de</strong> cliente
Estimativa do Tamanho das Junções<br />
<br />
<br />
<br />
<br />
O produto cartesiano r X s contém n r * n s tuplas.<br />
Cada tupla <strong>de</strong>ste produto cartesiano ocupa s r + s s bytes.<br />
Assim po<strong>de</strong>mos calcular o tamanho do produto<br />
cartesiano.<br />
Para junção natural ... Sejam r(R) e s(S) duas relações:<br />
Se R ∩ S = ∅, então r |X| s é igual a r X s;<br />
Se R ∩ S é uma chave para R, então sabemos que<br />
uma tupla <strong>de</strong> s irá juntar-se com no máximo uma<br />
tupla <strong>de</strong> r. Assim, o número <strong>de</strong> tuplas na junção não<br />
é maior que o número <strong>de</strong> tuplas <strong>de</strong> s.<br />
Se R ∩ S é uma chave estrangeira para S – vinda <strong>de</strong><br />
R – , então o número <strong>de</strong> tuplas em r |X| s é<br />
exatamente igual ao número <strong>de</strong> tuplas em s.
Estimativa do Tamanho das Junções<br />
No exemplo: <strong>de</strong>positante |x| cliente,<br />
nome_cliente em <strong>de</strong>positante é uma chave<br />
estrangeira vinda <strong>de</strong> cliente.<br />
O tamanho do resultado é exatamente<br />
n <strong>de</strong>positante , que é 5.000;<br />
Com calcular o tamanho da junção quando R ∩<br />
S não é uma chave para R ou para S?
Estimativa do Tamanho das Junções<br />
<br />
<br />
<br />
Suponha que cada valor aparece com probabilida<strong>de</strong> igual.<br />
Consi<strong>de</strong>re uma tupla t <strong>de</strong> r e suponha R ∩ S = {A}.<br />
Estima-se que a tupla t produz<br />
n s / V(A,s)<br />
tuplas em r |X| s, uma vez que esse é o número médio <strong>de</strong> tuplas em s<br />
com um <strong>de</strong>terminado valor para os atributos A.<br />
<br />
Consi<strong>de</strong>rando todas as tuplas em r, estima-se que há<br />
n r * n s / V(A, s)<br />
tuplas em r |X| s.
Estimativa do Tamanho das Junções<br />
<br />
Observe que se invertermos os papéis <strong>de</strong> r e s, as estimativas<br />
resultariam em valores diferentes se V(A,r) ≠ V(A,s).<br />
<br />
Se isso acontece, há a probabilida<strong>de</strong> <strong>de</strong> haver tuplas pen<strong>de</strong>ntes<br />
que não participam da junção . A estimativa mais baixa será,<br />
provavelmente, a mais precisa.<br />
<br />
Técnicas mais sofisticadas para a estimativa do tamanho da junção<br />
<strong>de</strong>vem ser usadas se a hipótese <strong>de</strong> distribuição uniforme não pu<strong>de</strong>r<br />
ser consi<strong>de</strong>rada.
Estimativa do Tamanho das Junções<br />
<br />
Calculando a estimativa do tamanho para <strong>de</strong>positante<br />
|X| clientes, sem utilizar informações sobre chaves<br />
entrangeiras.<br />
<br />
Como V(nome_cliente, <strong>de</strong>positante) = 2.500 e<br />
V(nome_cliente, cliente) = 10.000, as duas estimativas<br />
que obtemos são:<br />
(10.000 * 5.000) / 2.500 = 20.000<br />
(5.000 * 10.000)/10.000 = 5.000
Junção <strong>de</strong> Laço Aninhado<br />
for each tupla t r in r do<br />
begin<br />
for each tupla ts in s do<br />
begin<br />
teste o par (tr, ts) para ver se<br />
satisfazem a condição <strong>de</strong> junção;<br />
se satisfizerem, adicione tr.ts ao<br />
resultado<br />
end<br />
end<br />
r: relação externa<br />
s: relação interna<br />
t r .t s : tupla obtida concatenando os valores dos atributos das tuplas t r e<br />
t s
Junção <strong>de</strong> Laço Aninhado<br />
<br />
<br />
<br />
<br />
<br />
Este algoritmo não requer índices e po<strong>de</strong> ser usado seja qual for a<br />
condição <strong>de</strong> junção.<br />
É um algoritmo caro já que examina todos os pares <strong>de</strong> tuplas nas duas<br />
relações. O número <strong>de</strong> pares <strong>de</strong> tuplas a ser consi<strong>de</strong>rado é n r<br />
* n s<br />
(para<br />
cada registro r tem-se que executar uma varredura completa em s).<br />
No pior caso o buffer po<strong>de</strong> manter apenas um bloco <strong>de</strong> cada relação, e um<br />
total <strong>de</strong> nr * bs + br acessos à blocos serão necessários (ou seja, os blocos<br />
da relação r (br) são lidos uma vez por ocasião do laço mais externo e, os<br />
blocos da relação s (bs) são lidos para cada vez que uma tupla <strong>de</strong> r precisa<br />
ser comparada com todas as tuplas <strong>de</strong> s por ocasião do laço mais interno)<br />
No melhor caso, há espaço suficiente para que ambas as relações caibam<br />
na memória, assim cada bloco terá <strong>de</strong> ser lido somente uma vez,<br />
conseqüentemente, apenas br + bs acessos à blocos serão necessários.<br />
Note que, se a relação menor couber completamente na memória, é melhor<br />
usar essa relação como a mais interna.
Exemplo<br />
<br />
<br />
<br />
<br />
<br />
Consi<strong>de</strong>re a junção natural <strong>de</strong> <strong>de</strong>positante e cliente.<br />
Suponha que não existem índices para estas relações.<br />
Suponha que <strong>de</strong>positante é a relação mais externa e<br />
cliente é a relação mais interna.<br />
5.000 * 10.000 tuplas serão examinadas.<br />
Pior caso: 5.000 * 400 + 100 = 2.000.100 acessos à<br />
disco.<br />
Melhor caso: 400 + 100 = 500 acessos à disco.<br />
Trocando as relações dos laços internos e externos:<br />
10.000 * 100 + 400: 1.000.400 acessos à disco.
Merge-junção (Korth)<br />
Sejam r(R) e s(S) relações cuja junção natural<br />
será calculada, e seja R ∩ S a notação para<br />
seus atributos em comum.<br />
Suponha que ambas as relações estejam<br />
classificadas nos atributos R ∩ S.<br />
A junção <strong>de</strong>stas relações po<strong>de</strong> ser feita por<br />
meio <strong>de</strong> um merge.
pr := en<strong>de</strong>reço da primeira tupla <strong>de</strong> r;<br />
ps := en<strong>de</strong>reço da primeira tupla <strong>de</strong> s;<br />
while (ps nulo and pr nulo) do<br />
begin<br />
ts := tupla para qual ps aponta;<br />
Ss := {ts};<br />
configure ps para apontar para a próxima tupla <strong>de</strong> s;<br />
acabou := false;<br />
while (not acabou and ps nulo) do<br />
begin<br />
ts’ := tupla para qual ps aponta;<br />
if (ts’[AtribJunção] = ts[AtribJunção])<br />
then begin<br />
Ss = Ss ∪ {ts’};<br />
configure ps para apontar para<br />
a próxima tupla <strong>de</strong> s;<br />
end<br />
else acabou := verda<strong>de</strong>iro;<br />
end;<br />
// permanece varrendo s enquanto as tuplas contiverem valores iguais para o<br />
atributo <strong>de</strong> junção, e as coloca em uma relação auxiliar.
tr := tupla para a qual pr aponta;<br />
while ( pr nulo and tr[AtribJunção] < ts[AtribJunção]) do<br />
begin<br />
configure pr para apontar para a próxima tupla<br />
<strong>de</strong> r;<br />
tr := tupla para qual pr aponta;<br />
end<br />
// percorre r enquanto não encontrar uma tupla com um valor no atributo <strong>de</strong><br />
junção igual ou maior ao valor no atributo <strong>de</strong> junção das tuplas <strong>de</strong> s que estão<br />
na relação auxiliar<br />
while (pr nulo and tr[AtribJunção] = ts[AtribJunção]) do<br />
begin<br />
for each rs in Ss do<br />
begin<br />
adicione ts.tr ao resultado;<br />
end<br />
configure pr para apontar para a próxima tupla<br />
<strong>de</strong> r;<br />
tr := tupla para a qual pr aponta;<br />
end;<br />
// encontrando a tupla <strong>de</strong> r que <strong>de</strong>ve ser juntar às tuplas <strong>de</strong> Ss, realiza a<br />
concatenação das tuplas, percorrendo r para ver se existem outras a serem<br />
concatenadas.<br />
End;
Merge-junção<br />
<br />
Suponha <strong>de</strong>positante |x| cliente. Com o atributo <strong>de</strong><br />
junção sendo o nome do cliente. As relações já estão<br />
or<strong>de</strong>nadas neste atributo.<br />
<br />
O custo da junção é 400 + 100 = 500 acessos à disco.<br />
<br />
Caso a exigência <strong>de</strong> S caber em memória principal não<br />
pu<strong>de</strong>r ser atendida, um algoritmo <strong>de</strong> junção à parte <strong>de</strong>ve<br />
ser executado para junção tr à Ss.<br />
<br />
Caso as relações não estejam or<strong>de</strong>nadas mas possuam<br />
índices, o merge-junção po<strong>de</strong> ser executado usando os<br />
índices.
Junção Sort-merge (Navathe)<br />
<br />
<br />
<br />
<br />
<br />
Se os registros <strong>de</strong> R e S estiverem classificados (or<strong>de</strong>nados)<br />
fisicamente pelos atributos <strong>de</strong> junção A e B, respectivamente,<br />
po<strong>de</strong>remos implementar a junção da maneira mais eficiente<br />
possível.<br />
Ambos os arquivos são varridos simultaneamente na or<strong>de</strong>m dos<br />
atributos <strong>de</strong> junção, fazendo a correspondência dos registros que<br />
possuem os mesmos valores para A e B.<br />
Se os arquivos não estiverem classificados, eles <strong>de</strong>verão ser<br />
classificados primeiro por meio <strong>de</strong> uma or<strong>de</strong>nação externa.<br />
Pares <strong>de</strong> blocos <strong>de</strong> arquivos são or<strong>de</strong>nadamente copiados para<br />
buffers <strong>de</strong> memória, e os registros <strong>de</strong> cada arquivos são varridos<br />
apenas uma vez (a menos que A e B não sejam atributos chaves e,<br />
nesse caso, o método precisa ser modificado).<br />
Índices proporcionam a capacida<strong>de</strong> <strong>de</strong> acessar (varrer) os registros<br />
na or<strong>de</strong>m dos atributos <strong>de</strong> junção, mas os registros <strong>de</strong> fato estão<br />
fisicamente espalhados pelos blocos do arquivo.
Junção Sort-merge<br />
A seguir, um esboço do algoritmo para Junção,<br />
Projeção, União, Interseção e Diferença por<br />
meio <strong>de</strong> sort-merge, quando R possui n tuplas e<br />
S possui m tuplas.
T R |X| A=B S<br />
Or<strong>de</strong>nar as n tuplas <strong>de</strong> R baseando-se no atributo A;<br />
Or<strong>de</strong>nar as m tuplas <strong>de</strong> S baseando-se no atributo B;<br />
Inicializar i 1 , j 1;<br />
Enquanto (i
{<br />
(* Ri[A] = Sj[B], portanto realizamos o output <strong>de</strong> uma tupla: resultado<br />
da junção*)<br />
output a tupla combinada em T;<br />
(* output outras tuplas correspon<strong>de</strong>ntes a Ri se houver*)<br />
l j + 1;<br />
enquanto (l
T π (R)<br />
Criar uma tupla t[] em T’ para cada tupla t <strong>de</strong> R;<br />
(*T’ contém o resultado da projeção ANTES da eliminação <strong>de</strong> duplicatas*)<br />
Se incluir uma chave <strong>de</strong> R<br />
então T T’;<br />
Senão<br />
{<br />
or<strong>de</strong>nar as tuplas <strong>de</strong> T’<br />
inicializar i 1, j 2;<br />
enquanto i
T R ∪ S<br />
Or<strong>de</strong>nar as tuplas <strong>de</strong> R e S utilizando os mesmos e únicos atributos <strong>de</strong> or<strong>de</strong>nação;<br />
Inicializar i 1; j 1;<br />
Enquanto (i
T R ∩ S<br />
Or<strong>de</strong>nar as tuplas <strong>de</strong> R e S utilizando os mesmos e únicos atributos <strong>de</strong> or<strong>de</strong>nação;<br />
Inicializar i 1; j 1;<br />
Enquanto (i
T R - S<br />
Or<strong>de</strong>nar as tuplas <strong>de</strong> R e S utilizando os mesmos e únicos atributos <strong>de</strong> or<strong>de</strong>nação;<br />
Inicializar i 1; j 1;<br />
Enquanto (i
Merge-junção - Consi<strong>de</strong>rações<br />
<br />
<br />
<br />
<br />
Em relação ao algoritmo apresentado por (Korth): O algoritmo exige<br />
que a relação auxiliar caiba na memória principal. Modificações no<br />
algoritmo <strong>de</strong>vem ser feitas caso essa exigência não possa ser<br />
atendida.<br />
Dado que as relações estão na or<strong>de</strong>m <strong>de</strong> classificação, as tuplas<br />
com o mesmo valor nos atributos <strong>de</strong> junção estão em or<strong>de</strong>m<br />
consecutiva. Assim, cada tupla na or<strong>de</strong>m <strong>de</strong> classificação precisa<br />
ser lida somente uma vez, e, como resultado, cada bloco também é<br />
lido somente uma vez.<br />
Em relação ao algoritmo do Navathe, tem-se que assumir que<br />
conjuntos <strong>de</strong> tuplas com o mesmo valor no atributo <strong>de</strong> junção<br />
precisam estar carregadas na memória ao mesmo tempo;<br />
Então, para ambos, o número <strong>de</strong> acessos à disco é igual à soma do<br />
número <strong>de</strong> blocos em ambos as relações, br + bs.
Implementação do Outer Join<br />
A junção externa po<strong>de</strong> ser obtida por meio da<br />
modificação dos algoritmos <strong>de</strong> junção, como a<br />
junção <strong>de</strong> laços aninhados, sort-merge ou <strong>de</strong><br />
junção hash;<br />
Ou, <strong>de</strong> forma alternativa e simplificada, por meio<br />
da execução <strong>de</strong> uma combinação <strong>de</strong><br />
operadores da álgebra relacional.
Implementação do Outer Join<br />
<br />
Por exemplo, consi<strong>de</strong>re a consulta:<br />
select unome, pnome, dnome<br />
from empregado left outer join <strong>de</strong>partamento on<br />
dno=dnumero;<br />
<br />
Essa operação <strong>de</strong> junção externa é equivalente à<br />
seguinte seqüência <strong>de</strong> operaçoes da álgebra relacional:
Implementação do Outer Join<br />
<br />
Calcule a junção interna entre as tabelas.<br />
Temp1 π unome, pnome, dnome (empregado |X| <strong>de</strong>partamento)<br />
<br />
Encontre as tuplas <strong>de</strong> empregado que não aparecem no<br />
resultado da junção.<br />
Temp2 π unome, pnome (empregado) - π unome, pnome (Temp1)
Implementação do Outer Join<br />
<br />
Complete cada tupla da relação Temp2 com valor null<br />
para o campo dnome.<br />
Temp2 Temp2 X ‘NULL’<br />
<br />
Aplique a operação union em Temp1 e Temp2 para<br />
produzir o resultado do left outer join.<br />
Resultado Temp1 υ Temp2<br />
<br />
O custo <strong>de</strong>ssa junção externa é a soma dos custos da<br />
junção interna, das projeções e da união realizadas.
Junções Complexas<br />
Junção com condição conjuntiva:<br />
r |X| θ ∧ θ ∧ ... θ s<br />
As junções nas condições individuais po<strong>de</strong>m ser<br />
resolvidas, por exemplo, pelo algoritmo <strong>de</strong> junção por laços<br />
aninhados:<br />
r |X| θ s, r |X| θ s, r |X| θ s e assim por diante.<br />
1 2 n<br />
1 2 n<br />
A junção global por ser realizada calculando, primeiro o<br />
resultado <strong>de</strong> uma <strong>de</strong>ssas junções mais simples e <strong>de</strong>pois<br />
testando (a esse resultado) as tuplas produzidas pelas<br />
outras junções.
Junções Complexas<br />
Junção com condição disjuntiva:<br />
r |X| θ ∨ θ ∨ ... θ s<br />
1 2 n<br />
Neste caso, a junção po<strong>de</strong> ser calculada como a<br />
união dos registros nas junções individuais.
Junções Complexas<br />
Suponha r 1 r 2 ... r n em que as junções estão expressas<br />
sem or<strong>de</strong>m. Com n = 3, há 12 or<strong>de</strong>ns <strong>de</strong> junção diferentes:<br />
r1 (r2 r3)<br />
r2 (r1 r3)<br />
r3 (r1 r2)<br />
r1 (r3 r2)<br />
r2 (r3 r1)<br />
r3 (r2 r1)<br />
(r2 r3) r1<br />
(r1 r3) r2<br />
(r1 r2) r3<br />
(r3 r2) r1<br />
(r3 r1) r2<br />
(r2 r1) r3
Junções Complexas<br />
Em geral, com n relações, há (2(n-1))! / (n-1)!<br />
Or<strong>de</strong>ns <strong>de</strong> junção diferentes. Exemplos: Com n<br />
= 5 o n° é 1680 e com n = 7, o n° é 665.280.<br />
Felizmente, não é necessário gerar todas as<br />
expressões equivalentes a uma <strong>de</strong>terminada<br />
expressão.<br />
Uma <strong>de</strong>svantagem da otimização baseada no<br />
custo é o custo da própria otimização.
Junções Complexas<br />
Duas árvores <strong>de</strong> consulta (junção) profundas<br />
à esquerda
Junções Complexas<br />
O otimizador escolherá a árvore que possuir o<br />
menor custo estimado.<br />
Com árvores profundas a esquerda, o filho à<br />
direita é consi<strong>de</strong>rado ser a relação interna, para<br />
o caso da execução <strong>de</strong> laços aninhados.<br />
A idéia-chave sob o ponto <strong>de</strong> vista do otimizador<br />
em relação à or<strong>de</strong>m das junções é encontrar<br />
uma or<strong>de</strong>m que irá reduzir o tamanho dos<br />
resultados intermediários.
Junções Complexas<br />
<br />
Consi<strong>de</strong>re uma junção envolvendo três relações:<br />
empréstimo |X| <strong>de</strong>positante |X| cliente<br />
<br />
Neste caso, além da escolha da estratégia para o processamento<br />
da junção, tem-se ainda que escolher qual junção calcular primeiro.<br />
Vejamos algumas estratégias:<br />
Estratégia 1: calcule a junção <strong>de</strong>positante |X| cliente usando<br />
qualquer técnicas. Usando o resultado intermediário, calcule:<br />
empréstimo |X| (<strong>de</strong>positante |X| cliente);<br />
Estratégia 2: faça como na Estratégia 1, mas calcule primeiro<br />
empréstimo |X| <strong>de</strong>positante, e então faça a junção do resultado<br />
com cliente.<br />
Outra or<strong>de</strong>m <strong>de</strong> junções po<strong>de</strong> ser feita.
Junções Complexas<br />
Estratégia 3: Em vez <strong>de</strong> executar duas junções,<br />
execute o par <strong>de</strong> junções, da seguinte forma:<br />
Construa dois índices:<br />
Um para o número_empréstimo em empréstimo;<br />
Um para o nome_cliente em cliente.<br />
Consi<strong>de</strong>re cada tupla t em <strong>de</strong>positante. Para cada t, procure<br />
as tuplas correspon<strong>de</strong>ntes em cliente e as tuplas<br />
correspon<strong>de</strong>ntes em empréstimo.<br />
Assim, cada tupla <strong>de</strong> <strong>de</strong>positante é examinada exatamente<br />
uma vez.<br />
O custo relativo <strong>de</strong>sse procedimento <strong>de</strong>pen<strong>de</strong> da<br />
forma como as relações estão armazenadas, da<br />
distribuição <strong>de</strong> valores <strong>de</strong>ntro das colunas e da<br />
presença <strong>de</strong> índices.
Eliminação <strong>de</strong> Duplicida<strong>de</strong><br />
<br />
<br />
<br />
<br />
<br />
Po<strong>de</strong>-se implementar a eliminação <strong>de</strong> duplicida<strong>de</strong> usando a<br />
classificação.<br />
As tuplas idênticas aparecerão adjacentes umas às outras após a<br />
classificação, e todas, exceto uma cópia, po<strong>de</strong>m ser removidas.<br />
No sort-merge, as duplicatas encontradas enquanto um temporário<br />
está sendo criado po<strong>de</strong>m ser removidas antes que ele seja escrito<br />
no disco, reduzindo, assim, o número <strong>de</strong> transferências <strong>de</strong> blocos.<br />
Assim, po<strong>de</strong>-se dizer que o custo <strong>de</strong> eliminar as duplicatas é o<br />
custo <strong>de</strong> classificar uma relação.<br />
Devido ao custo relativamente alto da eliminação <strong>de</strong> duplicida<strong>de</strong>, as<br />
linguagens <strong>de</strong> consulta comerciais exigem um pedido explícito do<br />
usuário para remover duplicatas; caso contrário, as duplicatas são<br />
mantidas.
Operação <strong>de</strong> Projeção<br />
<br />
Po<strong>de</strong>-se executar a projeção por meio da execução da<br />
projeção em cada tupla, resultando uma relação que<br />
po<strong>de</strong>ria ter registros duplicados, e então, remover os<br />
registros duplicados.<br />
<br />
Se os atributos na lista <strong>de</strong> projeção incluem as chaves<br />
da relação (primária e/ou candidatas), nenhuma<br />
duplicata existirá.<br />
<br />
O tamanho <strong>de</strong> um projeção da forma Π A (r) é calculado<br />
como V(A,r), uma vez que a projeção elimina as<br />
duplicatas.
Transformações <strong>de</strong> Expressões<br />
Relacionais<br />
Uma consulta po<strong>de</strong> ser expressa <strong>de</strong> diversas<br />
maneiras diferentes, com diferentes custos <strong>de</strong><br />
avaliação.<br />
Equivalência <strong>de</strong> Expressões;<br />
Regras <strong>de</strong> Equivalência;<br />
Exemplos <strong>de</strong> Transformações;<br />
Or<strong>de</strong>namento <strong>de</strong> Junções.
<strong>Otimização</strong> Algébrica<br />
Objetivo do passo <strong>de</strong> transformação<br />
Entrada: Árvore da consulta inicial;<br />
Saída: Árvore da consulta otimizada (po<strong>de</strong><br />
manter a mesma árvore).<br />
Base:<br />
Regras <strong>de</strong> equivalência algébrica<br />
Devem ser conhecidas pelo otimizador para que<br />
possam ser geradas transformações válidas.<br />
Algoritmo <strong>de</strong> otimização algébrica<br />
Indica a or<strong>de</strong>m <strong>de</strong> aplicação das regras e <strong>de</strong> outros<br />
processamentos <strong>de</strong> otimização.
Equivalência <strong>de</strong> Expressões<br />
Consi<strong>de</strong>rando as tabelas a seguir e suas<br />
instâncias, encontre os nomes <strong>de</strong> todos os<br />
clientes que possuem uma conta em qualquer<br />
agência localizada no Brooklyn.<br />
π nome_cliente ( σ cida<strong>de</strong>_agência = “Brooklyn” (agência |X| (conta |X|<br />
<strong>de</strong>positante)))<br />
<br />
Para resolver esta expressão, seguindo a forma como<br />
ela está escrita, é necessário criar uma relação<br />
intermediária gran<strong>de</strong> (a junção das três relações, como<br />
posto no sli<strong>de</strong> 86).
710000<br />
Brooklyn<br />
Brighton<br />
370000<br />
Rye<br />
North Town<br />
30000<br />
Bennington<br />
Pownal<br />
8000000<br />
Horseneck<br />
Round Hill<br />
40000<br />
Horseneck<br />
Mianus<br />
170000<br />
Horseneck<br />
Perrydige<br />
210000<br />
Palo Alto<br />
Redwood<br />
900000<br />
Brooklyn<br />
Downtown<br />
fundos<br />
cida<strong>de</strong>_agência<br />
nome_agência<br />
Stamford<br />
Walnut<br />
Green<br />
Brooklyn<br />
Senator<br />
Brooks<br />
Woodsi<strong>de</strong><br />
Sand Hill<br />
Glenn<br />
Palo Alto<br />
Alma<br />
Johnson<br />
Pittsfield<br />
Spring<br />
Adams<br />
Princeton<br />
Nassau<br />
Williams<br />
Stamford<br />
Putnam<br />
Turner<br />
Pittfield<br />
Park<br />
Lindsay<br />
Rye<br />
North<br />
Curry<br />
Harrison<br />
Main<br />
Hayes<br />
Rye<br />
North<br />
Smith<br />
Harrison<br />
Main<br />
Jones<br />
cida<strong>de</strong>_cliente<br />
rua_cliente<br />
nome_cliente<br />
A-222<br />
Lindsay<br />
A-217<br />
Jones<br />
A-201<br />
Johnson<br />
A-305<br />
Turner<br />
A-102<br />
Hayes<br />
A-215<br />
Smith<br />
A-101<br />
Johnson<br />
número_conta<br />
nome_cliente<br />
agência<br />
<strong>de</strong>positante<br />
cliente<br />
750<br />
A-217<br />
Bringhton<br />
700<br />
A-222<br />
Redwood<br />
900<br />
A-201<br />
Bringhton<br />
350<br />
A-305<br />
Round Hill<br />
400<br />
A-102<br />
Perryridge<br />
700<br />
A-215<br />
Mianus<br />
500<br />
A-101<br />
Downtown<br />
saldo<br />
número_conta<br />
nome_agência<br />
conta
Junção (conta |X| <strong>de</strong>positante)<br />
nome_agência<br />
número_conta<br />
saldo<br />
nome_cliente<br />
número_conta<br />
Downtown<br />
A-101<br />
500<br />
Johnson<br />
A-101<br />
Mianus<br />
A-215<br />
700<br />
Smith<br />
A-215<br />
Perryridge<br />
A-102<br />
400<br />
Hayes<br />
A-102<br />
Round Hill<br />
A-305<br />
350<br />
Turner<br />
A-305<br />
Bringhton<br />
A-201<br />
900<br />
Johson<br />
A-201<br />
Redwood<br />
A-222<br />
700<br />
Lindsay<br />
A-222<br />
Bringhton<br />
A-217<br />
750<br />
Jones<br />
A-217<br />
Junção (agência |X| (conta |X| <strong>de</strong>positante))<br />
nome_agência<br />
número_conta<br />
saldo<br />
nome_cliente<br />
nome_agência<br />
cida<strong>de</strong>_agência<br />
fundos<br />
Downtown<br />
Mianus<br />
Perryridge<br />
Round Hill<br />
Bringhton<br />
Redwood<br />
A-101<br />
A-215<br />
A-102<br />
A-305<br />
A-201<br />
A-222<br />
500<br />
700<br />
400<br />
350<br />
900<br />
700<br />
Johnson<br />
Smith<br />
Hayes<br />
Turner<br />
Johson<br />
Lindsay<br />
Downtown<br />
Mianus<br />
Perrydige<br />
Round Hill<br />
Brighton<br />
Redwood<br />
Brooklyn<br />
Horseneck<br />
Horseneck<br />
Horseneck<br />
Brooklyn<br />
Palo Alto<br />
900000<br />
40000<br />
170000<br />
8000000<br />
710000<br />
210000<br />
Bringhton<br />
A-217<br />
750<br />
Jones<br />
Brighton<br />
Brooklyn<br />
710000
Equivalência <strong>de</strong> Expressões<br />
<br />
Entretanto, somente as tuplas que pertencem às<br />
agências localizadas no “Brooklyn” são interessantes.<br />
<br />
Reescrevendo a consulta, consegue-se eliminar a<br />
necessida<strong>de</strong> <strong>de</strong> consi<strong>de</strong>rar as tuplas que não têm<br />
cida<strong>de</strong>_agência = “Brooklyn”, reduzindo o tamanho do<br />
resultado intermediário:<br />
π nome_cliente (( σ cida<strong>de</strong>_agência = “Brooklyn” (agência)) |X| (conta |X|<br />
<strong>de</strong>positante))
Junção (conta |X| <strong>de</strong>positante)<br />
nome_agência<br />
número_conta<br />
saldo<br />
nome_cliente<br />
número_conta<br />
Downtown<br />
A-101<br />
500<br />
Johnson<br />
A-101<br />
Mianus<br />
Perryridge<br />
Round Hill<br />
Bringhton<br />
Redwood<br />
Bringhton<br />
A-215<br />
A-102<br />
A-305<br />
A-201<br />
A-222<br />
A-217<br />
700<br />
400<br />
350<br />
900<br />
700<br />
750<br />
Smith<br />
Hayes<br />
Turner<br />
Johnson<br />
Lindsay<br />
Jones<br />
A-215<br />
A-102<br />
A-305<br />
A-201<br />
A-222<br />
A-217<br />
σ cida<strong>de</strong>_agência = “Brooklyn”<br />
(agência)<br />
nome_agência<br />
Downtown<br />
Brighton<br />
cida<strong>de</strong>_agência<br />
Brooklyn<br />
Brooklyn<br />
fundos<br />
900000<br />
710000<br />
(σ cida<strong>de</strong>_agência = “Brooklyn”<br />
(agência)) |X| (conta |X| <strong>de</strong>positante)<br />
nome_agência<br />
número_conta<br />
saldo<br />
nome_cliente<br />
cida<strong>de</strong>_agência<br />
fundos<br />
Downtown<br />
A-101<br />
500<br />
Johnson<br />
Brooklyn<br />
900000<br />
Bringhton<br />
A-201<br />
900<br />
Johnson<br />
Brooklyn<br />
710000<br />
Bringhton<br />
A-217<br />
750<br />
Jones<br />
Brooklyn<br />
710000
Equivalência <strong>de</strong> Expressões<br />
π nome_cliente<br />
π nome_cliente<br />
σ cida<strong>de</strong>_agência = “Brooklyn”<br />
|X|<br />
|X|<br />
σ cida<strong>de</strong>_agência = “Brooklyn”<br />
|X|<br />
agência<br />
|X|<br />
agência<br />
conta<br />
<strong>de</strong>positante<br />
conta<br />
<strong>de</strong>positante<br />
(a) Árvore da expressão inicial<br />
(b) Árvore da expressão transformada
Equivalência <strong>de</strong> Expressões<br />
<br />
Dada uma expressão <strong>de</strong> álgebra relacional, é função do<br />
otimizador <strong>de</strong> consulta propor um plano <strong>de</strong> avaliação da<br />
consulta que gere o mesmo resultado da expressão<br />
fornecida e que seja uma maneira menos onerosa <strong>de</strong><br />
gerar o resultado (ou que, pelo menos, não seja muito<br />
mais cara que a maneira mais barata).<br />
<br />
Para isso o otimizador precisa gerar planos alternativos<br />
que produzam o mesmo resultado da expressão dada e<br />
escolher o menos caro.
Regras <strong>de</strong> Equivalência Algébrica<br />
<br />
<br />
<br />
<br />
Uma regra <strong>de</strong> equivalência diz que expressões <strong>de</strong> duas formas são<br />
equivalentes se po<strong>de</strong>mos transformar uma na outra preservando a<br />
equivalência.<br />
Preservar a equivalência significa que as relações geradas pelas<br />
duas expressões têm o mesmo conjunto <strong>de</strong> atributos e contêm o<br />
mesmo conjunto <strong>de</strong> tuplas, embora seus atributos possam estar<br />
or<strong>de</strong>nados <strong>de</strong> forma diferente.<br />
As regras <strong>de</strong> equivalência são usadas pelo otimizador para<br />
transformar expressões em outras logicamente equivalentes.<br />
Assuma que:<br />
θ: <strong>de</strong>nota predicados;<br />
L: <strong>de</strong>notas listas <strong>de</strong> atributos;<br />
E: <strong>de</strong>nota expressões da álgebra relacional.
Regras <strong>de</strong> Equivalência Algébrica<br />
1. Operações <strong>de</strong> seleção conjuntivas po<strong>de</strong>m ser quebradas<br />
em uma seqüência <strong>de</strong> seleções individuais (cascata <strong>de</strong> σ).<br />
σ θ1 ∧ θ2 (E) = σ θ1 (σ θ2 (E))<br />
2. Operações <strong>de</strong> seleção são comutativas.<br />
σ θ1 (σ θ2 (E)) = σ θ2 (σ θ1 (E))<br />
3. Apenas as operações finais em uma seqüência <strong>de</strong><br />
operações <strong>de</strong> projeção são necessárias, as outras po<strong>de</strong>m<br />
ser omitidas (cascata <strong>de</strong> π).<br />
π L1 (π L2 (...(π Ln (E))...)) = π L1 (E)
Regras <strong>de</strong> Equivalência Algébrica<br />
4. Seleções po<strong>de</strong>m ser combinadas com produtos<br />
cartesianos e junções teta.<br />
σ θ (E1 X E2) = E1 |X| θ E2<br />
5. Operações <strong>de</strong> junção teta são comutativas.<br />
E1 |X| θ E2 = E2 |X| θ E1<br />
6. Operações <strong>de</strong> junção natural são associativas.<br />
(E1 |X| E2) |X| E3 = E1 |X| (E2 |X| E3)<br />
7. Comutativida<strong>de</strong> <strong>de</strong> π e |X| (ou X): similar à 6.
Regras <strong>de</strong> Equivalência Algébrica<br />
8. Comutativida<strong>de</strong> <strong>de</strong> Operações <strong>de</strong> Conjunto<br />
R ∪ S ≡ S ∪ R<br />
R ∩ S ≡ S ∩ R<br />
e<br />
- A operação “⎯” não é comutativa.<br />
9. Associativida<strong>de</strong> <strong>de</strong> Operações Produtórias e <strong>de</strong> Conjunto<br />
(“οX”)<br />
(R “οX” S) “οX” T ≡ R “οX” (S “οX” T)<br />
- Por “οX” entenda-se: X ou X θ ou ou ∪ ou ∩.<br />
- A operação “⎯” não é associativa.
Regras <strong>de</strong> Equivalência Algébrica<br />
9. Associativida<strong>de</strong> <strong>de</strong> Operações Produtórias e <strong>de</strong> Conjunto<br />
(“οX”)<br />
(R “οX” S) “οX” T ≡ R “οX” (S “οX” T)<br />
Observação: Predicados <strong>de</strong> junção <strong>de</strong>vem ser<br />
<strong>de</strong>vidamente ajustados na associativida<strong>de</strong> <strong>de</strong> operações<br />
produtórias. Exemplo: Seja θ 1 um predicado sobre<br />
atributos <strong>de</strong> R e S, θ 2 um predicado sobre atributos <strong>de</strong> S<br />
e T, e θ 3 um predicado sobre atributos <strong>de</strong> R e T. Então,<br />
(R “X” θ1 S) “X” θ2 ∧ θ3 T ≡ R “X” θ1 ∧ θ3 (S “X” θ2 T)
Regras <strong>de</strong> Equivalência Algébrica<br />
10. Comutativida<strong>de</strong> <strong>de</strong> Seleção e Operações <strong>de</strong> Conjunto<br />
(“ο”)<br />
σ c (R “ο” S) ≡ (σ c (R)) “ο” (σ c (S))<br />
- Por “ο” entenda-se: ∪ ou ∩ ou ⎯<br />
11. Comutativida<strong>de</strong> <strong>de</strong> Projeção e União<br />
π listaAtributos (R ∪ S) ≡ (π listaAtributos (R)) ∪ (π listaAtributos (S))<br />
- As operações “⎯” e “∩” não são comutativas.
Regras <strong>de</strong> Equivalência Algébrica<br />
12. Fusão <strong>de</strong> Seleções e Operações Produtórias<br />
(a) σ c (R X S) ≡ R X θ = σ c S<br />
(b) σ c (R X S) ≡ R S<br />
c<br />
ou<br />
ou<br />
(c) R X θ = σ c S ≡ R S<br />
c
Exemplos <strong>de</strong> Transformações<br />
Exemplo 1:<br />
π nome_cliente ( σ cida<strong>de</strong>_agência = “Brooklyn” (agência |X| (conta |X| <strong>de</strong>positante)))<br />
π nome_cliente (( σ cida<strong>de</strong>_agência = “Brooklyn” (agência)) |X| (conta |X| <strong>de</strong>positante))<br />
Exemplo 2:<br />
π nome_cliente ( σ cida<strong>de</strong>_agência = “Brooklyn” ∧ saldo > 1000 (agência |X| (conta |X| <strong>de</strong>positante)))<br />
π nome_cliente ( σ cida<strong>de</strong>_agência = “Brooklyn” ∧ saldo > 1000 ((agência |X| conta) |X| <strong>de</strong>positante))<br />
π nome_cliente ( σ cida<strong>de</strong>_agência = “Brooklyn” ∧ saldo > 1000 (agência |X| conta)) |X| <strong>de</strong>positante)<br />
Exemplo3: Examinando uma subexpressão interna:<br />
σ cida<strong>de</strong>_agência = “Brooklyn” ∧ saldo > 1000 (agência |X| conta))<br />
σ cida<strong>de</strong>_agência = “Brooklyn” (σ saldo > 1000 (agência |X| conta))<br />
σ cida<strong>de</strong>_agência = “Brooklyn” (agência) |X| σ saldo > 1000 (conta)<br />
Exemplo 4: Usando projeções<br />
π nome_cliente (( σ cida<strong>de</strong>_agência = “Brooklyn” (agência) |X| conta) |X| <strong>de</strong>positante)<br />
π nome_cliente ((π número_conta (( σ cida<strong>de</strong>_agência = “Brooklyn” (agência)) |X| conta)) |X| <strong>de</strong>positante)
Or<strong>de</strong>nando Junções<br />
<br />
Uma boa or<strong>de</strong>nação <strong>de</strong> operações <strong>de</strong> junção é importante para reduzir o tamanho<br />
dos resultados intermediários.<br />
π nome_cliente (( σ cida<strong>de</strong>_agência = “Brooklyn” (agência)) |X| conta |X| <strong>de</strong>positante)<br />
<br />
Po<strong>de</strong>ríamos executar conta |X| <strong>de</strong>positante primeiro e, então, fazer a junção do<br />
resultado com:<br />
σ cida<strong>de</strong>_agência = “Brooklyn” (agência).<br />
<br />
Entretanto, conta |X| <strong>de</strong>positante provavelmente é uma relação gran<strong>de</strong>, já que contém<br />
uma tupla para cada conta. Em contrapartida,<br />
é, provavelmente, uma relação pequena.<br />
σ cida<strong>de</strong>_agência = “Brooklyn” (agência) |X| conta<br />
<br />
Para confirmar, observe que, como o banco tem um gran<strong>de</strong> número <strong>de</strong> agências<br />
amplamente distribuídas, é provável que apenas uma fração pequena dos clientes do<br />
banco tenha conta em agências localizadas no Brooklyn. Assim, a expressão prece<strong>de</strong>nte<br />
resulta em uma tupla para cada conta mantida em uma agência localizada no Brooklyn.<br />
Então, a relação temporária que precisa ser armazenada é menor que a que se obteria<br />
fazendo primeiro conta |X| <strong>de</strong>positante.
<strong>Otimização</strong> Heurística<br />
<br />
Uma árvore <strong>de</strong> consulta po<strong>de</strong> ser transformada passo a<br />
passo em outra árvore <strong>de</strong> consulta mais eficiente.<br />
<br />
Entretanto é preciso assegurar que os passos <strong>de</strong><br />
transformação sempre levem a uma árvore <strong>de</strong> consulta<br />
equivalente.<br />
<br />
Determinadas regras <strong>de</strong> transformação preservam essa<br />
equivalência.
Algoritmo <strong>de</strong> <strong>Otimização</strong> Algébrica<br />
<br />
<br />
Passo1:<br />
A regra 1, ao ser usada, quebra quaisquer<br />
operações SELECT com condições conjuntivas em<br />
uma cascata <strong>de</strong> operações SELECT, permitindo<br />
um maior grau <strong>de</strong> liberda<strong>de</strong> para transferir<br />
operações SELECT para ramos diferentes e<br />
abaixo na árvore.<br />
Passo2:<br />
Usando as regras 2, 4, 6, e 10 relativas à<br />
comutativida<strong>de</strong> do SELECT com outras operações,<br />
move cada operação SELECT o mais longe para<br />
baixo na árvores, que forem permitido pelos<br />
atributos envolvidos na condição <strong>de</strong> seleção.
Algoritmo <strong>de</strong> <strong>Otimização</strong> Algébrica<br />
Passo 3:<br />
Usando as regras 5 e 9, relativas à comutativida<strong>de</strong> e<br />
associativida<strong>de</strong> <strong>de</strong> operações binárias, rearraja os<br />
nós folhas da árvore utilizando o seguinte critério:<br />
<br />
Posiciona as relações do nó folha com operações <strong>de</strong><br />
SELECT mais restritivas, <strong>de</strong> forma que elas possam ser<br />
executadas o quanto antes.
Algoritmo <strong>de</strong> <strong>Otimização</strong> Algébrica<br />
Passo 4:<br />
Usando a regra 12, combina uma operação <strong>de</strong><br />
PRODUTO CARTESIANO com uma operação<br />
SELECT subseqüente na árvore, gerando uma<br />
operação JOIN se a condição representa uma<br />
condição <strong>de</strong> junção.<br />
Passo 5:<br />
Usando as regras 3, 4, 7 e 11, relativas à cascata <strong>de</strong><br />
PROJECT e à comutação <strong>de</strong> PROJECT com outras<br />
operações, quebra e transfere as listas <strong>de</strong> atributos<br />
<strong>de</strong> projeção para baixo na árvore.
Algoritmo <strong>de</strong> <strong>Otimização</strong> Algébrica<br />
Passo 6:<br />
I<strong>de</strong>ntifica subárvores que representam grupos <strong>de</strong><br />
operações que po<strong>de</strong>m ser executadas por um único<br />
algoritmo (execuções em pipeline).<br />
Como exemplo, consi<strong>de</strong>re a consulta:<br />
select unome<br />
from Empregado, Trabalha_Em, Projeto<br />
where pnome = ‘Aquarius’ and pnumero = pno and<br />
essn = ssn and datanasc > ’31-12-1957’;
Exemplo:<br />
Passos na conversão <strong>de</strong> uma<br />
árvore <strong>de</strong> consulta durante a<br />
otimização heurística. (a) Árvore<br />
<strong>de</strong> consulta inicial (canônica) para<br />
a consulta. (b) Transferência das<br />
operações SELECT para baixo na<br />
árvore <strong>de</strong> consulta. (continua)
Exemplo:<br />
Passos na conversão <strong>de</strong> uma<br />
árvore <strong>de</strong> consulta durante a<br />
otimização heurística. (c)<br />
Aplicação, em primeiro lugar, da<br />
operação SELECT mais restritiva.<br />
(d) Substituindo PRODUTO<br />
CARTESIANO e SELECT por<br />
operações JOIN.
Exemplo:<br />
Passos na conversão <strong>de</strong> uma<br />
árvore <strong>de</strong> consulta durante a<br />
otimização heurística. (e)<br />
Transferência das operações<br />
PROJECT para baixo na árvore<br />
<strong>de</strong> consulta.
A Escolha <strong>de</strong> Planos <strong>de</strong> Avaliação<br />
<br />
<br />
A geração <strong>de</strong> expressões é apenas parte do processo<br />
<strong>de</strong> otimização <strong>de</strong> consultas.<br />
Um plano <strong>de</strong> avaliação <strong>de</strong>fine exatamente qual algoritmo<br />
será usado para cada operação e como a execução das<br />
operações é coor<strong>de</strong>nada.<br />
π nome_cliente<br />
(classificar para remover duplicatas)<br />
|X| (hash-junção)<br />
|X| (merge_junção) <strong>de</strong>positante<br />
σ cida<strong>de</strong>_agência = Brooklyn<br />
σ saldo < 1000<br />
σ(use o índice 1) (use a varredura linear)<br />
agência<br />
conta
Interação <strong>de</strong> Técnicas <strong>de</strong> Avaliação<br />
<br />
<br />
<br />
<br />
Um modo <strong>de</strong> escolher um plano <strong>de</strong> avaliação para uma expressão <strong>de</strong><br />
consulta é simplesmente escolher o algoritmo mais barato para avaliar<br />
cada operação. E, olhando para os níveis da árvore, escolhe-se qualquer<br />
or<strong>de</strong>namento para a execução das operações, <strong>de</strong>s<strong>de</strong> que as operações<br />
nas camadas mais baixas da árvore sejam executadas antes das<br />
operações nas camadas mais altas.<br />
Entretanto, essa estratégia po<strong>de</strong> não ser a melhor. Embora uma mergejunção,<br />
sob certas condições, possa ser mais cara que uma hash-junção,<br />
ela consegue prover um resultado classificado que torna mais barata a<br />
avaliação <strong>de</strong> uma operação posterior (como uma eliminação <strong>de</strong> duplicatas<br />
ou uma outra merge-junção).<br />
Para escolher o melhor algoritmo global, se <strong>de</strong>ve consi<strong>de</strong>rar até mesmo os<br />
algoritmos que não são os melhores para as operações individuais.<br />
Abordagens para escolha do melhor plano <strong>de</strong> avaliação:<br />
Baseada no custo <strong>de</strong> todos os planos;<br />
Heurística.
<strong>Otimização</strong> Baseada em Custo<br />
<br />
O otimizador baseado no custo gera uma faixa <strong>de</strong> planos <strong>de</strong><br />
avaliação a partir <strong>de</strong> uma <strong>de</strong>terminada consulta usando as regras<br />
<strong>de</strong> equivalência e escolhe aquele <strong>de</strong> menor custo.<br />
<br />
Para uma consulta complexa, o número <strong>de</strong> planos diferentes por ser<br />
muito gran<strong>de</strong>.<br />
<br />
Diferentes técnicas po<strong>de</strong>m ser usadas para diminuir o número <strong>de</strong><br />
planos a serem avaliados:<br />
Quando se examina os planos para uma expressão, é possível<br />
terminar após examinar apenas uma parte da expressão, se for<br />
<strong>de</strong>terminado que o plano mais barato para aquela parte já está<br />
mais caro que a avaliação mais barata para uma expressão<br />
completa já examinada.
<strong>Otimização</strong> Heurística<br />
Regras heurística são utilizadas para<br />
transformar consultas da álgebra relacional;<br />
No otimizador heurístico, a estratégia mais<br />
eficiente para cada operação é escolhida.
Estrutura dos Otimizadores <strong>de</strong> Consulta<br />
<br />
<br />
<br />
<br />
Na prática, a maioria dos otimizadores <strong>de</strong> consultas combinam<br />
estratégias baseadas em custo com estratégias heurísticas.<br />
Alguns SGBDs consi<strong>de</strong>ram a probabilida<strong>de</strong> <strong>de</strong> já haver no<br />
buffer a página que contém o dado que se está precisando<br />
(isso é mais um dado estatístico e po<strong>de</strong> resultar em estimativas<br />
<strong>de</strong> custos menores).<br />
É possível usar a estratégia heurísticas <strong>de</strong> dividir as consultas<br />
em sub-consultas que não utilizem mais <strong>de</strong> duas tabelas.<br />
Transforma consultas em SQL em outras consultas em SQL<br />
que utilizam junções on<strong>de</strong> for possível facilita a transformação<br />
da consulta em SQL em uma consulta em álgebra relacional.
<strong>Otimização</strong> em PostgreSQL<br />
<br />
Seguem alguns breves comentários sobre otimização e<br />
<strong>de</strong>sempenho em PostgreSQL.<br />
<br />
Para otimização e <strong>de</strong>sempenho, PostgreSQL utiliza-se<br />
dos comandos vacuum, analyze e explain.
<strong>Otimização</strong> em PostgreSQL<br />
VACUUM O comando Vacuum tanto recupera<br />
espaço em disco, quanto otimiza o <strong>de</strong>sempenho do<br />
banco e previne contra perda <strong>de</strong> dados muito antigos,<br />
<strong>de</strong>vido ao recomeço do ID das transações. Portanto,<br />
<strong>de</strong>ve ser utilizado constantemente, pois também<br />
atualiza as estatísticas dos dados utilizados pelo<br />
planejador <strong>de</strong> comandos.<br />
Na linha <strong>de</strong> comando:<br />
vacuumdb -faze ou vacuumdb -fazq.
<strong>Otimização</strong> em PostgreSQL<br />
<br />
Exemplo <strong>de</strong> uso do vacuum:<br />
Em uma tabela:<br />
VACUUM VERBOSE ANALYZE nometabela;<br />
Em um banco <strong>de</strong> dados completo:<br />
Somente VACUUM ou VACUUM FULL ANALYZE;<br />
<br />
Recomendações:<br />
Para a maioria das instalações executar o comando VACUUM<br />
ANALYZE para todo o banco <strong>de</strong> dados, <strong>de</strong> vez em quando, em horário<br />
<strong>de</strong> pouca utilização.<br />
Quando for excluída a maioria dos registros <strong>de</strong> uma tabela, sugere-se a<br />
execução do comando VACUUM FULL. Contudo, este comando gera<br />
um forte bloqueio nas tabelas em que é executado.
<strong>Otimização</strong> em PostgreSQL<br />
ANALYZE O comando ANALYZE coleta estatísticas<br />
sobre o conteúdo das tabelas do banco <strong>de</strong> dados e armazena os<br />
resultados na tabela do sistema pg_statistic. Posteriormente, o<br />
planejador <strong>de</strong> comandos utiliza estas estatísticas para ajudar a<br />
<strong>de</strong>terminar o plano <strong>de</strong> execução mais eficiente. Caso estas<br />
estatísticas não sejam atualizadas com freqüência, po<strong>de</strong> ser<br />
comprometido o <strong>de</strong>sempenho do banco <strong>de</strong> dados, por uma<br />
<br />
escolha errada do plano <strong>de</strong> execução dos comandos.<br />
Normalmente, operações DELETE ou UPDATE não removem<br />
os registros automaticamente (somente após a execução do<br />
vacuum isso acontece).
<strong>Otimização</strong> em PostgreSQL<br />
No PostgreSQL, po<strong>de</strong> ser utilizado o comando<br />
explain para ver o plano (conjunto executável <strong>de</strong><br />
instruções) criado pelo sistema para qualquer<br />
comando.<br />
O comando explain traz informações sobre custo,<br />
como tamanho da tupla, custo estimado <strong>de</strong> execução,<br />
entre outros.<br />
Exemplo <strong>de</strong> uso do explain:<br />
EXPLAIN SELECT * FROM NOMETABELA;
<strong>Otimização</strong> em PostgreSQL<br />
EXPLAIN ANALYZE Executa a consulta e<br />
mostra o tempo real acumulado <strong>de</strong>ntro <strong>de</strong> cada<br />
nó do plano <strong>de</strong> execução, junto com os custos<br />
estimados que o comando explain simples<br />
mostraria.
Referências Bibliográficas<br />
<br />
Sistemas <strong>de</strong> Banco <strong>de</strong> Dados. (Cap. 12) Abraham<br />
Silberchatz, Henry F. Korth e S. Sudarshan. 3ª Edição.<br />
Makron Books, 1999.<br />
<br />
Sistemas <strong>de</strong> Banco <strong>de</strong> Dados. (Cap. 15) Ramez<br />
Elsmari, Shamkant B. Navathe. 4ª Edição. Pearson<br />
Addison Wesley, 2005.<br />
<br />
Manuais do PostgreSQL.