SQL Conceitos Básicos. Postgres + Docker
Tags: docker, sql, postgres, beginners
Published: 2020-01-29 14:42:38

Criei este tutorial para aprimorar e obter meus conhecimentos na tecnologia SQL para uso no meu dia-a-dia como desenvolvedora.

![sql](https://miro.medium.com/max/787/0*xZfzHRVLm4z4NE5Z)

**Structured Query Language (SQL)** é uma linguagem que usamos para *“conversar”* com o banco de dados. Ela é organizada em alguns subconjuntos, como:
+ **DQL** — Data Query Language A Linguagem de Consulta de Banco de Dados define qual comando utilizar para fazer a consulta *(SELECT)* dos dados;
+ **DML** — Data Manipulation Language A Linguagem de Manipulação de Dados define os comandos para manipular os dados já contidos no banco *(INSERT, UPDATE e DELETE)*;
+ **DDL** — Data Definition Language A Linguagem de Definição de Dados define quais comandos usar para criar tabelas, views, índices *(CREATE)*, atualizar *(ALTER)* e/ou remover *(DROP)*;
+ **DCL** — Data Control Language A Linguagem de Controle de Dados define os comandos para controlar o acesso aos dados do banco, adicionando *(GRANT)* e removendo *(REVOKE)* permissões de acesso;
+ **DTL** — Data Transaction Language A Linguagem de Transação de Dados define quais comandos gerenciam as transações executadas no banco de dados, como iniciar uma transação *(BEGIN)*, confirmar uma transação *(COMMIT)* ou desfazer uma transação *(ROLLBACK)*.

###Criação de Banco de Dados
A sintaxe do banco de dados é bem intuitiva. Para iniciarmos, precisamo criar um banco de dados:
```sql
CREATE DATABASE teste
```
Para criarmos um banco eficiente e útil, precisamos seguir alguns passos:
1. Identificar o objetivo do banco de dados, ou fazer uma análise de requisitos;
2. Organizar os dados em tabelas;
3. Especificar chaves primárias e analisar relações;
4. Padronizar as tabelas.

###Identificar o objetivo do banco de dados.
Precisamos compreender a finalidade do banco de dados para nos orientarmos durante todo o processo de criação, reunir dados existentes que serão incluídos, listar os tipos de dados, entidades, pessoas, eventos que serão armazenados.
Exemplo: Lista Telefônica
- Nome
- Sobrenome
- DDD
- Número do telefone
Depois de entender e saber quais dados serão incluídos em seu banco de dados, saber de onde esses dados vêm e como eles serão utilizados, podemos começar a planejar nosso banco de dados real.

###Estruturando o banco de dados.
Neste passo, é preciso entender como os bancos de dados relacionais são estruturados. Os dados relacionados são agrupados em tabelas, que possuem colunas *(campos ou atributos)* e linhas *(tuplas ou registros)* assim como uma planilha.
Cada linha é chamada de registro e esse registro inclui dados sobre algo, como uma pessoa.
As colunas contêm um único tipo de informação que aparece em cada registro.

![](https://miro.medium.com/max/525/0*1k5Rm-tv6Lri6-b9)

Os tipos de dados são para manter os dados consistentes de um registro para outro. Vejamos os tipos de dados mais comuns:
- INT — inteiro positivo ou negativo;
- CHAR — tamanho específico de texto;
- VARCHAR — texto de tamanhos variáveis;
- TEXTO — grande quantidade de texto;
- BOOL — boleanos;
- FLOAT, DOUBLE — números de pontos flutuantes.
‌Para criar uma visão geral do banco (diagrama entidade-relacionamento), cada tabela é uma caixinha do diagrama, o título de cada caixinha descreve o que essa tabela contém e nelas são listados seus atributos.

![](https://miro.medium.com/max/261/0*Bl6haAjbFZqAz6tu)

Após criar a visão geral da tabela, devemos decidir qual atributo será a chave primária *(PK)*, se houver.
> **Primary Key (PK)** — Chave Primária é um identificador para uma determinada entidade que contem um valor único, imutável e sempre presente, não podendo nunca ser nulo ou vazio.

###Criando relações entre entidades.
Quando falamos em relações entre entidade falamos de cardinalidade e essa cardinalidade se refere à quantidade de elementos que interagem entre duas tabelas relacionadas. Ao identificar a cardinalidade, estamos garantindo a eficiência da divisão dos dados nas tabelas.
Temos três tipos de relacionamentos:

- 1:1
Cada um dos elementos de uma entidade só pode ser relacionar com apenas um elemento de outra entidade. Exemplo: Uma pessoa só pode ter um CPF e um CPF só pode pertencer a uma pessoa.

![](https://miro.medium.com/max/488/0*1LYS32SlX1r-tA-O)

- 1:N
Cada elemento de uma entidade pode ter um relacionamento com vários elementos de outra entidade. Exemplo: Um departamento pode ter vários funcionários, mas um funcionário só pode pertencer a um departamento.

![](https://miro.medium.com/max/487/0*juh2cep51CkcnV8T)

- N:N
Vários elementos de uma entidade podem se relacionar com vários elementos de outra entidade. Mas diferente dos outros dois relacionamentos, é sugerido criar uma tabela intermediária que relacionada as outras duas.
Exemplo: Um filme possui vários atores, e um ator pode atuar em vários filmes.

![](https://miro.medium.com/max/770/0*qCEMDt2pDHcxGib4)

#####Vamos por em prática...

---

##Primeiro, o que é um Docker?

![docker](https://miro.medium.com/max/1300/0*NZvDAXlhaYgl5__e)

Docker é uma plataforma open source de alto desempenho, desenvolvida em Go e criada pelo Google, que garante maior facilidade em criar e administrar ambientes isolados, assegurando rápida disponibilização de programas para o usuário final.
Seu objetivo é criar, testar e implementar aplicações em um ambiente separado, chamado *container*, permitindo que o desenvolvedor consiga empacotar o software de maneira padronizada, já que a plataforma disponibiliza código, bibliotecas, *runtime* e ferramentas do sistema.

###E por quê usar Docker?
O maior benefício do uso da plataforma é agilizar até 7 vezes mais a disponibilização do software do que em virtualizações convencionais. Outros benefícios que podemos citar são:
- Possibilidade de configurar diferentes ambientes de forma rápida, diminuindo incompatibilidades entre sistemas disponíveis;
- Modularizar, permitindo desabilitar uma parte do dispositivo, para realizar atualizações de reparo ou adição de funcionalidades sem interromper todo o programa;
- Formar diversas camadas diferentes, onde se dividem em dois grupos: Imagens: o usuário pode compartilhar um aplicativo ou um conjunto de serviços em diversos ambientes. Quando há qualquer alteração na imagem, ou uso de um comando como executar ou copiar, é criada uma camada.

> **Containers**: é o local onde estão as modificações da aplicação que está em execução. Nele é onde o usuário pode modificar uma imagem.

- Reverter para recuperar a versão anterior. É possível por conta das camadas que foram criadas;
- Implantação rápida, pois o tempo e desempenho ocorrem simultaneamente.

##PostgreSQL

![Postgres](https://miro.medium.com/max/700/0*G2jQ9XpVHKTSCdn8)

O Postgres oferece opção de criar e executar instâncias por meio de containers Docker. 

###E por que escolhi o Postgres? 
Pela possibilidade de execução de sua ferramenta gráfica de administração a partir de um container.
Antes de mais nada, precisamos ter uma imagem presente na máquina onde os containers existirão. Para isso vamos executar o seguinte código no terminal:
```shell
docker pull postgres
```
Caso queira listar todas as imagens disponíveis no seu sistema:
```shell
docker images
```
![](https://miro.medium.com/max/789/0*Q1d9H13D8fSq6F3p)

Para manter o estado à medida que você vai trabalhando com o container, precisa commitar as alterações:
```shell
//docker commit [ID do container] [nome da imagem]
docker commit afd8110f1813 postgres
```
###Trabalhando com containers.
Listando todos os seus containers atuais *(tantos os running quanto os non-running)*:
```shell
docker ps -l
```

![](https://miro.medium.com/max/1319/0*Lz0CbT0w7jpM80d4)

###Iniciando uma instância.
```shell
//docker run --name [Nome do seu banco de dados] -e POSTGRES_PASSWORD=[Senha do seu banco de dados] -d postgres
docker run --name teste-postgres -e POSTGRES_PASSWORD=Postgres2019! -d postgres
```
E verifica se o docker foi instanciado em sua máquina:
O comando `docker ps -a` lista todas as instâncias.

![](https://miro.medium.com/max/1773/0*pGBW1ofDIKoRXbT_)

Agora dê um start com `docker start [CONTAINER ID]` e `docker ps` para conferir se foi:

![](https://miro.medium.com/max/1306/0*FOvDbxcX6YXSQEgt)

###Plataforma de banco de dados
Eu uso o **dbeaver-ce** e vou utilizá-lo neste tutorial.

Abra o **dbeaver** e clique em **New database connection**.

![](https://miro.medium.com/max/39/0*a15ucT7M8-jQ-6lL)

Selecione **PostgreSQL**

![](https://miro.medium.com/max/99/0*Qskp-RrWUXmIUAzk)

E configure sua conexão, nesse caso precisa apenas colocar a senha que colocamos ao dar `docker run`, lá em iniciando uma instância.
Password: *Postgres2019!*

![](https://miro.medium.com/max/817/0*0UxRlnHKEJz9-iFY)

Antes de concluir, teste a conexão. Logo após pode dar ok e mãos a obra.

###Criando Schemas.

![](https://miro.medium.com/max/204/0*HTGmGDiXk5TAIstE)

Você pode fazer de duas maneiras, uma é clicando com o botão direito sobre **Schema > New Schema**; ou clicar com o botão direito sobre **PostgreSQL — postgres > SQL Editor**.

Vamos com a segunda opção.

Vai abrir uma janela igual da imagem abaixo:

![](https://miro.medium.com/max/1793/0*18Y30Je8pSppixAC)

Nesta janela é onde vamos escrever nossos scripts.

```sql
CREATE SCHEMA lista_telefonica
```
‌Selecione tudo, dê `Ctrl + Enter`, clique com o botão direito sobre **PostgreSQL — postgres > Refresh**.
Note que seu Schema foi criado:

![](https://miro.medium.com/max/210/0*dABwbYeDcH-pqp0X)

###Criando Tabelas.
```sql
CREATE SEQUENCE sq_lista_telefonica START WITH 1;
CREATE TABLE lista_telefonica(
    id           INT8             NOT NULL,
    nome         VARCHAR(20)      NOT NULL,
    sobrenome    VARCHAR(20)      NOT NULL,
    ddd          CHAR(2)          NOT NULL,
    telefone     CHAR(9)          NOT NULL,
    CONSTRAINT pk_lista_telefonica PRIMARY KEY (ID),
    CONSTRAINT uk_lista_telefonica  UNIQUE (ddd, telefone)
);
CREATE UNIQUE INDEX ix_lista_telefonica ON lista_telefonica (ddd, telefone);
```
Primeiro vamos entender o script acima.

- `CREATE SEQUENCE sq_lista_telefonica START WITH 1;` -> Vai fazer com que o id da sua tabela seja auto incrementado a partir do 1. Mas há uma observação sobre o sequence: rodando o script direto no banco pelo dbeaver, o sequence não é usado, mas na aplicação onde tem explicitamente essa ligação, funciona. Para funcionar tanto na aplicação quanto direto no banco, teríamos que usar default nextval('nome_da_sequencia') quando a tabela fosse criada ;
- `CREATE TABLE lista_telefonica` -> cria uma tabela;
- `( ... )` -> Tudo o que estiver entre parêntesis serão dados de sua tabela;
- `id / nome / sobrenome / ddd / telefone` -> são suas colunas;
- `INT8` -> tipo inteiro com 8 bytes;
`VARCHAR(20)` -> comprimento variável com limite e o limite fica entre ( );
- `CHAR(2)` -> comprimento fixo, completado com brancos;
- `NOT NULL` -> ao usar not null, permitimos que valores sejam adicionados posteriormente;
- `CONSTRAINT pk_lista_telefonica PRIMARY KEY (ID)` -> A restrição PRIMARY KEY (Chave Primária) indica que uma coluna ou grupo de colunas identifica de forma única cada registro em uma tabela;
- `CONSTRAINT uk_lista_telefonica UNIQUE (ddd, telefone)` -> A restrição - `UNIQUE` assegura que os dados sejam únicos.

Puts, coloquei o tipo errado, e agora? Vamos supor que coloquei o tipo do id errado, ao invés de INT8, quero INT2:

![](https://miro.medium.com/max/311/0*htAQ_qj8Gj56-OTp)

```sql
ALTER TABLE lista_telefonica.lista_telefonica ALTER COLUMN id TYPE int2
USING id::int2;
```

> *lista_telefonica.lista_telefonica*
Porque está duplicado?
O primeiro 'lista_telefonica' é o nome do Schema e o segundo é o nome da tabela.

Ou você pode alterar clicando duas vezes sobre a coluna id(int8) e em Data type, selecionar int2:

![](https://miro.medium.com/max/1195/0*wOIFVkz9bEx5XPii)

Vamos ver as mudanças:

![](https://miro.medium.com/max/324/0*Zs6UAP7aMN7DuCOa)

###Inserção de Registros.
```sql
INSERT
  INTO
  lista_telefonica.lista_telefonica (id,
  nome,sobrenome,
  ddd,
  telefone)
VALUES (1,
  'Jennifer',
  'Omena',
  '21',
  '977778888');
```

![](https://miro.medium.com/max/562/0*mfzE8YTFWjQ4kViY)

```sql
INSERT
  INTO
  lista_telefonica.lista_telefonica (id,
  nome,sobrenome,
  ddd,
  telefone)
VALUES (2,
  'Leonardo',
  'Omena',
  '21',
  '988887777');
```

![](https://miro.medium.com/max/562/0*Jy_ryM7OclwFRi79)

###Exclusão de Registros.
Digamos que adicionei um registro erroneamente e quero excluir do meu banco.

![](https://miro.medium.com/max/568/0*7U0unguj7R6VNL_P)

Vamos deletar o quinto registro:
```sql
DELETE
FROM
  lista_telefonica.lista_telefonica
WHERE
  ID = 5;
```

![](https://miro.medium.com/max/569/0*-UjKAYfGb8mLk3SZ)

###Select Básico.
O Select serve para buscar os dados de uma tabela do banco de dados, retornando dados em forma de tabela. 
Digamos que quero saber o nome de todo mundo que está salvo na minha lista telefônica:

```sql
SELECT
	nome,
	sobrenome
FROM
	lista_telefonica.lista_telefonica;
```

![](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LtBlzfJpOHzLhCvqiJx%2F-LuXs0gdNFW10bTH8ypg%2F-LuYFRJk0X44dsdkkWln%2Fimage.png?alt=media&token=fc4a8ff0-bdc4-4af2-b23d-9b9e85569890)

###Atualização de Registro
Vamos fazer de conta que colocamos o sobrenome do Luciano e da Raquel errado, e precisamos atualizar esses dados. Quero mudar de Ferraz para Mascarenhas.

```sql
UPDATE
	lista_telefonica.lista_telefonica
SET
	sobrenome = 'Mascarenhas'
WHERE
	sobrenome = 'Ferraz'
```

Traduzindo o script acima fica: Atualize no schema lista_telefonica na tabela lista_telefonica para sobrenome *Mascarenhas* onde sobrenome está *Ferraz*.

![](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LtBlzfJpOHzLhCvqiJx%2F-LuXs0gdNFW10bTH8ypg%2F-LuZ-q79QYXePLl05Mb8%2Fimage.png?alt=media&token=b78254c5-fd77-4488-96c1-ae8b8041e674)

###Condicionais
Antes de mais nada, vou inserir mais dois registros na nossa lista telefônica. Observe:

![](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LtBlzfJpOHzLhCvqiJx%2F-LuXs0gdNFW10bTH8ypg%2F-LuZ3-E18xLuPMne0fcA%2Fimage.png?alt=media&token=4c0e2107-a08a-47bb-b4a2-d33ff0ebf4a0)

Mas descobrimos que o **ddd** de *Jessica* e *Giselle* foi salvo como 22, mas é ddd 21. Vamos corrigir usando  condicional, **where**:

```sql
UPDATE
	lista_telefonica.lista_telefonica
SET
	ddd = '21'
WHERE
	ddd = '22'
```

![](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LtBlzfJpOHzLhCvqiJx%2F-LuhmWTKPVes_bDvf_5J%2F-LuhnHCQnc_d_45ZKh3p%2Fimage.png?alt=media&token=f51b9de5-6dcc-418f-86d4-fba939e22ea0)

###Ordenação
Vamos pegar todas as colunas e registros e ordenar pelos nomes por ordem alfabética decrescente:

```sql
select
	*
from
	lista_telefonica.lista_telefonica
order by
	nome desc;
```

![](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LtBlzfJpOHzLhCvqiJx%2F-LuhnjS_xqaljCXbyMyj%2F-Luhq-rJjlTV8CbY_YJ0%2Fimage.png?alt=media&token=d1edafc4-4f97-46e1-af18-ef1d1206f548)

###Agrupamentos
Antes de mais nada, vamos inserir mais alguns registros na nossa lista telefônica:

```sql
INSERT INTO lista_telefonica.lista_telefonica (id, nome,sobrenome,ddd,telefone) VALUES (7,'Ana','Souza', '91', '922221111');

INSERT INTO lista_telefonica.lista_telefonica (id, nome,sobrenome,ddd,telefone) VALUES (8,'Amanda','Silva', '81', '922223333');

INSERT INTO lista_telefonica.lista_telefonica (id, nome,sobrenome,ddd,telefone) VALUES (9,'Amadel','Ferreira', '71', '944443333');

INSERT INTO lista_telefonica.lista_telefonica (id, nome,sobrenome,ddd,telefone) VALUES (10,'Jorge','Cabral', '91', '977773333');

INSERT INTO lista_telefonica.lista_telefonica (id, nome,sobrenome,ddd,telefone) VALUES (11,'Xandi','Lira', '81', '988882222');

INSERT INTO lista_telefonica.lista_telefonica (id, nome,sobrenome,ddd,telefone) VALUES (12,'Leandro','Mariani', '91', '900007777');

INSERT INTO lista_telefonica.lista_telefonica (id, nome,sobrenome,ddd,telefone) VALUES (13,'Fábio','Lopes', '51', '966661111');

INSERT INTO lista_telefonica.lista_telefonica (id, nome,sobrenome,ddd,telefone) VALUES (14,'Manuel','Freitas', '21', '955559999');

INSERT INTO lista_telefonica.lista_telefonica (id, nome,sobrenome,ddd,telefone) VALUES (15,'Sonia','Carvalho', '71', '988880000');

INSERT INTO lista_telefonica.lista_telefonica (id, nome,sobrenome,ddd,telefone) VALUES (16,'Marta','Estevez', '81', '911110000');
```

![](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LtBlzfJpOHzLhCvqiJx%2F-Lv6tFU2BxW57wbPtTj0%2F-Lv6wV_lQUKxU-lHjtd8%2Fimage.png?alt=media&token=cb799ef0-85f6-4130-9403-8cfc3771722e)

Agora quero agrupar todo mundo que tenha o mesmo sobrenome, ordernar por ddd e quero saber quantas pessoas têm o mesmo sobrenome. Para isso, vamos precisar de um contador.

```sql
SELECT
	sobrenome,
	ddd,
	count(id) "total agrupado"
FROM
	lista_telefonica.lista_telefonica lt
GROUP BY
	ddd, sobrenome
ORDER BY  
	ddd
```

![](https://blobscdn.gitbook.com/v0/b/gitbook-28427.appspot.com/o/assets%2F-LtBlzfJpOHzLhCvqiJx%2F-Lv6tFU2BxW57wbPtTj0%2F-Lv6wrvC6ZI_4sAKHVxw%2Fimage.png?alt=media&token=66d2772a-3c84-48bc-adf1-c9ecb40bc240)

Bom, é isso. Espero que tenham curtido. Aceito todo tipo de feedback.
Compartilhem com seus amigos.

Até a próxima!