Prévia do material em texto
Sistemas de Gerenciamento Sistemas de Gerenciamento de Banco de Dadosde Banco de Dados Ramakrishnan • Gehrke Tradução daTradução da Terceira EdiçãoTerceira Edição SISTEMAS DE GERENCIAMENTO DE BANCO DE DADOS 2011 Versão impressa desta obra: 2008 Raghu Ramakrishnan University of Wisconsin Madison, Wisconsin, USA Tradução da Terceira Edição Johannes Gehrke Cornell University Ithaca, New York, USA Tradução Célia Taniwake João Eduardo Nóbrega Tortello Revisão Técnica Elaine Parros Machado de Sousa Professora Doutora do Departamento de Ciências de Computação — Instituto de Ciências Matemáticas e de Computação da Universidade de São Paulo 52 CAPÍTULO 3 O grau, também chamado aridade, de uma relação é o número de campos. A cardina- lidade de uma instância de relação é o número de tuplas que ela contém. Na Figura 3.1, o grau da relação (o número de colunas) é cinco e a cardinalidade dessa instância é seis. Um banco de dados relacional é uma coleção de relações com nomes distintos. O esquema de banco de dados relacional é a coleção de esquemas das relações presentes no banco de dados. Por exemplo, no Capítulo 1, discutimos o banco de dados de uma universidade com relações chamadas Alunos, Professores, Cursos, Salas, Matriculado, Mi- nistra e Aula. Uma instância de um banco de dados relacional é uma coleção de instâncias de relação, uma por esquema de relação no esquema de banco de dados; naturalmente, cada instância de relação deve satisfazer as restrições de domínio nesse esquema. 3.1.1 Criando e Modifi cando Relações Usando SQL A linguagem SQL padrão usa a palavra tabela para denotar relação e freqüentemente seguimos essa convenção ao discutirmos a SQL. O subconjunto da SQL que suporta a criação, exclusão e modificação de tabelas é chamado Data Definition Language (DDL — linguagem de definição de dados). Além disso, embora exista um comando que permite aos usuários definirem novos domínios, análogo aos comandos de definição de tipo em uma linguagem de programação, deixaremos a discussão sobre definição de domínio para a Seção 5.7. Por enquanto, consideraremos apenas os domínios que são tipos internos, como integerinteger. A instrução CREATE TABLECREATE TABLE é usada para definir uma nova tabela.1 Para criarmos a relação Alunos, podemos usar a seguinte instrução: CREATE TABLECREATE TABLE Alunos (id-aluno CHAR(20)CHAR(20), nome CHAR(30)CHAR(30), login CHAR(20)CHAR(20), idade INTEGERINTEGER, média REALREAL ) As tuplas são inseridas usando-se o comando INSERTINSERT. Podemos inserir uma única tupla na tabela Alunos, como segue: INSERTINSERT INTOINTO Alunos (id-aluno, nome, login, idade, média) VALUESVALUES (53688, ‘Smith’, ‘smith@ee’, 18, 3,2) Opcionalmente, podemos omitir a lista de nomes de coluna na cláusula INTOINTO e lis- tar os valores na ordem apropriada, mas é considerado boa prática ser explícito quanto aos nomes de coluna. Podemos excluir tuplas usando o comando DELETEDELETE. Podemos excluir todas as tu- plas de Alunos com nome igual a Smith, usando o comando: DELETEDELETE FROMFROM Alunos A WHEREWHERE A.nome = ‘Smith’ Podemos modificar os valores de coluna em uma linha existente usando o comando UPDATEUPDATE. Por exemplo, podemos incrementar a idade e decrementar a média do aluno com id-aluno 53688: 1 A SQL também fornece instruções para destruir tabelas e alterar as colunas associadas a uma tabela; discutiremos essas instruções na Seção 3.7. esaito Retângulo O Modelo Relacional 53 UPDATE UPDATE Alunos A SET SET A.idade = A.idade + 1, A.média = A.média – 1 WHERE WHERE A.id-aluno = 53688 Esses exemplos ilustram alguns pontos importantes. A cláusula WHEREWHERE é aplicada primeiro e determina quais linhas devem ser modificadas. Então, a cláusula SETSET deter- mina como essas linhas devem ser modificadas. Se a coluna que está sendo modificada também é usada para determinar o novo valor, o valor usado na expressão no lado direito do sinal de igualdade (=) é o valor antigo; ou seja, antes da modificação. Para ilustrar melhor esses pontos, considere a seguinte variação da consulta anterior: UPDATE UPDATE Alunos A SET SET A.média = A.média – 0,1 WHERE WHERE A.média >= 3,3 Se essa consulta for aplicada na instância A1 de Alunos mostrada na Figura 3.1, obteremos a instância que aparece na Figura 3.3. id-aluno nome login idade média 50000 Dave dave@cs 19 3,2 53666 Jones jones@cs 18 3,3 53688 Smith smith@ee 18 3,2 53650 Smith smith@math 19 3,7 53831 Madayan madayan@music 11 1,8 53832 Guldu guldu@music 12 2,0 Figura 3.3 Instância A1 de Alunos após a atualização. 3.2 RESTRIÇÕES DE INTEGRIDADE SOBRE RELAÇÕES Um banco de dados é tão bom quanto as informações nele armazenadas e, portanto, um SGBD deve ajudar a evitar a entrada de informações incorretas. Uma restrição de integridade (RI) é uma condição especificada sobre um esquema de banco de dados e limita os dados que podem ser armazenados em uma instância do banco de dados. Se uma instância do banco de dados satisfaz todas as restrições de integridade especifi- cadas em seu esquema, então ela é uma instância válida. Um SGBD impõe restrições de integridade, no sentido de que ele permite o armazenamento apenas de instâncias válidas no banco de dados. As restrições de integridade são especificadas e verificadas em diferentes ocasiões: 1. Quando o administrador ou o usuário final define um esquema de banco de dados, ele especifica as RIs que devem valer em qualquer instância desse banco de dados. 2. Quando um aplicativo de banco de dados é executado, o SGBD verifica se existem violações e proíbe alterações nos dados que violem as RIs especificadas. (Em al- gumas situações, em vez de proibir a alteração, o SGBD poderia fazer algumas alterações de compensação nos dados, para garantir que a instância do banco de dados satisfaça todas as RIs. Em qualquer caso, não são permitidas alterações no banco de dados para criar uma instância que viole qualquer RI.) É importante es- pecificar exatamente quando as restrições de integridade são verificadas, em relação lfilho Retângulo 54 CAPÍTULO 3 à instrução que causa a alteração nos dados e à transação da qual ela faz parte. Discutiremos esse aspecto no Capítulo 16, após apresentarmos com mais detalhes o conceito de transação, introduzido no Capítulo 1. Muitos tipos de restrições de integridade podem ser especificados no modelo rela- cional. Já vimos um exemplo de restrição de integridade nas restrições de domínio associadas a um esquema de relação (Seção 3.1). Em geral, outros tipos de restrições também podem ser especificados; por exemplo, dois alunos não podem ter o mesmo valor de id-aluno. Nesta seção, discutiremos as restrições de integridade, além das restrições de domínio, que um administrador ou usuário de banco de dados pode espe- cificar no modelo relacional. 3.2.1 Restrições de Chave Considere a relação Alunos e a restrição de que dois alunos não podem ter a mesma identificação. Essa RI é um exemplo de restrição de chave. Uma restrição de chave é uma declaração de que certo subconjunto mínimo dos campos de uma relação é um identificador único para uma tupla. Um conjunto de campos que identifica uma tupla de acordo com uma restrição de chave é chamado chave candidata da relação; freqüen- temente, abreviamos isso apenas como chave. No caso da relação Alunos, o (conjunto de campos contendo apenas o) campo id-aluno é uma chave candidata. Vamos analisar melhor a definição anterior de chave (candidata). Existem duas partes na definição:2 1. Duas tuplas distintas em uma instância válida (uma instância que satisfaz todas as RIs, incluindo a restrição de chave) não podem ter valores idênticos em todos os campos de uma chave. 2. Nenhum subconjunto do conjunto de campos em uma chave é um identificador único para uma tupla. A primeira parte da definição significa que, em qualquer instância válida, os valores nos campos de chave identificam univocamente uma tupla na instância. Ao especificar uma restrição dechave, o administrador ou usuário do banco de dados deve certi- ficar-se de que essa restrição não o impeça de armazenar um conjunto “correto” de tuplas. (Um comentário semelhante também se aplica à especificação de outros tipos de RIs.) A noção de “correto” aqui depende da natureza dos dados que estão sendo armazenados. Por exemplo, vários alunos podem ter o mesmo nome, embora cada um tenha uma identificação única. Se o campo nome for declarado como uma chave, o SGBD não permitirá que a relação Alunos contenha duas tuplas descrevendo alunos diferentes com o mesmo nome! A segunda parte da definição significa, por exemplo, que o conjunto de campos {id-aluno, nome} não é uma chave para Alunos, pois esse conjunto contém a cha- ve {id-aluno}. O conjunto {id-aluno, nome} é um exemplo de superchave, que é um conjunto de campos que contém uma chave. Veja novamente a instância da relação Alunos na Figura 3.1. Observe que duas linhas diferentes sempre têm valores de id-aluno diferentes; id-aluno é uma chave e identifica uma tupla univocamente. Entretanto, isso não vale para campos que não são chave. Por exemplo, a relação contém duas linhas com Smith no campo nome. 2 O termo chave é muito utilizado. No contexto dos métodos de acesso, falamos sobre chaves de pesquisa, que são muito diferentes. O Modelo Relacional 55 Note que é garantido que toda relação tenha uma chave. Como uma relação é um conjunto de tuplas, o conjunto de todos os campos é sempre uma superchave. Se outras restrições valem, algum subconjunto dos campos pode formar uma chave, do contrário o conjunto de todos os campos é uma chave. Uma relação pode ter várias chaves candidatas. Por exemplo, os campos login e idade da relação Alunos, em conjunto, também podem identificar alunos de maneira única. Ou seja, {login, idade} também é uma chave. Pode parecer que login é uma chave, pois não existem duas linhas no exemplo de instância que tenham o mesmo valor de login. Entretanto, a chave deve identificar as tuplas univocamente em todas as instâncias válidas possíveis da relação. Dizendo que {login, idade} é uma chave, o usuário está declarando que dois alunos podem ter o mesmo login ou a mesma idade, mas não ambos. Além de todas as chaves candidatas disponíveis, um projetista de banco de dados pode identificar uma chave primária. Intuitivamente, uma tupla pode ser referenciada em qualquer outra parte do banco de dados armazenando-se os valores de seus campos de chave primária. Por exemplo, podemos nos referir a uma tupla de Alunos armaze- nando seu valor de id-aluno. Como conseqüência dessa maneira de referência às tuplas de alunos, freqüentemente as tuplas são acessadas especificando-se seu valor de id-alu- no. Em princípio, podemos usar qualquer chave, e não apenas a chave primária, para nos referirmos a uma tupla. Entretanto, usar a chave primária é preferível, pois é ela que o SGBD espera — esse é o significado de projetar uma chave candidata em par- ticular como chave primária — e para a qual faz otimizações. Por exemplo, o SGBD pode criar um índice com os campos de chave primária como chave de pesquisa, para tornar eficiente a recuperação de uma tupla, dado seu valor de chave primária. A idéia da referência a uma tupla será mais bem desenvolvida na próxima seção. Especifi cando Restrições de Chave em SQL Na SQL, podemos declarar que um subconjunto das colunas de uma tabela constituem uma chave, usando a restrição UNIQUEUNIQUE. No máximo uma dessas chaves candidatas pode ser declarada como chave primária, usando-se a restrição PRIMARY KEYPRIMARY KEY. (A SQL não exige que essas restrições sejam declaradas para uma tabela.) Vamos rever nosso exemplo de definição de tabela e especificar informações de chave: CREATE TABLE CREATE TABLE Alunos ( id-aluno CHAR(20)CHAR(20), nome CHAR(30)CHAR(30), login CHAR(20)CHAR(20), idade INTEGERINTEGER, média REALREAL, UNIQUE UNIQUE (nome, idade), CONSTRAINT CONSTRAINT Chave Alunos PRIMARY KEYPRIMARY KEY (id-aluno) ) Essa definição diz que id-aluno é a chave primária e a combinação de nome e idade também é uma chave. A definição da chave primária também ilustra como podemos nomear uma restrição, precedendo-a com CONSTRAINTCONSTRAINT nome-da-restrição. Se a restri- ção for violada, seu nome será retornado e poderá ser usado para identificar o erro. 3.2.2 Restrições de Chave Estrangeira Às vezes, as informações armazenadas em uma relação estão ligadas às informações armazenadas em outra relação. Se uma das relações for modificada, a outra deverá ser verificada e, talvez, modificada, para manter os dados consistentes. Uma RI envolvendo 56 CAPÍTULO 3 as duas relações deverá ser especificada, caso um SGBD precise fazer tais verificações. A RI mais comum envolvendo duas relações é uma restrição de chave estrangeira. Suponha que, além de Alunos, tenhamos uma segunda relação: Matriculado(id-aluno: stringstring, id-curso: stringstring, nota: stringstring) Para garantir que apenas estudantes legítimos possam se matricular nos cursos, qualquer valor que apareça no campo id-aluno de uma instância da relação Matricu- lado também deve aparecer no campo id-aluno de alguma tupla na relação Alunos. O campo id-aluno de Matriculado é chamado chave estrangeira e se refere a Alunos. A chave estrangeira na relação de referência (Matriculado, em nosso exemplo) deve corresponder à chave primária da relação referenciada (Alunos); ou seja, ela deve ter o mesmo número de colunas e tipos de dados compatíveis, embora os nomes das colunas possam ser diferentes. Essa restrição está ilustrada na Figura 3.4. Conforme mostra a figura, podem existir tuplas de Alunos que não sejam referenciadas a partir de Matriculado (por exemplo, o alu- no com id-aluno=50000). Entretanto, todo valor de id-aluno que aparece na instância da tabela Matriculado aparece na coluna de chave primária de uma linha na tabela Alunos. Chave estrangeira Chave primária id-curso nota id-aluno id-aluno nome login idade média Carnatic101 C 53831 50000 Dave dave@cs 19 3,3 Reggae203 B 53832 53666 Jones jones@cs 18 3,4 Topology112 A 53650 53688 Smith smith@ee 18 3,2 History105 B 53666 53650 Smith smith@math 19 3,8 53831 Madayan madayan@music 11 1,8 53832 Guldu guldu@music 12 2,0 Matriculado (relação que referencia) Alunos (relação referenciada) Figura 3.4 Integridade referencial. Se tentarmos inserir a tupla ·55555, Art104, AÒ em M1, a RI será violada, pois não há nenhuma tupla em A1 com id-aluno 55555; o sistema de banco de dados deve rejeitar essa inserção. Analogamente, se excluirmos a tupla ·53666, Jones, jones@ cs, 18, 3,4Ò de A1, violaremos a restrição de chave estrangeira, pois a tupla ·53666, History105, B Ò em M1 contém o valor de id-aluno 53666, o id-aluno da tupla de Alu- nos excluída. O SGBD deve proibir a exclusão ou, talvez, excluir também a tupla de Matriculado que referencia a tupla de Alunos excluída. Discutiremos as restrições de chave estrangeira e seu impacto sobre as atualizações na Seção 3.3. Finalmente, notamos que uma chave estrangeira poderia referenciar a mesma re- lação. Por exemplo, poderíamos ampliar a relação Alunos com uma coluna chamada parceiro e declarar essa coluna como uma chave estrangeira referindo-se a Alunos. Intuitivamente, cada aluno poderia então ter um parceiro e o campo parceiro conteria o id-aluno do parceiro. O leitor observador sem dúvida perguntará: “E se um aluno não tiver (ainda) um parceiro?”. Essa situação é tratada na SQL usando-se um valor especial chamado null. O uso de null em um campo de uma tupla significa que o O Modelo Relacional 57 valor nesse campo é desconhecido ou não é aplicável (por exemplo, não conhecemos o parceiro ainda ou não há nenhum parceiro). A presença de null em um campo de cha- ve estrangeira não viola a restrição de chave estrangeira. Entretanto, valores null não podem aparecer em um campo de chave primária (pois os campos de chave primária são usados para identificar uma tupla univocamente). Discutiremosmelhor os valores null no Capítulo 5. Especifi cando Restrições de Chave Estrangeira em SQL Vamos definir Matriculado(id-aluno: stringstring, id-curso: stringstring, nota: stringstring): CREATE TABLE CREATE TABLE Matriculado ( id-aluno CHAR(20)CHAR(20), id-curso CHAR(20)CHAR(20), nota CHAR(10)CHAR(10), PRIMARY KEY PRIMARY KEY (id-aluno, id-curso), FOREIGN KEY FOREIGN KEY (id-aluno) REFERENCESREFERENCES Alunos ) A restrição de chave estrangeira diz que todo valor de id-aluno em Matriculado também deve aparecer em Alunos; ou seja, id-aluno em Matriculado é uma chave es- trangeira referenciando Alunos. Especificamente, todo valor de id-aluno em Matricu- lado deve aparecer como o valor do campo de chave primária, id-aluno, de Alunos. A propósito, a restrição de chave primária de Matriculado diz que um aluno tem exata- mente uma nota para cada curso em que está matriculado. Se quisermos registrar mais de uma nota por aluno, por curso, devemos alterar a restrição de chave primária. 3.2.3 Restrições Gerais As restrições de domínio, de chave primária e de chave estrangeira são consideradas parte fundamental do modelo de dados relacional e recebem atenção especial na maio- ria dos sistemas comerciais. Às vezes, entretanto, é necessário especificar restrições mais gerais. Por exemplo, podemos exigir que a idade dos alunos esteja dentro de certo intervalo de valores; dada essa especificação de RI, o SGBD rejeitará as inserções e atualizações que violarem a restrição. Isso é muito útil na prevenção de erros de entrada de dados. Se especificarmos que todos os alunos devem ter pelo menos 16 anos, a instância de Alunos mostrada na Figura 3.1 será inválida, pois dois alunos têm idade menor do que essa. Se proibirmos a inserção dessas duas tuplas, teremos uma instância válida, como se vê na Figura 3.5. id-aluno nome login idade média 53666 Jones jones@cs 18 3,4 53688 Smith smith@ee 18 3,2 53650 Smith smith@math 19 3,8 Figura 3.5 Uma instância A2 da relação Alunos. A RI que diz que os alunos devem ser maiores de 16 anos pode ser considerada uma restrição de domínio estendida, pois estamos basicamente definindo o conjunto de valores de idade permitidos de maneira mais restrita do que é possível usando simples- mente um domínio padrão, como integerinteger. Em geral, contudo, podem ser especifica- 58 CAPÍTULO 3 das restrições que vão bem além das de domínio, de chave e de chave estrangeira. Por exemplo, poderíamos exigir que todo aluno cuja idade fosse maior do que 18 devesse ter uma média maior do que 3. Os sistemas de banco de dados relacionais atuais suportam essas restrições gerais na forma de restrições de tabela e assertivas. As restrições de tabela são associadas a uma única tabela e verificadas quando essa tabela é modificada. Em contraste, as as- sertivas envolvem várias tabelas e são verificadas quando qualquer uma dessas tabelas é modificada. Tanto as restrições de tabela como as assertivas podem usar o poder to- tal das consultas SQL para especificar a restrição desejada. Discutiremos o suporte da SQL para restrições de tabela e assertivas na Seção 5.7, pois uma avaliação completa de seu poder exige um bom entendimento dos recursos de consulta da SQL. 3.3 VERIFICANDO RESTRIÇÕES DE INTEGRIDADE Conforme observamos anteriormente, as RIs são especificadas quando uma relação é criada e verificadas quando uma relação é modificada. O impacto das restrições de domínio, PRIMARY KEYPRIMARY KEY e UNIQUEUNIQUE é simples de entender: se um comando de inserção, exclusão ou atualização causa uma violação, ele é rejeitado. Toda violação de RI em potencial geralmente é verificada no final da execução de cada instrução SQL, embora possa ser adiada até o final da transação que está executando a instrução, conforme veremos na Seção 3.3.1. Considere a instância A1 de Alunos mostrada na Figura 3.1. A inserção a seguir viola a restrição de chave primária, porque já existe uma tupla com id-aluno 53688, e será rejeitada pelo SGBD: INSERTINSERT INTO INTO Alunos (id-aluno, nome, login, idade, media) VALUES VALUES (53688, ‘Mike’, ‘mike@ee’, 17, 3,4) A inserção a seguir viola a restrição de que a chave primária não pode conter null: INSERTINSERT INTO INTO Alunos (id-aluno, nome, login, idade, media) VALUES VALUES (null, ‘Mike’, ‘mike@ee’, 17, 3,4) É claro que surge um problema semelhante quando tentamos inserir uma tupla com um valor em um campo que não esteja no domínio associado a esse campo; isto é, quando violamos uma restrição de domínio. A exclusão de tuplas não causa uma violação de restrições de domínio, chave primária ou exclusiva. Entretanto, uma atua- lização pode causar violações semelhantes a uma inserção: UPDATE UPDATE Alunos A SET SET A.id-aluno = 50000 WHERE WHERE A.id-aluno = 53688 Essa atualização viola a restrição de chave primária, pois já existe uma tupla com id-aluno 50000. O impacto das restrições de chave estrangeira é mais complexo, pois às vezes a SQL tenta retificar uma violação de restrição de chave estrangeira, em vez de simplesmente rejeitar a alteração. Discutiremos as etapas de verificação da integridade referencial executadas pelo SGBD em termos de nossas tabelas Matriculado e Alunos, com a restrição de chave estrangeira de que Matriculado.id-aluno é uma referência para (a chave primária de) Alunos. O Modelo Relacional 59 Além da instância A1 de Alunos, considere a instância de Matriculado mostrada na Figura 3.4. As exclusões de tuplas de Matriculado não violam a integridade referen- cial, mas as inserções de tuplas de Matriculado poderiam violar. A inserção a seguir é inválida, pois não há nenhuma tupla de Alunos com id-aluno 51111: INSERTINSERT INTO INTO Matriculado (id-curso, nota, id-aluno) VALUES VALUES (‘Hindi101’, ‘B’, 51111) Por outro lado, inserções de tuplas em Alunos não violam a integridade referencial, e as exclusões poderiam causar violações. Além disso, atualizações em Matriculado ou em Alunos que alteram o valor de id-aluno (respectivamente, id-aluno) poderiam violar a integridade referencial. A SQL fornece várias maneiras alternativas de tratar de violações de chave estran- geira. Devemos considerar três perguntas básicas: 1. O que devemos fazer se uma linha de Matriculado é inserida, com um valor na coluna id-aluno que não aparece em nenhuma linha da tabela Alunos? Nesse caso, o comando INSERTINSERT é simplesmente rejeitado. 2. O que devemos fazer se uma linha de Alunos é excluída? As opções são: Excluir todas as linhas de Matriculado que referenciam a linha de Alunos excluída. Proibir a exclusão da linha de Alunos, caso uma linha de Matriculado a refe- rencie. Configurar a coluna id-aluno com o valor de id-aluno de algum aluno (existen- te) “padrão”, para cada linha de Matriculado que referencie a linha de Alunos excluída. Para cada linha de Matriculado que referencia a linha excluída, configurar a coluna id-aluno como null. Em nosso exemplo, esta opção entra em conflito com o fato de que id-aluno faz parte da chave primária de Matriculado e, por- tanto, não pode ser configurada como null. Assim, em nosso exemplo estamos limitados às três primeiras opções, embora esta quarta opção (configurar a chave estrangeira como null) esteja disponível em geral. 3. O que devemos fazer se o valor da chave primária de uma linha de Alunos for atualizada? As opções aqui são semelhantes às do caso anterior. A SQL nos permite escolher qualquer uma das quatro opções em DELETEDELETE e UPDA-UPDA- TETE. Por exemplo, podemos especificar que, quando uma linha de Alunos é excluída, to- das as linhas de Matriculado que se referem a ela também devem ser excluídas, mas que, quando a coluna id-aluno de uma linha de Alunos é modificada, essa atualização deve ser rejeitada, caso uma linha de Matriculado se refira à linha de Alunos modificada: CREATE TABLE CREATE TABLE Matriculado ( id-aluno CHAR(20)CHAR(20), id-curso CHAR(20)CHAR(20), nota CHAR(10)CHAR(10), PRIMARY KEY PRIMARY KEY(id-aluno, id-curso), FOREIGN KEY FOREIGN KEY (id-aluno) REFERENCESREFERENCES Alunos ON DELETE CASCADEON DELETE CASCADE ON UPDATE NO ACTION ON UPDATE NO ACTION ) ■ ■ ■ ■ 60 CAPÍTULO 3 As opções são especificadas como parte da declaração da chave estrangeira. A op- ção padrão é NO ACTIONNO ACTION, que significa que a ação (DELETEDELETE ou UPDATEUPDATE) deve ser rejeitada. Assim, em nosso exemplo, a cláusula ON UPDATEON UPDATE poderia ser omitida, com o mesmo efeito. A palavra-chave CASCADECASCADE diz que, se uma linha de Alunos for excluída, todas as linhas de Matriculado que se referem a ela também serão excluídas. Se a cláu- sula UPDATEUPDATE especificasse CASCADECASCADE e a coluna id-aluno de uma linha de Alunos fosse atualizada, essa atualização também seria executada em cada linha de Matriculado que se referisse à linha de Alunos atualizada. Se uma linha de Alunos for excluída, podemos trocar a matrícula para um alu- no ‘padrão’, usando ON DELETE SET DEFAULTON DELETE SET DEFAULT. O aluno padrão é especificado como parte da definição do campo id-aluno em Matriculado; por exemplo, id-aluno CHAR(20)CHAR(20) DEFAULTDEFAULT ‘53666’. Embora a especificação de um valor padrão seja apro- priada em algumas situações (por exemplo, um fabricante de peças padrão, caso um fabricante em particular saia do ramo), não é adequado trocar as matrículas para um aluno padrão. A solução correta nesse exemplo é excluir também todas as tuplas de matrícula do aluno excluído (isto é, CASCADECASCADE) ou rejeitar a atualização. A SQL também permite o uso de null como valor padrão, especificando-se ON DE-ON DE- LETE SET NULLLETE SET NULL. 3.3.1 Transações e Restrições Conforme vimos no Capítulo 1, um programa executado em um banco de dados é cha- mado transação e pode conter várias instruções (consultas, inserções, atualizações etc.) que acessam o banco de dados. Se (a execução de) uma instrução em uma transação violar uma restrição de integridade, o SGBD deveria detectar isso imediatamente, ou todas as restrições deveriam ser verificadas em conjunto, imediatamente antes que a transação termine? Por padrão, uma restrição é verificada no final de cada instrução SQL que possa levar a uma violação e, se houver violação, a instrução será rejeitada. Às vezes essa estratégia é inflexível. Considere as variantes das relações Alunos e Cursos a seguir; todo aluno é obrigado a ter um curso de distinção, e todo curso é obrigado a ter um monitor, que é algum aluno. CREATE TABLE CREATE TABLE AlunosAlunos ( id-aluno CHAR(20)CHAR(20), nome CHAR(30)CHAR(30), login CHAR(20)CHAR(20), idade INTEGERINTEGER, distinção CHAR(10) NOT NULLCHAR(10) NOT NULL, media REALREAL ) PRIMARY KEY PRIMARY KEY (id-aluno), FOREIGN KEY FOREIGN KEY (distinção) REFERENCESREFERENCES Cursos (id-curso) CREATE TABLECREATE TABLE Cursos ( id-curso CHAR(10)CHAR(10), nomec CHAR(10)CHAR(10), créditos INTEGERINTEGER, monitor CHAR(20) NOT NULLCHAR(20) NOT NULL, PRIMARY KEY PRIMARY KEY (id-curso) FOREIGN KEY FOREIGN KEY (monitor) REFERENCESREFERENCES Alunos (id-aluno)) Quando uma tupla de Alunos é inserida, é feita uma verificação para saber se o curso de distinção está na relação Cursos e, quando uma tupla de Cursos é inserida, O Modelo Relacional 61 é feita uma verificação para saber se o monitor está na relação Alunos. Como vamos inserir a primeira tupla de curso ou de aluno? Uma não pode ser inserida sem a outra. A única maneira de realizar essa inserção é adiando a verificação da restrição, que normalmente seria feita no final de uma instrução INSERTINSERT. A SQL permite que uma restrição esteja no modo DEFERREDDEFERRED ou IMMEDIATEIMMEDIATE. SET CONSTRAINTSET CONSTRAINT nome-restrição DEFERREDDEFERRED Uma restrição no modo adiado (deferred) é verificada no momento da efetivação da transação (commit). Em nosso exemplo, as restrições de chave estrangeira sobre Alunos e Cursos podem ambas ser declaradas no modo adiado. Podemos então inserir um aluno com um curso de distinção inexistente (tornando o banco de dados tempora- riamente inconsistente), inserir o curso (restaurando a consistência) e depois efetivar e verificar se as duas restrições são satisfeitas. 3.4 CONSULTANDO DADOS RELACIONAIS Uma consulta de banco de dados relacional (abreviadamente, consulta) é uma pergun- ta sobre os dados, e a resposta consiste em uma nova relação contendo o resultado. Por exemplo, poderíamos querer encontrar todos os alunos com menos de 18 anos ou todos os alunos matriculados em Reggae203. Uma linguagem de consulta é uma linguagem especializada para escrever consultas. A SQL é a linguagem de consulta comercial mais popular para um SGBD relacio- nal. Apresentaremos agora alguns exemplos de SQL que ilustram como as relações podem ser facilmente consultadas. Considere a instância da relação Alunos mostrada na Figura 3.1. Podemos recuperar as linhas correspondentes aos alunos que têm menos de 18 anos com a seguinte consulta SQL: esaito Retângulo SQL: Consultas, Restrições, Gatilhos 113 O material on-line inclui instruções de como configurar o Oracle, o IBM DB2, o Micro- soft SQL Server, e o MySQL, e os scripts para criação de tabelas e consultas de exemplo. 5.2 O FORMATO DE UMA CONSULTA SQL BÁSICA Esta seção apresenta a sintaxe de uma consulta SQL simples e explica seu significa- do através de uma estratégia de avaliação conceitual, que é uma forma de avaliar a consulta, intencionalmente mais fácil de entender, mas não tão eficiente. Um SGBD executaria normalmente uma consulta de uma forma diferente e mais eficiente. O formato básico de uma consulta SQL é: SELECTSELECT [ DISTINCT ][ DISTINCT ] lista-seleção FROMFROM lista-from WHEREWHERE qualificação id-marin nome-marin avaliação idade 22 Dustin 7 45,0 29 Brutus 1 33,0 31 Lubber 8 55,5 32 Andy 8 25,5 58 Rusty 10 35,0 64 Horatio 7 35,0 71 Zorba 10 16,0 74 Horatio 9 35,0 85 Art 3 25,5 95 Bob 3 63,5 id-marin id-barco dia 22 101 10/10/98 22 102 10/10/98 22 103 10/8/98 22 104 10/7/98 31 102 11/10/98 31 103 11/6/98 31 104 11/12/98 64 101 9/5/98 64 102 9/5/98 74 103 9/8/98 Figura 5.1 Uma instância M3 de Marinheiros. Figura 5.2 Uma instância R2 de Reservas. id-barco nome-barco cor 101 Interlake azul 102 Interlake vermelho 103 Clipper verde 104 Marine vermelho Figura 5.3 Uma instância B1 de Barcos. Toda consulta deve ter uma cláusula SELECTSELECT, que especifica as colunas a serem mantidas no resultado, e uma cláusula FROMFROM, que especifica um produto cartesiano de tabelas. A cláusula opcional WHEREWHERE especifica as condições de seleção nas tabelas mencionadas na cláusula FROMFROM. Uma consulta como essa corresponde intuitivamente a uma expressão de álgebra relacional envolvendo seleções, projeções e produtos cartesianos. A íntima relação en- tre a SQL e a álgebra relacional é a base para a otimização de consulta em um SGBD relacional, conforme veremos nos Capítulos 12 e 15. De fato, os planos de execução lfilho Retângulo 114 CAPÍTULO 5 das consultas SQL são representados usando uma variação das expressões de álgebra relacional (Seção 15.1). Vamos considerar um exemplo simples. (C15) Encontre os nomes e as idades de todos os marinheiros. SELECTSELECT DISTINCTDISTINCT M.nome-marin, M.idade FROMFROM Marinheiros M A resposta é um conjunto de linhas, sendo cada linha um par ·nome-marin, idadeÒ. Se dois ou mais marinheiros tiverem o mesmo nome e a mesma idade, a resposta conterá apenas um par com esse nome e idade. Essa consulta é equivalente a aplicar o opera- dor projeção da álgebra relacional. Se omitíssemos a palavra reservada DISTINCTDISTINCT, obteríamos uma cópia da linha ·m, iÒ para cada marinheiro com nome m e idade i; a resposta seria um multiconjunto de linhas. Um multiconjunto é semelhante a um conjunto por ser uma coleção desor- denada de elementos, mas pode conter diversas cópias de cada elemento, e o número de cópias é significativo — dois multiconjuntos poderiam ter os mesmoselementos e ainda assim ser diferentes em razão do número de cópias ser diferente para alguns ele- mentos. Por exemplo, {a, b, b} e {b, a, b} denotam o mesmo multiconjunto, e diferem do multiconjunto {a, a, b}. A resposta a essa consulta com e sem a palavra reservada DISTINCTDISTINCT na instância M3 de Marinheiros é ilustrada nas Figuras 5.4 e 5.5. A única diferença é que a tupla de Horatio aparecerá duas vezes se DISTINCTDISTINCT for omitido; isso ocorre porque há dois marinheiros chamados Horatio, de idade 35. nome-marin idade Dustin 45,0 Brutus 33,0 Lubber 55,5 Andy 25,5 Rusty 35,0 Horatio 35,0 Zorba 16,0 Art 25,5 Bob 63,5 nome-marin idade Dustin 45,0 Brutus 33,0 Lubber 55,5 Andy 25,5 Rusty 35,0 Horatio 35,0 Zorba 16,0 Horatio 35,0 Art 25,5 Bob 63,5 Figura 5.4 Resposta a C15. Figura 5.5 Resposta a C15 sem DISTINCTDISTINCT. Nossa próxima consulta é equivalente a uma aplicação do operador seleção da ál- gebra relacional. (C11) Encontre todos os marinheiros com uma avaliação acima de 7. SELECTSELECT M.id-marin, M.nome-marin, M.avaliação, M.idade FROMFROM Marinheiros ASAS M WHEREWHERE M.avaliação > 7 SQL: Consultas, Restrições, Gatilhos 115 Essa consulta utiliza a palavra reservada opcional ASAS para introduzir uma variável de intervalo. Aliás, quando desejamos recuperar todas as colunas, como nessa consul- ta, a SQL provê um atalho conveniente: Podemos escrever simplesmente SELECT *SELECT *. Esta notação é útil para consultas interativas, mas é um estilo pobre para as consultas que deverão ser reutilizadas e mantidas porque o esquema do resultado não é claro a partir da consulta propriamente dita; devemos referenciar ao esquema da tabela sub- jacente Marinheiros. Conforme esses dois exemplos ilustram, a cláusula SELECTSELECT é usada realmente para fazer projeção, enquanto as seleções, no sentido da álgebra relacional, são expressas usando a cláusula WHEREWHERE! Essa confusão entre a nomeação dos operadores de seleção e projeção na álgebra relacional e na sintaxe da SQL é um acidente histórico infeliz. Consideraremos agora a sintaxe de uma consulta SQL básica com mais detalhes. A lista-from da cláusula FROMFROM é uma lista de nomes de tabela. Um nome de tabela pode ser seguido por uma variável de intervalo (range viariable), que é particu- larmente útil quando o mesmo nome de tabela aparece mais do que uma vez na lista-from. A lista-seleção é uma lista de (expressões envolvendo) nomes de coluna das tabelas nomeadas na lista-from. Os nomes de coluna podem ser prefixados por uma variá- vel de intervalo. A qualificação da cláusula FROMFROM é uma combinação booleana (isto é, uma expressão usando os conectivos lógicos ANDAND, OROR e NOTNOT) de condições no formato expressão opop expressão, onde opop é um dos operadores de comparação {<, <=, = < >, >=, >}.2 Uma expressão é um nome de coluna, uma constante ou uma expressão (aritmética ou de string). A palavra reservada DISTINCTDISTINCT é opcional. Ela indica que a tabela computada como uma resposta a essa consulta não deve conter duplicatas, ou seja, duas cópias da mesma linha. O padrão é que as duplicatas não sejam eliminadas. Embora as regras precedentes descrevam (informalmente) a sintaxe de uma con- sulta SQL básica, elas não nos informam o significado de uma consulta. A resposta a uma consulta é por si só uma relação — que em SQL! é um multiconjunto de linhas — cujo conteúdo pode ser compreendido considerando-se a seguinte estratégia de ava- liação conceitual: 1. Compute o produto cartesiano das tabelas da lista-from. 2. Exclua as linhas no produto cartesiano que não satisfazem as condições de qualifi- cação. 3. Exclua as colunas que não aparecem na lista-seleção. 4. Se DISTINCTDISTINCT for especificado, elimine as linhas duplicatas. Essa estratégia direta de avaliação conceitual torna explícitas as linhas que devem ser apresentadas na resposta à consulta. Entretanto, é bem provável que seja bem ine- ficiente. Consideraremos como um SGBD realmente avalia as consultas em capítulos posteriores; por ora, nosso propósito é simplesmente explicar o significado de uma con- sulta. Ilustraremos a estratégia de avaliação conceitual usando a seguinte consulta: (C1) Encontre os nomes de marinheiros que reservaram o barco 103. 2 As expressões com NOTNOT sempre podem ser substituídas por expressões equivalentes sem NOTNOT dado o con- junto de operadores de comparação listados. ■ ■ ■ ■ 116 CAPÍTULO 5 C1 pode ser expressa em SQL assim: SELECTSELECT M.nome-marin FROM FROM Marinheiros M, Reservas R WHEREWHERE M.id-marin = R.id-marin ANDAND R.id-barco= 103 Vamos computar a resposta a essa consulta nas instâncias R3 de Reservas e M4 de Marinheiros ilustradas nas Figuras 5.6 e 5.7, uma vez que a computação em nossas instâncias de exemplo usuais (R2 e M3) seriam desnecessariamente tediosas. id-marin id-barco dia 22 101 10/10/96 58 103 11/12/96 id-marin nome-marin avaliação idade 22 dustin 7 45,0 31 lubber 8 55,5 58 rusty 10 35,0 Figura 5.6 Instância R3 de Reservas. Figura 5.7 Instância M4 de Marinheiros. A primeira etapa é construir o produto cartesiano M4 ¥ R3, que é ilustrado na Figura 5.8. id-marin nome-marin avaliação idade id-marin id-barco dia 22 dustin 7 45,0 22 101 10/10/96 22 dustin 7 45,0 58 103 11/12/96 31 lubber 8 55,5 22 101 10/10/96 31 lubber 8 55,5 58 103 11/12/96 58 rusty 10 35,0 22 101 10/10/96 58 rusty 10 35,0 58 103 11/12/96 Figura 5.8 M4 ¥ R3. A segunda etapa é aplicar a qualificação M.id-marin = R.id-marin ANDAND R.id-barco = 103. (Observe que a primeira parte dessa qualificação requer uma operação junção.) Essa etapa elimina todas, exceto a última linha da instância ilustrada na Figura 5.8. A terceira etapa é eliminar as colunas não desejadas; apenas nome-marin aparece na cláusula SELECTSELECT. Essa etapa nos fornece o resultado ilustrado na Figura 5.9, que é uma tabela com uma única coluna e, por coincidência, apenas uma linha. nome-marin Rusty Figura 5.9 Resposta à consulta C1 sobre R3 e M4. 5.2.1 Exemplos de Consultas SQL Básicas Apresentaremos agora diversas consultas de exemplo, muitas das quais foram expressas anteriormente em álgebra e cálculo relacional (Capítulo 4). Nosso primeiro exemplo SQL: Consultas, Restrições, Gatilhos 117 ilustra que o uso de variáveis de intervalo é opcional, a menos que elas sejam necessá- rias para resolver uma ambigüidade. A Consulta C1, que discutimos na seção anterior, também pode ser expressa assim: SELECTSELECT nome-marin FROM FROM Marinheiros M, Reservas R WHEREWHERE M.id-marin = R.id-marin ANDAND R.id-barco= 103 Apenas as ocorrências de id-marin devem ser qualificadas, uma vez que essa colu- na aparece em ambas as tabelas Marinheiros e Reservas. Uma forma equivalente de escrever esta consulta é: SELECTSELECT nome-marin FROM FROM Marinheiros, Reservas WHEREWHERE Marinheiros.id-marin = Reservas.id-marin ANDAND id-barco= 103 Essa consulta mostra que os nomes de tabelas podem ser usados implicitamente como variáveis de linha. As variáveis de intervalo precisam ser introduzidas explici- tamente apenas quando a cláusula FROMFROM contiver mais que uma ocorrência de uma relação.3 Entretanto, recomendamos o uso explícito de variáveis de intervalo e a qua- lificação completa de todas as ocorrências das colunas com uma variável de intervalo para melhorar a legibilidade de suas consultas. Adotaremos essa convenção em todos os nossos exemplos. (C16) Encontre os id-marins dos marinheiros que reservaram um barco vermelho. SELECTSELECT R.id-marin FROM FROM Barcos B, Reservas R WHERE WHERE B.id-barco = R.id-barco ANDAND B.cor = ‘vermelho’ Essa consulta contém uma junção de duas tabelas, seguidas por uma seleção na cor dos barcos. Podemos considerar B e R como as linhas nas tabelas correspondentes que ‘demonstram’ que um marinheiro com id-marin R.id-marin reservou um barcovermelho B.id-barco. Em nossas instâncias de exemplo R2 e M3 (Figuras 5.1 e 5.2), a resposta consiste nos id-marins 22, 31 e 64. Se desejarmos os nomes dos marinheiros no resultado, deveremos também considerar a relação Marinheiros, uma vez que Reservas não contém essa informação, como ilustra o próximo exemplo. (C2) Encontre os nomes dos marinheiros que reservaram um barco vermelho. SELECTSELECT M.nome-marin FROM FROM Marinheiros M, Reservas R, Barcos B WHEREWHERE M.id-marin = R.id-marin ANDAND R.id-barco = B.id-barco ANDAND B.cor = ‘vermelho’ Essa consulta contém uma junção das três tabelas seguidas por uma seleção na cor dos barcos. A junção com Marinheiros nos permite encontrar o nome do marinheiro que, de acordo com a tupla R de Reservas, reservou um barco vermelho descrito pela tupla B. (C3) Encontre as cores dos barcos reservados por Lubber. 3 O nome da tabela não pode ser usado como uma variável de intervalo implícita, uma vez que uma variável de intervalo é introduzida para a relação. 118 CAPÍTULO 5 SELECT SELECT B.cor FROM FROM Marinheiros M, Reservas R, Barcos B WHERE WHERE M.id-marin = R.id-marin ANDAND R.id-barco = B.id-barco ANDAND M.nome-marin = ‘Lubber’ Essa consulta é muito semelhante à anterior. Observe que, em geral, pode haver mais do que um marinheiro chamado Lubber (uma vez que nome-marin não é uma chave de Marinheiros); essa consulta ainda é correta, pois ela retornará as cores dos barcos reservados por algum Lubber, se houver diversos marinheiros chamados Lubber. (C4) Encontre os nomes dos marinheiros que reservaram pelo menos um barco. SELECTSELECT M.nome-marin FROM FROM Marinheiros M, Reservas R WHEREWHERE M.id-marin = R.id-marin A junção de Marinheiros e Reservas assegura que, para cada nome-marin selecio- nado, o marinheiro tenha feito alguma reserva. (Se um marinheiro não tivesse feito uma reserva, a segunda etapa na estratégia de avaliação conceitual eliminaria todas as linhas no produto cartesiano que envolvessem esse marinheiro.) 5.2.2 Expressões e Strings no Comando SELECT A SQL suporta uma versão mais genérica da lista-seleção do que apenas uma lista de colunas. Cada item em uma lista-seleção pode ser da forma expressão ASAS nome-coluna, onde expressão é qualquer expressão aritmética ou de string envolvendo os nomes de colunas (possivelmente prefixadas por variáveis de intervalo) e constantes, e nome- coluna é um novo nome para essa coluna na saída da consulta. A lista-seleção também pode conter funções agregadas, tais como sum e count, que discutiremos na Seção 5.5. O padrão SQL também inclui expressões envolvendo valores de data e hora, os quais não trataremos. Embora não seja parte do padrão SQL, muitas implementações tam- bém suportam o uso de funções embutidas como sqrt, sin e mod. (C17) Compute incrementos das avaliações de pessoas que manobraram dois barcos diferentes no mesmo dia. SELECT SELECT M.nome-marin, M.avaliação+1 ASAS avaliação FROM FROM Marinheiros M, Reservas R1, Reservas R2 WHERE WHERE M.id-marin = R1.id-marin ANDAND M.id-marin = R2.id-marin ANDAND R1.dia = R2.dia ANDAND R1.id-barco < > R2.id-barco E, ainda, cada item em uma qualificação pode ser tão genérico quanto expressão1 = expressão2. SELECT SELECT M1.nome-marin ASAS nome1, M2.nome-marin ASAS nome2 FROM FROM Marinheiros M1, Marinheiros M2 WHERE WHERE 2*M1.avaliação = M2.avaliação-1 Para comparações de string, podemos usar os operadores de comparação (=, <, > etc.) com a ordem das strings determinada alfabeticamente como usual. Se necessi- tarmos ordenar strings em uma ordem que não seja alfabética (por exemplo, ordenar strings que denotam os nomes dos meses na ordem do calendário: Janeiro, Fevereiro, Março etc.), a SQL suporta um conceito genérico de collation, ou ordem de classifica- ção, para um conjunto de caracteres. Uma collation permite que o usuário especifique lfilho Retângulo Encerra aqui o trecho do livro disponibilizado para esta Unidade de Aprendizagem. Na Biblioteca Virtual da Instituição, você encontra a obra na íntegra.