Introdução
Salve salve galera da programação. Hoje vou abordar o tema de Banco de Dados utilizando a tecnologia do PostgreSQL. Mas porque PostgreSQL? Bem, estou precisando rever o conteúdo para a cadeira que estou fazendo de Implementação de Banco de Dados e no curso o professor utiliza PostgreSQL, então acredito ser bastante conveniente. Assim enriqueço o blog, fixo conhecimentos e de quebra contribuo para expandir a cultura dos leitores. Que eu sei que se for 1 é muito. Abaixo vou listar um pequeno índice do que será abordado para que possamos ter um script e eu não me perca,segue:
> Um pouco de História
> Álgebra Relacional
> Planejamento do Banco de Dados
> Criação de bancos de dados
> Criação de Tabelas
> Comandos de Inserção
> Instruções SELECT
> Critérios de Seleção
> Agrupamentos
> Funções de Agregação
> Critérios em Agrupamentos (Having)
> Sub Consultas
> Álgebra Relacional
> Planejamento do Banco de Dados
> Criação de bancos de dados
> Criação de Tabelas
> Comandos de Inserção
> Instruções SELECT
> Critérios de Seleção
> Agrupamentos
> Funções de Agregação
> Critérios em Agrupamentos (Having)
> Sub Consultas
Um pouco de História
Desde que o homem passou a tentar agrupar entidades (Entenda-se por entidade tudo aquilo que pode ser classificado) e ordená-las de forma a facilitar a consulta e o trabalho com os mesmos, houve a necessidade de se criar um banco de dados. Esta, portanto, seria uma boa definição para um banco dados, "conjunto de entidades organizadas em registros compostos por campos de dados vinculados a entidade através dos quais é possível realizar operações de consultas, agrupamento e ordenação".
O modelo mais utilizado atualmente nos SGBD (Sistemas Gerenciadores de Bancos de dados) é o "Modelo Relacional". Segundo o site da Wikipédia, um modelo relacional é um modelo de dados adequado a ser o modelo subjacente de um SGBD que se baseia no princípio em que todos os dados estão guardados em tabelas. Toda a sua definição é teórica e baseada na lógica de predicados e na teoria dos conjuntos. O conceito foi criado por Edgar Frank Codd em 1970. O modelo relacional foi o primeiro modelo de dados descrito teoricamente.
Álgebra Relacional
Trata-se da fundamentação matemática por trás das operações realizadas sobre tabelas quando enviamos instruções de consulta para o banco de dados. Entender estes conceitos é importante para introduzir o aprendizado da elaboração de consultas complexas em qualquer banco de dados relacional. Toda as operações realizadas na álgebra relacional possuem suas equivalências no SQL. Abrindo um parênteses - SQL do inglês Structured Query Language, como o próprio nome diz, é uma linguagem estruturada de consulta e também a linguagem padrão do mercado. Então se você quer entender como o SQL realizar as consultas, entenda álgebra relacional.
A álgebra relacional possui seus operadores primitivos e operadores derivados. Os operadores primitivos são todos os operadores mais fundamentais na elaboração das consultas, os quais serão descritos a seguir:
Projeção (
)
Operação unária(1 tabela), na qual se estabelece uma lista de atributos para que sejam retornados como resultado. A operação é representada na forma de
. Para melhor compreensão veja o exemplo a seguir:
Considere a tabela de nome Funcionarios
cod | nome | dept | funcao | salario |
2 | Cesar | 2 | 3 | 1500 |
3 | Emilio | 3 | 2 | 1000 |
1 | Rogerio | 3 | 3 | 1500 |
Para selecionarmos apenas as colunas de nome e salario é necessário aplicamos o operador de projeção da seguinte forma:
Seleção (
)
Operação unária para seleção de registros em uma relação de acordo com critérios especificados, a operação é representada na forma
onde
é o critério. Para exemplificar vamos utilizar a tabela de funcionários e criar uma consulta que retorne todos os registros cujo salário é igual a 1000. Veja abaixo como seria escrita na forma de algebra relacional:
como resultado teremos:
cod | nome | dept | funcao | salario |
3 | Emilio | 3 | 2 | 1000 |
Combinação, Cruzamento de Relações e Junção
É possível, e normalmente é necessário, realizar a combinação da seleção de campos de uma relação com base em critérios, o que naturalmente deve envolver os operadores
e
. Para exemplificar façamos uma consulta que retorne os campos cod, nome e salario de todos os funcionarios com salário igual a 1500. Perceba que além de restringirmos os campos que devem retornar aplicamos um critério para esse retorno, que no caso é salario = 1500. Veja abaixo como ficaria esta consulta:
como resultado,
cod | nome | salario |
2 | Cesar | 1500 |
1 | Rogerio | 1500 |
id | dep |
1 | Financeiro |
2 | Contabilidade |
3 | TI |
A representação algébrica para recuperar o nome do departamento com base no cruzamento de relação seria descrita da seguinte forma:
O mesmo retornaria a seguinte relação como resultado:
cod | nome | salario | dept | id | dep |
2 | Cesar | 1500 | 2 | 1 | Contabilidade |
2 | Cesar | 1500 | 2 | 2 | Financeiro |
2 | Cesar | 1500 | 2 | 3 | TI |
1 | Rogerio | 1500 | 3 | 1 | Contabilidade |
1 | Rogerio | 1500 | 3 | 2 | Financeiro |
1 | Rogerio | 1500 | 3 | 3 | TI |
Perceba que o resultado retornado trata-se do simples produto cartesiano das relações. Assim não faz muito sentido trazer departamentos que não correspondem ao departamento do funcionário. Para resolvermos este problema utilizamos o recurso de junção da algebra relacional.
Junção natural (
)
O operador de junção é binário pois envolve duas relações e sua sintaxe possui a seguinte forma R¹
R² . Esta operação retorna como resultado um cruzamento entra as relações(ou tabelas) assumindo como critério implícito os campos de mesmo nome. No nosso exemplo de 'Funcionarios x Departamentos' as colunas que deveriam ter o mesmo nome são respectivamente dep em 'Funcionarios' e cod em 'Departamentos', dessa forma, por serem nomes diferentes mas de equivalencia, é necessário especificar explicitamente o critério de junção, nossa operação seria descrita da seguinte forma:
cod, nome, salario,dept (
salario = 1500Funcionarios
dept = id
id,dep Departamentos)
retornando como resultado,
Bem estes foram os operadores mais fundamentais da algebra relacional, prosseguindo no assunto ainda teriamos Anti-Junção, Left Outer Joing, Right Outer Join, Outer Join. Mas acredito que iria me estender muito no temamais do que ja me estendi, além do que veremos alguns se não todos na parte de SQL. Como sugestão o site da Wikipédia pode ser útil para continuidade no assunto.
Planejamento do Banco de Dados
O planejamento é uma das mais importantes fases de qualquer projeto, se não for a mais importante, pois é o momento de conceber o que virar a ser o produto final. Como tal na elaboração de uma base de dados não é diferente, e como em qualquer projeto esta etapa gera uma documentação muito importante e que guiará nas demais etapas. Em se tratando de banco de dados, o processo incorpora o desenvolvimento de modelos que irão representar a base, os mesmos são descritos a seguir.
Modelo Conceitual
O primeiro momento é onde se elabora um esbouço da base, criando o que chamamos de entidades, evidenciando de que forma estas entidades irão interagir através de "relações nomeadas"(O losango que conecta as duas entidades) e o que chamamos de "cardinalidade"(a numeração ao lado da relação). Abaixo segue uma pequena representação da relação entre duas entidades e sua cardinalidade:
Os tipos de cardinalidade são três, a saber (KORTH, SILBERCHATZ e SUDARSHAN, 2006)
Um-para-um: uma instância em A está associado com no máximo uma instancia em B.
Um-para-muitos: uma instancia em A esta associado a um numero variado de instancias em B, no entanto uma instancia em B só pode estar associado a no máximo uma instancia em A.
Muitos-para-muitos: várias instancias em A podem esta associadas a varias instancias em B, e vice-versa.
Modelo Lógico
A segunda etapa é onde se analisa todo o trabalho feito no modelo conceitual e com base nas conclusões se cria o modelo lógico que irá definir todas as tabelas do nosso projeto e eventualmente suprimir outras constante no modelo conceitual de acordo com as adaptações necessárias. Como os modelos de dados são progressivos, não existe um modelo definitivo e podem haver mais de uma proposta de solução para determinado contexto o que vai depender da visão de cada projetista. Por tanto o modelo lógico representa o que deve ser implementado em um banco de dados, diferentemente do modelo conceitual que representa apenas a ideia inicial da base de dados. A forma utilizada para representar o modelo lógico é a DTR (Diagrama de Tabelas Relacionais) e a representação da cardinalidade é feita pela notação conhecida pelo nome 'Pé de galinha'. Observe a exemplificação abaixo:
O modelo lógico é independente de SGBD e pode vir explicitando os atributos da tabela ou por uma questão de praticidade pode ocultá-los também.
Modelo Físico
O modelo físico nada mais é que a implementação do projeto em um SGBD específico, ou seja, com todas as características, facilidades e restrições da tecnologia adotada. A principal diferença entre o modelo lógico e o modelo físico reside exatamente nas especificações do banco de dados, pois enquanto estamos na etapa do modelo lógico, possuímos apenas um modelo genérico com aplicabilidade em qualquer tecnologia de banco de dados relacional, por isso não há um aprofundamento maior. Já no modelo físico encontramos especificações de um SGBD tais como tipos de dados, regras de validação, triggers, stored procedures e tantos dados quanto recursos disponíveis na tecnologia são passíveis de serem descritos no modelo físico.
Tratamento de Dados
Ao estruturar um banco de dados afim de atender determinados requisitos de sistema é importante respeitar o que chamamos de "formais normais", que são aplicadas para definição de tabelas no nosso banco em um processo intitulado normalização. A finalidade destas regras é exatamente garantir que os dados serão consistentes, enxutos e livres de redundâncias, irei citar adiante as 3 das regras de normalização mais comuns, sem muito aprofundamento, a saber(DATE, 2004):
Criação de banco de dados
Para abordar esta parte, e adiante com os comandos SQL, irei adotar o banco de dados PostgreSQL. O PostgreSQL é um sistema gerenciador de banco de dados relacional desenvolvido como projeto de código aberto. Hoje, o PostgreSQL é um dos SGBD's de código aberto mais avançados, contando com recursos como:
(Cliente)
id = 3(Cliente)
O operador de junção é binário pois envolve duas relações e sua sintaxe possui a seguinte forma R¹
retornando como resultado,
cod | nome | salario | dep |
2 | Cesar | 1500 | Financeiro |
1 | Rogerio | 1500 | TI |
Bem estes foram os operadores mais fundamentais da algebra relacional, prosseguindo no assunto ainda teriamos Anti-Junção, Left Outer Joing, Right Outer Join, Outer Join. Mas acredito que iria me estender muito no tema
Planejamento do Banco de Dados
O planejamento é uma das mais importantes fases de qualquer projeto, se não for a mais importante, pois é o momento de conceber o que virar a ser o produto final. Como tal na elaboração de uma base de dados não é diferente, e como em qualquer projeto esta etapa gera uma documentação muito importante e que guiará nas demais etapas. Em se tratando de banco de dados, o processo incorpora o desenvolvimento de modelos que irão representar a base, os mesmos são descritos a seguir.
Modelo Conceitual
O primeiro momento é onde se elabora um esbouço da base, criando o que chamamos de entidades, evidenciando de que forma estas entidades irão interagir através de "relações nomeadas"(O losango que conecta as duas entidades) e o que chamamos de "cardinalidade"(a numeração ao lado da relação). Abaixo segue uma pequena representação da relação entre duas entidades e sua cardinalidade:
Os tipos de cardinalidade são três, a saber (KORTH, SILBERCHATZ e SUDARSHAN, 2006)
Um-para-um: uma instância em A está associado com no máximo uma instancia em B.
Um-para-muitos: uma instancia em A esta associado a um numero variado de instancias em B, no entanto uma instancia em B só pode estar associado a no máximo uma instancia em A.
Muitos-para-muitos: várias instancias em A podem esta associadas a varias instancias em B, e vice-versa.
Modelo Lógico
A segunda etapa é onde se analisa todo o trabalho feito no modelo conceitual e com base nas conclusões se cria o modelo lógico que irá definir todas as tabelas do nosso projeto e eventualmente suprimir outras constante no modelo conceitual de acordo com as adaptações necessárias. Como os modelos de dados são progressivos, não existe um modelo definitivo e podem haver mais de uma proposta de solução para determinado contexto o que vai depender da visão de cada projetista. Por tanto o modelo lógico representa o que deve ser implementado em um banco de dados, diferentemente do modelo conceitual que representa apenas a ideia inicial da base de dados. A forma utilizada para representar o modelo lógico é a DTR (Diagrama de Tabelas Relacionais) e a representação da cardinalidade é feita pela notação conhecida pelo nome 'Pé de galinha'. Observe a exemplificação abaixo:
O modelo lógico é independente de SGBD e pode vir explicitando os atributos da tabela ou por uma questão de praticidade pode ocultá-los também.
Modelo Físico
O modelo físico nada mais é que a implementação do projeto em um SGBD específico, ou seja, com todas as características, facilidades e restrições da tecnologia adotada. A principal diferença entre o modelo lógico e o modelo físico reside exatamente nas especificações do banco de dados, pois enquanto estamos na etapa do modelo lógico, possuímos apenas um modelo genérico com aplicabilidade em qualquer tecnologia de banco de dados relacional, por isso não há um aprofundamento maior. Já no modelo físico encontramos especificações de um SGBD tais como tipos de dados, regras de validação, triggers, stored procedures e tantos dados quanto recursos disponíveis na tecnologia são passíveis de serem descritos no modelo físico.
Tratamento de Dados
Ao estruturar um banco de dados afim de atender determinados requisitos de sistema é importante respeitar o que chamamos de "formais normais", que são aplicadas para definição de tabelas no nosso banco em um processo intitulado normalização. A finalidade destas regras é exatamente garantir que os dados serão consistentes, enxutos e livres de redundâncias, irei citar adiante as 3 das regras de normalização mais comuns, sem muito aprofundamento, a saber(DATE, 2004):
1º Forma Normal(1FN): Toda relação deve ter uma chave primária e deve-se garantir que todo atributo seja atômico. Atributos compostos devem ser separados. Por exemplo, um atributo Endereço deve ser subdividido em seus componentes: Logradouro, Número, Complemento, Bairro, Cidade, Estado e CEP. Além disso, atributos multivalorados devem ser discriminados separadamente ou separados em uma outra relação. Por exemplo, um atributo multivalorado Telefones poderia ser separado em Telefone Residencial, Telefone Comercial e Telefone Celular ou, ainda, ser convertido em outra relação que pudesse representar um número indeterminado de telefones.
2º Forma Normal(2FN): Toda relação deve estar na 1FN e devem-se eliminar dependências funcionais parciais, ou seja, todo atributo não chave deve ser totalmente dependente da chave primária. Como exemplo, uma relação que contenha os atributos Código da Obra, Código do Fornecedor, Nome do Fornecedor e Preço de Venda, considerando que a chave primária é composta pelos atributos Código da Obra e Código do Fornecedor, não está na Segunda Forma Normal, uma vez que o Nome do Fornecedor depende apenas do Código do Fornecedor, e não do Código da Obra. Uma nova relação deve ser criada contendo os campos Código do Fornecedor (como chave) e Nome do Fornecedor. Na relação original, ficariam os atributos Código da Obra e o Código do Fornecedor, ambos formando a chave primária composta, e o atributo Preço de Venda. Além disso, o atributo Código do Fornecedor também seria uma chave estrangeira para a nova relação criada. Esta forma normal ajuda a diminuir redundâncias de informações criadas indevidamente.
3º Forma Normal(3FN): Toda relação deve estar na 2FN e devem-se eliminar dependências funcionais transitivas, ou seja, todo atributo não chave deve ser mutuamente independente. Como exemplo, uma relação que contenha os atributos Matrícula do Funcionário (atributo chave), Nome do Funcionário, Código do Departamento e Nome do Departamento não está na Terceira Forma Normal. O Nome do Departamento é dependente do Código do Departamento, e não da Matrícula do Funcionário. Uma mudança no nome do departamento, por exemplo, levaria a modificações em todos os funcionários daquele departamento. Para eliminar este problema, cria-se uma nova relação (Departamento) contendo Código do Departamento e Nome do Departamento. Na relação original, retira-se o Nome de Departamento, mantendo-se o Código do Departamento, agora como chave estrangeira. Esta forma normal também ajuda a diminuir redundâncias e aumentar a independência das relações.
Criação de banco de dados
Para abordar esta parte, e adiante com os comandos SQL, irei adotar o banco de dados PostgreSQL. O PostgreSQL é um sistema gerenciador de banco de dados relacional desenvolvido como projeto de código aberto. Hoje, o PostgreSQL é um dos SGBD's de código aberto mais avançados, contando com recursos como:
Consultas complexas
Chaves estrangeiras
Integridade transacional
Controle de concorrência multi-versão
Suporte ao modelo híbrido
objeto-relacional
Ele tem facilidade de Acesso
Gatilhos
Visões
Linguagem Procedural (PL/pgSQL, PL/Python, PL/Java, PL/Perl)
Indexação por texto
Estrutura para guardar dados
Georeferenciados PostGIS
(Extraído do site da wikipédia)
O PostgreSQL pode ser baixado clicando neste link, daí em diante é escolher o sistema operacional corresponde e efetuar o download. As instruções de instalação podem ser consultados neste link. É um artigo gratuito disponível no site da devmedia que explica passo a passo a instalação no windows XP.
Bem, para darmos continuidade irei adotar um exemplo prático e simples apenas para abordarmos os conceitos importantes. O exemplo trata de uma locadora de vídeos, cujo o modelo conceitual é descrito a seguir (retirado do livro do Prof. Dr. Carlos Alberto Heuser, intitulado Projeto de Banco de Dados):
Por se tratar de um modelo feito a muito tempo atrás, efetuarei algumas mudanças começando pelo objeto de aluguel. Ao invés de alugarmos fitas VHS teremos o aluguel de DVD's os mesmo terão os campos 'Registro' e 'Cativo' em substituição aos campos 'Numero' e 'Rolo'. O campo cativo é de valor binário apenas para indicar se o exemplar do filme é o original ou cópia para aluguel.
O próximo passo é transformar o modelo conceitual em um modelo lógico para implementação no PostgreSQL. Observando a relação 'EMPRESTIMO' perceba que um cliente pode fazer nenhum ou vários empréstimos de fitas na locadora, em contrapartida uma fita pode ser emprestada para nenhum ou apenas um cliente por vez. A relação cliente - fita é de "um para muitos", e a relação fita - cliente (mudando apenas o sentido do fluxo) é de "um para um". Perceba que há a necessidade de guardamos registros de aluguel e não faz sentido alterarmos a estrutura de qualquer uma das duas tabelas para atendermos esta necessidade, neste caso a relação 'EMPRESTIMO' deve se tornar uma tabela, e o mesmo poderá conter todos os dados relacionados ao aluguel tais como: data do empréstimo, data da devolução, numero do cliente e numero de registro do DVD. Lembrando que o número de registro do DVD deve ser único, assim como o número do cliente. A própria tabela de empréstimos deverá ter um numero de registro único para identificação.
Na relação seguinte entre filme e fita(no nosso caso DVD), um filme possui um ou vários dvds, não menos que um DVD - como não poderia deixar de ser, para cada filme da locadora é necessário ter pelo menos um exemplar. E um dvd tem um único filme, ou pertence a um único filme, valor inflexível por motivos óbvios. Neste caso como um DVD pertence a um filme ou é exemplar de um filme então a tabela de cadastro para dvds terá um campo que irá identificar o filme correspondente na tabela de filmes, este tipo de campo que expressa uma correspondência em uma tabela estrangeira qualquer chamamos de 'chave estrangeira', da mesma forma o campo único que identifica o registro chamamos de 'chave primária'. A mesma situação encontramos nas relações fita - categoria e fita - ator. Por tanto, ambas tabelas 'categoria' e 'ator' terão suas chaves primárias como chave estrangeira na tabela fita. Por fim a tabela locadora será identificada como campo na tabela de empréstimos, pois um empréstimo é feito para uma locadora. Neste ponto poderíamos ter duas abordagens diferentes. "A locação é feita para um filme que pertence a uma locadora" ou "A locação é feita para uma locadora que possui uma série de filmes" . Definir a abordagem mais adequada é importante porque isso implica na estrutura das tabelas relacionadas. A abordagem seria ainda mais impactante se estivéssemos trabalhando com a possibilidade de ter filiais da locadora, mas como não é o caso quaisquer uma das abordagens aqui seriam válidas.
Após a análise necessária foi criado o modelo lógico com as definições de tabelas e seus atributos. À princípio utilizei uma versão antiga do DBDesigner mas o resultado que obtive não correspondia com o esperado, principalmente com relação as cardinalidades. O DBDesigner é uma ferramenta para modelagem de dados, foi absorvida e descontinuada pela Oracle que atualmente utiliza a tecnologia na sua ferramente MySQL Workbench, muito boa por sinal, a utilizei para criar o modelo lógico que podemos apreciar abaixo:
Agora que temos em mãos nosso modelo lógico da base dados podemos colocar a mão na massa para criarmos o modelo físico do banco no PostgreSQL , para tanto, admitindo que o leitor já o tenha instalado, passaremos a utilizar os comandos disponíveis no SQL para criarmos as bases, tabelas e posteriormente inserções de dados. Gostaria apenas de abrir um pequeno parêntese para comentar sobre o SQL.
SQL (Structured Query Language)
Foi desenvolvida pela IBM no início dos anos 70 para uso com o SGBD System R e inicialmente era chamada de "SEQUEL". Até o final dos anos 70, a IBM e a Oracle já a empregavam como linguagem de consulta. A SQL é composta por quatro tipos de comandos a saber:
DML(Data Manipulation Language) - Comandos de consulta e atualização.
DDL(Data Definition Language) - Comandos para alteração no esquema do banco de dados.
DCL(Data Control Language) - Controle de usuários e permissões.
DTL(Data Transaction Language) - Controle de transações.
Para ler mais sobre o SQL clique aqui
Criação do Banco de Dados
Na pasta de instalação do seu PostgreSQL na opção SQL Shell (psql) você poderá iniciar o prompt de comando. Utilizaremos este recurso para evitarmos a interface gráfica uma vez que pretendemos aplicar os comandos estudados. Ao abrir o prompt será solicitado apenas que se confirme algumas informações como server, database, port, mas o mais importante é quando for solicitado a senha, deverá ser informada a senha fornecida para o usuário postgres no momento da instalação, ou a senha correspondente do usuário à logar. Uma vez logado podemos executar o comando para criação da base de dados. O comando para criar um banco de dados no postgres é,
CREATE DATABASE NOME_DO_BANCO;
no nosso caso,
CREATE DATABASE LOCADORA;
O comando para listar todos os bancos de dados criados é descrito a seguir:
SELECT DATNAME FROM PG_DATABASE;
Se correu tudo bem o nome da nossa base (Locadora) deve aparecer listado no retorno. Agora que nossa base foi criada precisamos logar nela para criarmos as tabelas, para tanto podemos utilizar o meta-comando (comando simplificado)
\c locadora;
Abaixo uma lista dos meta-comandos mais comuns:
* \q - sai do psql.
* \c [NOME] - conecta a outro banco de dados. Por exemplo:
* \c template0 : conecta ao banco de dados template0;
* \l - lista todos os bancos de dados disponibilizados.
* \d [NOME] - descreve uma tabela, índice, visão ou sequência. Por exemplo:
* \d tabela : mostra todas as informações sobre as colunas da tabela 'tabela';
* \du - lista todos os usuários cadastrados no servidor;
* \dg - lista todos os grupos cadastrados no servidor;
* \dT - lista todos os tipos de dados.
* \h [NOME] - Mostra a sintaxe do comando SQL. Por exemplo:
* \h SELECT : mostra a sintaxe do comando SELECT;
* \? - lista todos os comandos disponíveis.
* \c [NOME] - conecta a outro banco de dados. Por exemplo:
* \c template0 : conecta ao banco de dados template0;
* \l - lista todos os bancos de dados disponibilizados.
* \d [NOME] - descreve uma tabela, índice, visão ou sequência. Por exemplo:
* \d tabela : mostra todas as informações sobre as colunas da tabela 'tabela';
* \du - lista todos os usuários cadastrados no servidor;
* \dg - lista todos os grupos cadastrados no servidor;
* \dT - lista todos os tipos de dados.
* \h [NOME] - Mostra a sintaxe do comando SQL. Por exemplo:
* \h SELECT : mostra a sintaxe do comando SELECT;
* \? - lista todos os comandos disponíveis.
Criação de Tabelas
Bem, vamos começar pela criação da tabela CLIENTE. Na tabela cliente definimos o campo 'id' como chave primária e como tal este campo não deve se repetir sob hipótese alguma. Em outros bancos de dados existe a opção 'auto_increment' para iterar sobre o último valor atribuído no campo de chave primaria quando este se trata de um inteiro, o mesmo é definido no momento da criação da tabela, no entanto o postgres não trabalha com auto incremento, nossa opção para contornar o problema é utilizar uma 'sequencia' para atribuir valores auto incrementados no nosso campo id, pois de fato é essa a finalidade deste tipo de recurso. O comando para criarmos nossa sequencia é o seguinte,
CREATE SEQUENCE ID_CLIENTE;
Este comando irá criar nossa sequencia para usarmos posteriormente.
Agora iremos criar nossa tabela CLIENTE. A seguinte instrução SQL foi utilizada com sucesso no Shell,
CREATE TABLE CLIENTE (
ID INTEGER,
NOME VARCHAR(45),
TELEFONE VARCHAR(12),
ENDERECO VARCHAR(50),
CONSTRAINT CLIENTE_ID_PK PRIMARY KEY (ID));
O mesmo padrão iremos adotar para as tabelas de base, ou seja, as tabelas que fornecem chave estrangeira para demais tabelas e que portanto devem ser criadas primeiro, segue abaixo o código para criação dessas tabelas e suas respectivas sequencias,
CREATE SEQUENCE COD_CATEGORIA;
CREATE TABLE CATEGORIA (
COD_CAT INTEGER,
NOME VARCHAR(45),
CONSTRAINT CATEGORIA_COD_PK PRIMARY KEY (COD_CAT));
CREATE SEQUENCE COD_ATOR;
CREATE TABLE ATOR (
COD_ATOR INTEGER,
NOME VARCHAR(45),
NOME_ART VARCHAR(45),
DATA_NASC DATE,
CONSTRAINT ATOR_COD_PK PRIMARY KEY (COD_ATOR));
CREATE SEQUENCE ID_LOCADORA;
CREATE TABLE LOCADORA (
ID INTEGER,
CGC VARCHAR(11),
RAZAO_SOCIAL VARCHAR(45),
FANTASIA VARCHAR(45),
CONSTRAINT LOCADORA_ID_PK PRIMARY KEY (ID));
CREATE SEQUENCE FILME;
CREATE TABLE FILME (
ID INTEGER,
TITULO VARCHAR(45),
COD_CAT INTEGER,
COD_ATOR INTEGER,
ID_LOC INTEGER,
CONSTRAINT FILME_ID_PK PRIMARY KEY (ID),
CONSTRAINT COD_CAT_FK FOREIGN KEY(COD_CAT) REFERENCES CATEGORIA(COD_CAT),
CONSTRAINT COD_ATOR_FK FOREIGN KEY(COD_ATOR) REFERENCES ATOR(COD_ATOR),
CONSTRAINT ID_LOC_FK FOREIGN KEY(ID_LOC) REFERENCES LOCADORA(ID)
);
CREATE SEQUENCE REG_DVD;
CREATE TABLE DVD (
REG INTEGER,
ID_FILME INTEGER,
ISCATVIO INTEGER,
CONSTRAINT REG_PK PRIMARY KEY (REG),
CONSTRAINT ID_FILME_FK FOREIGN KEY (ID_FILME) REFERENCES FILME(ID)
);
CREATE SEQUENCE CHAVE_EMPRESTIMO;
CREATE SEQUENCE ID_EMPRESTIMO;
CREATE TABLE EMPRESTIMO(
ID INTEGER,
ID_EMP INTEGER,
REG_DVD INTEGER,
ID_CLIENTE INTEGER,
DATA_LOC DATE,
DATA_DEV DATE,
CONSTRAINT EMPRESTIMO_ID_PK PRIMARY KEY (ID),
CONSTRAINT REG_DVD_FK FOREIGN KEY (REG_DVD) REFERENCES DVD(REG),
CONSTRAINT ID_CLIENTE_FK FOREIGN KEY (ID_CLIENTE) REFERENCES CLIENTE(ID)
);
Comandos de Inserção
Bem, agora que terminamos de criar as tabelas é necessário que as populemos para que tenhamos dados para trabalhar. A sintaxe do comando de inserção é bem simples e segue o seguinte modelo,
INSERT INTO [NOME DA TABELA] ([CAMPOS DA TABELA]) VALUES ([VALORES PARA INSERÇÃO NA MESMA ORDEM DOS CAMPOS]);
Abaixo o código para inserção na tabela de clientes,
INSERT INTO CLIENTE (ID, NOME, TELEFONE, ENDERECO) VALUES (NEXTVAL('ID_CLIENTE'),
'VILTON DA COSTA CRUZ', '0000-00-00', 'BENJAMIM CONSTANT 439 BELA VISTA')
INSERT INTO CLIENTE (ID, NOME, TELEFONE, ENDERECO) VALUES (NEXTVAL('ID_CLIENTE'),
'ANDERSON FERREIRA DOS SANTOS', '0000-00-00', 'RUA TRES CORACOES 456 SANTA CECILIA')
INSERT INTO CLIENTE (ID, NOME, TELEFONE, ENDERECO) VALUES (NEXTVAL('ID_CLIENTE'),
'ANTONIO ADALBERTO DA SILVA', '0000-00-00', 'RUA DAS FLORES 324 PARQUE ARUARA')
INSERT INTO CLIENTE (ID, NOME, TELEFONE, ENDERECO) VALUES (NEXTVAL('ID_CLIENTE'),
'MARIA ELENICE DAS DORES', '0000-00-00', 'RUA DAS FLORES 324 PARQUE ARUARA')
Com esse código inserimos quatro clientes diferentes na tabela de clientes. Observe que no lugar do campo id colocamos a função 'NEXTVAL()' onde dentro do parenteses informamos o nome da sequencia que controla o campo, assim toda inserção sempre incluirá o próximo número da sequencia, seguindo o mesmo padrão vamos partir para inserção nas demais tabelas base,
ATOR
INSERT INTO ATOR (COD_ATOR, NOME_ART, DATA_NASC) VALUES (NEXTVAL('COD_ATOR'),'ANTONIO BANDERAS', '22/05/1978');
INSERT INTO ATOR (COD_ATOR, NOME_ART, DATA_NASC) VALUES (NEXTVAL('COD_ATOR'),'EMILIO ESTEVEZ', '12/07/1958');
INSERT INTO ATOR (COD_ATOR, NOME_ART, DATA_NASC) VALUES (NEXTVAL('COD_ATOR'),'JHON TRAVOLTA', '03/04/1957');
INSERT INTO ATOR (COD_ATOR, NOME_ART, DATA_NASC) VALUES (NEXTVAL('COD_ATOR'),'BRUCE WILLIS', '21/12/1965');
CATEGORIA
INSERT INTO CATEGORIA (COD_CAT, NOME) VALUES (NEXTVAL('COD_CATEGORIA'),'SUSPENSE');
INSERT INTO CATEGORIA (COD_CAT, NOME) VALUES (NEXTVAL('COD_CATEGORIA'),'TERROR');
INSERT INTO CATEGORIA (COD_CAT, NOME) VALUES (NEXTVAL('COD_CATEGORIA'),'ROMANCE');
INSERT INTO CATEGORIA (COD_CAT, NOME) VALUES (NEXTVAL('COD_CATEGORIA'),'COMEDIA');
LOCADORA
INSERT INTO LOCADORA (ID, CGC, RAZAO_SOCIAL, FANTASIA) VALUES (NEXTVAL('ID_LOCADORA'), '00000001', 'VILTON CRUZ LTDA', 'LOCADORA CINEMART');
FILME
INSERT INTO FILME (ID, TITULO, COD_CAT, COD_ATOR, ID_LOC) VALUES (NEXTVAL('ID_FILME'), 'O ENIGMA', 1, 2, 1);
INSERT INTO FILME (ID, TITULO, COD_CAT, COD_ATOR, ID_LOC) VALUES (NEXTVAL('ID_FILME'), 'UM LOUCO NA SELVA', 4, 4, 1);
INSERT INTO FILME (ID, TITULO, COD_CAT, COD_ATOR, ID_LOC) VALUES (NEXTVAL('ID_FILME'), 'A CASA DA COLINA', 2, 2, 1);
INSERT INTO FILME (ID, TITULO, COD_CAT, COD_ATOR, ID_LOC) VALUES (NEXTVAL('ID_FILME'), 'AMOR EM LAS VEGAS', 3, 3, 1);
DVD
INSERT INTO DVD (REG, ID_FILME, ISCATIVO) VALUES (NEXTVAL('REG_DVD'), 1, 1);
INSERT INTO DVD (REG, ID_FILME, ISCATIVO) VALUES (NEXTVAL('REG_DVD'), 1, 0);
INSERT INTO DVD (REG, ID_FILME, ISCATIVO) VALUES (NEXTVAL('REG_DVD'), 2, 1);
INSERT INTO DVD (REG, ID_FILME, ISCATIVO) VALUES (NEXTVAL('REG_DVD'), 2, 0);
INSERT INTO DVD (REG, ID_FILME, ISCATIVO) VALUES (NEXTVAL('REG_DVD'), 3, 1);
INSERT INTO DVD (REG, ID_FILME, ISCATIVO) VALUES (NEXTVAL('REG_DVD'), 3, 0);
INSERT INTO DVD (REG, ID_FILME, ISCATIVO) VALUES (NEXTVAL('REG_DVD'), 4, 1);
EMPRESTIMO
INSERT INTO EMPRESTIMO (ID, ID_EMP, REG_DVD, ID_CLIENTE, DATA_LOC, DATA_DEV) VALUES (NEXTVAL('CHAVE_EMPRESTIMO'), 0, 2, 1, '01/01/2013','03/01/2012');
INSERT INTO EMPRESTIMO (ID, ID_EMP, REG_DVD, ID_CLIENTE, DATA_LOC, DATA_DEV) VALUES (NEXTVAL('CHAVE_EMPRESTIMO'), 0, 4, 1, '01/01/2013','03/01/2012');
INSERT INTO EMPRESTIMO (ID, ID_EMP, REG_DVD, ID_CLIENTE, DATA_LOC, DATA_DEV) VALUES (NEXTVAL('CHAVE_EMPRESTIMO'), 0, 6, 1, '01/01/2013','03/01/2012');
Perceba que na tabela de empréstimos possuímos dois tipos de identificadores, neste caso o campo 'id' identifica um registro único em toda a tabela de empréstimos este mesmo é controlado pela sequencia 'chave_emprestimo', já o campo 'id_emp' é o campo que identifica o empréstimo efetuado, que na realidade pode ser composto por mais de um item, por tanto, as três inserções que realizamos estão vinculadas ao mesmo empréstimo pois todas possuem o mesmo numero de empréstimo '0', diferente do id único, que é iterado a cada nova inserção, independente de ser de um mesmo empréstimo ou não. Para exemplificar, é fato que um único cliente em um único pedido pode fazer o empréstimo de mais de um dvd, é isso que tentamos tratar na tabela.
Instruções SELECT
O 'select' pertence ao grupo de comandos DML, ou seja, comandos para manipulação de dados. Juntamente com o select temos também o 'insert', 'update' e 'delete'. Como vimos anteriormente, o 'insert' é utilizado para realização a inserção de dados em uma tabela qualquer, da mesma forma o 'update' é utilizado para realizar a atualização de dados em uma tabela qualquer. A sintaxe de ambos foi apresentada acima no processo de população das tabelas.
A sintaxe do comando select é apresentada a seguir:
SELECT * FROM [NOME DA TABELA];
Como você pode ver é uma sintaxe muito simples de fácil compreensão. No exemplo acima o símbolo * indica que o meu retorno deve ser todos os campos da tabela que é informada na área que indiquei por 'nome da tabela'. Aplicando o select com o exemplo do nosso banco de dados da locadora, imagine que precisamos saber todos os clientes cadastrados na base, para tanto basta executar a seguinte consulta de seleção no Shell do postgres:
SELECT * FROM CLIENTE;
Recordando álgebra relacional, o mesmo select seria escrito assim:
Critérios de Seleção
Critérios de seleção são especificados quando queremos aplicar filtros no retorno de nossa consulta. Por exemplo, imagine que queremos saber qual o cliente de 'id' igual a 3. O select ficaria da seguinte forma:
SELECT * FROM CLIENTE WHERE ID = 3;
Em álgebra relacional ficaria assim:
Perceba a utilização da palavra 'where' antecedendo o critério de seleção, ela deve ser usada sempre que for necessário empregar algum critério. Como você deve ter notado todo o SQL é escrito dessa forma 'declarativa' em sua língua nativa o inglês, então para quem conhece o básico de inglês - o que é uma obrigação para praticamente todos ramos hoje em dia - é bem mais fácil assimilar os comandos.
Os critérios de seleção também pode ser utilizados para especificar de que forma duas ou mais tabelas se relacionam para produzir o retorno desejado. Existem funções de junção que produzem o mesmo efeito, mas para entendermos como pode ser útil a aplicação dos critérios em cruzamento de tabelas irei demonstrar na prática tomando nossa base locadora como exemplo. Vamos criar um 'select' na tabela de filmes cujo retorno seja o nome dos filmes cadastrados juntamente com a categoria, nome do ator e nome fantasia da locadora. A consulta ficaria da seguinte forma:
SELECT FIL.TITULO, CAT.NOME AS CATEGORIA, AT.NOME_ART AS ESTRELA, LOC.FANTASIA AS LOCADORA FROM FILME AS FIL,CATEGORIA AS CAT, ATOR AS AT, LOCADORA AS LOC WHERE FIL.COD_CAT = CAT.COD_CAT AND FIL.COD_ATOR = AT.COD_ATOR AND FIL.ID_LOC = LOC.ID;
Este select não irei demonstrar a forma em álgebra relacional pois além da difícil compreensão iria fugir muito do foco. Bem, comentando o código, utilizei o recurso de renomeação de colunas e tabelas do sql, o mesmo pode ser realizado utilizando a palavra chave 'as' logo após a coluna/tabela e em seguida dentro da área de critérios especifiquei de que forma as tabelas se relacionam para me retornar o resultado desejado. Perceba que a tabela filme possui tres chaves estrangeiras, são elas que justamente fazem a relação com as tabelas de ator, categoria e locadora, são estas relaçoes que informo nos critérios. O resultado desse select deve retornar a tabela a seguir:
TITULO | CATEGORIA | ESTRELA | LOCADORA |
O ENIGMA | SUSPENSE | ANTONIO BANDERAS | LOCADORA CINEMART |
UM LOUCO NA SELVA | COMEDIA | EMILIO ESTEVEZ | LOCADORA CINEMART |
AMOR EM LAS VEGAS | ROMANCE | BRUCE WILLIS | LOCADORA CINEMART |
A CASA DA COLINA | TERROR | ANTONIO BANDERAS | LOCADORA CINEMART |
Assim, demonstrei que os critérios podem ser utilizados para especificar as relações entre tabelas em uma consulta.
ALTER TABLE
Analisando nosso banco percebi que não faz sentido manter uma cópia de filme como cativo, ou seja, exemplar da locadora, e que é necessário um campo para informar se o exemplar está disponível ou não. Assim, poderiamos reaproveitar o campo isCativo para guardar este tipo de informação, mas primeiro seria necessário renomear a tabela, no entanto, não há um comando no sql para renomear campos. Então a solução é remover o campo isCativo e acrescentar o campo isLocado. Neste tipo de procedimento utilizamos o comando ALTER TABLE do sql. A sintaxe simplificada do comando é a seguinte:
ALTER TABLE [NOME DA TABELA] [OPERACAO] [NOME DA COLUNA] [TIPO] [DEMAIS ATRIBUTOS]
Nosso comando para realizar a remoção do campo é o seguinte:
ALTER TABLE DVD DROP COLUMN ISCATIVO;
E para realizar a inclusão da nova coluna isLocado:
ALTER TABLE DVD ADD COLUMN ISLOCADO INTEGER NOT NULL DEFAULT 0;
Como ele é um campo que não pode ser nulo, já que informa se o dvd está locado ou não, ele é criado com o valor padrão 0, entenda 1 para true e 0 para false;
Foi realizada também uma alteração na tabela de emprestimos para informar se o emprestimo está em aberto ou fechado. O comando para inclusão do campo é descrito a seguir:
ALTER TABLE EMPRESTIMO ADD COLUMN ABERTO INTEGER NOT NULL DEFAULT 1;
Já na tabela empréstimo como toda nova inserção diz respeito à efetivação da locação de um dvd, então cada novo registro tem o valor padrão 1, ou seja, true para isAberto.
Toda vez que uma locação é finalizada é necessário atualizar a tabela de dvd para que os registros inclusos no pedido de locação fiquem marcados como locado, para tanto é necessário atualizar o campo isLocado com base nos registros em aberto da tabela emprestimos. O update correspondente a este procedimento pode ser verificado a seguir:
UPDATE DVD SET ISLOCADO = 1 WHERE REG IN (SELECT REG_DVD FROM EMPRESTIMO WHERE ABERTO = 1);
E o mesmo update com uma pequena alteração para atualizar o status dos dvds que já foram devolvidos:
UPDATE DVD SET ISLOCADO = 0 WHERE REG NOT IN (SELECT REG_DVD FROM EMPRESTIMO WHERE ABERTO = 1);
Nestes dois últimos exemplos encontramos as palavras chave 'in' e 'not in' como novidades, alem de um select entre parenteses. Este tipo de recurso é muito utilizado para filtrar dados com base em uma consulta. O que o primeiro código descreve é exatamente atualizar todos os registro em dvd que estão dentro da consulta entre parenteses, e a consulta entre parenteses seleciona os registros de empréstimo em aberto. A segunda consulta atualiza todos os registros em dvd que não estão dentro da consulta de registros em aberto - por isso utilizamos 'not in'. Neste caso atualizamos o campo isLocado para 0, pois é nossa representação para dvd liberado.
Agrupamentos
Para trabalhar com agrupamento é necessário primeiro definir em função de qual coluna será realizado. Como assim? Para exemplificar tomemos a tabela de empréstimo, perceba que a coluna 'id_empréstimo' possui um registro de numero que se repete tantas vezes quanto a quantidade de dvds alugados nesse mesmo pedido. Então para saber quantos pedidos de empréstimo foram realizados devemos realizar o agrupamento em função da coluna 'id_emprestimo'. Para que nosso exemplo se torne mais claro vamos realizar mais dois emprestimos de dvds onde um terá dois dvds e no outro apenas um. O código correspondente segue abaixo:
INSERT INTO EMPRESTIMO (ID, ID_EMP, REG_DVD, ID_CLIENTE, DATA_LOC, DATA_DEV, ABERTO) VALUES (NEXTVAL('CHAVE_EMPRESTIMO'), 1, 1, 3, '02/01/2013','04/01/2012', 1);
INSERT INTO EMPRESTIMO (ID, ID_EMP, REG_DVD, ID_CLIENTE, DATA_LOC, DATA_DEV, ABERTO) VALUES (NEXTVAL('CHAVE_EMPRESTIMO'), 1, 3, 3, '02/01/2013','04/01/2012', 1);
Segundo emprestimo,
INSERT INTO EMPRESTIMO (ID, ID_EMP, REG_DVD, ID_CLIENTE, DATA_LOC, DATA_DEV, ABERTO) VALUES (NEXTVAL('CHAVE_EMPRESTIMO'), 2, 5, 4, '03/01/2013','05/01/2012', 1);
Assim, podemos aplicar uma operação de agrupamento para saber quantos pedidos de locação foram efetuados na base e quantos dvds foram alugados em cada pedido bem como o nome do cliente que alugou, a consulta ficaria assim:
SELECT ID_EMP, COUNT(*) AS QTD_DVD, CLI.NOME AS CLIENTE FROM EMPRESTIMO EMP, CLIENTE CLI WHERE EMP.ID_CLIENTE = CLI.ID GROUP BY ID_EMP, CLI.NOME ORDER BY ID_EMP;
A saída para essa consulta podemos verificar a seguir:
ID_EMP | QTD_DVD | CLIENTE |
0 | 3 | VILTON DA COSTA CRUZ |
1 | 2 | ANDERSON FERREIRA DOS SANTOS |
2 | 1 | ANTONIO ADALBERTO DA SILVA |
As palavras chaves 'GROUP BY' e 'ORDER BY' são respectivamente agrupamento e ordenamento dos campos pela ordem em que são colocados, ou seja, primeiro agrupa-se pelo id do empréstimo, depois pelo nome, da mesma forma se ordena pelo id do empréstimo.
Funções de Agregação
Funções de agregação são funções do sql que podem ser utilizadas sobre dados agrupados, as funções mais comuns são: SUM, MIN, MAX, AVG e COUNT. A seguir irei descrever cada uma.
SUM
Função de soma aplicada sobre dados agrupados que retorna o somatório dos valores de determinado campo. Como exemplo considere a seguinte tabela resultado da ultima consulta exemplificada:
ID_EMP | QTD_DVD | CLIENTE |
0 | 3 | VILTON DA COSTA CRUZ |
1 | 2 | ANDERSON FERREIRA DOS SANTOS |
2 | 1 | ANTONIO ADALBERTO DA SILVA |
Vamos chamar estar relação de SAIDA, para aplicarmos a função SUM nesta relação podemos solicitar o somatorio de todos os dvds alugados, dessa forma a instrução sql ficará da seguinte forma:
SELECT SUM(QTD_DVD) AS SOMATORIO FROM SAIDA;
MIN
Função que retorna o menor valor em um campo de uma determinada relação. Utilizando a mesma relação SAIDA, podemos exemplificar através da seguinte instrução:
SELECT MIN(QTD_DVD) AS MINIMO FROM SAIDA;
A saida é a menor quantidade de dvds alugados nos pedidos de empréstimos.
MAX
A função max possui a mesma sintaxe e ao contrário do min retorna a maior quantidade de dvds alugados em um pedido de empréstimo. O exemplo é descrito a seguir utilizando a relação SAIDA:
SELECT MAX(QTD_DVD) AS MAXIMO FROM SAIDA;
AVG
A função avg retorna a media aritmética dos valores de determinado campo. A sintaxe é semelhante as demais funções e a exemplificaremos solicitando a media das quantidades de dvds alugados em empréstimos através da seguinte instrução:
SELECT AVG(QTD_DVD) AS MAXIMO FROM SAIDA;
COUNT
Como demonstrado na consulta que retorna a relação SAIDA, a função count retorna a contagem dos registros existente na relação em questão, a sintexa é semelhante aos demais e ainda utilizando a relação SAIDA podemos exemplificar da seguinte forma:
SELECT COUNT(QTD_DVD) AS REGISTROS FROM SAIDA;
O retorno dessa instrução é o valor 3, pois é a quantidade de registros na relação.
Critérios em Agrupamentos (Having)
Critérios em agrupamentos são aplicados através da palavra chave 'having', com este recurso podemos definir um filtro com base em uma condição imposta sobre o agrupamento. Para exemplificar, vamos utilizar a consulta que gerou a relação SAIDA.
SELECT ID_EMP, COUNT(*) AS QTD_DVD, CLI.NOME AS CLIENTE FROM EMPRESTIMO EMP, CLIENTE CLI WHERE EMP.ID_CLIENTE = CLI.ID GROUP BY ID_EMP, CLI.NOME HAVING COUNT(*) > 1 ORDER BY ID_EMP;
Neste exemplo aplicamos um filtro que retorna apenas os registros de agrupamento cuja quantidade é maior que 1. Perceba que o 'having' é aplicado logo após o 'group by' e antes do 'order by', se ele for colocado no final após o 'order by' vai retornar um erro.
Sub Consultas
Sub consultas são utilizadas quando é necessário realizar mais de uma seleção na mesma consulta, isso ocorre quando por exemplo precisamos aplicar um filtro ou agrupamento em uma tabela para então realizar a consulta sobre os dados de saída. Um exemplo bem prático pode ser demonstrado ainda sobre a consulta que gera a relação saida. Neste caso vamos aplicar umas das funções de agrupamento sobre a relação dentro de um 'sub select'.
SELECT MAX(QUERY.QTD_DVD) FROM (SELECT ID_EMP, COUNT(*) AS QTD_DVD, CLI.NOME AS CLIENTE FROM EMPRESTIMO EMP, CLIENTE CLI WHERE EMP.ID_CLIENTE = CLI.ID GROUP BY ID_EMP, CLI.NOME HAVING COUNT(*) > 1 ORDER BY ID_EMP) AS QUERY;
No exemplo acima, o que antes era a consulta principal agora é uma sub consulta, perceba que ela é colocada entre parenteses e é renomeada com o nome query isso é importante para referenciamos seus campos e toda sub consulta deve ter um nome atribuido, principalmente quando se trata de uma consulta com varias tabelas e/ou relações, e por fim aplicamos a função max sobre o campo 'query.qtd_dvd' afim de que o retorno seja o maior valor retornado na relação.
Finalizando
Bem, chegamos ao final de mais um artigo, devo dizer que este foi particularmente extenso. De forma alguma minha intenção é de esgotar o assunto, até porque não há como, todo o conteúdo abordado aqui é apenas a ponta do icebergue. Espero que todo este trabalho possa ser útil ao leitor e que sirva para iniciar ou clarear o aprendizado sobre banco de dados. Quero deixar minha indicação de curso, um curso muito bom e gratuito de SQL, com certificado, é oferecido pela softblue, empresa de treinamentos online. Fiz o curso e recomendo. Sem mais, muito obrigado pelo tempo dedicado a leitura, lembrando que fico aberdo a dúvidas e sugestões de artigos. Um grande abraço e até a próxima.