quinta-feira, 24 de setembro de 2020

Matemática básica e estatística com SQL


Se o seu banco de dados incluir algum tipo de dados do tipo inteiro, decimais ou pontos flutuantes é provável que você vai precisar realizar alguns cálculos com esses dados. Por exemplo, se você precisar saber qual é o valor da soma de todos os elementos de uma coluna, ou você precise adicionar um valor a cada elemento de uma coluna ou linha. Felizmente podemos fazer essas tarefas utilizando a linguagem SQL. Com ela podemos realizar desde operações matemáticas básicas a estatísticas avançadas.
Nesse tutorial, vamos iniciar no básico das operações matemáticas e avançar para as funções matemáticas mais complexas e iniciar em estatística. O dataset que vamos usar pode ser baixado aqui.
Veja abaixo como importar esses dados:

MariaDB [(none)]> create database meu_db;
Query OK, 1 row affected (0.151 sec)

MariaDB [(none)]> use meu_db;
Database changed
MariaDB [meu_db]> create table censu_2015
    -> (CensusTract INT,State VARCHAR(15),County VARCHAR(15),
    -> TotalPop INT,Men INT,Women INT,
    -> Hispanic FLOAT,White FLOAT,Black FLOAT,
    -> Native FLOAT,Asian FLOAT,Pacific FLOAT,
    -> Citizen INT,Income FLOAT,IncomeErr FLOAT,
    -> IncomePerCap FLOAT,IncomePerCapErr FLOAT,Poverty FLOAT,
    -> ChildPoverty FLOAT,Professional FLOAT,Service FLOAT,
    -> Office FLOAT,Construction FLOAT,Production FLOAT,
    -> Drive FLOAT,Carpool FLOAT,Transit FLOAT,
    -> Walk FLOAT,OtherTransp FLOAT,WorkAtHome FLOAT,
    -> MeanCommute FLOAT,Employed INT,PrivateWork FLOAT,
    -> PublicWork FLOAT,SelfEmployed FLOAT,FamilyWork FLOAT,
    -> Unemployment FLOAT);
Query OK, 0 rows affected (0.914 sec)

MariaDB [meu_db]> LOAD DATA LOCAL INFILE
    -> 'C:/Users/user/acs2015_census_tract_data.csv'
    -> INTO TABLE censu_2015
    -> FIELDS TERMINATED BY ','
    -> LINES TERMINATED BY '\n'
    -> IGNORE 1 LINES;
Query OK, 74001 rows affected, 65535 warnings (8.923 sec)
Records: 74001  Deleted: 0  Skipped: 0  Warnings: 97104

Operações matemáticas

Nos primeiros exemplos vamos utilizar as operações básicas que você aprendeu na escola. Na tabela abaixo é mostrado as operações que vamos utilizar com mais frequência.

Operador Operação
+ Adição
- Subtração
* Multiplicação
/ Divisão sem resto
%, MOD(x, y) Modulo ou resto
^ Exponenciação
SQRT(x) Raiz quadrada

Esses operadores podem ser utilizados com números, como numa calculadora, ou com valores em tabelas. Nesse tutorial vamos mostrar os dois.

Tipos de dados e matemática

Quando estiver trabalhando com operações matemáticas é importante saber o tipo de dado onde essas operações serão aplicadas. Não ter conhecimento do tipo de dado pode resultar num erro ou numa operação indesejada.
Aplicando uma operação entre dois números, o resultado retornado segue o padrão abaixo:

  • Dois inteiros retorna um inteiro.
  • Um NUMERIC com outro retorna um NUMERIC.
  • Qualquer operação com um float retorna um float.

Mas com a exponenciação e a raiz quadrada isso não acontece. A operação de raiz quadrada pode retornar um número de ponto flutuante mesmo quando a operação é com um inteiro e a exponenciação pode retornar um inteiro.

Adição, subtração e multiplicação

Até o momento utilizamos a declaração SELECT para recuperar dados de uma tabela. Nesse tutorial vamos utilizá-lo em conjunto com os operadores para realizar operações matemáticas. Vamos começar com as operações de adição, subtração e multiplicação.

MariaDB [(none)]> SELECT 2 + 2;
+-------+
| 2 + 2 |
+-------+
|     4 |
+-------+
1 row in set (0.038 sec)

MariaDB [(none)]> SELECT 9 - 2;
+-------+
| 9 - 2 |
+-------+
|     7 |
+-------+
1 row in set (0.001 sec)

MariaDB [(none)]> SELECT 3 * 4;
+-------+
| 3 * 4 |
+-------+
|    12 |
+-------+
1 row in set (0.007 sec)

Não temos surpresas aqui. Como esperado dois mais dois é quatro, nove menos dois é sete e, como sempre, três vezes quatro é doze. O resultado é mostrado numa coluna, mas não se preocupe, nenhuma tabela está sendo alterada.

Divisão, modulo e raiz quadrada

Na divisão, o resultado sempre será um número de ponto flutuante. Isso porque o operador de divisão faz a divisão exata. Já o operador módulo retorna o resto de uma divisão. Esse operador realiza uma divisão e retorna o resto dessa divisão. Se a divisão for exata o resto é zero, do contrário, o resto será um inteiro. A função de raiz quadrada retorna a raiz quadrada do número passado como parâmetro. Veja um exemplo de utilização:

MariaDB [(none)]> SELECT 9 / 2;
+--------+
| 9 / 2  |
+--------+
| 4.5000 |
+--------+
1 row in set (0.001 sec)

MariaDB [(none)]> SELECT MOD(9,2);
+----------+
| MOD(9,2) |
+----------+
|        1 |
+----------+
1 row in set (0.001 sec)

MariaDB [(none)]> SELECT 9 % 2; -- O mesmo da declaração acima
+-------+
| 9 % 2 |
+-------+
|     1 |
+-------+
1 row in set (0.001 sec)

MariaDB [(none)]> SELECT SQRT(9);
+---------+
| SQRT(9) |
+---------+
|       3 |
+---------+
1 row in set (0.001 sec)

Ordem das operações

Nas expressões matemáticas existe uma ordem em que as operações devem ser feitas. No SQL é a mesma coisa. Por exemplo, qual operação deve ser executada primeiro? A subtração ou a multiplicação? Por isso é importante saber qual operação deve ser aplicada primeiro para atingir o resultado esperado. Essa é a ordem de prioridade dos operadores:

  1. Funções. No caso a função SQRT().
  2. Multiplicação, divisão e módulo.
  3. Adição e subtração.

Seguindo essa ordem as funções serão executadas primeiro, depois a multiplicação, divisão e módulo e, por último, a adição e subtração.
Se em algum momento você precisar a operação de adição ocorra antes da multiplicação, você pode alterar essa ordem de prioridade com parênteses. No exemplo abaixo, o resultado é diferente para cada declaração. Isso porque foi alterada a prioridade das operações.

MariaDB [(none)]> SELECT 5 + 4 * 9;
+-----------+
| 5 + 4 * 9 |
+-----------+
|        41 |
+-----------+
1 row in set (0.003 sec)

MariaDB [(none)]> SELECT (5 + 4) * 9;
+-------------+
| (5 + 4) * 9 |
+-------------+
|          81 |
+-------------+
1 row in set (0.060 sec)

Operações com colunas

Agora que já aprendemos como utilizar os operadores na linguagem SQL vamos aprender a usar esses mesmos operadores com valores em colunas. Quando usamos esses operadores em colunas a operação é realizada em cada linha da tabela.

MariaDB [meu_db]> SELECT State AS 'Estado',
    -> County AS 'Município',
    -> Asian AS 'Asiático',
    -> Hispanic AS 'Hispanico',
    -> Hispanic + Asian AS "Total de Hispânicos e Asiáticos"
    -> FROM censu_2015
    -> WHERE  State = 'Kansas';
+--------+--------------+----------+-----------+---------------------------------+
| Estado | Município    | Asiático | Hispanico | Total de Hispânicos e Asiáticos |
+--------+--------------+----------+-----------+---------------------------------+
| Kansas | Allen        |        0 |         3 |                               3 |
| Kansas | Allen        |      1.5 |         1 |                             2.5 |
| Kansas | Allen        |        0 |       0.1 |             0.10000000149011612 |
| Kansas | Allen        |      1.3 |       6.3 |              7.6000001430511475 |
| Kansas | Allen        |        0 |       5.9 |               5.900000095367432 |
| Kansas | Anderson     |        0 |       0.5 |                             0.5 |
| Kansas | Anderson     |        0 |       0.3 |             0.30000001192092896 |
| Kansas | Wyandotte    |        0 |       7.7 |               7.699999809265137 |
| Kansas | Wyandotte    |        0 |        44 |                              44 |
| Kansas | Wyandotte    |     16.5 |      33.4 |              49.900001525878906 |
| Kansas | Wyandotte    |      2.3 |      28.9 |              31.199999570846558 |
+--------+--------------+----------+-----------+---------------------------------+
770 rows in set (0.130 sec)

Essa não é a saída completa da busca, na sua máquina deve está bem maior. Mas, para o nosso proposito vai servir. Vamos começar explicando o código linha por linha. Na primeira linha selecionamos a coluna “State” e demos um apelido para ela (Estado). Fizemos a mesma coisa na linha dois, três e quatro. Depois pedimos para o gerenciador do banco de dados fazer a soma da coluna Hispanic e Asian e, como nas linhas anteriores, demos um apelido a essa coluna – Lembre-se que cada consulta retorna os dados organizados em colunas. Na linha seis informamos onde esses dados devem ser procurados. E por último fazemos uma filtragem dos dados com WHERE. Com a declaração WHERE estamos dizendo que só queremos os dados das colunas State, County, Asian e Hispanic onde o valor da coluna State seja igual a Kansas.
Com esse exemplo fica fácil perceber como as operações funcionam em tabelas. Como exercício tente realizar as outras operações e mudar o estado.

Porcentagem

Para o nosso próximo exemplo vamos descobrir qual é a porcentagem de homens no estado do Texas. Para isso vamos precisar saber a quantidade total da população e a quantidade total de homens. Nosso banco de dados já tem essas informações.
Veja como descobrir a porcentagem de homens no estado do Texas no exemplo abaixo:

MariaDB [meu_db]> SELECT State AS 'Estado',
    -> SUM(Men) AS 'Total Homens',
    -> SUM(Women) AS 'Total Mulheres',
    -> (SUM(Men) * 100) / (SUM(Men) + SUM(Women)) AS 'Porcentagem de Homens'
    -> FROM censu_2015
    -> WHERE State = 'Texas';
+--------+--------------+----------------+-----------------------+
| Estado | Total Homens | Total Mulheres | Porcentagem de Homens |
+--------+--------------+----------------+-----------------------+
| Texas  |     13171316 |       13367298 |               49.6308 |
+--------+--------------+----------------+-----------------------+
1 row in set (0.316 sec)

Nesse exemplo usamos a função SUM() para somar todos as linhas das colunas Men e Women, lembre-se que estamos somando apenas os valores das linhas que tenham o valor Texas na coluna State. Depois utilizamos as operações de multiplicação, divisão e soma para descobrir a porcentagem de homens no estado do Texas (Regra de três).

Função de soma e médias

As duas funções mais utilizadas na análise de dados são a SUM() e AVG(). Isso porque a função SUM() faz a soma de todas as linhas de uma coluna, como vimos no exemplo anterior. E a função AVG() retorna o valor médio de uma coluna numérica. O que a função AVG() faz é somar todos os valores de uma coluna e depois dividir pelo número de linhas. Veja um exemplo de utilização das duas funções abaixo:

MariaDB [meu_db]> SELECT AVG(TotalPop) FROM censu_2015;
+---------------+
| AVG(TotalPop) |
+---------------+
|     4325.5915 |
+---------------+
1 row in set (1.760 sec)

MariaDB [meu_db]> SELECT SUM(TotalPop) FROM censu_2015;
+---------------+
| SUM(TotalPop) |
+---------------+
|     320098094 |
+---------------+
1 row in set (0.168 sec)

Com a função AVG() descobrimos que a média dos valores da coluna TotalPop é 4325.5915 e com a função SUM() descobrimos que o total da soma da coluna TotalPop é 320098094.

domingo, 30 de agosto de 2020

O que é um contêiner web?

Imagem de Pexels por Pixabay

Um contêiner web é o componente de um servidor da web que interage com servlets Java. Um contêiner da web gerencia o ciclo de vida dos servlets; ele mapeia uma URL para um servlet específico, garantindo que o solicitante tenha direitos de acesso relevantes.
O contêiner web implementa o aspecto do componente da web da arquitetura de engenharia Java; ele especifica um ambiente de tempo de execução para vários componentes, como segurança, simultaneidade, transação e implantação.


Os servlets Java não têm um método main() definido, portanto, um contêiner é necessário para carregá-los. O servlet é implantado no contêiner.
Vejamos o que acontece quando um cliente envia uma determinada solicitação que requer interação com o servlet:

  • O cliente envia uma solicitação a um servidor web.
  • O servidor da web, que contém um servlet, envia essa solicitação ao contêiner.
  • O contêiner passa a solicitação para o respectivo servlet.
  • Os métodos de servlet são carregados.
  • O servlet entrega a resposta relevante ao contêiner, que a passa para o servidor. Eventualmente, a resposta chega ao cliente.

Fonte: What is a web container?
Licença: Creative Commons -Attribution -ShareAlike 4.0 (CC-BY-SA 4.0)

quinta-feira, 27 de agosto de 2020

Como você escreve um Dockerfile?

Photo by Jonas Smith on Unsplash

Um Dockerfile é um documento de texto (sem uma extensão de arquivo) que contém as instruções para configurar um ambiente para um contêiner do Docker. Você pode construir uma imagem Docker usando um Dockerfile.
O comando docker build . constrói uma imagem Docker usando o Dockerfile no diretório em que este comando é executado.
Aqui está um exemplo de arquivo docker:

# Usando a imagem oficial do Ubuntu como uma imagem pai
FROM ubuntu:latest

# Configurando o diretório de trabalho para /app
WORKDIR /app

# Copie o conteúdo do diretório atual para o contêiner em /app
COPY . /app

# Obtendo as atualizações para o Ubuntu e instalando o Python em nosso ambiente
RUN apt-get -y update  && apt-get install -y python

# Execute app.py quando o contêiner for iniciado
CMD ["python", "app.py"]

O Dockerfile é explicado linha por linha abaixo:

# Usando a imagem oficial do Ubuntu como uma imagem pai
FROM ubuntu:latest

A maioria dos arquivos docker começa a partir de uma imagem "pai". A imagem pai é adicionada por meio da palavra-chave FROM. Sua imagem se baseia na imagem principal. Adicionamos a imagem oficial do Ubuntu usando FROM ubuntu:latest.

# Configurando o diretório de trabalho para /app
WORKDIR /app

Em seguida, você define o diretório de trabalho em seu contêiner com WORKDIR. WORKDIR /app define o diretório atual para /app quando o contêiner começa a ser executado.

# Copie o conteúdo do diretório atual para o contêiner em /app
COPY . /app

Até agora, temos o Ubuntu OS em nosso ambiente com o diretório atual definido como /app. Agora, queremos transferir nossos próprios arquivos de fora para o contêiner. Fazemos isso usando COPY . /app em que o comando COPY copia todos os arquivos de nosso diretório atual (aquele que contém o Dockerfile) para o /app. Nosso contêiner conterá agora o Ubuntu OS e os arquivos de nosso diretório local com o diretório de trabalho definido como ./app. É isso aí! O contêiner terá apenas as coisas que você especificar.

# Obtendo as atualizações para o Ubuntu e instalando o Python em nosso ambiente
RUN apt-get -y update  && apt-get install -y python

O comando RUN é executado quando construímos a imagem e quaisquer dependências ou pacotes adicionais são geralmente instalados usando o comando RUN. Assumimos que temos a imagem do sistema operacional que especificamos e construímos outros pacotes sobre ela.

# Execute app.py quando o contêiner for iniciado
CMD ["python", "app.py"]

O CMD especifica o comando que é executado quando iniciamos o contêiner.
Nota: Você pode inserir comentários em seu Dockerfile usando #.

Fonte: How do you write a Dockerfile?
Licença: Creative Commons -Attribution -ShareAlike 4.0 (CC-BY-SA 4.0)

segunda-feira, 24 de agosto de 2020

Introdução ao Docker2 - compor e empacotar um aplicativo Spring Boot no Docker

Imagem de Pexels por Pixabay

Introdução

No artigo anterior, vimos o que é o Docker, como os contêineres são úteis como uma nova tendência de devOps e aprendemos a diferença entre contêineres e imagens. Também aprendemos sobre Dockerfile.
Agora, aprenderemos sobre o comando docker-compose e como executar um aplicativo SpringBoot de um contêiner local. Mas, antes de fazer isso, precisamos empacotar um aplicativo SpringBoot com o Docker. Vamos ver como fazer.

Dockerizing um aplicativo SpringBoot

A primeira coisa que faremos é obter um aplicativo básico baseado em SpringBoot que podemos encaixar. Para esse propósito, podemos usar o aplicativo básico que vem com o andaime Spring ao usar o Spring Initializr no IntelliJ, e podemos adicionar um endpoint muito simples que retornará uma mensagem de saudação no formato JSON.
Não importa se você não tem o Spring Initializr, desde que você possa expor um endpoint e consultá-lo por meio do objeto de teste mockMvc ou de uma ferramenta como o Postman. Para referência, aqui está nossa estrutura de pacote de aplicativo:

Você pode notar que o Dockerfile é colocado na pasta raiz de todo o projeto. Este será o arquivo usado para controlar todos os detalhes da nossa imagem, sua especificação.
Em seguida, o arquivo será usado para criar uma imagem Docker e, a partir dessa imagem, quando estiver efetivamente em execução, teremos nosso contêiner.
Para encaixar nosso aplicativo SpringBoot (ou qualquer aplicativo), é necessário especificar em nosso Dockerfile tudo o que precisamos para executar nosso aplicativo. Precisa ser, em essência, uma descrição em camadas de todas as etapas que tomaríamos para construir e executar nosso aplicativo, localmente, em um formato que pode ser:

  • reproduzível: um Dockerfile deve sempre resultar na mesma imagem depois de executado e totalmente especificado. Qualquer pessoa que usar essa imagem sempre obterá exatamente a mesma funcionalidade.
  • independente do ambiente: em essência, o poder do Docker depende do fato de que muitos serviços de infraestrutura e devOps agora o suportam em seus pipelines de desenvolvimento para que, sempre que um envio de novo código para um branch especificado acontecer, ele acione uma reconstrução completa de a imagem do Docker. Em seguida, os desenvolvedores podem usá-lo via docker-compose perfeitamente ou a imagem mais recente estará disponível em um registro para ser exposta como um micro-serviço para o mundo via nginx, etc.

Uma vez que a única coisa que realmente precisamos para iniciar o aplicativo SpringBoot após o término da compilação é o arquivo .jar contendo o código compilado, contanto que usemos uma imagem Java base para construir a nossa, podemos envolver nosso aplicativo SpringBoot em um Contêiner do Docker definindo o ponto de entrada como executando o arquivo .jar:

FROM openjdk:8
COPY target/greeter-*.jar greeter.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","greeter.jar"]

Portanto, construímos nossa imagem a partir de uma imagem base Java 8 openJDK e, em seguida, copiamos o arquivo JAR (que precisa existir em nossa máquina local no mesmo nível de pasta de destino e o próprio Dockerfile) de nosso sistema local para o diretório raiz do contêiner.
Em seguida, expomos a porta 8080 do contêiner para ficar disponível para o exterior. Esta é a mesma porta do serviço SpringBoot; caso contrário, o contêiner não saberá onde o serviço está sendo executado.
Por fim, chamamos o Java para iniciar nosso serviço a partir do JAR que acabamos de copiar para o contêiner de nossa máquina local. Isso significa que, ao iniciar um contêiner a partir da imagem construída por meio deste Dockerfile, iniciaremos efetivamente o aplicativo SpringBoot.

O comando docker-compose

O Dockerfile nos permite especificar o que é necessário para construir uma imagem.
docker-compose é um comando que nos permite especificar e iniciar vários serviços compostos de muitas imagens Docker juntos, de forma coordenada, enquanto nos permite especificar qual imagem usar para qual serviço e garantir que os serviços sejam iniciados corretamente. Também nos permite definir as variáveis de ambiente que são expostas como tal no contêiner em execução.
Vamos supor que nosso aplicativo exige autenticação de nome de usuário e senha em nosso endpoint. Para definir essas variáveis, podemos escrever um arquivo docker-compose.yml que colocamos no mesmo nível que o Dockerfile, e lá, podemos especificar de qual Dockerfile construir o contêiner, qual imagem usar (ambos para local cria e usa imagens remotas), que atende a composição de contêineres que usará e as variáveis de ambiente necessárias.
Um simples docker-compose.yml poderia ser:

version: '3'
services:
  web:
    build: .
    ports:
      - "8080:8080"
  environment:
    - username="User"
    - password="Pass"

Definimos um serviço chamado web que é construído a partir do Dockerfile no diretório atual, expõe a porta 8080 para o exterior e o serviço em execução no contêiner também está na porta 8080.
Em seguida, definimos as variáveis de ambiente disponíveis para nosso serviço, assim como definimos em um arquivo application.properties ou em um shell.
Para usar este arquivo para iniciar um contêiner, simplesmente fazemos:
docker-compose -f <caminho para compor o arquivo> -d up
Isso usará o arquivo de composição para construir e instanciar nosso contêiner e iniciar o serviço configurado com esse ambiente.
-d garante que o contêiner continue em execução em segundo plano e o inicializa.
O Compose permite aos desenvolvedores extrair uma imagem localmente em suas máquinas, executá-la e testá-la localmente, o que é muito útil.

Conclusão

Vimos o que é docker-compose, como ajuda os desenvolvedores a testar certas funcionalidades e como isola questões em um ambiente controlado. Também vimos o que é necessário para empacotar um aplicativo SpringBoot com Docker.

Fonte: Intro to Docker 2- Docker-compose and packaging a SpringBoot application by Bruno Oliveira
Licença: Creative Commons -Attribution -ShareAlike 4.0 (CC-BY-SA 4.0)

Introdução ao Docker - contêineres, imagens e o Dockerfile

Imagem de Valdas Miskinis por Pixabay

Introdução

Docker é um sistema de conteinerização que permite aos desenvolvedores e equipes lidar com a implantação e dimensionamento de aplicativos de uma maneira muito mais fácil.
Assim como o nome sugere, podemos pensar em um contêiner como um sistema isolado que contém tudo o que é necessário para executar um determinado aplicativo em uma determinada configuração.
As imagens podem ser pré-construídas, recuperadas de registros, criadas a partir de registros já existentes, combinadas, iniciadas por meio de uma rede comum, etc.
Veremos a diferença entre uma imagem e um contêiner, como eles se relacionam e como esses dois conceitos nos permitem tornar um aplicativo independente e pronto para ser implantado.

Imagens e recipientes

Podemos pensar em imagens e contêineres como dois estados diferentes do mesmo conceito subjacente. Basicamente, podemos dizer que um container é uma instância em execução de uma imagem que empacota e representa um determinado aplicativo. Para fins de analogia, vamos voltar às classes e objetos Java. Um contêiner pode ser visto como um objeto e uma imagem pode ser vista como a classe.
As imagens são usadas para iniciar os contêineres. A partir de contêineres em execução, podemos obter imagens, e tudo isso pode ser composto em conjunto para formar uma maneira muito poderosa e independente do sistema de empacotar e implantar aplicativos. Gosto de pensar nisso quase como "enviar seu host local". Por quê? Como o Docker requer que todas as configurações necessárias para executar um aplicativo estejam contidas e instaladas em uma imagem, criar uma imagem é essencialmente replicar o estado de execução atual do ambiente e do aplicativo, agrupando-o e disponibilizando-o na nuvem.

Então, para recapitular:

  • As imagens podem ser recuperadas de registros que contêm várias imagens pré-construídas para serem usadas pelos desenvolvedores para construir imagens mais complexas.
  • Os contêineres podem ser vistos como instâncias em execução de imagens. É possível orquestrar vários contêineres para serem iniciados e interrompidos como um "serviço agrupado", o que permite efetivamente que aplicativos inteiros sejam estruturados como um cluster de contêineres Docker relacionados.

Dockerfile - A maneira de encaixar um aplicativo

Para encaixar um aplicativo, o fluxo que seguimos é criar uma imagem Docker que é construída sobre uma já existente e contém, por exemplo, nosso aplicativo JAR ou o script de inicialização de um aplicativo Python como seu comando de inicialização.
Um comando de inicialização para um contêiner é o comando que será executado quando o contêiner for iniciado. Portanto, essencialmente, a instância em execução de nossa imagem se torna o contêiner que executa nosso aplicativo.
Um Dockerfile é uma especificação que usamos para construir um novo contêiner a partir de uma imagem pré-construída e adicionar nossa lógica customizada para iniciar nosso aplicativo. Em um Dockerfile, usamos o comando docker build para construir uma imagem.
Um Dockerfile funciona em camadas. A primeira camada começa com a palavra-chave FROM e define qual imagem pré-construída usar para construir nossa própria imagem. Posteriormente, podemos definir mais coisas como permissões de usuário, quais arquivos copiar para o contêiner do sistema local (pense, por exemplo, o requirements.txt de um aplicativo da web Python) e quais scripts de inicialização executar. Abaixo está um exemplo de Dockerfile:

FROM python:3
 
WORKDIR /usr/src/app
 
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
 
COPY . .
 
CMD [ "python", "./your-daemon-or-script.py" ]

Vamos revisar o conteúdo deste Dockerfile simples e ver o que está acontecendo.
A primeira linha, FROM python: 3, está declarando que a imagem que vamos construir se expandirá na imagem base que contém todo o tempo de execução necessário para executar aplicativos baseados em Python 3.
Todos os comandos após este serão aplicados sobre a imagem Python 3 já existente e, como resultado, criaremos nossa própria imagem personalizada Python 3 que contém nosso aplicativo.
WORKDIR define o diretório de trabalho. O diretório especificado será assumido para qualquer outro comando que segue no Dockerfile e onde nosso aplicativo está. Nos comandos CMD e COPY abaixo, o diretório . refere-se ao WORKDIR.
Posteriormente, o comando COPY é usado para copiar arquivos de nosso sistema de arquivos local, onde está o Dockerfile, para o contêiner para que esses arquivos estejam prontamente disponíveis em nossa imagem.
O comando RUN é autodescritivo e executa um determinado comando, que, neste exemplo, está instalando nossas dependências.
Finalmente, o comando CMD define o comando de inicialização a ser executado quando o contêiner é iniciado; neste caso, simplesmente iniciaremos nosso aplicativo.

Conclusão

Esses conceitos básicos são a base sobre a qual o Docker desenvolve recursos adicionais. Depois de conhecer esses conceitos, você pode aprender qualquer outra coisa. Agora você pode construir suas próprias imagens e entender o que verá quando encontrar um Dockerfile em um projeto do Docker.
Por último, mas não menos importante, você pode encontrar muitas imagens oficiais pré-construídas no Docker Hub.
A seguir, veremos como empacotar um aplicativo Springboot simples com o Docker e apresentaremos o comando docker-compose e o arquivo docker-compose.

Fonte: Intro to Docker - Containers, images, and the Dockerfile
Licença: Creative Commons -Attribution -ShareAlike 4.0 (CC-BY-SA 4.0)

terça-feira, 18 de agosto de 2020

Importando e exportando dados com SQL

Photo by Ken Yam on Unsplash

Até o momento aprendemos a criar um banco de dados, criar uma tabela e inserir dados nessa tabela com a declaração INSERT. O modo que vinhamos fazendo, inserindo linha a linha na tabela, pode ser útil para tabelas de testes rápidos ou adicionar novas linhas numa tabela já existente, mas o mais comum é inserir centenas ou até mesmo milhares de linhas. E fazer isso do jeito que vinhamos fazendo não é viável. Mas não se preocupe, existem outros meios.
Se os seus dados estiverem num arquivo com uma estrutura definida, separado em colunas, como os arquivos CSV, você pode importar esses dados diretamente para uma tabela com a instrução LOAD DATA INFILE. Essa instrução é utilizada para importar dados em massa.
Também podemos utilizar a instrução SELECT INTO OUFILE para exportar os dados de uma tabela. Essa instrução é muito útil quando você quer compartilhar os dados com um colega ou exportar os dados para outro formato, como um arquivo do Excel.
Nesse tutorial vamos aprender como importar e exportar dados utilizando a linguagem SQL. Mas antes de começarmos vamos verificar se a importação de dados é permitida com a instrução SHOW VARIABLES LIKE '%infile%'; e verificar onde o arquivo que vamos importar deve estar, com a instrução SHOW VARIABLES LIKE 'secure_file_priv';.

mysql> SHOW VARIABLES LIKE '%infile%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| local_infile  | ON    |
+---------------+-------+
1 row in set (0.01 sec)

mysql> SHOW VARIABLES LIKE 'secure_file_priv';
+------------------+------------------------------------------------+
| Variable_name    | Value                                          |
+------------------+------------------------------------------------+
| secure_file_priv | C:\ProgramData\MySQL\MySQL Server 8.0\Uploads\ |
+------------------+------------------------------------------------+
1 row in set (0.01 sec)

Com essas duas instruções, descobrimos que a importação de arquivos é permitida e a pasta onde devemos colocar o arquivo que vamos importar é a Uploads. Se o valor da variável local_infile for OFF mude o seu valor com a instrução SET GLOBAL local_infile=1;.

Importando dados de um arquivo CSV com SQL

Os arquivos CSV são arquivos de texto puro. Nesses arquivos não existe formatação, os dados, nesse tipo de arquivo, são delimitados por vírgula. Você não precisa de um arquivo com extensão csv, pode ser um simples arquivo txt, mas os dados precisam estar delimitados por vírgulas. Esse é o conteúdo do arquivo que vamos utilizar:

Nome, Idade, Endereco
Amelia, 23, Rua Antonio Monteiro
Rafael, 52, 25 de Marco
Daniel, 32, Teotonio alcantara

Para importar esses dados vamos precisar criar uma tabela com três colunas. Veja o exemplo abaixo:

mysql> CREATE TABLE cadastro
    -> (nome VARCHAR(10), idade SMALLINT, endereco VARCHAR(25));
Query OK, 0 rows affected (0.76 sec)

mysql> LOAD DATA INFILE
    -> 'C:/ProgramData/MySQL/MySQL Server 8.0/Uploads/dados.txt'
    -> INTO TABLE cadastro
    -> FIELDS TERMINATED BY ','
    -> LINES TERMINATED BY '\n'
    -> IGNORE 1 LINES;
Query OK, 3 rows affected (0.16 sec)
Records: 3  Deleted: 0  Skipped: 0  Warnings: 0

mysql> SELECT * FROM cadastro;
+--------+-------+------------------------+
| nome   | idade | endereco               |
+--------+-------+------------------------+
| Amelia |    23 |  Rua Antonio Monteiro  |
| Rafael |    52 |  25 de Marco           |
| Daniel |    32 |  Teotonio alcantara    |
+--------+-------+------------------------+
3 rows in set (0.02 sec)

Como funciona a instrução LOAD DATA INFILE

Na primeira instrução criamos uma tabela com três colunas. Cada uma para guardar strings, números e strings, respectivamente. Repare que esses são os tipos de dados do nosso arquivo dados.txt.
Na primeira linha da segunda instrução, estamos informando que vamos importar um arquivo. Na segunda linha, passamos o caminho desse arquivo entre aspas simples (Repare que não usamos barras invertidas). Depois definimos que os dados estão delimitados por vírgula, a linha termina com uma quebra de linha e que na leitura desse arquivo deve ser ignorado uma linha – a primeira.
Ignoramos uma linha porque, geralmente, a primeira linha dos arquivos csv contêm os nomes das colunas e não os dados.

Exportando uma tabela para um arquivo CSV

Você pode exportar tabelas de um banco de dados para um arquivo CSV com a instrução INTO OUTFILE. Com essa instrução você pode exportar os dados de uma tabela delimitando os valores com qualquer carácter especifico. Veja um exemplo:

mysql> SELECT nome, idade, endereco
    -> INTO OUTFILE 'C:/ProgramData/MySQL/MySQL Server 8.0/Uploads/dados.csv'
    -> FIELDS TERMINATED BY ','
    -> LINES TERMINATED BY '\n'
    -> FROM cadastro;
Query OK, 3 rows affected (0.03 sec)

Como funciona a instrução INTO OUTFILE

Na primeira linha, utilizamos SELECT para selecionar as colunas que queremos exportar. Na segunda linha definimos o caminho do arquivo. Depois definimos que os dados serão delimitados por vírgula, as linhas vão terminar com quebra de linha e, por último, informamos de qual tabela vamos exportar os dados.

domingo, 16 de agosto de 2020

Entendendo tipos de dados do SQL

Photo by travelnow.or.crylater on Unsplash

Quando se procura um banco de dados é bom verificar os tipos de dados em cada em cada tabela e coluna. As vezes o criador do banco de dados disponibiliza um dicionário dos dados. O dicionário é um arquivo que explica os dados do bancos de dados, como os dados foram obtidos, a relação entre eles, a disposição dos dados, e mais. Infelizmente, encontrar um banco de dados com a documentação é muito difícil. Então, o jeito é aprender sobre o banco de dados fazendo inspeções das tabelas e seus dados.
Entender os tipos de dados é uma tarefa importante na analise e criação de bancos de dados. Com os dados armazenados de forma correta a analise e a criação do banco de dados se torna mais precisa. Entender o conceito de tipos de dados pode ser útil além da linguagem SQL, esse é um conceito que pode ser útil em outras linguagens. Os conhecimentos aprendidos nesse tutorial não se aplica apenas a linguagem SQL.
As colunas dos bancos de dados SQL, só podem armazenar um tipo de dado por coluna. Não é possível armazenar dois tipo de de dados na mesma coluna. Quando o tipo de dado é definido com a declaração CREATE TABLE, esse é o único tipo de dado permitido na coluna. A definição do tipo é especifica logo após o nome da coluna. Veja um exemplo:

MariaDB [meu_BD]> CREATE TABLE jogos_brasil(
    -> data_jogo DATE,
    -> gols INTEGER
    -> );

Na tabela jogos_brasil, criamos a coluna data_jogo e definimos que essa coluna vai guardar dados do tipo data adicionando DATE depois do seu nome. Fizemos a mesma coisa com a tabela gols, a diferença é o tipo de dados que essa tabela vai guardar, que é dados do tipo INTEGER (inteiro).
Esses são os tipos de dados mais comuns nos bancos de dados:

  • Caracteres: Qualquer carácter ou simbolo
  • Números: Incluí todos os números
  • Data e hora: Informações de dados e horas

Nesse tutorial vamos aprender a utilizar esses tipos de dados a fundo.

Caracteres

As sequências de caracteres são o tipo de dados mais comum. Isso porque esse tipo pode ser utilizado para guardar dados um conjunto de números, texto ou datas. Mas o dado continua sendo do tipo carácter, '1' e 1 são tipos de dados diferentes. O dado que pode ser guardado no tipo carácter é o '1'. Esses são os tipos de caracteres:

  • CHAR(n) Esse tipo de dado tem um comprimento máximo definido com n. Criando uma coluna com CHAR(25), o tamanho máximo de caracteres que podem ser guardados em cada linha é de vinte caracteres. Se for armazenado menos caracteres o restante será preenchido com espaços em branco. Esse tipo também pode ser escrito na forma longa, CHARACTER(n). A forma mais comum é a curta.
  • VARCHAR(n): Esse tipo de dado é parecido com o anterior, a diferença é que se definirmos o tamanho máximo de 25 caracteres, mas só adicionamos dez caracteres, o restante não será preenchido. Esse tipo de dados é mais utilizado em grandes bancos de dados, porque esse tipo de dado economiza espaço. A sua forma longa é VARYING(n).
  • TEXT: Com esse tipo de dado você pode guardar dados de comprimento ilimitado (A sequência mais longa pode ter o tamanho de até 1GB).

Não a uma diferença de desempenho entre os três tipos de dados. A economia de espaço utilizando os tipos de dados VARCHAR() e TEXT pode ser uma vantagem, mas só em questão de armazenamento. Já que esses tipos de dados vão alocar apenas o espaço necessário.

Números

As colunas do tipo numérico, além de poder guardar dados desse tipo, nos permitem realizar operações matemáticas com esses dados como a soma, multiplicação, divisão entre outros. E nos permite ordenar os dados de forma crescente ou decrescente. Essa é uma vantagem, se a ordem é importante no seu projeto. Por isso, se no seu projeto vai ser preciso fazer operações matemáticas ou precisar de uma ordem, utilize os tipos de dados numéricos.
Esses são os tipos numéricos da linguagem SQL:

  • Inteiros: números inteiros, positivos e negativos
  • Ponto fixo e ponto flutuante: dois formatos de frações de números inteiros

Inteiros

Os dados mais comum nos bancos de dados são os do tipo inteiro. Isso porque os números inteiros estão em toda parte. Olhe ao seu redor e veja quantas coisas podem ser contadas ou medidas com números inteiros.
Existem três tipos de dados do tipo inteiro: smallint, integer e bigint. A diferença entre esses três tipos é o valor máximo que cada tipo pode guardar. Na tabela abaixo, é mostrado a quantidade que cada tipo pode armazenar e a quantidade de memoria utilizada no armazenamento:

Tipo de dado Tamanho em bytes Intervalo
smallint 2 bytes −32768 a +32767
integer 4 bytes −2147483648 a +2147483647
bigint 8 bytes −9223372036854775808 a +9223372036854775807

Uma preocupação ao definir o tipo de dados é o seu consumo de memoria e a escalonabilidade dos dados. Se você escolher um tipo que consome pouca memória e depois esse tipo não conseguir armazenar os seus dados, você terá um problema. Do outro lado é possível que você escolha um tipo que consome mais memoria e nunca venha a precisar dessa precisão extra. Por isso faça um estudo pra definir qual tipo usar.

Tipos de ponto flutuante

Existem dois tipos de de dados de ponto flutuante, real e de precisão dupla. a diferença entre os dois é a quantidade de dados que eles podem representar. O tipo real tem precisão de seis casas decimais e o de precisão dupla quinze. Veja uma lista dos tipos de ponto flutuante:

Tipo de dado Tamanho em bytes Intervalo
numeric, decimal Variável Até 131072 dígitos antes da vírgula decimal;
Até 16383 dígitos após o ponto decimal
real 4 bytes Precisão de até 6 casas decimais
double precision 8 bytes Precisão de até 15 casas decimais

Veja um exemplo de como utilizar cada tipo de ponto flutuante:

MariaDB [meu_BD]> CREATE TABLE tipos_numericos(
    -> coluna_numeric NUMERIC(20, 5),
    -> coluna_real REAL,
    -> coluna_d_precisao DOUBLE PRECISION
    -> );
Query OK, 0 rows affected (0.021 sec)

MariaDB [meu_BD]> INSERT INTO tipos_numericos
    -> VALUES
    -> (.8, .8, .8),
    -> (3.13579, 3.13579, 3.13579),
    -> (2.1234469287, 2.1234469287, 2.1234469287);
Query OK, 3 rows affected, 1 warning (0.006 sec)
Records: 3  Duplicates: 0  Warnings: 1

MariaDB [meu_BD]> SELECT * FROM tipos_numericos;
+----------------+--------------+-------------------+
| coluna_numeric | coluna_real  | coluna_d_precisao |
+----------------+--------------+-------------------+
|        0.80000 |          0.8 |               0.8 |
|        3.13579 |      3.13579 |           3.13579 |
|        2.12345 | 2.1234469287 |      2.1234469287 |
+----------------+--------------+-------------------+
3 rows in set (0.001 sec)

Note que os dois último números da coluna coluna_numeric foram arredondados e o primeiro foi preenchido com zeros. Isso acontece porque definimos a precisão de cinco casas decimais. Assim, se o número não tiver precisão suficiente as casas decimais serão preenchidas com zeros. E se passar do número especificado, o número é arredondado.

Escolhendo o tipo de dados numérico

Definir qual tipo de dados usar é uma tarefa que demanda estudo e depende de cada caso, mas existem alguma dicas que podem ser útil na escolha:

  1. Sempre que possível utilize números inteiros. Só use tipos de ponto flutuante se for necessário.
  2. Se precisar utilizar dados de ponto flutuante e precisar de exatidão no cálculos utilize o NUMERIC ou se equivalente DECIMAL.
  3. Escolha um tipo de dado grande o suficiente. Só use um tipo de dado menor quando você tiver certeza que não vai precisar de um número maior.

Data e hora

Quando você digita uma data em um formulário de pesquisa, você está obtendo os benefícios dos bancos de dados terem conhecimento da hora atual, além da capacidade de suportar os formatos de datas, horas e as variedades do calendário, como os anos bissextos e fusos horários. Isso é crucial para narrar histórias com dados, porque a questão de quando algo ocorreu normalmente é uma questão tão importante quanto quem, o quê ou quantos estavam participando da historia.
Veja uma lista dos tipos de dados de data e hora mais usados:

Tipo de dado Tamanho em bytes Descrição Intervalo
timestamp 8 bytes Data e hora 4713 AC to 294276 DC
date 4 bytes Data 4713 AC to 5874897 DC
time 8 bytes Hora 00:00:00 to 24:00:00

Veja um exemplo de como utilizar esses tipos de dados:

MariaDB [meu_BD]> CREATE TABLE data_hora(
    -> coluna_timestamp TIMESTAMP,
    -> coluna_data DATE,
    -> coluna_time TIME
    -> );
Query OK, 0 rows affected (0.021 sec)

MariaDB [meu_BD]> INSERT INTO data_hora
    -> VALUES
    -> ('2018-12-31 01:00', '2018-04-23', '12:00:21'),
    -> ('2012-06-29 02:00', '2019-04-23', '14:30:22'),
    -> (now(), '2018-04-23', '12:00:21');

Query OK, 3 rows affected (0.003 sec)

A função now() retorna a data e hora de quando a função é chamada. Se for passado uma data errada isso vai gerar um erro. Um exemplo de data errada é: '2018-02-30'.