Você está na página 1de 26

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 SQL-
92.
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 <= 10000
atenção à ordem, portanto.

4.9.4. Actualizações de uma Visão


é preciso?...

4.10. Composição de Relações


Além de fornecer o mecanismo básico do produto cartesiano para a composição dos
tuplos de uma relação disponível nas primeiras versões de SQL, a SQL-92 também
oferece diversos outros mecanismos para composição de relações como as junções
condicionais e as junções naturais, assim como várias formas de junções externas.
Essas operações adicionais são usadas tipicamente como expressões de
subconsultas na cláusula from.

4.10.1. Exemplos
empréstimo inner join devedor on empréstimo.número_empréstimo =
devedor.número_empréstimo
Os atributos do resultado consistem nos atributos da relação da esquerda seguidos
dos atributos da relação da direita (com repetição).
podemos rebaptizar a relação resultado, bem assim como os atributos resultado, com
a cláusula as.

O left outer join é processado como se segue: Primeiro, o resultado da junção interna
(inner join) é processado como anteriormente. Então, para todo o tuplo t da relação
empréstimo do lado esquerdo que não apresente correspondência com nenhum tuplo
da relação devedor do lado direito da junção interna, um tuplo r é adicionado ao
resultado da junção da maneira como será descrita. Os atributos do tuplo r que são
derivados da relação do lado esquerdo são preenchidos pelos valores do tuplo t e os
restantes são preenchidos com o valor nulo.

O right outer join é semelhante, mas ao contrário.

Finalmente consideremos o natural join


ex: empréstimo natural inner join devedor
É semelhante ao inner join inicial, excepto que os atributos não aparecem repetidos.
4.10.2. Tipos de Junções e Condições
Embora as expressões para junção externa sejam normalmente usadas na cláusula
from, elas podem ser usadas em qualquer lugar onde se usa uma relação.
Cada uma das variantes das operações de junção em SQL-92 consiste num tipo de
junção e numa condição de junção. As condições de junção definem quais os tuplos
das duas relações apresentam correspondência e quais atributos são apresentados no
resultado de uma junção. O tipo de junção define como os tuplos em cada relação que
não possuam nenhuma correspondência (baseado na condição de junção) com os
tuplos da outra relação devem ser tratados.
Tipos de Junção
inner join Condições de junção
left outer join natural
right outer join on <predicate>
full outer join using (A1,A2,...,An)
O uso de uma condição de junção é obrigatório para junções externas, mas opcional
para junções internas (se for omitido, o resultado é o produto cartesiano).
Sintacticamente, a palavra-chave natural aparece antes do tipo de junção, embora as
condições on e using apareçam no final de uma expressão de junção. As palavras-
chave inner e outer são opcionais, uma vez que os nomes dos demais tipos de
junções nos permitem deduzir do que se trata.

Na natural, os atributos da junção (isto é, os atributos comuns a ambas as relações)


aparecem primeiro, conforme a ordem que aparecem na relação do lado esquerdo.
Depois vêm todos os atributos para os quais não há correspondência aos da relação
do lado esquerdo e, finalmente, todos os atributos sem correspondência aos da
relação do,lado direito.

A condição de junção using(A1,A2,...,An) é similar à condição de junção natural,


excepto pelo facto de que seus atributos de junção são os atributos A,A2,...,An em vez
de todos os atributos comuns a ambas as relações. Os atributos A1,A2,...,An devem
ser somente os atributos comuns a ambas as relações e eles aparecem apenas uma
vez no resultado da junção.

ex: “Encontre todos os clientes que tenham uma conta, mas nenhum empréstimo no
banco”
select d-CN
from (depositante left outer join devedor
on depositante.nome_cliente = devedor.nome_cliente)
as db1 (d-CN, número_conta, b-CN, número_empréstimo)
where b-CN is null

cross join = junção interna sem uma condição de junção


union join = junção externa total na condição de falso, em que a junção interna é vazia.

4.11. Linguagem de Definição de Dados


A SQL DDL permite não só a especificação de um conjunto de relações, como
também infromações acerca de cada uma das relações, incluindo:
O esquema de cada relação
O domínio dos valores associados a cada atributo
As regras de integridade
O conjunto de índices para manutenção de cada relação
Informações sobre segurança e autoridade sobre cada relação
A estrutura de armazenamento físico de cada relação no disco.
vamos, agora, só discutir os dois primeiros:
4.11.1. Tipos de Domínios em SQL
Cap 4. EXERCÍCIOS
4.1. Considere o banco de dados da empresa de seguros mostrada abaixo, em que as
chaves primárias estão sublinhadas. Construa as seguintas consultas em SQL para
este banco de dados relacional.
pessoa(ss#, nome, endereço)
automóvel(licença, ano, modelo)
acidente(data, motorista, total_danos)
proprietários(ss#, licença)
sinistro(licença, data, motorista)
(a) Encontre o número total de pessoas cujos automóveis estiveram envolvidos em
acidentes em 1989.
select count distinct motorista
from acidente
where data=1989
(b) Encontre o número de acidentes em que os automóveis de “John Smith” estiveram
envolvidos
CAP. 6 – REGRAS DE INTEGRIDADE
As regras de integridade fornecem a garantia de que mudanças feitas na base de
dados por utilizadores autorizados não resultem em perda de consistência dos dados.
Como vimos no cap.2 para o modelo E-R, essas regras possuem a seguinte forma:
Declaração de chaves: não duplos de chaves candidatas.
Forma de um relacionamento: um para um, ...
Embora arbitrárias, na prática são limitadas às que podem ser verificadas com o
mínimo tempo de processamento.

6.1. Restrições de Domínios


São as mais elementares formas de restrições de integridade.
A cláusula check permite modos poderosos de restrições de domínios. Permite ao
projecto do esquema determinar um predicado que deva ser satisfeito por qualquer
valor designado a uma variável cujo tipo seja o domínio.
ex: create domain turno_trabalho numeric(5,2)
constraint valor_teste_turno check(value >= 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 <nome_asserção> check <predicado>

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<0
then (insert into empréstimo values
(T.nome_agência, T.número_conta, - new T.saldo)
insert into devedor
(select nome_cliente, número_conta
from depositante
where T.número_conta = depositante.número_conta)
update conta S
setS.saldo=0
where S.número_conta=T.número.conta))
Os gatilhos são chamdaos às vezes de regras(rules) ou regras activas (active rules),
mas não devem ser confundidas com as regras da datalog.

6.5. Dependência Funcional


É um tipo particular de restrições. É uma generalização da noção de (super)chave.

6.5.1. Conceitos Básicos


α --> β 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 B C
a1 b1 c1
a1 b1 c2
a2 b1 c1
a2 b1 c3

A --> B
AC --> B
+ as triviais : A --> A 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.

Você também pode gostar