SQL e Teoria Relacional Como escrever c贸digos SQL precisos C. J. Date
Novatec
Authorized Portuguese translation of the English edition of titled SQL and Relational Theory 2E ISBN 9781449316402 © 2012 C. J. Date. This translation is published and sold by permission of O'Reilly Media, Inc., the owner of all rights to publish and sell the same. Tradução em português autorizada da edição em inglês da obra SQL and Relational Theory 2E ISBN 9781449316402 © 2012 C. J. Date. Esta tradução é publicada e vendida com a permissão da O'Reilly Media, Inc., detentora de todos os direitos para publicação e venda desta obra. © Novatec Editora Ltda. [2015]. Todos os direitos reservados e protegidos pela Lei 9.610 de 19/02/1998. É proibida a reprodução desta obra, mesmo parcial, por qualquer processo, sem prévia autorização, por escrito, do autor e da Editora. Editor: Rubens Prates Tradução: Rafael Zanolli Revisão técnica: Edgard Damiani / Rodrigo Stulzer Revisão gramatical: Marta Almeida de Sá Editoração eletrônica: Carolina Kuwabata Assistente editorial: Priscila A. Yoshimatsu ISBN: 978-85-7522-433-5 MP20150506 Histórico de impressões: Maio/2015
Primeira edição
Novatec Editora Ltda. Rua Luís Antônio dos Santos 110 02460-000 – São Paulo, SP – Brasil Tel.: +55 11 2959-6529 Fax: +55 11 2950-8869 Email: novatec@novatec.com.br Site: www.novatec.com.br Twitter: twitter.com/novateceditora Facebook: facebook.com/novatec LinkedIn: linkedin.com/in/novatec Nutshell Handbook, o logotipo Nutshell Handbook e o logotipo da O’Reilly são marcas registradas da O’Reilly Media, Inc. SQL e Teoria Relacional: Como Escrever Códigos SQL Precisos e os elementos comerciais relacionados são marcas registradas da O’Reilly Media, Inc. Muitas das designações usadas por fabricantes e vendedores para distinguir seus produtos são marcas registradas. Nos locais em que termos desse tipo foram utilizados neste livro, estando a O’Reilly Media, Inc. ciente da existência de uma marca registrada, as designações foram escritas em letras maiúsculas ou com a letra inicial maiúscula. Ainda que todas as precauções tenham sido tomadas na preparação deste livro, a editora e os autores não se responsabilizam por erros, omissões ou danos que resultem do uso das informações contidas neste texto.
capítulo 1
Preparando o cenário
Senta-te pacientemente, minha alma; Não julgues antes de apresentada a peça; Seu enredo tem muitas variações; cada dia Traz uma nova cena; o último ato coroa a peça. – Francis Quarles: Emblemas (1635)
Uma abordagem relacional à linguagem SQL: esse é o tema – ou um dos temas – deste livro. Evidentemente, para tratar de tal tópico adequadamente, preciso abordar questões relacionais assim como questões de SQL em si – e ainda que essa afirmação obviamente se aplique ao livro como um todo, ela se aplica ao primeiro capítulo com força especial. Em consequência, este capítulo tem comparativamente pouco a dizer sobre SQL como tal. O que desejo fazer é revisar material que, na sua maior parte, espero que, de qualquer maneira, você já conheça. Minha intenção é estabelecer um ponto de partida como tal: em outras palavras, estabelecer algumas bases sobre as quais o restante do livro possa ser construído. No entanto, ainda que eu espere que você esteja familiarizado com a maioria do que tenho a dizer neste capítulo, gostaria de sugerir, respeitosamente, que você não o pule. Você precisa saber o que precisa saber (se entende o que eu quero dizer); mais especificamente, você precisa ter certeza de que tem os pré-requisitos necessários para compreender o material que virá nos capítulos futuros. Na realidade, gostaria de recomendar educadamente que, ao longo deste livro, você não pule a discussão de algum assunto só porque acredita que já esteja familiarizado com o tópico. Por exemplo, você tem certeza de que sabe o que é uma chave, em termos relacionais? Ou uma junção?1 1 Existe pelo menos um especialista que não sabe. A seguir temos uma citação direta de um documento que afirma (como este livro!) oferecer conselhos para usuários SQL: “Não utilize junções ... Oracle e SQL Server têm abordagens fundamentalmente diferentes sobre esse conceito ... Você pode terminar com conjuntos de resultado inesperados ... Você deve compreender os tipos básicos de cláusulas de junções ... Junções equivalentes são formadas recuperando todos os dados de duas fontes distintas e combinando-os em uma grande tabela ... Junções internas são feitas nas colunas internas de duas tabelas. Junções externas são feitas nas colunas externas de duas tabelas. Junções à esquerda são feitas nas colunas à esquerda de duas tabelas. Junções à direita são feitas nas colunas à direita de duas tabelas.”
23
24
SQL e Teoria Relacional
O modelo relacional é muito mal-compreendido Profissionais em qualquer disciplina precisam conhecer os fundamentos de seu campo. Por isso, se você é um profissional de bancos de dados, precisa conhecer o modelo relacional, pois o modelo relacional é a base (ou uma grande parte da base, de qualquer modo) do campo dos bancos de dados em particular. Agora, todo curso em gerenciamento de bancos de dados, seja ele acadêmico ou comercial, realmente manifesta pelo menos a ideia de ensinar o modelo relacional – mas a maioria desse ensino parece ser feita de modo muito equivocado, se tivermos por base os resultados. Certamente, o modelo não é bem entendido pela comunidade de bancos de dados como um todo. Aqui estão alguns dos possíveis motivos para essa situação atual: • O modelo é ensinado em um vácuo. Ou seja, pelo menos para principiantes, é difícil ver a relevância do material, entender os problemas que ele deve resolver, ou ambos. • Os próprios instrutores não entendem completamente ou não apreciam a significância do material. • Talvez, mais provavelmente na prática, o modelo não seja sequer ensinado – a linguagem SQL, ou algum dialeto específico dessa linguagem, como o dialeto Oracle, é ensinado em vez disso. Então este livro é direcionado aos praticantes de bancos de dados em geral, e aos praticantes de SQL em particular, que já tiveram alguma exposição ao modelo relacional, mas que não sabem tanto sobre ele quanto deveriam, ou gostariam de saber. Este livro definitivamente não é para iniciantes; no entanto, ele também não é apenas um curso para refrescar sua memória. Para ser mais específico, estou certo de que você sabe algo sobre SQL; mas – e peço desculpas pelo tom possivelmente ofensivo aqui – se o seu conhecimento do modelo relacional deriva apenas de seu conhecimento de SQL, temo que você não conheça o modelo relacional tão bem como deveria e que provavelmente “saiba algo que não sabe”. Não tenho como enfatizar isso o suficiente: SQL e o modelo relacional não são iguais. Aqui, para ilustrar, estão algumas questões relacionais sobre as quais a SQL não é tão clara (para dizer o mínimo): • O que são realmente bancos de dados, relações e tuplas. • A diferença entre valores de relação e variáveis de relação. • A relevância de predicados e proposições. • A importância de nomes de atributos. • A função crucial de restrições de integridade. • O princípio da informação e seu significado.
Capítulo 1 ■ Preparando o cenário
25
E assim por diante (essa não é uma lista completa). Todas essas questões, e muitas outras, são abordadas neste livro. Digo novamente: se o seu conhecimento do modelo relacional deriva apenas de seu conhecimento de SQL, então pode ser que você “saiba algo que não sabe”. Uma consequência é que você poderá perceber, lendo este livro, que terá de desaprender um pouco – e desaprender, infelizmente, é algo muito difícil.
Algumas observações sobre terminologia Provavelmente você percebeu de imediato, na lista de itens de questões relacionais da seção anterior, que utilizei os termos formais relação, tuplas e atributo. A SQL não usa esses termos, é claro – ela utiliza, em vez disso, os termos mais “amigáveis” tabela, linha e coluna. E, em geral, sou simpático à ideia de usar termos mais amigáveis, se eles podem ajudar a tornar as ideias mais palatáveis. No caso presente, entretanto, parece-me que, infelizmente, eles não tornam as ideias mais palatáveis; em vez disso, eles as distorcem e, na realidade, prestam um grande desserviço à causa do entendimento genuíno. A verdade é que uma relação não é uma tabela, uma tupla não é uma linha e um atributo não é uma coluna. E ainda que possa ser aceitável fingirmos o contrário em contextos informais – de fato, eu mesmo muitas vezes faço isso –, diria que isso é aceitável apenas se todos entenderem que os termos mais amigáveis ao usuário são apenas uma aproximação da verdade e que falham, no sentido geral, em capturar a essência do que está realmente acontecendo. Colocando de outra forma: se você entende a situação real, então o uso criterioso de termos amigáveis pode ser uma boa ideia; mas para aprender e apreciar a situação real, em primeiro lugar, você realmente precisa entender os termos formais. Neste livro, portanto, tenderei a usar esses termos formais (pelo menos quando estiver falando sobre o modelo relacional, em vez de SQL) e darei definições precisas para eles no momento relevante. Em contextos SQL, por contraste, usarei os termos da própria SQL. E outro ponto sobre terminologia. Tendo dito que a SQL tenta simplificar um conjunto de termos, devo também dizer que ela faz tudo que pode para complicar outros. Refiro-me ao seu uso dos termos operador, função, procedimento, rotina e método – todos os quais denotam essencialmente o mesmo elemento (com, talvez, diferenças muito pequenas). Neste livro, utilizarei o termo operador em todo o texto; portanto, vou me referir, por exemplo, a “=” (comparação de igualdade), “:=” (atribuição), “+” (adição), DISTINCT, JOIN, SUM, GROUP BY (etc., etc.), todos especificamente como operadores. Falando sobre SQL, incidentalmente, deixe-me lembrá-lo de que (como foi dito no prefácio) uso esse termo para representar exclusivamente a versão padrão da linguagem, exceto em alguns locais em que o contexto exige o contrário2. Entretanto: 2 O padrão tem passado por várias versões, ou edições, ao longo dos anos. A versão atual quando da redação deste texto é SQL:2008 (para a qual uma referência formal pode ser encontrada no
26
SQL e Teoria Relacional • O uso padronizado da terminologia às vezes não é muito adequado. Em tais situações, geralmente prefiro utilizar minha própria terminologia. Por exemplo, uso o termo expressão de tabela em vez do termo padrão expressão de consulta por estes motivos, dentre outros: primeiro, o valor que tais expressões denotam é realmente uma tabela e não uma consulta; segundo, consultas, de qualquer forma, não são o único contexto em que tais expressões são utilizadas. (Na realidade, o padrão de fato utiliza o termo expressão de tabela, mas, uma vez mais, ele o faz de modo inapropriado; para ser específico, ele o utiliza para se referir ao que vem depois da cláusula SELECT em uma expressão SELECT.) • Dando continuidade ao ponto anterior, devo acrescentar que nem todas as expressões de tabela – seja em meu sentido ou no sentido padronizado – são válidas em SQL em todos os contextos em que poderíamos esperar que elas o fossem. Em particular, uma invocação JOIN específica, ainda que certamente denote uma tabela, não pode aparecer como uma expressão de tabela “independente” (isto é, no nível mais externo de aninhamento), nem pode aparecer como a expressão de tabela entre parênteses que constitui uma subconsulta (veja o capítulo 12)3. Por favor, note que essas afirmações se aplicam a muitas das discussões individuais do corpo do livro; no entanto, seria muito tedioso as seguir repetindo, e eu não o farei. (Entretanto, elas são refletidas na gramática BNF no capítulo 12.) • Neste livro, ignoro aspectos do padrão que poderiam ser considerados como esoterismo barato – especialmente se não fizerem parte do que o padrão chama de “Core SQL” ou se não tiverem muita relação com processamento relacional como tal. Exemplos aqui incluem as funções chamadas analíticas ou de janela (OLAP); SQL dinâmico; tabelas temporárias; e detalhes de tipos definidos pelo usuário. • Por motivos que não são importantes aqui, utilizo um estilo para comentários que difere daquele do padrão. Para ser específico, mostro comentários como strings de texto em itálico, entre delimitadores “/*” e “*/”.
Esteja ciente, no entanto, de que todos os produtos SQL incluem funcionalidades que não são parte do padrão em si. IDs de linhas fornecem um exemplo comum. Meu conselho geral a respeito de tais funcionalidades é: utilize-as, se desejar – mas não se elas violarem princípios relacionais (afinal, estou defendendo o que deveria ser uma abordagem relacional à SQL). Por exemplo, IDs de linha, em particular, provavelmente violam o Princípio da intercambialidade (veja o capítulo 9), o Princípio da informação apêndice G); a versão anterior era SQL:2003, antes dessa, SQL:1999; e a anterior a essa, SQL:1992. A maioria das funcionalidades SQL discutidas neste livro estava presente na SQL:1992, e muitas vezes até em versões anteriores. 3 Essas limitações particulares foram adicionadas no padrão SQL:2003; elas não se aplicavam ao SQL:1992, que é onde invocações JOIN explícitas foram primeiro apresentadas, nem ao SQL:1999.
Capítulo 1 ■ Preparando o cenário
27
(veja o apêndice A) ou ambos; e se o fizerem, então eu certamente não os utilizaria. No entanto, aqui e em todos os locais, a regra suprema é: você pode fazer o que quiser, desde que saiba o que está fazendo.
Princípios, não produtos Vale parar por um momento para analisar a questão de por que, como eu disse antes, você, como um profissional de bancos de dados, precisa conhecer o modelo relacional. O motivo é que o modelo relacional não é específico a produtos; em vez disso, ele se preocupa com princípios. O que quero dizer com princípios? Bem, aqui está uma definição (do Chambers Twentieth Century Dictionary): princípio: uma fonte, raiz, origem: aquilo que é fundamental: essencial em natureza: base teórica: uma verdade fundamental a qual outras têm por base ou da qual se originam.
O ponto sobre princípios é: eles perduram. Por contraste, produtos e tecnologias (e a linguagem SQL, nesse sentido) mudam o tempo todo – mas princípios não. Por exemplo, vamos supor que você conheça Oracle; aliás, suponha que você seja um especialista em Oracle. Mas se Oracle é tudo que você conhece, então seu conhecimento não é necessariamente transferível para, digamos, um ambiente DB2 ou SQL Server (ele pode até dificultar o progresso nesse novo ambiente). Contudo, se você conhece os princípios subjacentes – em outras palavras, se você conhece o modelo relacional –, você tem conhecimentos e habilidades que serão transferíveis: conhecimentos e habilidades que você poderá aplicar em todos os ambientes e que nunca serão obsoletos. Neste livro, portanto, estaremos preocupados com princípios, não produtos, e fundamentos, não tendências ou modismos. Porém eu realmente percebo que, às vezes, você tem de fazer compromissos e concessões no mundo real. Como um exemplo, às vezes você pode ter boas razões pragmáticas para não projetar o banco de dados de modo teoricamente ótimo. Como outro exemplo, considere a SQL novamente. Ainda que certamente seja possível utilizar SQL relacionalmente (na maioria dos casos, de qualquer modo), às vezes você perceberá – porque implementações existentes estão longe de serem perfeitas – que existem severas penalidades de desempenho em fazê-lo... caso em que você pode ser mais ou menos forçado a fazer algo não “verdadeiramente relacional” (como escrever uma consulta de modo não natural para obrigar a implementação a usar um índice). No entanto, acredito firmemente que você deva sempre assumir esses compromissos e concessões a partir de uma posição de força conceitual. Ou seja: • Você deve compreender o que está fazendo quando decide assumir tal compromisso.
28
SQL e Teoria Relacional • Você deve saber qual é a situação teoricamente correta e deve ter fortes motivos para se afastar dela. • Você também deve documentar esses motivos, para que, caso eles deixem de ser válidos em algum momento futuro (por exemplo, em razão de um novo lançamento do produto utilizado fazer um trabalho melhor em algum sentido), possa ser possível abandonar o compromisso original.
A citação a seguir – que é atribuída a Leonardo da Vinci (1452-1519) e tem, portanto, aproximadamente 500 anos de idade – resume a situação admiravelmente: Aqueles que se enamoram da prática sem a teoria são como um navegador que entra em um navio sem leme nem bússola, que jamais tem certeza para onde vai. Sempre a prática deve ser edificada sobre um conhecimento sólido de teoria.
(Está bem, o itálico fui eu que acrescentei.)
Uma revisão do modelo original O propósito desta seção é servir como um pontapé inicial para discussões subsequentes; ela revisa alguns dos aspectos mais básicos do modelo relacional como originalmente definido. Note esse qualificador – “como originalmente definido”! Um dos equívocos mais difundidos sobre o modelo relacional é o de que ele é algo totalmente estático. Ele não é. Ele é, nesse sentido, como a matemática: a matemática também não é algo estático, mas muda ao longo do tempo. Na realidade, o modelo relacional pode, ele mesmo, ser visto como um pequeno ramo da matemática; como tal, ele evolui ao longo do tempo, à medida que novos teoremas são provados e novos resultados são descobertos. Além disso, essas novas contribuições podem ser feitas por qualquer pessoa que seja competente para fazê-lo; assim como outros ramos da matemática, o modelo relacional, ainda que tenha sido originalmente inventado por um homem, tornou-se um esforço comunitário e agora pertence ao mundo. Por falar nisso, caso você não saiba, esse homem foi E. F. Codd, na época, pesquisador da IBM (E de Edgar e F de Frank – mas ele sempre assinava com suas iniciais; para seus amigos, entre os quais tenho orgulho em me incluir, ele era Ted). Foi no final de 1968 que Codd, um matemático por treinamento, primeiro percebeu que a disciplina da matemática poderia ser utilizada para injetar princípios sólidos em um campo, o gerenciamento de bancos de dados, que antes era bastante deficiente em tais qualidades. Sua definição original do modelo relacional apareceu em um relatório de pesquisa da IBM em 1969; falarei mais sobre esse texto no apêndice G.
Capítulo 1 ■ Preparando o cenário
29
Características estruturais O modelo original tinha três componentes principais – estrutura, integridade e manipulação; descreverei brevemente um de cada vez. Por favor, note de imediato, no entanto, que todas as “definições” que darei são bastante genéricas; eu as tornarei mais precisas à medida que e quando for apropriado em capítulos futuros. Em primeiro lugar, então, estrutura. A principal característica estrutural é, evidentemente, a relação em si, e como todos sabem é comum desenharmos tais relações no papel como tabelas (veja a figura 1.1 para um exemplo autoexplicativo). Relações são definidas por tipos (também conhecidos como domínios); um tipo é basicamente um acervo conceitual de valores a partir dos quais atributos efetivos, em relações efetivas, assumem seus valores efetivos. Com referência, por exemplo, ao simples banco de dados de departamentos (departments) e funcionários (employees) da figura 1.1 pode haver um tipo chamado DNO (“números de departamentos”), que é o conjunto de todos os números de departamentos válidos, e daí o atributo chamado DNO na relação DEPT e o atributo chamado DNO na relação EMP conteriam valores desse acervo conceitual. (Por falar nisso, não é necessário – ainda que, muitas vezes, seja uma boa ideia – que atributos tenham o mesmo nome do tipo correspondente, e, frequentemente, eles não o terão. Veremos muitos contraexemplos futuramente.)
Figura 1.1 – Valores de exemplo do banco de dados de departamentos e funcionários.
Como disse, tabelas como aquela da figura 1.1 representam relações: relações de grau n, para sermos precisos. Uma relação de grau n pode ser imaginada como uma tabela com n colunas; as colunas nessa figura representam atributos da relação e as linhas representam tuplas. O valor n pode ser qualquer inteiro não negativo. Uma relação de grau 1 é dita como sendo unária; uma relação de grau 2 é dita binária; uma relação de grau 3, ternária; e assim por diante. O modelo relacional também suporta vários tipos de chaves. Para começar – e esse ponto é crucial! –, toda relação tem pelo menos uma chave candidata4. Uma chave candidata é apenas um identificador exclusivo; em outras palavras, ela é uma combinação de atributos – muitas vezes, mas nem sempre, uma “combinação” consistindo 4 Estritamente falando, essa frase deveria dizer “Cada relvar tem pelo menos uma chave candidata” (veja a seção “Relações versus relvars”, mais adiante). Nota: na realidade, uma afirmação similar também se aplica em outro ponto deste capítulo. O exercício 1.1 ao final do capítulo trata dessa questão.
30
SQL e Teoria Relacional
de apenas um único atributo –, de tal modo que toda tupla na relação tenha um valor exclusivo para a combinação em questão. Na figura 1.1, por exemplo, todo departamento tem um número de departamento exclusivo e todo funcionário tem um número de funcionário exclusivo, de modo que podemos dizer que {DNO} é uma chave candidata para DEPT e {ENO} é uma chave candidata para EMP. A propósito, repare nos caracteres de chaves de abertura e de fechamento; repetindo: chaves candidatas são sempre combinações, ou conjuntos, de atributos (mesmo quando o conjunto em questão contém apenas um atributo), e a representação convencional de um conjunto no papel é uma lista de elementos, separados por vírgulas, entre caracteres de chaves. Nota paralela: essa é a primeira vez que mencionei a expressão lista separada por vírgulas, mas vou utilizá-la bastante em páginas futuras. Ela pode ser definida desta forma: deixe que xyz seja algum construto 5 sintático (por exemplo, “nome de atributo”). Então a expressão lista separada por vírgulas xyz denota uma sequência de zero ou mais xyzs na qual cada par de xyzs adjacentes é separado por uma vírgula (assim como, opcionalmente, por um ou mais espaços antes ou depois da vírgula, ou em ambas as posições). Por exemplo, se A, B e C forem nomes de atributos, então todos os exemplos a seguir serão listas separadas por vírgulas de nomes de atributos: A,B,C C,A,B B A,C
Assim também é a sequência vazia de nomes de atributos. Além disso, quando alguma lista separada por vírgulas é colocada entre caracteres de chaves e, portanto, denota um conjunto, então (a) a ordem em que os elementos são mostrados dentro dessa lista é imaterial (pois conjuntos não têm ordenamento para seus elementos), e (b) se um elemento aparece mais de uma vez, ele é tratado como se aparecesse apenas uma vez (pois conjuntos não contêm elementos duplicados). Fim da nota paralela.
Continuando, uma chave primária é uma chave candidata que foi selecionada para tratamento especial de alguma forma. Se a relação em questão tem apenas uma chave candidata, não faz nenhuma diferença real se decidirmos chamar essa chave de “primária”. No entanto, se essa relação tem duas ou mais chaves candidatas, então é habitual escolher uma delas como primária, significando que ela é, de alguma forma, “mais igual do que as outras”. Vamos supor, por exemplo, que todo funcionário 5 N.T.: Construto é um elemento não tangível, criado para realizar uma determinada função (a rigor, tudo em programação são construtos, já que nenhum deles existe fisicamente).
Capítulo 1 ■ Preparando o cenário
31
sempre tenha tanto uma chave exclusiva de funcionário quanto um nome exclusivo de funcionário – esse talvez não seja um exemplo muito realista, mas é bom o suficiente para os propósitos atuais – de modo que {ENO} e {ENAME} sejam ambas chaves candidatas para EMP. Então poderíamos escolher {ENO}, por exemplo, para ser a chave primária. Observe que eu disse que é habitual escolher uma chave primária. De fato, é habitual – mas não é 100% necessário. Se houver apenas uma chave candidata, então haverá pouca escolha e nenhum problema; mas se houver duas ou mais, então ter de escolher uma e torná-la primária pode soar um tanto quanto arbitrário (pelo menos para mim). Certamente, há situações em que não parece haver nenhum bom motivo para fazermos tal escolha. Neste livro, portanto, vou geralmente seguir a disciplina da chave primária – e, em figuras como a figura 1.1, vou indicar atributos de chave primária com um sublinhado duplo6 –, mas desejo reforçar o fato de que são realmente as chaves candidatas, e não as chaves primárias, que são significativas a partir de um ponto de vista relacional. Parcialmente por esse motivo, deste ponto em diante vou utilizar o termo chave, sem qualificação, para significar qualquer chave candidata, independentemente de a chave candidata em questão ter sido designada como “primária”. (Caso você esteja se perguntado, o “tratamento especial” dado às chaves primárias com relação às chaves candidatas é, de qualquer modo, principalmente sintático em natureza; ele não é fundamental e não é muito importante.) Por fim, uma chave estrangeira (foreign key) é uma combinação, ou conjunto, de atributos FK em alguma relação r2 de modo que cada valor de FK tenha de ser igual a algum valor de alguma chave K em alguma relação r1 (r1 e r2 não são necessariamente distintas)7. Com referência à figura 1.1, por exemplo, {DNO} é uma chave estrangeira em EMP cujos valores devem corresponder a valores da chave {DNO} em DEPT (como tentei sugerir por meio de uma seta identificada adequadamente na figura). Aqui, por devem corresponder eu quero dizer que se, por exemplo, EMP contiver uma tupla na qual o atributo DNO tenha o valor D2, então DEPT também deve conter uma tupla na qual o atributo DNO tenha o valor D2 – pois, do contrário, EMP mostraria algum funcionário como se estivesse em um departamento inexistente, e o banco de dados não seria um “modelo fiel da realidade”.
Características de integridade Uma restrição de integridade (restrição, para abreviar) é, basicamente, apenas uma expressão booleana que deve ser avaliada como TRUE. No caso de departamentos e funcionários, por exemplo, podemos ter uma restrição de forma que valores de 6 Veja o exercício 5.27 no capítulo 5 para obter mais explicações sobre essa convenção. 7 Essa definição é deliberadamente um tanto quanto simplificada. Uma definição melhor pode ser encontrada no capítulo 5.
32
SQL e Teoria Relacional
SALARY (salário) devam ser maiores do que zero. Agora, qualquer banco de dados estará sujeito a inúmeras restrições; no entanto, todas essas restrições serão necessariamente específicas ao banco de dados e serão, portanto, expressas em termos das relações desse banco de dados. Por contraste, o modelo relacional como originalmente formulado inclui duas restrições genéricas – genéricas, no sentido de que se aplicam a qualquer banco de dados, livremente falando. Uma tem relação com chaves primárias e outra, com chaves estrangeiras. Aqui estão elas: • A regra de integridade de entidade: atributos de chave primária não permitem nulos. • A regra de integridade referencial: não deve haver nenhum valor de chave estrangeira não correspondido. Vou explicar a segunda regra, primeiro. Por valor de chave estrangeira não correspondido quero dizer um valor de chave estrangeira para o qual não exista um valor igual ao da chave candidata pertinente (a “chave-alvo”); assim, por exemplo, o banco de dados de departamentos para funcionários estaria em violação da regra de integridade referencial se incluísse, digamos, uma tupla EMP com um valor DNO de D2, mas nenhuma tupla DEPT com esse mesmo valor DNO. Assim, a regra de integridade referencial simplesmente explicita a semântica das chaves estrangeiras; o nome “integridade referencial” deriva do fato de que um valor de chave estrangeira pode ser considerado uma referência à tupla com esse mesmo valor para a chave-alvo correspondente. Na prática, portanto, a regra simplesmente diz: se B referencia A, então A deve existir. Quanto à regra de integridade de entidade, bem, aqui tenho um problema. O fato é que rejeito o conceito de “nulos” inteiramente; ou seja, tenho a forte opinião de que nulos não têm lugar no modelo relacional. (Codd, obviamente, pensava diferente, mas tenho razões significativas para assumir essa posição.) Para explicar a regra de integridade de entidade, portanto, preciso, nesse caso, suspender minha descrença (pelo menos por alguns momentos). O que começarei a fazer agora... Mas, por favor, entenda que voltarei a toda essa questão dos nulos nos capítulos 3 e 4. Em essência, então, um nulo é um “marcador” que significa valor desconhecido. Crucialmente, ele não é, em si, um valor; ele é, para repetir, um marcador, ou sinalizador. Por exemplo, vamos supor que não conheçamos o salário do funcionário E2. Então, em vez de inserir algum valor de SALARY real na tupla para o empregado E2 em relação a EMP – não podemos inserir um valor real, por definição, precisamente porque não sabemos qual deve ser esse valor – marcamos a posição do SALARY dentro dessa tupla como um nulo, como indicado aqui:
Capítulo 1 ■ Preparando o cenário
33
Agora, é importante entender que essa tupla não contém nada na posição SALARY. Porém é muito difícil fazer uma imagem do nada! Tentei mostrar que a posição SALARY está vazia, na figura anterior, usando um sombreado, mas seria mais correto não mostrar nada nessa posição. Seja como for, utilizarei essa mesma convenção de representação de posições vazias com sombreado em outros locais deste livro – mas esse sombreado, repito, não representa nenhum tipo de valor. Você pode pensar nisso (o sombreado, quer dizer) como constituindo o “marcador” – ou sinalizador de nulo, se preferir. Retornando à regra de integridade de entidade: em termos da relação EMP, então, essa regra diz, de maneira solta, que a tupla de determinado funcionário pode ter um nome desconhecido, ou um número de departamento desconhecido, ou um salário desconhecido – mas ela não pode ter um número de funcionário desconhecido. A justificativa para essa situação é que, se o número de funcionário fosse desconhecido, nem sequer saberíamos de qual “entidade” (isto é, qual funcionário) estaríamos falando. Isso é tudo que desejo dizer sobre nulos por enquanto. Por favor, esqueça-se deles até segunda ordem.
Características manipulativas A parte manipulativa do modelo, por sua vez, se divide em duas: • A álgebra relacional, que é uma coleção de operadores (por exemplo, de diferença, ou MINUS) que podem ser aplicados à relação. • Um operador de atribuição relacional, que permite que o valor de alguma expressão relacional (por exemplo, r1 MINUS r2, onde r1 e r2 são relações) seja atribuído a alguma relação. O operador de atribuição relacional é, fundamentalmente, a forma como atualizações são feitas no modelo relacional; terei mais a dizer sobre ele futuramente, na seção “Relações versus Relvars”. Nota: sigo a convenção habitual ao longo deste livro ao utilizar o termo genérico atualização para me referir aos operadores INSERT, DELETE e UPDATE (e de atribuição) considerados coletivamente. Quando quiser me referir ao operador UPDATE especificamente, vou utilizá-lo todo em letras maiúsculas, como acabamos de ver. Quanto à álgebra relacional, ela consiste em um conjunto de operadores que – falando bastante livremente – permitem-nos derivar “novas” relações de relações “antigas”. Cada operador desse tipo toma uma ou mais relações como entrada e produz outra relação como saída; por exemplo, a diferença (MINUS) toma duas relações como entrada e “subtrai” uma da outra, para derivar outra relação como saída. E é muito importante que a saída seja outra relação: essa é a bem conhecida propriedade de fechamento da álgebra relacional. A propriedade de fechamento é o que nos permite
34
SQL e Teoria Relacional
escrever expressões relacionais aninhadas; já que a saída de cada operação é do mesmo tipo da entrada, a saída de uma operação pode se tornar a entrada de outra. Por exemplo, podemos tomar a diferença r1 MINUS r2, alimentar o resultado como entrada de uma união com alguma relação r3, alimentar esse resultado para uma intersecção com alguma relação r4, e assim por diante. Agora, um número qualquer de operadores pode ser definido que se encaixe na simples definição de que “uma ou mais relações entram, exatamente uma relação sai”. Aqui, descrevo brevemente quais costumam ser imaginados como os operadores originais (essencialmente, aqueles que Codd definiu em seus textos iniciais)8; darei mais detalhes no capítulo 6 e, no capítulo 7, também descreverei vários operadores adicionais. A figura 1.2 é uma representação pictórica desses operadores originais.
Figura 1.2 – Álgebra relacional original. Nota: se você não está familiarizado com esses operadores e considera as descrições um pouco difíceis de acompanhar, não se preocupe com isso; como já disse, vou falar sobre isso muito mais detalhadamente, com vários exemplos, em capítulo futuros. 8 Exceto que Codd definiu também um operador chamado de divisão. Vou explicar no capítulo 7 porque omito esse operador aqui.
Capítulo 1 ■ Preparando o cenário
35
Restrição Retorna uma relação contendo todas as tuplas de uma relação específica que satisfaçam a uma condição especificada. Por exemplo, podemos restringir a relação EMP a apenas aquelas tuplas cujo valor DNO seja D2.
Projeção Retorna uma relação contendo todas as (sub)tuplas que permaneçam em uma relação especificada depois que atributos especificados foram removidos. Por exemplo, podemos projetar a relação EMP em apenas os atributos ENO e SALARY (removendo, dessa forma, os atributos ENAME e DNO).
Produto Retorna uma relação contendo todas as possíveis tuplas que sejam uma combinação de duas tuplas, uma de cada duas relações especificadas. Nota: esse operador também é conhecido variavelmente como produto cartesiano (às vezes, produto cartesiano estendido ou expandido), produto cruzado, junção cruzada e junção cartesiana; aliás, ele é apenas um caso especial de junção, como veremos no capítulo 6.
Intersecção Retorna uma relação contendo todas as tuplas que apareçam em ambas as relações especificadas. (Na realidade, a intersecção, tal como o produto, é também um caso especial de junção, como veremos no capítulo 6.)
União Retorna uma relação contendo todas as tuplas que apareçam em qualquer uma das duas, ou em ambas as relações especificadas.
Diferença Retorna uma relação contendo todas as tuplas que apareçam na primeira, mas não na segunda das duas relações especificadas.
Junção Retorna uma relação contendo todas as tuplas possíveis que sejam uma combinação de duas tuplas, uma de cada duas relações especificadas, de modo que as duas tuplas que contribuam para qualquer tupla de resultado tenham um valor comum para os atributos comuns das duas relações (e esse valor comum aparece apenas uma vez, e não duas, nessa tupla de resultado). Nota: esse tipo de junção era originalmente chamado de junção natural, para distingui-lo dos vários outros tipos que serão discutidos futuramente neste livro. Como, no
36
SQL e Teoria Relacional entanto, uma junção natural está longe de ser o tipo mais importante, tornou-se prática padrão tomar-se o termo não qualificado junção como significando a junção natural especificamente; seguirei essa prática neste livro.
Um último ponto antes de fecharmos esta subseção: como você provavelmente sabe, existe também algo chamado cálculo relacional. O cálculo relacional pode ser considerado uma alternativa à álgebra relacional; ou seja, em vez de dizer que a parte manipulativa do modelo relacional consiste na álgebra relacional (mais a atribuição relacional), podemos igualmente dizer que ela consiste no cálculo relacional (mais a atribuição relacional). Os dois são equivalentes e intercambiáveis, no sentido de que para cada expressão algébrica existe uma expressão logicamente equivalente no cálculo e vice-versa. Falarei mais sobre cálculo futuramente, principalmente nos capítulos 10 e 11.
O exemplo corrente Finalizarei esta breve revisão apresentando o exemplo que vou usar como base para a maioria das discussões, se não todas, do restante do livro: o familiar – para não dizer banalizado – banco de dados de fornecedores e peças. (Peço desculpas por mais uma vez trazer à vista esse velho cavalo de batalha, mas acredito que o uso de um mesmo exemplo em uma variedade de livros e outras publicações possa ajudar, e não prejudicar, o aprendizado.) Valores de exemplo são mostrados na figura 1.3. Para elaborar:
Figura 1.3 – Banco de dados de fornecedores e peças – valores de exemplo.
Fornecedores (suppliers) A relação S denota fornecedores (mais precisamente, fornecedores por contrato). Cada fornecedor tem um número de fornecedor (SNO), exclusivo para esse fornecedor (como você pode ver pela figura, tornei {SNO} a chave primária); um nome (SNAME), não necessariamente exclusivo (ainda que os valores de
Capítulo 1 ■ Preparando o cenário
37
SNAME na figura 1.3 parecem ser exclusivos); um valor de estado (STATUS), representando algum tipo de nível de classificação ou de preferência entre fornecedores disponíveis; e uma localização (CITY).
Peças (parts) A relação P denota peças (mais precisamente, tipos de peças). Cada tipo de peça tem um número de peça (PNO) que é exclusivo ({PNO} é a chave primária); um nome (PNAME); uma cor (COLOR); um peso (WEIGHT); e uma localização onde as peças desse tipo estão armazenadas (CITY).
Entregas (shipments) A relação SP denota entregas (ela mostra quais peças são fornecidas, ou entregues, e por quais fornecedores). Cada entrega tem um número de fornecedor (SNO), um número de peça (PNO) e uma quantidade (QTY). Para efeito do exemplo, presumo que haja, em um momento qualquer, no máximo uma entrega para um determinado fornecedor, e uma determinada peça ({SNO,PNO} é a chave primária; além disso, {SNO} e {PNO} são ambas chaves estrangeiras, correspondendo às chaves primárias de S e P, respectivamente). Note que o banco de dados da figura 1.3 inclui um fornecedor, o fornecedor S5, que não tem nenhuma entrega.
Modelo versus implementação Antes de avançarmos, há um ponto importante que preciso explicar, pois sustenta todo o restante a ser discutido neste livro. O modelo relacional é, evidentemente, um modelo de dados. Infelizmente, no entanto, esse último termo tem dois significados bastante distintos no mundo dos bancos de dados. O primeiro e mais fundamental é este: Definição: um modelo de dados (primeiro sentido) é uma definição abstrata, autocontida e lógica das estruturas de dados, dos operadores de dados, e assim por diante; reunidos, esses fatores formam a máquina abstrata com a qual os usuários interagem. Esse é o significado em que pensamos quando falamos sobre o modelo relacional em particular. E, armados dessa definição, podemos, de modo proveitoso e importante, avançar para distinguir um modelo de dados, nesse primeiro sentido, de sua implementação, que pode ser definida desta forma: Definição: uma implementação de determinado modelo de dados é uma realização física em uma máquina real dos componentes da máquina abstrata que, reunidos, constituem esse modelo.
38
SQL e Teoria Relacional
Deixe-me ilustrar essas definições em termos do modelo relacional, especificamente. Em primeiro lugar, considere o conceito de relação em si. Esse conceito é parte do modelo: usuários têm de saber quais são as relações, têm de saber que elas são formadas por tuplas e atributos, têm de saber como interpretá-las, e assim por diante. Tudo isso é parte do modelo. Porém eles não têm de saber como as relações são fisicamente armazenadas no disco, como valores de dados individuais são fisicamente codificados ou que índices e outros caminhos de acesso existem; tudo isso é parte da implementação, e não parte do modelo. Ou considere o conceito de uma junção: usuários têm de saber o que é uma junção, têm de saber como invocar uma junção, têm de saber qual a apresentação do resultado de uma junção, e assim por diante. Novamente, tudo isso é parte do modelo. No entanto eles não têm de saber como as junções são fisicamente implementadas, quais transformações de expressão ocorrem nos bastidores, quais índices ou outros caminhos de acesso são utilizados ou que operações físicas de E/S ocorrem; tudo isso é parte da implementação, e não parte do modelo. E mais um exemplo: chaves candidatas (chaves, para abreviar) são, uma vez mais, parte do modelo, e usuários definitivamente têm de saber o que as chaves são; em particular, eles têm de saber que tais chaves têm a propriedade de exclusividade. Agora, a exclusividade das chaves é reforçada nos sistemas atuais por meio do que é chamado de um “índice exclusivo”; mas índices em geral, e índices exclusivos em particular, não são parte do modelo, eles são parte da implementação. Assim, um índice exclusivo não deve ser confundido com uma chave no sentido relacional, ainda que o primeiro possa ser utilizado para implementar o segundo (mais precisamente, para implementar alguma restrição de chave – veja o capítulo 8). Em resumo, então: • O modelo (primeiro significado) é o que o usuário precisa saber. • A implementação é o que o usuário não precisa saber. Por favor, compreenda que não estou dizendo aqui que usuários estão proibidos de conhecer a implementação; estou apenas dizendo que eles não têm de fazê-lo. Em outras palavras, tudo que está relacionado à implementação deve estar, pelo menos potencialmente, escondido do usuário. Aqui estão algumas consequências importantes das definições anteriores. Em primeiro lugar, observe que tudo que está relacionado ao desempenho é fundamentalmente uma questão de implementação, e não de modelo. Esse ponto é amplamente mal compreendido! Por exemplo, muitas vezes ouvimos afirmações no sentido de que “junções são lentas”. Porém tais afirmações simplesmente não fazem sentido. Uma junção é parte do modelo, e o modelo como tal não pode ser dito como sendo rápido
Capítulo 1 ■ Preparando o cenário
39
ou lento; apenas a implementação pode ser dita como detentora de qualquer qualidade desse tipo. Assim, podemos razoavelmente dizer que algum produto específico X tenha uma implementação mais rápida ou mais lenta de alguma junção específica, em alguns dados específicos, do que algum outro produto específico Y – mas isso é praticamente tudo. Agora, não quero passar uma impressão errada aqui. É verdade que o desempenho é fundamentalmente uma questão de implementação; no entanto, isso não significa que uma boa implementação terá um bom desempenho se você utilizar o modelo erroneamente. Na realidade, essa é precisamente uma das razões por que você precisa conhecer o modelo: para que você não o utilize erroneamente. Se você escrever uma expressão como S JOIN SP, está dentro de seus direitos esperar que o sistema a implemente com eficiência; mas se você insistir, com efeito, em codificar manualmente a junção, talvez desta forma (em pseudocódigo) – do for all tuples in S ; fetch S tuple into TS , TN , TT , TC ; do for all tuples in SP with SNO = TS ; fetch SP tuple into TS , TP , TQ ; emit TS , TN , TT , TC , TP , TQ ; end ; end ;
–, então não haverá como você obter um bom desempenho. Recomendação: não faça isso. Sistemas relacionais não devem ser utilizados como simples métodos de acesso.9 Por falar nisso, essas observações sobre desempenho também se aplicam à SQL. Assim como os operadores relacionais (de junção e outros), não se pode dizer que a SQL seja rápida ou lenta – apenas implementações podem razoavelmente ser descritas em tais termos –, mas também é possível usar SQL de forma a garantir um mau desempenho. Ainda que, em geral, eu tenha pouco a dizer sobre desempenho neste livro, vou ocasionalmente apontar certas implicações de desempenho naquilo que estou recomendando. Nota paralela: gostaria de me aprofundar por um momento na questão de desempenho. Em geral, minhas recomendações neste livro nunca têm por base o desempenho como um motivador principal; afinal, sempre foi um objetivo do modelo relacional retirar considerações de desempenho das mãos do usuário e colocá-las, em vez disso, nas mãos do sistema. Entretanto, não é preciso dizer que esse objetivo não foi totalmente atingido, e assim (como eu já disse) 9 Mais de um revisor observou que essa frase não fazia sentido (como um sistema pode ser utilizado como um método?). Bem, se você é jovem demais para estar familiarizado com o termo método de acesso, eu o invejo; mas a realidade é que, esse termo, ainda que inapropriado fosse (e seja), foi amplamente usado durante muitos anos como significando um simples recurso de E/S de nível de registro, de um tipo ou de outro.
40
SQL e Teoria Relacional a meta de utilizar SQL relacionalmente deve, às vezes, ser comprometida no interesse de se obter um desempenho satisfatório. Essa é outra razão por que, como disse antes neste capítulo, a regra suprema deve ser: “você pode fazer o que quiser, desde que saiba o que está fazendo”. Fim da nota paralela.
De volta ao modelo versus implementação, e aos pontos que surgem dessa distinção, o segundo ponto é que, como você provavelmente percebeu, é precisamente a separação de modelo e implementação que nos permite obter independência de dados físicos. A independência de dados físicos – não é um ótimo termo, mas parece que estamos presos a ele – significa que temos a liberdade de fazer mudanças na forma como os dados são fisicamente armazenados e acessados sem termos de fazer as mudanças correspondentes na forma como os dados são percebidos pelo usuário. Agora, a razão por que podemos querer alterar esses detalhes de armazenamento e acesso é, tipicamente, desempenho; e o fato de podermos fazer tais alterações sem ter de modificar a forma como os dados aparecem para o usuário significa que programas e consultas existentes, e outros elementos do tipo, podem todos ainda funcionar depois da alteração. De forma muito importante, então, a independência de dados físicos significa proteger o investimento em treinamento de usuário e aplicações (investimento também no projeto de bancos de dados lógicos, devo acrescentar). É uma consequência de tudo que acabamos de ver que, como indicado previamente, índices e, na realidade, caminhos de acesso físico de qualquer tipo sejam adequadamente parte da implementação, e não do modelo; eles pertencem aos bastidores e devem estar escondidos do usuário. (Note que caminhos de acesso como tais não são mencionados em nenhum lugar do modelo relacional.) Pelos mesmos motivos, eles também devem ser rigorosamente excluídos da SQL. Recomendação: evite o uso de qualquer construto SQL que viole esse preceito. (Na realidade, não há nada no padrão que o faça, até onde estou ciente, mas sei que o mesmo não é verdadeiro quanto a certos produtos SQL.) De qualquer forma, como você pode ver a partir das definições anteriores, a distinção entre modelo e implementação é, na realidade, apenas um caso especial – um caso especial muito importante – da distinção habitual entre considerações lógicas e físicas em geral. Infelizmente, entretanto, a maioria dos sistemas SQL atuais não faz essas distinções tão claramente como deveria. Como consequência direta, entregam muito menos independência de dados físicos do que deveriam, e muito menos do que, em princípio, sistemas relacionais são capazes. Voltarei a essa questão na próxima seção. Agora, volto-me para o segundo significado do termo modelo de dados, com o qual, ouso dizer, você deve estar bastante familiarizado. Ele pode ser definido desta forma: Definição: um modelo de dados (segundo sentido) é um modelo dos dados – especialmente dos dados persistentes – de alguma empresa específica.
Capítulo 1 ■ Preparando o cenário
41
Em outras palavras, um modelo de dados no segundo sentido é apenas um projeto de banco de dados (lógico e, possivelmente, um tanto quanto abstrato). Por exemplo, podemos falar do modelo de dados de algum banco, algum hospital ou algum departamento do governo. Tendo explicado esses dois significados distintos, gostaria de chamar sua atenção para uma analogia que acredito que esclareça o relacionamento entre eles: • Um modelo de dados no primeiro sentido é como uma linguagem de programação, cujos construtos podem ser usados para solucionar muitos problemas específicos, mas que, por si só, não têm conexão direta com nenhum desses problemas específicos. • Um modelo de dados no segundo sentido é como um programa específico escrito nessa linguagem – ele utiliza os recursos fornecidos pelo modelo, no primeiro sentido do termo, para solucionar algum problema específico. Por falar nisso, é uma consequência do que vimos que, se estamos falando sobre modelos de dados no segundo sentido, então podemos razoavelmente falar de “modelos relacionais” no plural, ou de “um” modelo relacional (com um artigo indefinido). Porém, se estamos falando sobre modelos de dados no primeiro sentido, então há apenas um modelo relacional, e ele é o modelo relacional (com o artigo definido). Terei mais a dizer sobre esse último ponto no apêndice A. Pelo restante deste livro, vou utilizar o termo modelo de dados, ou, de forma geral, apenas modelo, para simplificar, exclusivamente no primeiro sentido.
Propriedades de relações Agora vamos retornar para nossa análise de conceitos relacionais básicos. Nesta seção, quero me concentrar em algumas propriedades das relações em si. Em primeiro lugar, toda relação tem um cabeçalho e um corpo: o cabeçalho é um conjunto de atributos (onde, pelo termo atributo, quero dizer, muito especificamente, um par nome-de-atributo/nome-de-tipo, sendo que não há dois atributos com o mesmo nome de atributo), e o corpo é um conjunto de tuplas que se conformam a esse cabeçalho. No caso da relação de fornecedores (S) da figura 1.3, por exemplo, há quatro atributos no cabeçalho e cinco tuplas no corpo. Note, portanto, que na realidade uma relação não contém tuplas – ela contém um corpo, e esse corpo, por sua vez, contém as tuplas –, mas falamos geralmente como se as relações contivessem tuplas diretamente, por simplicidade. Por falar nisso, ainda que esteja estritamente correto dizer que o cabeçalho consiste em pares nome-de-atributo/nome-de-tipo, é habitual omitir os nomes de tipo em figuras como a figura 1.3, e, dessa forma, agir como se o cabeçalho fosse apenas um conjunto de nomes de atributos. Por exemplo, o atributo STATUS tem, de fato, um
42
SQL e Teoria Relacional
tipo – INTEGER, digamos –, mas eu não o mostrei na figura 1.3. Entretanto, você nunca deve esquecer que ele está lá! Em seguida, o número de atributos no cabeçalho é o grau (às vezes, a aridade), e o número de tuplas no corpo é a cardinalidade. Por exemplo, a relação S na figura 1.3 tem grau 4 e cardinalidade 5; da mesma forma, a relação P dessa figura tem grau 5 e cardinalidade 6, e a relação SP dessa figura tem grau 3 e cardinalidade 12. Nota: o termo grau também é usado em conexão com as tuplas10. Por exemplo, as tuplas na relação S são (assim como a relação S em si) todas de grau 4. Além disso, relações nunca contêm tuplas duplicadas. Essa propriedade se verifica porque um corpo é definido como um conjunto de tuplas, e conjuntos em matemática não contêm elementos duplicados. Agora, a SQL falha aqui, como estou certo de que você sabe: é permitido que tabelas SQL contenham linhas duplicadas, não sendo, portanto, relações em geral. Por favor, compreenda, portanto, que ao longo deste livro vou sempre usar o termo “relação” para representar uma relação – sem tuplas duplicadas, por definição – e não uma tabela SQL. Compreenda também, por favor, que operações relacionais sempre produzem um resultado sem tuplas duplicadas, novamente por definição. Por exemplo, projetar a relação de fornecedores da figura 1.3 sobre CITY produz o resultado mostrado aqui na esquerda, e não aquele da direita:
(O resultado da esquerda pode ser obtido por meio da consulta SQL SELECT DISTINCT CITY FROM S. Omitindo esse DISTINCT, conseguimos o resultado não relacional da direita. Note, em particular, que a tabela da direita não tem nenhum sublinhado duplo; isso porque ela não tem nenhuma chave e, portanto, nenhuma chave primária a fortiori.) Além disso, as tuplas de uma relação são não ordenadas, de cima para baixo. Essa propriedade se verifica porque, novamente, um corpo é definido como sendo um conjunto, e conjuntos em matemática não têm ordenamento em seus elementos (portanto, por exemplo, {a,b,c} e {c,a,b} são o mesmo conjunto em matemática, e uma afirmação semelhante naturalmente se aplica ao modelo relacional). Evidentemente, quando desenhamos uma relação como uma tabela no papel, temos realmente de mostrar as linhas em alguma ordem de cima para baixo, mas essa ordenação não corresponde a nada relacional. No caso da relação de fornecedores como representada na figura 1.3, por exemplo, eu poderia ter mostrado as linhas em qualquer ordem – digamos 10 Ele também é utilizado em conexão com chaves (veja o capítulo 5).
Capítulo 1 ■ Preparando o cenário
43
fornecedor S3, depois S1, depois S5, depois S4, depois S2 –, e a figura ainda representaria a mesma relação. Nota: o fato de as relações não terem ordenação para suas tuplas não significa que consultas não possam incluir uma especificação ORDER BY, mas significa, na realidade, que tais consultas produzem um resultado que não é uma relação. ORDER BY é útil para apresentação de resultados, mas não é um operador relacional como tal. De modo semelhante, os atributos de uma relação também são não ordenados da esquerda para a direita, pois um cabeçalho também é um conjunto matemático. Novamente, quando desenhamos uma relação como uma tabela no papel, temos de apresentar as colunas em alguma ordem da esquerda para a direita, mas essa ordenação não corresponde a nada relacional. No caso da relação de fornecedores como representada na figura 1.3, por exemplo, eu poderia ter mostrado as colunas em qualquer ordem da esquerda para a direita – digamos STATUS, SNAME, CITY, SNO –, e a figura ainda representaria a mesma relação no modelo relacional. Incidentalmente, a SQL também falha aqui. Tabelas SQL têm uma ordenação da esquerda para a direita para suas colunas (outro motivo pelo qual tabelas SQL não são relações em geral). Por exemplo, estas duas figuras representam a mesma relação, mas tabelas SQL diferentes:
(As consultas SQL correspondentes são SELECT SNO, CITY FROM S e SELECT CITY, SNO FROM S, respectivamente. Agora, você pode estar pensando que as diferenças entre essas duas consultas, e entre essas duas tabelas, dificilmente sejam muito significativas; na realidade, entretanto, elas têm algumas consequências sérias, algumas das quais veremos em capítulos futuros. Veja, por exemplo, a discussão do operador JOIN explícito da SQL no capítulo 6.) Por fim, relações são sempre normalizadas (de forma equivalente, elas estão na primeira forma normal, 1NF)11. Informalmente, o que isso significa é que, em termos da figura tabular de uma relação, em toda intersecção de linha e coluna sempre vemos apenas um único valor. Mais formalmente, isso significa que toda tupla em toda relação contém apenas um único valor, do tipo apropriado, em cada posição de atributo. Nota: terei muito mais a dizer sobre essa questão particular no próximo capítulo. Antes de encerrar esta seção, gostaria de enfatizar algo que já mencionei várias vezes: mais especificamente, o fato de que há uma diferença lógica entre uma relação como 11 “Primeira” forma normal porque, como estou certo de que você sabe, é possível definir uma série de formas normais “mais elevadas” – segunda forma normal, terceira forma normal e assim por diante – que são relevantes à disciplina do projeto de bancos de dados.
44
SQL e Teoria Relacional
tal por um lado, e uma imagem de uma relação, como mostrada, por exemplo, nas figuras 1.1 e 1.3, por outro. Para dizer mais uma vez, os construtos nas figuras 1.1 e 1.3 não são relações, mas, em vez disso, imagens de relações – às quais geralmente me refiro como tabelas, apesar do fato de tabela ser uma palavra carregada em contextos SQL. Evidentemente, relações e tabelas realmente têm certos pontos de semelhança, e, em contextos informais, é habitual, e geralmente aceitável, dizermos que elas representam o mesmo conceito. Porém quando estamos tentando ser precisos – e agora estou tentando ser um pouco preciso – temos realmente de reconhecer que os dois conceitos não são idênticos. Como uma nota paralela, observo que, mais genericamente, há uma diferença lógica entre um elemento de qualquer tipo e uma imagem desse elemento. Há uma famosa pintura de Magritte que ilustra belamente o ponto que estou tentando defender aqui. A pintura é a de um cachimbo de tabaco normal, mas na parte inferior Magritte escreveu Ceçi n’est pas une pipe (Isto não é um cachimbo)... O sentido, é claro, é de que obviamente a pintura não é um cachimbo – em vez disso, ela é a pintura de um cachimbo. Tendo dito tudo isso, devo agora dizer também que é, na realidade, uma grande vantagem do modelo relacional que seu objeto abstrato básico, a relação, tenha uma representação tão simples no papel; é essa representação simples no papel que faz com que os sistemas relacionais sejam fáceis de ser usados e fáceis de ser compreendidos, e também facilita o raciocínio sobre a forma como tais sistemas se comportam. No entanto, também é, infelizmente, provável que essa representação simples realmente sugira alguns pontos que não sejam verdadeiros (por exemplo, que haja uma ordenação de tuplas de cima para baixo). E mais um ponto adicional: como já disse, há uma diferença lógica entre uma relação e uma imagem de uma relação. O conceito da diferença lógica deriva de uma máxima de Wittgenstein: Todas as diferenças lógicas são grandes diferenças.
Essa noção é extraordinariamente útil; como uma “ferramenta da mente”, ela é de grande auxílio para um raciocínio claro e preciso, e pode ser muito útil na identificação e análise de algumas confusões que são, infelizmente, muito comuns no mundo dos bancos de dados. Vou recorrer a ela muitas vezes nas páginas seguintes. Nesse meio-tempo, deixe-me destacar que já encontramos algumas diferenças lógicas importantes. Aqui estão algumas delas: • SQL versus o modelo relacional. • Modelo versus implementação. • Modelo de dados (primeiro sentido) versus modelo de dados (segundo sentido). E encontraremos muitas outras nas páginas que virão.
Capítulo 1 ■ Preparando o cenário
45
Alguns pontos cruciais Neste momento, gostaria de mencionar alguns pontos cruciais que vou elaborar em capítulos futuros (especialmente no capítulo 3). Os pontos em questão são estes: • Todo subconjunto de uma tupla é uma tupla: por exemplo, considere a tupla do fornecedor S1 na figura 1.3. Essa tupla tem quatro componentes que correspondem aos quatro atributos SNO, SNAME, STATUS e CITY. Se removermos (digamos) o componente SNAME, o que sobra ainda é realmente uma tupla: a saber, uma tupla com três componentes (uma tupla de grau 3). • Todo subconjunto de um cabeçalho é um cabeçalho: por exemplo, considere o cabeçalho da relação de fornecedores na figura 1.3. Esse cabeçalho tem quatro atributos: SNO, SNAME, STATUS e CITY. Se removermos (digamos) os atributos SNAME e STATUS, o que sobra ainda é um cabeçalho, um cabeçalho de grau 2. • Todo subconjunto de um corpo é um corpo: por exemplo, considere o corpo da relação de fornecedores da figura 1.3. Esse corpo tem cinco tuplas, correspondendo aos cinco fornecedores S1, S2, S3, S4 e S5. Se removermos (digamos) as tuplas S1 e S3, o que sobra ainda é um corpo, um corpo de cardinalidade três. Nota: talvez eu deva afirmar apenas para registro que, ao longo deste livro – de acordo com a prática normal – tomarei expressões na forma “B é um subconjunto de A” como incluindo a possibilidade de que A e B possam ser iguais. Assim, por exemplo, toda tupla é um subconjunto dela mesma (e assim também todo cabeçalho, e assim também todo corpo). Quando quiser excluir tal possibilidade, falarei explicitamente em termos de subconjuntos próprios. Por exemplo, nossa tupla habitual do fornecedor S1 é certamente um subconjunto de si mesma, mas não é um subconjunto próprio de si mesma. Além disso, as afirmações anteriores aplicam-se igualmente aos superconjuntos, mutatis mutandis (mudando o que tem que ser mudado); por exemplo, a tupla do fornecedor S1 é um superconjunto de si mesma, mas não um superconjunto próprio de si mesma.12
Também gostaria de dizer algo sobre a noção crucial de igualdade – especialmente pelo fato de essa noção ser aplicada a tuplas e a relações, especificamente. Em geral, dois valores são iguais se – e apenas se – eles tiverem o mesmo valor. Por exemplo, o inteiro 3 é igual ao inteiro 3, e a nada mais – em particular, ele não é igual a qualquer outro inteiro. Exatamente da mesma forma, duas tuplas são iguais se – e apenas se – elas 12 O que descrevi nesse parágrafo é a convenção matemática padrão; no entanto, você pode ter encontrado uma convenção diferente em contextos menos formais. Para ser específico, algumas pessoas utilizam “B é um subconjunto de A” como significando o que quero dizer quando digo que B é um subconjunto próprio de A, e utilizam “B é um subconjunto de ou igual a A” como significando o que quero dizer quando digo que B é um subconjunto de A. Da mesma forma para superconjuntos, mutatis mutandis.
46
SQL e Teoria Relacional
forem exatamente a mesma tupla. Com referência à figura 1.3, por exemplo, a tupla do fornecedor S1 é igual à tupla do fornecedor S1, e a nada mais – em particular, ela não é igual a qualquer outra tupla. Em outras palavras, duas tuplas são iguais se – e apenas se – (a) elas envolverem exatamente os mesmos atributos e os (b) valores de atributo correspondentes forem iguais, por sua vez. Ademais (isso pode parecer óbvio, mas precisa ser dito), duas tuplas são duplicatas uma da outra se – e apenas se – elas forem iguais. Voltando-nos agora para as relações: exatamente da mesma forma, duas relações são iguais se – e apenas se – elas forem exatamente a mesma relação. Com referência à figura 1.3, por exemplo, a relação de fornecedores é igual à relação de fornecedores e a nada mais – em particular, ela não é igual a qualquer outra relação. Em outras palavras, duas relações são iguais se – e apenas se – os seus cabeçalhos forem iguais e seus corpos forem iguais. Como já disse, vou retornar a essas questões no capítulo 3. Aqui, deixe-me apenas acrescentar que a noção de igualdade de tuplas em particular é absolutamente fundamental – praticamente tudo no modelo relacional é crucialmente dependente dela, como veremos.
Relações de base versus relações derivadas Como expliquei antes, os operadores da álgebra relacional nos permitem iniciar com algumas relações dadas, como aquelas representadas na figura 1.3, e obter relações adicionais a partir das primeiras (por exemplo, efetuando consultas). As relações dadas são referidas como relações de base, as outras são relações derivadas. Para darmos nosso primeiro passo, portanto, um sistema relacional tem, em primeiro lugar, de fornecer meios para definição dessas relações de base. Em SQL, essa tarefa é realizada pela instrução CREATE TABLE (a contraparte SQL para uma relação de base seria, é claro, uma tabela de base, que é o que CREATE TABLE cria). E relações de base obviamente precisam ser nomeadas – por exemplo: CREATE TABLE S ... ;
Mas certas relações derivadas, incluindo, em particular, as que são chamadas de visões (views), também são nomeadas. Uma visão (também conhecida como uma relação virtual) é uma relação nomeada cujo valor em um tempo qualquer t é o resultado da avaliação de determinada expressão relacional nesse tempo t. Aqui está um exemplo SQL: CREATE VIEW SST_PARIS AS ( SELECT SNO , STATUS FROM S WHERE CITY = 'Paris' ) ;
Capítulo 1 ■ Preparando o cenário
47
Em princípio, você pode operar sobre visões exatamente como se elas fossem relações de base13, mas elas não são relações de base. Em vez disso, você pode pensar em uma visão como sendo “materializada” – na prática, você pode pensar em uma relação de base sendo construída e cujo valor seja obtido avaliando-se a expressão relacional especificada – no momento em que a visão em questão é referenciada. Porém devo enfatizar que pensar em visões como sendo materializadas dessa forma quando elas são referenciadas é puramente conceitual; isso é apenas uma forma de pensar; não é o que deve acontecer; e não funcionaria para operações de atualização de qualquer forma. A forma como as visões devem realmente funcionar é explicada no capítulo 9. Por falar nisso, há um ponto importante que preciso defender aqui. Você muitas vezes ouvirá a diferença entre relações de base e visões sendo descrita desta forma (cuidado! aí vêm algumas inverdades!): • Relações de base realmente existem – ou seja, elas são fisicamente armazenadas no banco de dados. • Visões, por contraste, não “existem realmente” – elas meramente fornecem formas diferentes de analisarmos as operações de base. No entanto o modelo relacional não tem nada a dizer quanto ao que é fisicamente armazenado! – aliás, ele não tem nada a dizer sobre nenhuma questão de armazenamento físico. Em particular, ele categoricamente não diz que relações de base são fisicamente armazenadas. O único requisito é que haja algum mapeamento entre o que é fisicamente armazenado e essas relações de base, para que essas relações de base possam de alguma forma ser obtidas quando forem necessárias (conceitualmente, de qualquer modo). Se as relações de base puderem ser obtidas a partir do que quer que esteja fisicamente armazenado, então tudo mais também poderá. Por exemplo, poderíamos armazenar fisicamente a junção de fornecedores e entregas, em vez de armazená-las separadamente; então, relações de base S e SP poderiam ser obtidas, conceitualmente, tomando-se projeções apropriadas dessa junção. Em outras palavras: relações de base não são mais (nem menos!) “físicas” do que visões, no que se refere ao modelo relacional. O fato de o modelo relacional não dizer nada sobre o armazenamento físico é deliberado, é claro. A ideia era fornecer a implementadores a liberdade de implementar o modelo da forma que escolhessem – em particular, de qualquer forma que parecesse produzir bom desempenho – sem comprometer a independência de dados físicos. O fato triste, no entanto, é que a maioria dos fornecedores de produtos SQL não parece ter entendido esse ponto (ou não se dispôs a enfrentar o desafio, de qualquer 13 Talvez você esteja pensando que essa afirmação não possa ser 100% verdadeira para operações de atualização. Se for assim, você pode estar certo no que se refere aos produtos SQL de hoje; ainda assim, continuo afirmando que isso é verdade em princípio. Veja a seção “Operações de atualização” no capítulo 9 para discussões adicionais.
48
SQL e Teoria Relacional
forma); em vez disso, eles mapeiam tabelas de base de modo razoavelmente direto ao armazenamento físico14, e (como mencionado previamente) seus produtos, portanto, oferecem muito menos independência de dados físicos do que sistemas relacionais são ou deveriam ser capazes. De fato, essa situação é refletida no próprio padrão SQL (assim como na maioria das outras documentações SQL), o qual tipicamente – quase sempre, aliás – fala em termos de “tabelas e visões”. Claramente, qualquer pessoa que fale dessa forma está com a impressão de que tabelas e visões sejam elementos distintos, e provavelmente também que “tabelas” sempre signifiquem especificamente tabelas de base e que tabelas de base sejam fisicamente armazenadas e visões não. Porém todo o sentido de uma visão é o de que ela é uma tabela (ou, como eu preferiria dizer, uma relação); ou seja, podemos efetuar os mesmos tipos de operações em visões que podemos efetuar em relações regulares (pelo menos no modelo relacional), pois visões são “relações regulares”. Ao longo deste livro, portanto, utilizarei o termo relação para representar uma relação (possivelmente uma relação de base, possivelmente uma visão, possivelmente um resultado de consulta, e assim por diante); e, se quiser me referir a uma relação de base especificamente, direi “relação de base”. Recomendação: sugiro veementemente que você adote a mesma disciplina. Não caia na armadilha habitual de pensar que o termo relação significa especificamente uma relação de base – ou, em termos SQL, de pensar que o termo tabela significa especificamente uma tabela de base. Da mesma forma, não caia na armadilha habitual de pensar que relações de base (ou tabelas de base, em SQL) têm de ser fisicamente armazenadas.
Relações versus relvars Agora, é inteiramente possível que você já soubesse tudo que vim lhe dizendo até aqui neste capítulo; aliás, até espero que você saiba, apesar de também esperar que isso não signifique que você tenha achado o material maçante. De qualquer forma, agora abordarei algo que você pode ainda não conhecer. O fato é que historicamente tem havido muita confusão sobre mais uma diferença lógica: mais precisamente, aquela entre relações como tais por um lado, e variáveis de relações (relation variables) por outro. Esqueça os bancos de dados por um momento; considere, em vez disso, o simples exemplo de linguagem de programação a seguir. Vamos supor que eu diga, em alguma linguagem de programação: DECLARE N INTEGER ... ; 14 Digo isso totalmente consciente de que a maioria dos produtos SQL de hoje de fato fornece opções para operações de hash, particionamento, indexação, agrupamento, entre outras formas de organização dos dados armazenados em disco. Apesar dessa situação, ainda considero o mapeamento entre tabelas de base e armazenamento físico nesses produtos como razoavelmente direto.
49
Capítulo 1 ■ Preparando o cenário
Então, N aqui não é um inteiro. Em vez disso, ele é uma variável, cujos valores são inteiros como tais –inteiros diferentes em momentos distintos. Todos nós entendemos isso. Bem, exatamente da mesma forma, se digo em SQL CREATE TABLE T ... ;
então T não é uma tabela. Ela é uma variável, uma variável de tabela ou (como preferiria dizer, ignorando diversas peculiaridades SQL tais como linhas duplicadas e ordenação de colunas da esquerda para a direita) uma variável de relação, cujos valores são relações como tais (relações diferentes em momentos distintos). Dê mais uma olhada na figura 1.3 – o banco de dados de fornecedores e peças. Essa figura mostra três valores – mais especificamente, valores de relação que chegam a existir no banco de dados em algum momento específico. No entanto, se fôssemos verificar o banco de dados em algum momento distinto, provavelmente veríamos três valores de relação diferentes sendo mostrados no lugar deles. Em outras palavras, S, P e SP nesse banco de dados são realmente variáveis: variáveis de relação, para sermos precisos. Por exemplo, vamos supor que a variável de relação S atualmente tenha o valor mostrado na figura 1.3 – o valor de relação, quer dizer – e vamos supor que excluamos o conjunto de tuplas (na realidade há apenas uma) para fornecedores em Atenas: DELETE
S
WHERE
CITY
=
'Athens'
;
Aqui está o resultado:
Conceitualmente, o que aconteceu aqui é que o valor antigo de S foi totalmente substituído por um novo valor. Evidentemente, o valor antigo (com cinco tuplas) e o novo (com quatro) são bastante semelhantes em certo sentido, mas eles definitivamente são valores distintos. Aliás, o DELETE que acabamos de mostrar é logicamente equivalente, e, na realidade, uma abreviação da atribuição relacional que vemos a seguir: S := S MINUS ( S WHERE CITY = 'Athens' ) ;
Assim como em todas as atribuições, o efeito aqui é que (a) a expressão de origem no lado direito é avaliada e, daí, (b) o valor que é o resultado dessa avaliação é, então, atribuído à variável-alvo no lado esquerdo, com o resultado geral já explicado. Nota paralela: não posso demonstrar a afirmação precedente em SQL porque a SQL não suporta diretamente uma atribuição relacional. Em vez disso, demonstrei essa afirmação (assim como o DELETE original) em uma linguagem mais ou menos autoexplicativa chamada Tutorial D. Tutorial D é a linguagem
50
SQL e Teoria Relacional que Hugh Darwen e eu usamos para ilustrar ideias relacionais em nosso livro Databases, Types, and the Relational Model: The Third Manifesto (veja o apêndice G) – e também vou usá-la no presente livro, quando estiver explicando conceitos relacionais15. Contudo, uma vez que meu público-alvo seja os praticantes de SQL, também vou, na maioria dos casos, mostrar os análogos em SQL. Nota: uma gramática BNF para a Tutorial D pode ser encontrada no apêndice D. Fim da nota paralela.
Para repetir, DELETE é a abreviação para uma determinada atribuição relacional – e, é claro, uma observação análoga se aplica também a INSERT e UPDATE: eles também são basicamente apenas abreviações para determinadas atribuições relacionais. Dessa forma, como mencionei na seção “Uma revisão do modelo original”, a atribuição relacional é o operador de atualização fundamental no modelo relacional; de fato, ela é o único operador de atualização de que realmente precisamos, logicamente falando. Assim, há uma diferença lógica entre valores de relação e variáveis de relação. O problema é que a literatura de bancos de dados tem historicamente usado o mesmo termo, relação, para significar ambos, e essa prática certamente levou à confusão16. Neste livro, portanto, vou distinguir muito cuidadosamente entre os dois daqui em diante – falarei em termos de valores de relação quando quiser dizer valores de relação, e em variáveis de relação quando quiser dizer variáveis de relação. No entanto, também vou, na maioria dos casos, abreviar valor de relação como apenas relação (exatamente como abreviamos, na maioria dos casos, valor inteiro como apenas inteiro). E vou abreviar variável de relação, na maioria dos casos, como relvar; por exemplo, direi que o banco de dados de fornecedores e peças contém três relvars (mais precisamente, três relvars de base). Como um exercício, você pode se interessar por revisar o texto deste capítulo até aqui e verificar exatamente onde usei o termo relação quando na realidade deveria ter usado, em vez disso (ou também), o termo relvar.
Valores e variáveis A diferença lógica entre relações e relvars é, na realidade, um caso especial de diferença lógica entre valores e variáveis em geral, e gostaria de dedicar alguns momentos 15 Vários revisores reclamaram desse fato – isto é, sentiam que eu deveria utilizar a própria SQL, e não alguma linguagem não padronizada como a Tutorial D, para ilustrar ideias relacionais. (Um até sugeriu que o livro fosse renomeado “Tutorial D e teoria relacional”!) Mas a SQL como tal nunca foi pretendida como um veículo para ilustração de ideias relacionais, enquanto a Tutorial D explicitamente o foi; e, de qualquer modo, a SQL simplesmente não é adequada à tarefa. De fato, se ela o fosse, um livro como este não seria necessário, em primeiro lugar. 16 A SQL comete o mesmo erro, é claro, pois ela também tem apenas um termo, tabela, que deve ser entendido como, às vezes, significando um valor de tabela e, em outros casos, uma variável de tabela.
Capítulo 1 ■ Preparando o cenário
51
para analisar o caso de forma geral. (Essa é uma leve digressão, mas penso que vale a pena dedicar o tempo aqui, pois um raciocínio claro nessa área pode ser – de muitas maneiras – de grande ajuda). Aqui estão, portanto, algumas definições: Definição: um valor é o que os especialistas em lógica chamam de uma “constante individual”, como o inteiro 3. Um valor não tem localização no tempo e no espaço. Entretanto, valores podem ser representados na memória por meio de alguma codificação, e essas representações ou codificações realmente têm uma localização no tempo e no espaço. De fato, representações distintas do mesmo valor podem aparecer em um número variado de localizações distintas no tempo e no espaço – significando, livremente, que um número qualquer de variáveis distintas (veja a próxima definição) pode ter o mesmo valor, ao mesmo tempo ou em momentos distintos. Observe em particular que, por definição, um valor não pode ser atualizado; pois, se pudesse, então depois de tal atualização ele não seria mais esse valor. Definição: uma variável é uma detentora de uma representação de um valor. Uma variável tem localização no tempo e no espaço. Além disso, variáveis, diferentemente de valores, podem ser atualizadas; isto é, o valor atual da variável pode ser substituído por outro valor. (Afinal, é isso que “variável” significa – ser uma variável significa ser atualizável, e ser atualizável significa ser uma variável; de modo equivalente, ser uma variável significa ser atribuível, e ser atribuível significa ser uma variável.) Por favor, observe com atenção que não são apenas elementos como o inteiro 3 que são valores legítimos. Pelo contrário, valores podem ser arbitrariamente complexos – por exemplo, um valor pode ser um ponto geométrico; ou um polígono; ou um raio X; ou um documento XML; ou uma impressão digital; ou um array; ou uma pilha; ou uma lista; ou uma relação (e assim por diante). Observações análogas também se aplicam às variáveis, é claro. Terei mais a dizer sobre tais assuntos no próximo capítulo. Agora, você pode pensar que é difícil imaginar pessoas se confundindo com uma distinção tão óbvia e fundamental como a que existe entre valores e variáveis. Na realidade, no entanto, é bastante fácil cair em armadilhas nessa área. Como uma forma de ilustração, considere o trecho a seguir, retirado de um tutorial sobre bancos de dados de objetos (as porções em itálico dentro das chaves são comentários feitos por mim mesmo): Distinguimos o tipo declarado de uma variável... do tipo do objeto que é o valor atual da variável [então um objeto é um valor]... Distinguimos objetos de valores [então um objeto não é um valor afinal de contas]... Um modificador [é um operador de tal modo que é] possível observar seu efeito em algum objeto [então, na realidade, um objeto é uma variável].
52
SQL e Teoria Relacional
Observações de encerramento Isso nos traz ao final deste capítulo preliminar. Na maior parte, meu objetivo tem sido apenas lhe dizer o que espero que você já saiba (e você pode, portanto, ter sentido que o capítulo foi um pouco leve em substância técnica). De qualquer forma, apenas para revisar brevemente: • Expliquei porque estaríamos preocupados com princípios, e não produtos, e porque vou usar a terminologia formal, como relação, tupla e atributo (pelo menos em contextos relacionais) – em vez de suas contrapartes SQL mais amigáveis. • Ofereci uma visão geral do modelo original, abordando, em particular, estes conceitos: tipo (ou domínio), relação de grau n, tuplas, atributo, chave candidata (chave, para abreviar), chave primária, chave estrangeira, integridade de entidade, integridade referencial, atribuição relacional e a álgebra relacional. (Também mencionei brevemente o cálculo relacional.) Com respeito à álgebra, mencionei a propriedade de fechamento e descrevi muito brevemente os operadores de restrição, projeção, produto, intersecção, união, diferença e junção. • Discuti várias propriedades das relações, apresentando os termos cabeçalho, corpo, cardinalidade e grau. Relações não têm tuplas duplicadas, nenhuma ordenação de tuplas de cima para baixo e nenhuma ordenação de atributos da esquerda para a direita. Também discuti as diferenças entre relações de base (ou relvars de base, na realidade) e visões. E expliquei que todo subconjunto de uma tupla é uma tupla, todo subconjunto de um cabeçalho é um cabeçalho e todo subconjunto de um corpo é um corpo. • Discuti as diferenças lógicas entre modelo e implementação, valores e variáveis em geral, e relações e relvars em particular. A discussão de modelo versus implementação, em particular, levou-nos a uma discussão sobre independência de dados físicos. • Afirmei que a SQL e o modelo relacional não representam o mesmo conceito. Já vimos algumas diferenças – por exemplo, o fato de que a SQL permite linhas duplicadas, de que tabelas SQL têm uma ordenação de colunas da esquerda para a direita e de que a SQL não distingue entre valores de tabela e variáveis de tabela – e veremos muitas outras nas páginas seguintes. Um último ponto (não mencionei isso explicitamente antes, mas espero que esteja claro a partir de tudo que disse): em geral, o modelo relacional é declarativo, e não procedural, por natureza; ou seja, ele sempre favorece soluções declarativas em vez de soluções procedurais, sempre que tais soluções sejam viáveis. A razão é óbvia: declarativo significa que o sistema faz o trabalho, procedural significa que o usuário faz o trabalho (então, estamos falando sobre produtividade, dentre outros conceitos). É por isso que o modelo relacional suporta consultas declarativas, atualizações
Capítulo 1 ■ Preparando o cenário
53
declarativas, definições de visão declarativas, restrições de integridade declarativas e assim por diante. Nota: depois de ter escrito o parágrafo anterior pela primeira vez, fui informado de que pelo menos um produto SQL bem conhecido aparentemente utiliza o termo “declarativo” para significar que o sistema não faz o trabalho! Isto é, ele permite que o usuário faça certas afirmações declarativamente (por exemplo, o fato de uma determinada visão ter uma determinada chave), mas não obriga a restrição indicada por essa declaração – ele simplesmente presume que o usuário irá obrigá-la. Tais abusos terminológicos fazem pouco para ajudar a causa do entendimento genuíno. Caveat lector.
Exercícios 1.1 (Retirado do corpo do capítulo, mas levemente reescrito aqui.) Se você ainda não o fez, verifique o capítulo novamente e identifique todos os locais em que usei o termo relação e deveria, por direito, ter usado o termo relvar, em vez disso. 1.2 Quem foi E. F. Codd? 1.3 O que é um domínio? 1.4 O que você entende pelo termo integridade referencial? 1.5 Os termos cabeçalho, corpo, atributo, tupla, cardinalidade e grau, definidos no corpo do capítulo para valores de relação, podem ser todos interpretados da forma óbvia para se aplicar também a relvars. Procure entender essa observação. 1.6 Faça a distinção entre os dois significados do termo modelo de dados. 1.7 Explique, com suas próprias palavras, (a) independência de dados físicos e (b) a diferença entre modelo e implementação. 1.8 No corpo do capítulo, eu disse que tabelas como aquelas das figuras 1.1 e 1.3 não eram relações como tais, mas, em vez disso, imagens de relações. Quais são alguns dos pontos de diferença específicos entre essas imagens e as relações correspondentes? 1.9 (Tente este exercício sem revisar o corpo do capítulo.) Quais relvars o banco de dados de fornecedores e peças contém? Que atributos elas envolvem? Que chaves e chaves estrangeiras elas têm? (O sentido deste exercício é que vale a pena se tornar o mais familiarizado possível com a estrutura, pelo menos em termos gerais, do exemplo corrente. Não é tão importante lembrar-se detalhadamente dos valores dos dados em si – ainda que não seja má ideia lembrar-se deles.) 1.10 “Existe apenas um modelo relacional.” Explique essa afirmação. 1.11 A seguir, temos um trecho retirado de certo livro teórico de bancos de dados: “É importante fazer uma distinção entre relações armazenadas, que são tabelas, e relações
54
SQL e Teoria Relacional
virtuais, que são visões... Utilizaremos relação apenas onde uma tabela ou visão puder ser utilizada. Quando quisermos enfatizar que uma relação está armazenada, em vez de uma visão, vamos, às vezes, utilizar o termo relação de base ou tabela de base.” Esse texto revela várias confusões e equívocos com relação ao modelo relacional. Identifique tantos quantos você puder.
1.12 A seguir, temos um trecho retirado de outro livro teórico de bancos de dados: “O modelo relacional (...) define tabelas simples para cada relação e relacionamentos muitos-para-muitos. Chaves de referência cruzada ligam as tabelas, representando os relacionamentos entre as entidades. Índices primários e secundários fornecem acesso rápido aos dados com base em qualificações.” Pretende-se que esse texto seja uma definição (!) do modelo relacional... O que há de errado nele? 1.13 Escreva instruções CREATE TABLE para uma versão SQL do banco de dados de fornecedores e peças. 1.14 A seguir temos uma típica instrução SQL INSERT no banco de dados de fornecedores e peças: INSERT INTO SP ( SNO , PNO , QTY ) VALUES ( 'S5' , 'P6' , 250 ) ;
Mostre uma operação de atribuição relacional equivalente. Nota: reconheço que ainda não expliquei a sintaxe da atribuição relacional detalhadamente, por isso não se preocupe tanto em fornecer uma resposta sintaticamente correta – apenas faça o melhor que puder.
1.15 (Mais difícil.) A seguir temos uma típica instrução SQL UPDATE no banco de dados de fornecedores e peças: UPDATE S SET STATUS = 25 WHERE CITY = 'Paris' ;
Mostre uma operação de atribuição relacional equivalente. (O propósito deste exercício é fazer você pensar sobre o que está envolvido. Ainda não lhe disse o suficiente neste capítulo para permitir que você o responda completamente. Veja as discussões sobre consultas “e se” no capítulo 7 para uma explicação detalhada.)
1.16 Ao longo do capítulo, eu disse que a SQL não suporta diretamente a atribuição relacional. Ele a suporta indiretamente? Se afirmativo, de que forma? Uma questão relacionada: podem todas as atribuições relacionais ser expressas em termos de INSERT e/ou DELETE e/ou UPDATE? Se negativo, por que não? Quais são as implicações? 1.17 De um ponto de vista prático, por que você acha que tuplas duplicadas, ordenação de tuplas de cima para baixo e ordenação de atributos da esquerda para a direita são todas ideias muito ruins? (Essas questões deliberadamente não foram respondidas ao longo do capítulo, e este exercício pode servir melhor como uma base para discussões em grupo. Analisaremos mais de perto tais questões futuramente no livro.)