Resumo SBD
Short Description
Download Resumo SBD...
Description
Cap. 4 – SQL As linguagens formais proporcionam uma notação concisa para a representação de consultas, mas os bancos de dados comerciais precisam de uma linguagem de consulta mais fácil para o utilizador – a SQL – combinação de construtores em álgebra e cálculo relacional. Ela é mais que simples linguagem de consulta pois permite também definir estruturas de dados, modificar dados e especificação de restrições de segurança. 4.1. Histórico SQL=Structured Query Language – Linguagem de Consulta Estruturada. Actual SQL92. A linguagem SQL tem diversas partes: Linguagem de definição de dados – DDL Linguagem interactiva de manipulação de dados –DML: baseada na álgebra relacional e no cálculo relacional de tuplos. Incorporação DML Definição de visões Autorização Integridade Controle de transacções Neste capítulo cobre-se a DML interactiva e os recursos DML básicos da SQL. Usaremos como exemplo empresa com os seguintes esquemas de relações: Esquema_agência=(nome_agência, cidade_agência, fundos) Esquema_cliente=(nome_cliente, rua_cliente, cidade_cliente) Esquema_empréstimo=(nome_agência, número_empréstimo, total) Esquema_devedor=(nome_cliente, número_empréstimo) Esquema_conta=(nome_agência, número_conta, saldo) Esquema_depositante=(nome_cliente, número_conta) 4.2. Estruturas básicas Um banco de dados relacional consiste numa colecção de relações, cada uma designada por um único nome. A SQL permite o uso de valores nulos para indicar valores desconhecidos ou inexistentes. Há cláusulas que permitem ao utilizador especificar quais os atributos não poderão receber valores nulos. A estrutura básica de uma expressão em SQL consiste em três cláususlas: selct, from e where select – corresponde à operação de projecção from – corresponde ao produto cartesiano where – corresponde à selecção do predicado ex: select A1, A2, ..., An from r1, r2, ... , rm where P é equivalente a πA1,A2,...,An (σP(r1xr2x...xrm) Se a cláusula where for omitida, o predicado P é verdadeiro. Diferentemente da álgebra relacional, o resultado de uma consulta em SQL pode conter cópias múltiplas de alguns tuplos. 4.2.1. A Cláusula Select O resultado de uma consulta de SQL é, naturalmente, uma relação. ex: select nome_agência from empréstimo Na prática a eliminação de duplicidade consome tempo. As linguagens formais de consulta baseia-se no facto de uma relação ser um conjunto. Se quisermos mesmo eliminarmos a duplicidade: select distinct ...
Select all ...= por defeito. O asterisco * pode ser usado para denotar “todos os atributos” ex: select empréstimo.* ex: select * A cláusula select pode conter expressões aritméticas envolvendo os operadores + - * /, e os operandos constantes ou atributos das tuplas: ex: select nome_agência, número_empréstimo, total*100 4.2.2. A Cláusula Where ex: select número_empréstimo from empréstimo where nome_agência = “Perryridge” and total > 1200 A SQL permite o uso de operadores de comparação para strings e expressões aritméticas, assim como tipos especiais, como tipos de datas. A SQL permite o operador between para simplificar a cláusula where: ex: where total between 90000 and 100000 4.2.3. A Cláusula From Por si só define um produto cartesiano das relações na cláusula. Uma vez que a junção natural é definida em termos de produto cartesiano, uma selecção e uma projecção são um meio relativamente simples de escrever a expressão SQL para uma junção natural: πnome_cliente, número_empréstimo(devedor >< empréstimo) select distinct nome_cliente, devedor.número_empréstimo from devedor, empréstimo where devedor.número_empréstimo=empréstimo.número_empréstimo 4.2.4. A Operação Rename Mecanismo para rebaptizar tanto relações como atributos usando a cláusula as. A cláusula as pode aparecer tanto na cláusula select como na from Por vezes, numa consulta derivamos nomes de atributos das antigas relações para as novas relações/resultados. Nem sempre convém: Primeiro porque nas relações da cláusula from podem existir atributos com o mesmo nome e que resultaria em nomes duplicados; Segundo, se usarmos uma expressão aritmética na cláusula select, o atributo resultante não teria um nome; terceiro, mesmo que o nome do atributo possa ser derivado das relações de base, podemos desejar mudar o nome: ex: select distinct nome_cliente, devedor.número_empréstimo as id_empréstimo 4.2.5. Variáveis Tuplos A cláusula as é particularmente útil na definição do conceito de variável tuplo, como é feito no cálculo realcional. Uma variável tuplo precisa de estar associada a uma relação em particular. São definidas na cláusula from por meio do uso da cláusula as. ex: “Para todos os clientes que possuem um empréstimo no banco, encontre os seus nomes e respectivos números de empréstimo”: select distinct nome_cliente, T.número_empréstimo from devedor as T.empréstimo, empréstimo as S where T.número_empréstimo = S.número_empréstimo Variáveis tuplos são mais úteis para comparação de dois tuplos da mesma relação. Poderíamos também usar a operação rename. ex: Encontrar os nomes de todas as agências que tenham fundos maiores que ao menos uma agência daquelas localizadas em Brooklyn” select T.nome_agência from agência as T, agência as S where T.fundos > S.fundos and S.cidade_agência = “Brooklyn”
A ordenação de tuplos é feita atributo a atributo. 4.2.6. Operações com Strings As operações mais usadas são as checagens para verificação de coincidências de pares, usando ooperador like. Identificaremos esses pares por meio do uso de dois caracteres especiais: % - compara qualquer substring _ - compara qualquer caracter São sensíveis a minúsculas e maiúsculas (case sensitive) ex: “Perry%” corresponde a qualquer string que comece por “Perry” ex: select nome_cliente from cliente where rua_cliente like “%Main%” É permitido uso caracter de escape ex: like “ab\%cd%” escape “\” A SQL permite pesquisar diferenças em vez de coincidências, usando o not like Também permite concatenação (usando “||”), extracção de substrings, indicação de tamanhos de strings, conversão de maiúsculas para minúculas, etc. 4.2.7. Ordenação e Apresentação de Tuplos Cláusula order by ex: select distinct nome_cliente from devedor, empréstimo where devedor.número_empréstimo=empréstimo.número_empréstimo and nome_agência=”Perryridge” order by nome_cliente Por defeito ordena de forma ascendente (asc). Se quisermos descendente devemos acrescentar, no fim, desc. 4.2.8. Duplicidade O uso de relações com duplicidade (tuplos repetidos) é por vezes útil. A SQL dá também o número de repetições. Podemos definir a semântica da duplicidade de uma consulta SQL usando versões multiconjuntos dos operadores relacionais. ... será preciso??? 4.3. Operações de Conjuntos Os operadores SQL-92 union, intersect e except operam relações e correspondem às operações U ∩ e - da álgebra relacional. Também aqui as relações participantes das operações precisam de ser compatíveis, isto é, ter o mesmo conjunto de atributos. 4.3.1. A Operação de União ex: “Todos os clientes que possuam empréstimos ou conta ou ambos: (select nome_cliente from depositante) union (select nome_cliente from devedor) union, ao contrário de select, elimina automaticamente as repetições. Se quisermos as repetições fazemos union all 4.3.2. A Operação Intersecção “Encontrar todos os clientes que tenham tanto empréstimos quanto contas no banco: (select distinct nome_cliente from depositante)
intersect (select distinct nome_client from devedor) A operação intersect elimina todas as repetições. Para aparecerem -> intersect all 4.3.3. A Operação Excepto “Encontrar todos os clientes que tenham uma conta e nenhum empréstimo (select distinct nome_cliente from depositante) except (select nome_cliente from devedor) Também elimina repetições, excepto se escrevermos except all 4.4. Funções Agregadas São funções que tomam uma colecção (um conjunto ou subconjunto) de valores como entrada e retornam um valor simples. Há 5: Média (average) : avg Mínimo (minimum) : min Máximo (maximum) : max Total (total) : sum Contagem (count) : count A entrada para sum e avg precisa ser um conjunto de números. Os outros podem operar também como outros tipos de dados, como strings, etc. ex: “Encontrar a média dos saldos em contas na agência Perriridge” select avg (slado) from conta where nome_agência = “Perryridge” O resultado é uma relação com atributo único, contendo uma única linha. Podemos dar-lhe um nome com a cláusula as. Existem circunstâncias em que queremos aplicar uma função agregada não somente a um conjunto de tuplos, mas também a um grupo de conjuntos de tuplos. Teremos de usar a cláusula group by. O atributo ou atributos fornecidos numa cláusula group by são usados para formar grupos. ex: “Encontrar a média dos saldos nas contas de cada uma das agências do banco” select nome_agência, avg (saldo) from conta group by nome_agência Ás vezes, é mais interessante definir condições e aplicá-las a grupos do que a tuplos. Deve usar-se a cláusula having. ex: select nome_agência, avg (saldo) from conta group by nome_agência having avg (saldo) > 1200 Usamos a função count com muita frequência para contar o número de tuplos numa relação. A notação para essa função é count(*) ex: select count(*) from cliente A SQL não permite o uso de distinct em count(*), mas permite em max e min. Se uma cláusula where e uma cláusula having aparecem na mesma consulta, o predicado que aparece em where é aplicado primeiro. Os tuplos que satisfazem a cláusula where são, então, colocadas em grupos por meio da cláusula gropup by. A cláusula having, se presente, é então aplicada a cada grupo. Os grupos que não satisfazem o predicado da cláusula having são removidos. Os grupos remanescentes são usados pela cláusula select.
ex: “encontre o saldo médio para cada cliente que mora em Harrison e tenha ao menos 3 contas” select depositante.nome_cliente, avg(saldo) from depositante, conta, cliente where depositante.número_conta=conta.número_conta and depositante.nome_cliente=client.nome_cliente and cidade_cliente = “Harrison” group by depositante.nome_cliente having count (distinct depositante.número_conta) >= 3 4.5. Valores Nulos Podemos usar a palavra-chave null como predicado para testar a existência de valores nulos. ex: select número_empréstimo from empréstimo where total is null O predicado is not null testa a ausência de valores nulos. O uso de valores nulos em operações aritméticas e comparações causa diversas complicações. O resultado de uma expressão aritmética (envolvendo, por exemplo, +, -, * ou /) é nula se qualquer um dos valores for nulo. O resultado de qualquer comparação envolvendo uma valor nulo pode ser imaginado como falso. Mais precisamente, a SQL trata os resultado destas comparações como unknown, que não é true nem false, embora muitas vezes seja tratado comop false. Também complica o cálculo de funções agregadas, como sum. Em vez de dizer que a soma é nula, o operador sum pode ignorar os valores nulos de entrada. Em geral, todas as funções agregadas (excepto count(*)) ignoram os valores nulos. 4.6. Subconsultas aninhadas As aplicações mais comuns para as subconsultas são testes para membros de conjuntos, comparações de conjuntos e cardinalidade de conjuntos. 4.6.1. Membros de Conjuntos Verificar se um tuplo é membro ou não de uma relação. Usa-se o conectivo in (ou not in) que testa os mebros de um conjunto, no qual o conjunto é a colecção de valores produzidos por select. ex: select distinct nome_cliente from devedor where nome_cliente in (select nome_cliente from depositante) ===> Há um volume substancial de redundância no SQL. Os operadores in e not in também podem ser usados em conjuntos enumerados: ex: select distinct nome_cliente from devedor where nome_cliente not in (“Smith”, “Jones”) 4.6.2. Comparação de Conjuntos “Encontre os nomes de todas as agências que tenham fundos maiores que ao menos uma agência localizada no Brooklyn” (dif de 4.2.5.) select nome_agência from agência where fundos > some (select fundos from agência where cidade_agência = “Brooklyn”)
=some é idêntico a in, mas não é a mesma coisa que not in. any é sinónimo de some, mas as últimas versões só permitem o some devido à ambiguidade linguística de any. ex: de uso de > all select nome_agência from agência where fundos > all (select fundos from agência where cidade_agência = “Brooklyn”) all é idêntico a not in. Como exemplo final de comparações de conjuntos, “encontre a agência que tem o maior slado médio”. Funções agregadas não podem ser agregadas em SQL, pelo que não podemos usar max(avg(...)). Então, estrategicamente: select nome_agência from conta group by nome_agência having avg(saldo) >= all (select avg (saldo) from conta group by nome_agência) 4.6.3. Verificação de Relações Vazias A SQL possui meios para testar se o resultado de uma subconsulta possui algum tuplo. Usa-se o construtor exists que retorna o valor true de o argumento de uma subconsulta é n-ao-vazio. ex: “Encontre todos os clientes que tenham tanto conta como empréstimo no banco” select nome_cliente from devedor where exists (select * from depositante where depositante.nome_cliente = devdor.nome_cliente) também se pode testar a não existência de tuplos na subconsulta por meio do construtor not exists. Este construtor pode ser usado para simular operações com conjuntos contidos (superconjuntoos): podemos escrver “a relação A contém a relação B” como not exists (B except A)”. Embora não seja padrão do SQL-92, o operador contains existiu nalgumas versões de SQL. ex: “Encontre todos os clientes que tenham uma conta em todas as agências do Brooklyn” select distinct S.nome_cliente from depositante as S where not exists (select nome_agência from agência where cidade_agência = “Brooklyn”) except (select R.nome_agência from depositante as T, conta as R where T.número_conta = R.número_conta and S.nome_cliente = T.nome_cliente)) 4.6.4. Teste para a Ausência de Tuplos Repetidos (numa subconsulta) O construtor unique retorna o valor true caso o argumento da subconsulta não possua nenhum tuplo repetido. ex: “Encontre todos os clientes que tenham somente uma conta na agência Perryridge”
select T.nome_cliente from depositante as T where unique (select R.nome_cliente from conta, depositante as R where T.nome_cliente = R.nome_cliente and R.número_conta = conta.número_conta and conta.nome_agência = “Perryridge”) O not unique pode ser utilizado para encontrar todos os clientes que tenham pelo menos duas contas... O teste de unique falha se um dos campos for null 4.7. Relações Derivadas O SQL permite o uso de uma expressão de subconsulta na cláusula from. Nesse caso, a relação resultante deve receber um nome e os atributos precisam de ser rebaptizados, com as. ex: (select nome_agência, avg(saldo) from depositante group by nome_agência as resultado (nome_agência, saldo_médio) Depois podemos usar o resultado desta subconsulta numa consulta. 4.8. Visões será preciso??? 4.9. Modificação no Banco de Dados Adicionar, Remover e Alterar 4.9.1. Remoção Um pedido de remoção de dados é expresso muitas vezes do mesmo modo que uma consulta. Podemos remover apenas tuplos inteiros. delete from r where P ex: delete from depositante where nome_cliente = “Smith” ex: delete from conta where nome_agência in (select nome_agência from agência where cidade_agência = “Perryridge”) Assim, podemos ver que podemos atingir mais que uma relação numa operação de delete, por meio de comandos select-from-where aninhados numa cláususla where de um delete. Os tuplos a remover também podem ser da relação da cláusula delete: ex: delete from conta where saldo < (select avg (saldo) from conta) É importante que todos ostestes sejam efectuados antes de remover. 4.9.2. Inserção Para inserir dados numa relação podemos especificar um tuplo a ser inserido ou escrever uma consulta cujo resultado é um conjunto de tuplos a inserir. ex: + simples: insert into conta values (“Perryridge”, A-9732”, 1200) Para quem não se lembrar da ordem: insert into conta (nome_agência, número_conta, saldo)
values (“Perryridge”, A-9732”, 1200) ou qualquer outra ordem. + genericamente: insert into conta select nome_agência, número_empréstimo, 200 from empréstimo where nome_agência = “Perryridge” mal: insert into conta select * from conta Pode inserir-se tuplos com atributos a null, mas, com DDL pode impedir-se isso se assim o pretendermos. 4.9.3. Actualizações ex: update conta set saldo = saldo * 1.05 ex: update conta set saldo = saldo * 1.06 where saldo > 10000 update conta set saldo = saldo*1.05 where saldp = 4,00) ex: create domain número_conta char(10) constraint teste_nulo_número_conta check(value not null) ex: create domain tipo_conta char(10) constraint teste_tipo_conta check (value in (“Corrente”, “Poupança”)) 6.2. Integridade Referencial Frequentemente desejamos garantir que um valor que aparece numa relação para um dado conjunto de atributos também apareça para um certo conjunto de atributos de outra relação. Essa condição é chamada integridade referencial 6.2.1. Conceitos Básicos tuplos pendentes – quando há tuplos de uma relação r que não pode ser combinado com um tuplo da relação s, aquando da sua junção natural. Tuplos pendentes podem ser aceitáveis ou não. ex: indesejável – em conta, existir t1[nome agência] = “Lunartown” e na relação agência não haver nenhuma “Lunartown”. aceitável: o contrário. A diferença tem origem em: - O atributo nome_agÊncia do Esquema_conta é uma chave estrangeira (foreign key) cuja referência é a chave primária do Esquema_agência - O atributo nome_agência do Esquema_agência não é uma chave estrangeira. ==> Regras de integridade referencial ou subconjunto dependente: πα(r2) ⊆ πK1(r1) 6.2.2. Integridade Referencial no Modelo E-R Cada Ki do esquema de R é uma chave estrangeira que leva a uma regra de integridade referencial. Outra fonte de regras de integridade referencial são os conjuntos de entidades fracas. 6.2.3. Modificações no Banco de Dados As modificações no banco de dados podem originar violação das regras de integridade referencial. Há que fazer pois verificações para preservar πα(r2) ⊆ πK1(r1): - Inserção: Se t2 é inserida em r2 ---> t2[α] ∈ Πk(r1) - Remoção: Se t1 é removida de r1, o siatema deve tratar também o conjunto de tuplos em r2 que são referidos por t1. Pode haver cascata. - Actualização: Mistura das duas anteriores... 6.2.4. Integridade Referencial em SQL
É possível definir chaves primárias, secundárias (candidatas ?) e estrangeiras como parte do comando create table da SQL: Forma simplificada para declarar que uma única coluna é uma chave estrangeira: Nome_agência char(15) references agência ex: create table cliente (nome_cliente char(20) not null, rua_cliente char(30), cidade_cliente char(30), primary key (nome_cliente)) create table agência (nome_agência char(15) not null, cidade_agência char(30), fundos integer, primary key (nome_agência), check (fundos >= 0)) create table conta (número_conta char(10) not null, nome_agência char(15), saldo integer primary key (número_conta), foreign key (nome_agência) references agência, check (saldo >=0)) create table depositante (nome_cliente char(20) not null, número_conta char(10) not null, primary key (nome_cliente, número_conta) foreign key (nome_cliente) references cliente, foreign key (número_conta) references conta) Quando uma regra de integridade referencial é violada, o procedimento normal é rejeitar a acção que ocasionou essa violação. Mas, na cláusula relativa a foreign key pode especificar-se os passos para modificação do tuplo que contém a referência, de modo a garantir a regra de integridade. ex: create table conta ... foreign key (nome_agência) references agência on delete cascade on update cascade, ...) A SQL-92 também permite que a cláusula foreign key especifique outros tipos de acções além de cascata, como alterar o campo em questão (no caso nome_agÊncia) com nulos, ou um valor padrão... A semântica de chaves em SQL torna-se mais complexa pelo facto de a SQL permitir valores nulos. As seguintes regras, algumas das quais arbitrárias, são usadas para tratar esses valores nulos. Todos os atributos de uma chave primária são declarados implicitamente not null. Atributos de uma declaração unique (isto é, atributos de uma chave candidata) podem ser nulos, contanto que não sejam declarados não-nulos de outro modo. Só saõ iguais se não houver nulos numa das colunas, pelo menos. Atributos nulos em chaves estrangeiras são permitidos, o que implica a sua aprovação na regra da integridade.
Dada esta complexidade e arbitrariedade natural das formas e comportamento de restrições (ou regras) de integridade em relação a valores nulos, o melhor é assegurar que todas as colunas especificadas em unique e foreign key sejam declaradas não permitindo nulos. 6.3. Asserções Uma asserção é um predicado que expressa uma condição que desejamos que seja sempre satisfeita no banco de dados. Restrições de domínio e regras de integridade são formas especiais de asserções. Outros ex: A soma de todos os totais em conta empréstimo de cada uma das agências deve ser menor que a soma de todos os saldos das contas dessa agência. Todo o empréstimo deve ter ao menos um cliente que mantenha uma conta com saldo mínimo de 1000 dólares. create assertion check ex:1: create assertion restrição_soma check (not exists (select * from agência where (select sum(total) from empréstimo where empréstimo.nome_agência = agência.nome_agência) >= (select sum(total) from conta where conta.nome_agência = agência.nome_agência))) É preciso usar as asserções com muito cuidado pois a sua verificação pode ser pesada para o sistema, que precisa de as verificar em cada actualização, para além do início. 6.4. Gatilhos (Triggers) Um gatilho é um comando que é executado pelo siatema automaticamente, em consequência de uma modificação no banco de dados. Duas exigências devem ser satisfeitas para o projecto de um mecanismo de gatilho: 1. Especificar as condições sob as quais o gatilho deve ser executado 2. Especificar as acções que serão executadas quando um gatilho for disparado. São úteis para avisos, por ex. ex: define trigger saldo_negativo on update of conta T (if new T.saldo β se para qualquer t1[α] = t2[α], t1[β] =t2[β] A dependência funcional permite-nos expressar restrições que as superchaves não expressam.
Podemos usar dependência funcional de dois modos: 1. Usando-as para estabelecimento de restrições sobre um conjunto de relações válidas... F realiza-se em R 2. Usando-as para verificação de relações... r satisfaz F. α --> β é trivial se β ⊆ α Para distinguir os conceitos de uma relação que satisfaz uma dependência e de uma dependência realizando-se num esquema, voltemos ao ex. do banco. Se considerarmos a relação cliente (com o Esquema_cliente), como mostrado, notamos que rua_cliente -> cidade_cliente é satisfeita. Mas, no mundo real, é possível que duas cidades distintas tenham o mesmo nome de rua. Logo, não incluiremos a dependência no conjunto de dependências funcionais que são realizadas no Esquema_cliente. Na relação empréstimo (do Esquema_empréstimo) vemos que número_empréstimo>total é satisfeita. Aqui é diferente, pois na vida real é normal que cada conta tenha apenas um total. Portanto queremos que a condição número_empréstimo -> toatla seja sempre satisfeita para a relação empréstimo. Por outras palavras, precisamos da restrição número_empréstimo -> total para o Esquem_empréstimo. Já na relação agência, nome_agência -> fundos é realaizada no Esquema_agência, já o mesmo não se passando com o inverso, embora na relação, essa dependência possa ser satisfeita. Embora o SQL não forneça um modo simples para especificação de dependências funcionais, podemos escrever consultas para verificação de dependências funcionais, assim como criar asserções para garantia de dependências funcionais. Quando projectamos um banco de dados relacional, primeiro relacionamos as dependências funcionais que sempre precisam ser realizadas. Ex: No Esquema_agência: nome_agência --> cidade_agência nome_agência --> fundos No Esquema_cliente: nome_cliente --> cidade_cliente nome_cliente --> rua_cliente 6.5.2. CLAUSURA (FECHO) DE UM CONJUNTO DE DEPENDÊNCIAS FUNCIONAIS Não basta considerar um dado conjunto de dependências funcionais. É preciso considerar todos os conjuntos de dependências funcionais que são realizadas. Podemos mostrar que dado um conjunto F de dependências funcionais, prova-se que outras dependências funcionais realizam-se (conjunto logicamente implícito em F). ex:ex: esquema R(A,B,C,G,H,I) e A --> B A --> C CG --> H CG --> I B --> H A --> H é logicamente implícita O fecho de F é o conjunto de todas as dependências funcionais logicamente implícitas em F. Denotamos por F+ Dado F podemos computar F+ pela definição de DF, mas existem técnicas mais simples. 1 – Três axiomas ou regras para inferência Regra da Reflexividade: Se α é conjunto de atributos e β ⊆ α, então α --> β realiza-se Regra de Incremento:
Se α --> β se realiza e γ é um conjunto de atributos, então γα --> γβ também se realiza Regra da Transitividade: Se α --> β e β --> γ se realizam, então α --> γ também se realiza. Estas regras são sólidas e completas. São os Axiomas de Armstrong. Embora sejam completos, é enfadonho utilizá-los directamente para computação de F+. Então, para simplificar, adicionamos regras adicionais: Regra da União: Se α --> β e α --> γ se realizam, então α --> βγ também se realiza. Regra de Decomposição: Se α --> βγ se realiza, então α --> β e α --> γ também se realizam. Regra da Pseudotransitividade: Se α --> β e γβ -->δ se realizam, então αγ --> δ também se realiza 6.5.3. CLAUSURA DE CONJUNTOS DE ATRIBUTOS Para verificar se um conjunto α de atributos é uma superchave, precisamos conceber um algoritmo para computar o conjunto de atributos determinados funcionalmente por α. Esse algoritmo também será útil na determinação do fecho de um conjunto F. Chamamos o conjunto dos atributos funcionalmente determinados por α, sob um conjunto de dependências funcionais F, de fecho de α. Denotamos por α+ Em pseudo pascal: resultado := α; while (mudanças em resultado) do for each funcional dependência β --> γ in F do begin if β ⊆ resultado then resultado := resultado U γ; end exercício: provar que AG é superchave. 6.5.4. COBERTURA CANÓNICA Sempre que uma actualização é realizada na relação, o sistema de banco de dados deve garantir que todas as dependências funcionais em F sejam satisfeitas no novo estado do banco de dados. Para reduzir os esforços de teste: Qualquer banco de dados que satisfaça um conjunto simplificado de dependências funcionais deve também satisfazer o conjunto original, e vice-versa, uma vez que os dois conjuntos têm o mesmo fecho. Um atributo de uma dependência funcional é extrínseco se podemos removê-lo sem alterar o fecho do conjunto de dependências funcionais. Formalmente: A é extrínseco a α se A∈α, e F implica logicamente (F-{α-->β}) U {(α-A) --> β} A é extrínseco a β se A∈β, e o conjunto de dependências funcionais (F-{α-->β}) U {(α-> (β-A)} implica logicamente F. Uma cobertura canónica Fc para F é o conjunto de dependências tal que F implique logicamente todas as dependências de Fc e Fc implique logicamente todas as dependências de F. Além disso Fc deve apresentar as seguintes propriedades: Nenhuma dependência funcional em Fc contém um atributo extrínseco Cada lado esquerdo da dependência funcional em Fc é único. Isto é, não há duas dependências α1 --> β1 e α2 -.-> β2 em Fc tal que α1 = α2 Uma cobertura canónica pode ser computada como:
repeat Use a regra de união para substituir todas as dependências funcionais em F da forma α1 --β1 e α1 --> β2 por α1 --> β1β2 Encontre as dependências funcionais α --> β com um atributo extrínseco em α ou em β Se um atributo extrínseco é encontrado, remova-o de α --> β until F não mude exercício: Computar a cobertura canónica para F, sendo F, no esquema (A,B,C) A --> BC B --> C A --> B AB --> C Solução: A --> B B --> C EXERCÍCIOS: 6.7. Por que há certas dependências funcionais chamadas de triviais? Porque são dependências funcionais satisfeitas por todas as relações. Em geral, uma dependência funcional α --> β é trivial se β ⊆ α 6.8. Relacione todas as dependências atendidas (satisfeitas na) pela relação da figura: A a1 a1 a2 a2 A --> B AC --> B + as triviais : A --> A
B b1 b1 b1 b1
C c1 c2 c1 c3
B --> B
C --> C
AB --> A, etc.
6.9. Use a definição de dependência funcional para discutir como funciona cada um dos axiomas da Armstrong (reflexividade, aumento e transitividade) Reflexividade: se t1[α] = t2[α] e β ⊆ α então forçosamente α=βγ. Logo t1[β] = t2 [β] Incremento: se t1[α] = t2[α] , de α --> β temos que t1[β] = t2[β]. Como, por reflexividade γ --> γ , se t1[γ] = t2[γ] então t1[γα] = t2[γβ] Transitividade: Está feito na página 204 do livro. 6.10. Explique como a dependência funcional pode ser usada par explicar o seguinte: Um conjunto de relacionamentos um para um entre o conjunto de entidades estudantes e orientador. Um conjunto de relacionamentos muitos para um entre o conjunto de entidades estudantes e orientador. estudantes --> orientador orientador --> estudantes estudantes --> orientador
6.15. Compute a clausura do seguinte conjunto F de dependências funcionais do esquema de relação R= (A,B,C,D,E) A --> BC CD --> E B --> D E --> A Relacione as chaves candidatas de R BC --> E pela pseudotransitividade E --> BC pela transitividade A --> B e A --> C pela decomposição E --> B e E --> C pela decomposição E --> D pela transitividade E --> ABCD pela união E --> ABCDE pela reflexividade e união A --> E pela transitividade A --> BCE pela união A --> ABCD pela reflexividade, transitividade e união A --> ABCDE pela união BC --> ABCDE pela transitividade CD --> ABCDE pela transitividade ===> chaves candidatas: A , E , BC e CD 6.16. Usando as dependências funcionais do exercício anterior, compute B+ pelo algoritmo da página 206 do livro: 0. resultado = B 1. resultado = B U D pois B --> D resultado final = BD Se fosse pedido para o A: 6.17. Usando as dependências funcionais dos exercícios anteriores, compute a cobertura canónica Fc Usando o algoritmo da página 207 do livro: ver se C é extrínseco em A --> BC (F-{A-->BC}) U {A --> (BC-C)} CD-->E A-->B B-->D E-->A BC-->E pela pseudotransitividade E -->A pela transitividade E --> B pela transitividade A-->D pela transitividade AC-->E A-->BD AC-->E pela pseudotransitividade AC-->AB pela transitividade e união não me parece e o B? também não.
ver se C ou D são extrínsecos em CD-->E (F – {CD->E}) U {(CD – D) -->E} A-->BC U C-->E B-->D E-->A CD-->E A-->B A-->C A-->D A-->E CD-->A BC-->E BC-->A Então a cobertura canónica será mesmo o conjunto das dependências dadas. Não é possível reduzir.
CAP. 13 – TRANSACÇÕES Normalmente considera-se que um conjunto de várias operações no banco de dados é uma única unidade do ponto de vista do utilizador. O essencial será a conclusão de todo o conjunto de operações, ou que, no caso de uma falha, nenhuma delas ocorra. Seria inaceitável o débito sem o crédito numa transferência. As operações que formam uma única unidade lógica de trabalho são chamadas transacções. Além disso o banco deve administrar a execução de várias transacções de modo a evitar a ocorrência de inconsistências. 13.1. Conceito de transacção Uma transacção é uma unidade de execução de programa que acessa e, possivelmente, actualiza vários itens de dados. É geralmente o resultado de um programa em SQL e é delimitada por begin transaction e end transaction. Para assegurar a integridade dos dados, exigimos que o sistema de banco de dados mantenha as seguintes propriedades (ACID) das transacções: Atomicidade: todas as operações ou nenhumas Consistência: Uma transacção isolada preserva a consistência do banco de dados. Isolamento: Embora possa ocorrer transacções concorrentes cada uma delas tem a sensação de que a outra acabou antes de se iniciar Durabilidade: Mudanças persistem até mesmo se houver falhas no sistema. Ex: Transacção em contas de um banco. Vamos supor que o banco de dados reside permanentemente em disco, mas que alguma parte dele reside, temporariamente, na memória principal. O acesso ao banco de dados é obtido pelas duas seguintes operações: read(X), que transfere o item de dados para um buffer alocado à transacção write(X) que transfere do buffer localpara o banco de dados. Num banco de dados real, o write não resulta necessariamente na actualização dos dados em disco, pode ser armazenado na memória temporariamente. ex: Ti: read(A); A:= A – 50; write(A); read(B); B:=B+50; write(B) Consistência: A exigência de consistência significa que que a soma de A com B deve permanecer inalterada após a execução da transacção. É responsabilidade do programador da aplicação que codifica a transacção. Esta tarefa pode ser facilitada por meio do teste automático dos requisitos de integridade, conforme visto no cap. 6. Atomicidade: Suponhamos que há uma falha a meio, depois de write(A) mas antes de write(B) da transacção (falta de energia, falha da máquina ou de software). Então a soma de A+B não é preservada --> estado inconsistente. A ideia básica por detrás da atomicidade é a seguinte: O sistema de banco de dados mantém um registo (em disco) dos antigos valores de quaisquer dados sobre os quais a transacção executa uma gravação e, se a transacção não for completada, os valores antigos são restabelecidos para parecer que nada dela foi executado. É da responsabilidade do próprio sistema de banco de dados. Durabilidade:
Garante que uma vez completada a transacção com sucesso, todas as actualizações realizadas permanecerão mesmo que depois haja uma falha no sistema. Suponhamos agora que uma falha se dá e há perda de dados na memória, mas não no disco. Podemos garantir a durabilidade se se garantir uma de: 1. As actualizações realizadas pela transacção foram gravadas em disco, antes da transacção se completar 2. Informações gravadas no disco, sobre as actualizações realizadas pela transacção, são suficientes para que o banco de dados possa reconstruir essas actualizações quando o sitema for reiniciado após uma falha. É da responsabilidade do componente de gerenciamento de recuperação. Isolamento: Quando há mais que uma transacção em simultâneo (concorrentes) com operações que podem ser intercaladas. Uma solução é realizar transacções em série mas isso é muito ineficiente. Há pois outras técnicas que veremos à frente. É da responsabilidade do componente de controlo de concorrência. 13.2. ESTADO DA TRANSACÇÃO Se houver falha a transacção deve ser abortada e, nesse caso, quaisquer actualizações já feitas devem ser desfeitas --> transacção desfeita (rolled back – retornada). É da responsabilidade do sistema de recuperação. Uma transacção efectuada com sucesso diz-se efectivada (committed). Para desfazer os seus efeitos só através de uma transacção de compensação. Uma transacção deve estar num dos seguintes estados: Activa, ou estado inicial: quando está a executar-se Em efectivação parcial: após a execução da última declaração (ainda na memória principal, pelo que ainda pode ser abortada) Em falha: Quando se descobre que a execução normal já não se pode continuar Abortada: depois de desfeita Em efectivação: Após conclusão com sucesso. Diz-se concluída se estiver em efectivação ou abortada. Depois de entrar no estado abortada, o sistema tem duas opções: Reiniciar a transacção (excepto se for falha por erro lógico da transacção) --> nova transacção Matar a transacção (erro lógico da transacção) 13.3. IMPLEMENTAÇÃO DE ATOMICIDADE E DURABILIDADE O componente de recuperação de um banco de dados implementa o suporte à atomicidade e durabilidade. Ex de esquema simples mas muito ineficiente: cópias shadow (sombra) com db_pointer. Diz-se que uma transacção foi efectivada quando o db_pointer actualizado é escrito no disco. O sistema de disco garante que actualizará o db_pointer atomicamente. 13.4. EXECUÇÕES CONCORRENTES Traz diversas complicações em relação à consistência dos dados. É muito fácil insistir na execução sequencial das transacções, porém há dois fortes motivos para que os sistemas reais permitam a concorrência: 1. A CPU e os discos num sistema informático podem operar em paralelo, o que aumenta a eficiência. 2. Na sequencial, uma transacção curta poderia ter de esperar muito tempo por uma longa. Logo, se as transacções estão operando em diferentes partes do banco de dados, é melhor deixá-las concorrer, o que reduz os tempos de atraso e o tempo médio de resposta.
Vamos introduzir o conceito de escalas de execução (schedules) para ajudar na identificação de quais ordens de execução podem garantir a manutenção da consistência. Aqui a consistência é garantida pelos mecanismos de controlo de concorrência. EX: Escala1: T1 T2 read(A) A:=A-50 write(A) read(B) B:=B+50 write(B) read(A) temp:=A*0,1 A:=A-temp write(A) read(B) B:=B+temp write(B) Início: A=1000 e B=2000 Se T1 e depois T2 --> A=855 e B=2145 (escala 1) Se T2 e depois T1 --> A=850 e B=2150 (escala 2) As sequências apresentadas são chamadas de escalas de execução ou só de escalas: representam a ordem cronológica por meio da qual as instruções são executadas. As escalas 1 e 2 são sequenciais. Há sempre n! escalas sequenciais. Se o controlo da execução concorrente é deixado completamente sob responsabilidade do sistema operativo, muitas escalas de execução possíveis, são factíveis, mesmo aquelas que deixam o sistema num estado inconsistente (criação ou desaparecimento de dinheiro) É tarefa do banco de dados, através do componente de controlo de concorrência garantir quer isso não possa suceder. Temos pois que garantir que a escala usada seja equivalente a uma escala sequencial. 13.5. SERIALIZAÇÃO Vamos só considerar as operações significativas do ponto de vista da escala de execução: read e write. 13.5.1. SERIALIZAÇÃO DE CONFLITO vamos considerar uma escala de execução S com duas instruções sucessivas, Ii e Ij , das transacções Ti e Tj (i≠ j), respectivamente. Se Ii e Ij se referem a itens de dados diferentes, então podemos alternar Ii e Ij sem afectar os resultados de qualquer instrução da escala. Porém, se Ii e Ij se referem ao mesmo item de dados Q, então a ordem dos dois passos pode importar. Como só considerámos read e write, há 4 casos a analisar: 1. Ii=read(Q) , Ij=read(Q) : a sequência de execução de Ii eIj não importa já que o mesmo valor de Q é lido a despeito da ordem. 2. Ii=read(Q) , Ij=write(Q) : A ordem importa 3. Ii=write(Q) , Ij=read(Q) : A ordem importa 4. Ii=write(Q) , Ij=write(Q) : Importa, do ponto de vista do próximo read Dizemos que Ii e Ij entram em conflito caso elas pertençam a difeentes transacções, agindo no mesmo item de dado, e pelo menos uma dessas operações é de write.
Se Ii e Ij não entram em conflito podemos trocar a sua ordem que a escala S’ assim obtida é equivalente a S. Se formos trocando a ordem e chegarmos a uma escala sequencial, quer dizer que os efeitos da escala inicial são os mesmos de uma escala sequencial. Dizemos que S e S’ são equivalentes no conflito. Dizemos que uma escala de execução é conflito serializável se ela é equivalente no conflito a uma escala de execução sequencial. É possível ter duas escalas de execução que produzam o mesmo resultado, mas que não seja, equivalentes no conflito. Há definições menos restritivas de equivalência de escala que a equivalência de conflito, mas em geral tal análise é onerosa em termos computacionais. Mas há outras puramente baseadas em read e write: 13.5.2. VISÃO SERIALIZADA S e S’ são ditas equivalentes na visão se as três condições seguintes forem satisfeitas: 1. Para cada item de dados Q, se a transacção Ti fizer uma leitura no valor inicial de Q na escala S, então a transacção Ti também deve, na escala S’ , ler o valor inicial de Q. 2. Para cada item de dados Q, se a transacção Ti executar um read(Q) na escala S, e aquele valor foi produzido por meio da transacção Tj (se houver), então a transacção Ti também deverá, na escala S’, ler o valor de Q que foi produzido por meio da transacção Tj 3. Para cada item de dados Q, a transacção (se houver) que executa a operação final writ(Q) na escala S tem de executar a operação write(Q) final em S’. A escalas 1 e 2 não são equivalentes na visão mas a 1 e 3 já são. escala 3: T1 T2 read(A) write(A) read(A) write(A) read(B) write(B) read(B) write(B) Dizemos que S tem visão serializada se for equivalente em visão a uma escala de execução sequencial. Ex: de escala visão serializável mas não conflito serializável: T3 T4 T6 read(Q) write(Q) write(Q) write(Q) 13.6. RECUPERAÇÃO Se uma Ti falha, precisamos de desfazer os seus efeitos, mas também é necessa´rio assegurar que qualquer transacção Tj que dependa de Ti seja abortada. Para assegurar isso temos de colocar restrições aos tipos de escalas que se podem usar. 13.6.1. ESCALAS DE EXECUÇÃO RECUPERÁVEIS Só deverão ser permitidas escalas recuperáveis.
Uma escala recuperável é aquela na qual, para cada par de transacções Ti e Tj, tal que Tj leia itens de dados previamente escritos por Ti, a operação de efectivação de Ti apareça antes de operação de efectivação de Tj. 13.6.2. ESCALAS EM CASCATA O retorno em cascata é indesejável, mesmo em escalas recuperáveis, já que leva a destruir uma quantidade de trabalho. Logo, é conveniente restringir às sem cascata. Uma escala sem cascata é aquela na qual para cada par de transacções Ti e Tj, tal que Tj leia um item de dados previamente escrito por Ti, a operação de efectivação de Ti apareça antes da operação de leitura de Tj. Toda a escala sem cascata é recuperável. 13.7. IMPLEMENTAÇÃO DO ISOLAMENTO As escalas que são conflito ou visão serializável e sem cascata assegura que o banco de dados fica sempre num estado consistente e trata seguramente as possíveis falhas de transacção. Há vários esquemas para assegurar isso (controle de concorrência) a despeito do S.Operativo usado. O mais trivial é o lock usado por uma transacção que entra em execução. mas isso é ineficiente, sendo geradas apenas escalas sequenciais. 13.8. DEFINIÇÃO DE TRANSACÇÃO EM SQL Uma linguagem de manipulação de dados deve possuir um construtor para especificar o conjunto de acções que constitui uma transacção. No padrão SQL ela começa de modo subentendido e termina por Commit work (executa a efectivação da transacção corrente e começa uma nova) ou Rollback work (aborta a transacção corrente) O nível de consistência especificado pelo SQL-92 é: Serializável Read repetitivo Read com efectivação Read sem efectivação 13.9. TESTE DE SERIALIZAÇÃO Nesta secção apresentaremos métodos para determinar serialização de conflito (há algoritmo simples) e serialização de visão. 13.9.1. TESTE PARA SERIALIZAÇÃO DE CONFLITO Seja S uma escala. Construímos um gráfico direccionado, chamado gráfico de precedência de S. Esse gráfico consiste num par G=(V,E), em que V é um conjunto de vértices e E é um conjunto de arestas. O conjunto de vértices consiste em todas as transacções que partiocipam na escala. O conjunto de arestas consiste em todas as arestas Ti --> Tj para as quais uma das seguintes condições se verifica: 1.Ti executa write(Q) antes de Tj executar read(Q) 2.Ti executa read(Q) antes de Tj executar write(Q) 3.Ti executa write(Q) antes de Tj executar write(Q) Se há uma aresta Ti --> Tj no gráfico de precedência, então, em qualquer escala sequencial S’ equivalente a S, Ti deve aparecer antes de Tj. Se o gráfico de precedência tem um ciclo, então a escala S não é conflito serializável. Se o gráfico não contém ciclos, então a escala S é conflito serializável. A ordem de serialização pode ser obtida por meio da classificação topológica. EXERCÍCIOS
13.1.Liste as propriedades ACID. Explique a utilidade de cada uma. Atomicidade: Para garantir que uma transacção (com várias operações) ou é totalmente feita ou nada é feito. Se assim não fosse corríamos facilmente o risco de inconsistência de dados. Da responsabilidade do programador. Consistência: Para garantir que nada “desaparece” ou “aparece” sempre que é feita qualquer transacção. É da responsabilidade do sistema de base de dados. Isolamento: Apesar de duas transacções poderem ser executadas em simultâneo, para rendibilizar os recursos de sistema, para cada uma delas é como se a outra não existisse. É da responsabilidade do sistema de controlo de concorrência Durabilidade: Depois de efectivada a transacção há que garantir que os dados se mantêm, mesmo que haja qualquer falha de sistema. Da responsabilidade do sistema de recuperação. 13.2. Suponha que haja um sistema de banco de dados que nuca falhe. Um gerenciador de recuperação é necessário para esse sistema? Não, uma vez que este é responsável pela recuperação de dados consistentes quando há uma falha a meio de uma transacção que tem de ser pois abortada, ou de uma falha mesmo após a efectivação de uma transacção para garantir a durabilidade. Se não há falhas, não se justifica este componente. 13.3. Considere o sistema de arquivo de seu sistema operativo favorito. Os aspectos de atomicidade e durabilidade são relevantes em relação aos seguintes itens? Justifique a sua resposta. (a) Utilizador do sistema operativo (b) Implementador do sistema de ficheiros. (b) Sim. Por exemplo se o utilizador manda gravar um ficheiro para actualização, espera-se que o sistema grave/actualize a versão antiga totalmente, ou, se impossível por falha, que não faça nada, de modo a ter acessível a versão antiga. Por outro lado, quanto à durabilidade também pois se se termina a operação (“efectivação”) que, eventualmente, apaga versões anteriores, espera-se que essa versão se mantenha “para sempre”. (a) Não. O utilizador não tem que se preocupar com isso. 13.4. Os implementadores de sistemas de banco de dados prestaram muito mais atenção às propriedades ACID que os implementadores de sistemas de arquivo. Por quê? Porque uma base de dados, se não atender aquelas propriedades, rapidamente deixa de ser fiável e os dados tornam-se completamente inconsistentes. Além disso as bases de dados jogam com grandes quantidades de informação interrelacionada e por vezes sensível e, onde, inconsistências não são fáceis de descobrir. No caso dos sistemas de ficheiros embora possa ser produzida informação inconsistente, é muito mais fácil descobrir esse facto. 13.5. Durante a sua execução, uma transacção atravessa vários estados, até ser finalmente efectivada ou abortada. Liste todas as possíveis sucessões de estados pelos quais uma transacção pode passar. Explique por que cada transição de estado pode acontecer. Activa – estado inicial – em execução parcialmente efectivada – depois de a última operação já ter sido feita mas ainda em memória. Daqui pode ir para abortada ou efectivada efectivada – Concluída, sem possibilidade de ser abortada Em falha... abortada – Chegou-se à conclusão que a transacção não pode ser terminada. Há que recuperar os dados antigos e desfazer as operações da transacção já realizadas. 13.6. Explique a distinção entre os termos escala sequencial e escala serializável Escala sequencial é quando as transacções se realizam uma a seguir à outra. Não há concorrência. Escala serializável é uma escala em que embora isso não se verifique, os resultados são equivalentes a uma escala sequencial. 13.7. Considere as duas transacções seguintes:
T1:
read(A); read(B); if A = 0 then B:= B+1; write(B) T2: read(B); read(A); if B = 0 then A:=A+1; write(A) Seja um requisito de consistência A=0 V B=0, com valores iniciais A=B=0. (a) Mostre que toda a execução sequencial que envolve essas duas transacções preserva a consistência do banco de dados. Se T1, T2, o resultado final é B=1 e A=0 pois o if de T2 é falso. Similarmente se T2,T1 o resultado final é A=1 e B=0, pelo que a consistência se preserva. (b) Mostre uma execução concorrente de T1 e T2 que produza uma escala não serializável T1 T2 read(A) read(B) read(B) read(A) if B ... write(A) if A... write(B) Aqui a consistência já não seria preservada pois ambas as transacções fazem as leituras dos valores antes de qualquer outra operação, o que leva a que A e B sejam zero nessas leituras e que vão depois ser alterados para 1. A razão da não serialização é que a escala não é equivalente a nenhuma sequencial. Pela disposição pode ver-se que nunca vou poder obter (por trocas sucessivas de posição) uma escala sequencial visto que não posso trocar a ordem de write(A) de T2 por write(B) de T1 nem de read(A) de T1 por write(A) de T2 (c) Há uma execução concorrente de T1 e T2 que produza uma escala serializável? Sim porque o gráfico de precedência não contém ciclos T1 --------> T2 ex: read(B) read(A) read(A) if A ... write(B) read(B) if B... write(A) 13.8. Dado que toda a escala conflito serializável é visão serializável, por que enfatizamos serialização de conflito em vez de serialização de visão? Porque o algoritmo para testar a serialização de conflito é muito mais simples. 13.9. Considere o gráfico de precedência da figura. A escala correspondente é conflito serializável? Justifique. É. O gráfico de precedência não tem ciclos. 13.10.
View more...
Comments