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'.

sábado, 15 de agosto de 2020

Exploração de dados com SELECT

Photo by Edu Grande on Unsplash

A melhor parte de analisar os dados é quando vou entrevistar os dados (obter informações). É nessa etapa que descobrimos se os dados estão completos, se existe algum dado que não é necessário e, acima de tudo, que tipo de informações podemos obter com esses dados. Uma maneira de pensar nessa etapa é como se ela você uma entrevista de emprego. Você deve fazer perguntas que vão lhe dar as informações para contratar essa pessoa e onde ela será mais produtiva. Do mesmo modo você deve fazer com os dados: Esses dados me serão útil? Onde?
A entrevista é importante porque você obtém informações. Por exemplo, numa entrevista de emprego você descobre que metade dos entrevistados não colocou o nome do pai ou não informou um endereço de e-mail. Na entrevista você pode descobrir se os seus dados estão incorretos: nomes trocados, faltando letras, data no formato errado, entre outras coisas. Esse tipo de informação se torna parte dos dados.
Na linguagem SQL, começamos a entrevista com a declaração SELECT. Essa declaração é utilizada para recuperar informações de linhas e colunas nos bancos de dados. A instrução SELECT pode ser simples, retornando toda a informação sem nenhum filtro, ou pode ser complexa, retornando dezenas de tabelas e aplicando filtros e cálculos.
Nesse tutorial vamos começar com instruções SELECT simples.

Sintaxe básica da declaração SELECT

Nesse exemplo, vamos selecionar todas as linhas e colunas da tabela funcionarios. Essa coluna está no banco de dados que criamos no último tutorial. É importante ter um banco de dados selecionado antes de clamar a instrução SELECT. Você pode notar o banco de dados selecionado entre colchetes.

MariaDB [(none)]> USE meu_banco_de_dados;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [meu_banco_de_dados]> SELECT * FROM funcionarios;
+------+---------+----------+-------------------+-------------+---------+
| id   | p_nome  | u_nome   | profissao         | data_inicio | salario |
+------+---------+----------+-------------------+-------------+---------+
|    0 | Eveline | Ferreira | Vendedor          | 2018-02-03  |    3400 |
|    1 | Osvaldo | Perreira | Vendedor          | 2018-03-03  |    3400 |
|    2 | Maria   | Saturno  | Gerente de vendas | 2018-01-01  |    8500 |
|    3 | Mariana | Reis     | Diretor           | 2018-01-01  |    5000 |
|    4 | Marisa  | Lima     | Gerente           | 2019-03-01  |    9000 |
+------+---------+----------+-------------------+-------------+---------+
5 rows in set (0.001 sec)

Esse exemplo mostra a forma mais básica de selecionar dados com a linguagem SQL. Utilizamos o asterisco, depois da declaração SELECT, como um curinga. O curinga é utilizado no lugar de um valor especifico. O asterisco assume o valor de todos os valores possíveis. A instrução utilizada nesse exemplo é uma forma de dizer: 'selecione todas as colunas da tabela funcionarios'. Se você passar o nome de uma coluna, no lugar do asterisco, apenas os valores dessa coluna serão apresentados. A declaração FROM indica onde a declaração SELECT deve procurar os dados. O ponto e vírgula é utilizado para sinalizar o fim da instrução.

Consultando um conjunto de colunas

O asterisco é utilizado para descobrir todo o conteúdo de uma tabela. Com grandes bancos de dados é mais prático limitar o número de coluna que a declaração deve retornar. Você pode não ter interesse nos dados sobre id ou data de entrada, você pode estar procurando informações sobre profissão e salário. Podemos definir os dados que temos interesse passando os nomes das colunas separados por vírgula depois da declaração SELECT. Veja um exemplo:

MariaDB [meu_banco_de_dados]> SELECT salario, profissao, p_nome FROM funcionarios;
+---------+-------------------+---------+
| salario | profissao         | p_nome  |
+---------+-------------------+---------+
|    3400 | Vendedor          | Eveline |
|    3400 | Vendedor          | Osvaldo |
|    8500 | Gerente de vendas | Maria   |
|    5000 | Diretor           | Mariana |
|    9000 | Gerente           | Marisa  |
+---------+-------------------+---------+
5 rows in set (0.001 sec)

Note que os dados foram retornados na ordem em que passamos os nomes das colunas. Primeiro o salário, depois a profissão e por último o nome.
Mesmos esses exemplos básicos, mostram uma boa estrategia para iniciar a analise de dados. É uma prática comum verificar se todos os dados estão presentes e no formato certo. As datas estão completas? Dia, mês e ano? Ou só o mês e o ano? Existem nomes começando, misteriosamente, com a letra C? Esses são alguns problemas que você pode achar nessa "entrevista".
Nesse exemplo, utilizamos um banco de dados com poucas linhas, mas quando você se deparar com um banco de dados, com centenas ou milhares de linhas, é preciso fazer uma analise rápida da quantidade e qualidade dos dados. Vamos fazer isso com mais algumas declarações do SQL.

Selecionando valores únicos com a declaração DISTINCT

É muito comum encontrar colunas com valores duplicados. Na coluna profissao temos algumas linhas com o valor vendedor. Isso porque uma empresa pode ter vários vendedores. Se você precisar saber quais profissões estão presentes na tabela profissao, podemos fazer uma consulta que vai retornar apenas valores únicos, eliminando os valores duplicados com a declaração DISTINCT (os valores não serão mostrados, mais continuam na tabela).

MariaDB [meu_banco_de_dados]> SELECT DISTINCT profissao
    -> FROM funcionarios;
+-------------------+
| profissao         |
+-------------------+
| Vendedor          |
| Gerente de vendas |
| Diretor           |
| Gerente           |
+-------------------+
4 rows in set (0.001 sec)

Mesmo a tabela funcionarios tendo cinco linhas, essa instrução nos mostro apenas quatro. Isso porque o valor Vendedor aparece mais de uma vez e a declaração DISTINCT nos mostra apenas valores únicos. Essa declaração é útil para analisar a quantidade e a qualidade dos dados. Com ela podemos saber o quanto de dados vamos analisar e quantos desses dados estão numa boa qualidade. Por exemplo, com a declaração DISTINCT podemos saber se existem dados escrito de forma de forma errada, como o valor Vendedor escrito dessa forma: Cendedor.
Podemos ver os valores únicos de mais de uma coluna por vez. Para isso precisamos passar os nomes das colunas separadas por vírgula.

MariaDB [meu_banco_de_dados]> SELECT DISTINCT profissao, salario
    -> FROM funcionarios;
+-------------------+---------+
| profissao         | salario |
+-------------------+---------+
| Vendedor          |    3400 |
| Gerente de vendas |    8500 |
| Diretor           |    5000 |
| Gerente           |    9000 |
+-------------------+---------+
4 rows in set (0.001 sec)

Desse modo podemos saber, por exemplo, quantos profissões tem salários diferentes. Se tiver uma pessoa que tem a mesma profissão, mas que recebe um salário diferente, será retornado o valor profissão para cada salário diferente.
Adicione uma nova linha na tabela funcionarios com o valor Vendedor e com um salário diferente de 3400 (Veja como fazer isso no tutorial anterior). Depois chame a mesma instrução do exemplo acima, o resultado deve ser parecido com esse:

MariaDB [meu_banco_de_dados]> SELECT DISTINCT profissao, salario
    -> from funcionarios;
+-------------------+---------+
| profissao         | salario |
+-------------------+---------+
| Vendedor          |    3400 |
| Gerente de vendas |    8500 |
| Diretor           |    5000 |
| Gerente           |    9000 |
| Vendedor          |    6500 |
+-------------------+---------+
5 rows in set (0.001 sec)

Note que agora temos dois valores Vendedor na coluna profissão, mas com os valores da coluna salário diferentes.

Ordenando dados com a declaração ORDER BY

Um maneira de encontrar algum sentido nos dados é organizando esses dados de alguma forma. Quando os dados estão organizados fica mais fácil encontrar algum padrão que não percebemos quando os dados estão misturados. Com a linguagem SQL podemos organizar os dados de uma consulta com a declaração ORDER BY seguido do nome da coluna ou das colunas.
Quando utilizamos ORDER BY numa consulta não estamos modificando a tabela, apenas os dados retornados pela consulta. Veja um exemplo de como utilizar ORDER BY:

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> ORDER BY salario DESC;
+---------+-------------------+---------+
| p_nome  | profissao         | salario |
+---------+-------------------+---------+
| Marisa  | Gerente           |    9000 |
| Maria   | Gerente de vendas |    8500 |
| Fabiana | Vendedor          |    6500 |
| Mariana | Diretor           |    5000 |
| Eveline | Vendedor          |    3400 |
| Osvaldo | Vendedor          |    3400 |
+---------+-------------------+---------+
6 rows in set (0.001 sec)

Nesse exemplo, organizamos os dados de forma decrescente adicionando a palavra-chave DESC. Por padrão ORDER BY organiza os dados de forma crescente. Ordenando os valores de forma decrescente descobrimos qual é a profissão que ganha mais.
A organização nos permite fazer consultas de forma mais direta e uma maior flexibilização na hora de visualizar os dados. Um dessas flexibilidades é a capacidade de organizar mais de uma coluna.

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, data_inicio
    -> FROM funcionarios
    -> ORDER BY p_nome ASC, data_inicio DESC;
+---------+-------------------+-------------+
| p_nome  | profissao         | data_inicio |
+---------+-------------------+-------------+
| Eveline | Vendedor          | 2018-02-03  |
| Fabiana | Vendedor          | 2019-03-20  |
| Maria   | Gerente de vendas | 2018-01-01  |
| Mariana | Diretor           | 2018-01-01  |
| Marisa  | Gerente           | 2019-03-01  |
| Osvaldo | Vendedor          | 2018-03-03  |
+---------+-------------------+-------------+
6 rows in set (0.001 sec)

Nesse exemplo, organizamos a coluna p_nome de forma crescente (ordem alfabética) e, a coluna data_inicio, de forma decrescente. Mas se a coluna data_inicio esta organizada de forma decrescente porque a data 2019-03-20 não está no topo? Porque a prioridade é a coluna p_nome. Primeiro os resultados são organizados por nomes e depois por data, criando subgrupos de organização.
Com esse exemplo, podemos obter a resposta para o tipo de pergunta: “Qual é a pessoa, que o nome começa com F, que está na empresa a mais tempo?”. Esse é um exemplo bobo, mas com os dados certos essa informação é muito importante.

Filtrando linhas com WHERE

Com um banco de dados grande você vai precisar limitar os resultados de uma consulta. Talvez você queira apenas os resultados que satisfaça algumas condições. Por exemplo, as profissões que ganham mais de três mil ou o nome das pessoas que trabalham como vendedor. Para limitar os resultados com condições que os dados devem ter usamos a declaração WHERE.
A declaração WHERE é utilizada para encontrar dados que correspondam a um valor especifico ou satisfação uma condição. Utilizando WHERE você pode excluir linhas com base em condições.
A declaração WHERE vem depois da declaração FROM e do nome da tabela onde vamos fazer uma consulta. Veja um exemplo:

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE profissao = 'Vendedor';
+---------+-----------+---------+
| p_nome  | profissao | salario |
+---------+-----------+---------+
| Eveline | Vendedor  |    3400 |
| Osvaldo | Vendedor  |    3400 |
| Fabiana | Vendedor  |    6500 |
+---------+-----------+---------+
3 rows in set (0.003 sec)

Nesse exemplo, fizemos uma consulta nas colunas p_nome, profissao e salario da tabela funcionarios e definimos que queremos ver apenas as linhas onde o valor da coluna profissao é igual a Vendedor. No exemplo acima, utilizamos apenas o operador igual, mas a linguagem SQL tem suporte a outros operadores. Veja a lista dos operadores suportados pela linguagem SQL:

Operador Função Exemplo
= Igual WHERE profissao = ‘Vendedor’
<> Diferente WHERE profissao <> ‘Vendedor’
> Maior que WHERE salario > 3000
< Menor que WHERE salario < 6000
>= Maior ou igual WHERE salario >= 6500
<= Menor ou igual WHERE salario <= 6500
BETWEEN Entre x e y WHERE salario BETWEEN 6000 AND 9000
IN Corresponde a um dos elementos passados WHERE profissao IN (‘Vendedor’, 'Gerente de vendas')
LIKE Corresponde a um padrão (Case sensitive) WHERE p_nome LIKE ‘Mar%’
NOT Nega uma condição WHERE p_nome NOT ILIKE ‘mar%’

Agora que já sabemos quais operadores podemos usar, vamos utilizar cada operador num exemplo. Vamos começar com o operador igual. Vamos selecionar todas as linhas com o valor da coluna salario igual a 3400.

MariaDB [meu_banco_de_dados]> SELECT u_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE salario = 3400;
+----------+-----------+---------+
| u_nome   | profissao | salario |
+----------+-----------+---------+
| Ferreira | Vendedor  |    3400 |
| Perreira | Vendedor  |    3400 |
+----------+-----------+---------+
2 rows in set (0.001 sec)

Para o nosso próximo exemplo, vamos selecionar todas as linhas que tenham um valor diferente de Vendedor na coluna profissao.

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE profissao <> 'Vendedor';
+---------+-------------------+---------+
| p_nome  | profissao         | salario |
+---------+-------------------+---------+
| Maria   | Gerente de vendas |    8500 |
| Mariana | Diretor           |    5000 |
| Marisa  | Gerente           |    9000 |
+---------+-------------------+---------+
3 rows in set (0.001 sec)

No exemplo abaixo, vamos usar os operadores >, <, >=, <=.

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE salario > 4000;
+---------+-------------------+---------+
| p_nome  | profissao         | salario |
+---------+-------------------+---------+
| Maria   | Gerente de vendas |    8500 |
| Mariana | Diretor           |    5000 |
| Marisa  | Gerente           |    9000 |
| Fabiana | Vendedor          |    6500 |
+---------+-------------------+---------+
4 rows in set (0.001 sec)

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE salario < 7000;
+---------+-----------+---------+
| p_nome  | profissao | salario |
+---------+-----------+---------+
| Eveline | Vendedor  |    3400 |
| Osvaldo | Vendedor  |    3400 |
| Mariana | Diretor   |    5000 |
| Fabiana | Vendedor  |    6500 |
+---------+-----------+---------+
4 rows in set (0.001 sec)

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE salario >= 3400;
+---------+-------------------+---------+
| p_nome  | profissao         | salario |
+---------+-------------------+---------+
| Eveline | Vendedor          |    3400 |
| Osvaldo | Vendedor          |    3400 |
| Maria   | Gerente de vendas |    8500 |
| Mariana | Diretor           |    5000 |
| Marisa  | Gerente           |    9000 |
| Fabiana | Vendedor          |    6500 |
+---------+-------------------+---------+
6 rows in set (0.001 sec)

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE salario <= 9000;
+---------+-------------------+---------+
| p_nome  | profissao         | salario |
+---------+-------------------+---------+
| Eveline | Vendedor          |    3400 |
| Osvaldo | Vendedor          |    3400 |
| Maria   | Gerente de vendas |    8500 |
| Mariana | Diretor           |    5000 |
| Marisa  | Gerente           |    9000 |
| Fabiana | Vendedor          |    6500 |
+---------+-------------------+---------+
6 rows in set (0.001 sec)

Para o nosso próximo exemplo vamos selecionar todas as linhas que o valor da coluna salario esteja entre 3500 e 8900.

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE salario BETWEEN 3500 AND 8900;
+---------+-------------------+---------+
| p_nome  | profissao         | salario |
+---------+-------------------+---------+
| Maria   | Gerente de vendas |    8500 |
| Mariana | Diretor           |    5000 |
| Fabiana | Vendedor          |    6500 |
+---------+-------------------+---------+
3 rows in set (0.004 sec)

Com operador IN você pode passar um conjunto de valores para a declaração WHERE. Desse modo a declaração WHERE vai fazer mais de uma verificação por dado. Por exemplo, você pode querer selecionar as linhas que tenham os valores 'Vendedor' ou 'Gerente' na coluna profissao. Veja o exemplo disso:

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE profissao IN ('Gerente', 'Vendedor');
+---------+-----------+---------+
| p_nome  | profissao | salario |
+---------+-----------+---------+
| Eveline | Vendedor  |    3400 |
| Osvaldo | Vendedor  |    3400 |
| Marisa  | Gerente   |    9000 |
| Fabiana | Vendedor  |    6500 |
+---------+-----------+---------+
4 rows in set (0.001 sec)

Usando o operador LIKE

Para o nosso próximo exemplo vamos utilizar o operador LIKE. Esse operador vai selecionar dados que começam com a string que passamos para o operador. É importante notar que o operador LIKE diferencia letras maiúsculas de minusculas, então Mar e mar são diferentes.

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE p_nome LIKE 'Mar%';
+---------+-------------------+---------+
| p_nome  | profissao         | salario |
+---------+-------------------+---------+
| Maria   | Gerente de vendas |    8500 |
| Mariana | Diretor           |    5000 |
| Marisa  | Gerente           |    9000 |
+---------+-------------------+---------+
3 rows in set (0.001 sec)

O sinal de porcentagem (%) no final da string é utilizado para sinalizar que queremos qualquer valor que comece com 'Mar' não importa o tamanho que esse valor tenha. Esse caractere também pode ser utilizado para selecionar valores com um final de string em comum. Por exemplo, na coluna p_nome existem dois nomes que terminam com ‘ana’ no final, Fabiana e Mariana. Para selecionar as linhas que tenham esses valores passaríamos a string ‘%ana’ para o operador LIKE.
Outro carácter utilizado é o underline (_). Esse caractere é usado para sinalizar que queremos os valores com apenas um carácter a mais dos que já passamos. Por exemplo, passando ‘Marian_’ para o operador LIKE, o operador vai retornar todos os valores que comecem com ‘Marian’ e tenham um carácter a mais. Esse carácter pode ser qualquer um, pode ser um a, um b ou c. Se começar com ‘Marian’ e tiver somente mais um carácter o valor será selecionado pelo operador. E o underline pode ser usado no meio da palavra.
Veja outros exemplos de utilização dos caracteres de porcentagem e underline:

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE p_nome LIKE '%ana';
+---------+-----------+---------+
| p_nome  | profissao | salario |
+---------+-----------+---------+
| Mariana | Diretor   |    5000 |
| Fabiana | Vendedor  |    6500 |
+---------+-----------+---------+
2 rows in set (0.000 sec)

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE p_nome LIKE '%ia%';
+---------+-------------------+---------+
| p_nome  | profissao         | salario |
+---------+-------------------+---------+
| Maria   | Gerente de vendas |    8500 |
| Mariana | Diretor           |    5000 |
| Fabiana | Vendedor          |    6500 |
+---------+-------------------+---------+
3 rows in set (0.001 sec)

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE p_nome LIKE 'Marian_';
+---------+-----------+---------+
| p_nome  | profissao | salario |
+---------+-----------+---------+
| Mariana | Diretor   |    5000 |
+---------+-----------+---------+
1 row in set (0.001 sec)

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE p_nome LIKE 'Mar_ana';
+---------+-----------+---------+
| p_nome  | profissao | salario |
+---------+-----------+---------+
| Mariana | Diretor   |    5000 |
+---------+-----------+---------+
1 row in set (0.001 sec)

Combinando operadores com AND ou OR

A linguagem SQL nos permite utilizar mais de um operador ao mesmo tempo. Assim podemos definir mais de uma condição para selecionar um valor. Por exemplo, você pode querer selecionar apenas os valores que tenham o valor 'Vendedor' na coluna profissao e o valor da coluna salario maior que 3400. Para isso usamos a palavra chave AND. Com ela o dado só será selecionado se atender as duas condições, ter o valor ‘Vendedor’ na coluna profissao E (AND) o valor da coluna salario maior que 3400.
A palavra-chave OR define duas condições e se qualquer uma dessas condições for satisfeita o dado é selecionado. Por exemplo, se você quiser selecionar os dados que tenham o valor ‘Vendedor’ na coluna profissao ou o valor da coluna salario seja maior que 3000, você deve usar OR.
Veja exemplos de como utilizar AND e OR:

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE profissao = 'Vendedor'
    -> AND salario > 3400;
+---------+-----------+---------+
| p_nome  | profissao | salario |
+---------+-----------+---------+
| Fabiana | Vendedor  |    6500 |
+---------+-----------+---------+
1 row in set (0.001 sec)

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE profissao = 'Vendedor'
    -> OR salario > 3000;
+---------+-------------------+---------+
| p_nome  | profissao         | salario |
+---------+-------------------+---------+
| Eveline | Vendedor          |    3400 |
| Osvaldo | Vendedor          |    3400 |
| Maria   | Gerente de vendas |    8500 |
| Mariana | Diretor           |    5000 |
| Marisa  | Gerente           |    9000 |
| Fabiana | Vendedor          |    6500 |
+---------+-------------------+---------+
6 rows in set (0.001 sec)

Utilizando WHERE E ORDER BY juntos

Você já pode perceber que até as consultas mais simples nos permitem analisar os dados com precisão e flexibilidade para encontrar o que procuramos. Aprendemos a usar as palavras chaves AND e OR para definir mais de uma condição ao selecionar dados. E ainda aprendemos a utilizar a declaração WHERE e organizar os dados com a declaração ORDER BY.
Agora vamos combinar o que aprendemos nesse tutorial num exemplo. O SQL é específico quanto à ordem das palavras chaves, então siga essa ordem:

SELECT nome_colunas
FROM nome_tabelas
WHERE condição
ORDER BY nome_colunas;

Veja um exemplo utilizando os conhecimentos que você acabou de adquirir:

MariaDB [meu_banco_de_dados]> SELECT p_nome, profissao, salario
    -> FROM funcionarios
    -> WHERE p_nome LIKE 'Mar' OR salario > 3400
    -> ORDER BY id DESC;
+---------+-------------------+---------+
| p_nome  | profissao         | salario |
+---------+-------------------+---------+
| Fabiana | Vendedor          |    6500 |
| Marisa  | Gerente           |    9000 |
| Mariana | Diretor           |    5000 |
| Maria   | Gerente de vendas |    8500 |
+---------+-------------------+---------+
4 rows in set (0.001 sec)

Note que não selecionamos a coluna id, mas podemos utilizá-la com a declaração ORDER BY.

sexta-feira, 14 de agosto de 2020

Criando seu primeiro banco de dados e tabela com SQL

Photo by Jeremy Manoto on Unsplash

A linguagem SQL oferece mais do que um meio de obter informações de um banco de dados. Com ela podemos definir as estruturas que serão utilizadas para guardar e organizar os dados. A principal estrutura e a TABLE (tabela).
Uma tabela funciona como uma grade, com linhas e colunas. Essas linhas e colunas criam células (como no Excel), onde podemos armazenar os dados. As linhas possuem colunas e, colunas armazenam dados. Os dados mais comuns são do tipo números, datas e strings (cadeia de caracteres). A linguagem SQL nos permite criar uma estrutura de tabela e definir como essa tabela se relaciona com outras no banco de dados. Outro uso comum do SQL é fazer consultas, para extrair dados, das tabelas.
Tabela é a principal estrutura que você deve aprender para entender os dados do banco de dados. Quando começar a trabalhar com um banco de dados novo, a primeira coisa que você deve fazer é olhar as tabelas. Procure por algo que possa chamar sua atenção nos nomes das tabelas e na estrutura das colunas. A coluna contem texto, número ou os dois? Qual é a quantidade de linhas em cada tabela?
Em seguida, veja quantas tabelas existem. O mais simples dos bancos de dados pode ter apenas uma tabela. Um banco de dados que armazena dados de clientes pode ter dezenas de tabelas, até mesmo centenas. Com essa informação você vai saber o quanto de dados você vai precisar analisar.

Criando um banco de dados com SQL

Os programas que vamos utilizar para nesse tutorial é o MariaDB e o MySQL. Esses programas são conhecidos como sistemas de gerenciamento de banco de dados. Com esses softwares podemos definir, criar e consultar bancos de dados. Ao instalar um desses programas, ele vai criar um servidor de banco de dados que já possui um banco de dados chamado mysql. Esse banco de dados contêm objetos que inclui usuários, tabelas e muito mais.
Nesse tutorial de SQL, e nos próximos, vamos usar o MariaDB e o MySQL a partir do terminal (Linux) e Prompt de Comando (Windows). Você pode utilizar tanto o MySQL quanto o MariaDB. A diferença entre os dois é quase só o nome.
Depois de instalar, qualquer um dos dois programas, digite mysql no terminal ou no prompt de comando. Você deve ver uma tela parecida com essa: 

Para ver os bancos de dados use a declaração SHOW DATABASES;. Esse comando vai lista todos os bancos de dados presentes na sua máquina. 

Não vamos mexer com nenhum desses bancos de dados, vamos criar o nosso próprio banco de dados. Para fazer isso precisamos digitar apenas uma linha: CREATE DATABASE meu_banco_de_dados;

Para criar um novo banco de dados utilizamos duas palavras chaves - CREATE e DATABASE - seguido pelo nome do banco de dados. Após o nome do novo banco de dados, terminamos a instrução com ponto e vírgula. O ponto e vírgula é utilizado para encerrar todas as instruções da linguagem SQL.
Agora que criamos um banco de dados vamos olhar os bancos de dados novamente e ver o nosso banco de dados meu_banco_de_dados

Criando uma tabela com SQL

É nas tabelas que os dados são armazenados e o relacionamento definido. Para criar uma tabela precisamos informar o seu nome e o tipo de dados que essa coluna vai guardar. O tipo de dado de uma coluna é muito importante, é esse tipo de dado que defini quais dados podem ser guardados na coluna. Por exemplo, uma coluna do tipo numérico, se você tentar guardar dados do tipo data ou texto vai gerar um erro.
Podemos acessar e analisar os dados das tabelas com declarações SQL. Também podemos visualizar e alterar os dados da tabela facilmente, se suas necessidades mudarem mais tarde.
Agora vamos criar uma tabela no nosso banco de dados meu_banco_de_dados.

Declaração CREATE TABLE do SQL

Vamos criar uma tabela dentro do nosso banco de dados para guardar informações sobre funcionários. As informações que vamos guardar são básicas: nome, salário, profissão, etc. Nesse primeiro momento vamos criar apenas a estrutura pra guardar os dados. É isso que a declaração CREATE TABLE faz, cria uma estrutura, uma tabela.
Antes de criar uma tabela, precisamos definir em qual banco de dados a tabela será criada. Fazemos isso usando a declaração USE. Veja abaixo, um exemplo de como selecionar o banco de dados e criar uma tabela com SQL:

Agora vamos entender o que acabamos de fazer. A declaração USE seleciona o banco de dados. Com a declaração CREATE TABLE funcionarios (...); criamos uma tabela com o nome funcionarios. Dentro dos parênteses, da declaração CREATE TABLE, definimos os nomes das colunas e o tipo de dados que essa coluna vai guardar. A primeira coluna é a de id. Essa coluna vai guardar somente dados do tipo inteiro (1, 2, 3, etc). Na segunda linha, dentro dos parênteses, criamos uma coluna chamada u_nome e definimos que ela vai guardar até 25 caracteres (a, b, c, 1, 23, #, etc). A coluna data_inicio vai guardar dados do tipo data (DD-MM-AAAA). E por fim a coluna salario vai guardar dados do tipo numérico (1, 0.23, 7865, etc).

Declaração INSERT do SQL

Para inserir dados numa tabela usamos a declaração INSERT INTO nome_da_tabela. Nessa declaração também definimos quais colunas vão receber valores. Veja o exemplo abaixo:

Na primeira linha definimos em qual tabela vamos inserir os dados. Na segunda linha definimos quais colunas vão receber os dados. VALUES indica o conjunto de valores que vão ser guardados na tabela. Cada conjunto de valores com o id, p_nome, u_nome, profissao, data_inicio e salario é passado entre parênteses e separados por vírgula. Note que todas essas linhas fazem parte de uma instrução, por isso só existe um ponto e vírgula. Essa instrução pode ser escrita numa linhas apenas.
Para ver os dados gravados use a declaração SELECT * FROM funcionarios;. Vamos aprender a utilizar essa declaração no próximo tutorial.

quinta-feira, 13 de agosto de 2020

Hash MD5 no Python

Imagem de TheDigitalWay por Pixabay

Os hashes criptográficos são muitos utilizados no dia-a-dia. Esse tipo de criptografia esta mais presente na sua vida do que você imagina. Por exemplo, eles são utilizados em assinaturas digitais, detecção de manipulação, códigos de autenticação de mensagens, impressões digitais, somas de verificação (verificação de integridade de mensagem ou arquivo), tabelas de hash, armazenamento de senha e muito mais. Os hashes têm mostrados a sua utilidade no envio de mensagens pela rede e para a segurança no armazenamento de senhas em servidores. Isso porque, a mensagem ou senha não fica salva diretamente no banco de dados, apenas o seu hash. Desse modo, mesmo que o banco de dados seja invadido as senhas estão salvas. Pelo menos em teoria (Nada é cem por cento seguro).
Para trabalhar com hash, python fornece várias funções no módulo hashlib. Nesse tutorial, vamos utilizar algumas dessas funções e explicar seu funcionamento do hash MD5.

MD5 Hash

A função de hash MD5 aceita sequências de bytes e retorna um valor hash de 128 bits. Esse hash é muito utilizado para verificar a integridade dos dados. Você já deve ter baixado algum arquivo, de um site, onde o hash MD5 é disponibilizado. Esse hash md5 é disponibilizado para que você possa ter certeza que o arquivo que você baixou é o mesmo que você queria. Geralmente são sites de torrents que fazem isso.
Essas são as funções que vamos utilizar para criar um hash md5:

  • encode(): Converte a string em bytes para ser aceitável pela função hash.
  • digest(): Retorna os dados codificados em formato de byte.
  • hexdigest(): Retorna os dados codificados em formato hexadecimal.

No exemplo abaixo, vamos passar um valor em bytes e vamos receber uma saída em bytes:

import hashlib 
  
# codificando a string Acervo Lima usando a função de hash md5
hash = hashlib.md5(b'Acervo Lima') 
  
# imprimir o equivalente em bytes. 
print("Equivalente em bytes ao hash: ", hash.digest())

No exemplo, chamamos a função md5() passando um valor em bytes. A função md5() faz a codificação dessa string e, depois, chamamos a função digest(). A função digest() retorna o resultado da função md5() em bytes.
Mas os hashes que vemos por ai não são disponibilizados em bytes, mas sim em hexadecimal. No exemplo abaixo, vamos retornar um hash md5 em hexadecimal.

>>> string = 'Acervo Lima'.encode()
>>> hash = hashlib.md5(string)
>>> print('O equivalente em hexadecimal ao hash: ',
... hash.hexdigest())
O equivalente em hexadecimal ao hash:  409b83920744582bf8b286e4e63e2207

Nesse exemplo, convertemos a string 'Acervo Lima' em bytes usando a função encode(). Depois, chamamos a função md5() para obter o hash md5 em bytes. E, em seguida, chamamos a função hexdigest(). A função hexdigest() converte os bytes em hexadecimal.

Referência:
Módulo hashlib

quarta-feira, 12 de agosto de 2020

concat() e append() do pandas, como usar

Imagem de Steve Buissinne por Pixabay

A biblioteca pandas possui várias facilidades para juntar séries e dataframes com outros tipos de conjuntos de dados. Nesse tutorial vamos aprender algumas dessas facilidades. Os métodos mais utilizados para concatenar uma série ou dataframe é o concat() e o append().

Concatenando Séries e DataFrames com o método concat

A função concat() é utilizada para concatenar (juntar) séries ou dataframes ao longo de um eixo. Com o método concat() podemos definir o eixo em que o método deve operar, isso no caso dos dataframes visto que as séries possuem somente um eixo. Antes de nos aprofundarmos nos parâmetros e peculiaridades desse método, vamos ver um exemplo simples de como o método opera.

>>> import pandas as pd
>>> dados1 = {
... 'A': ['A0', 'A1', 'A2', 'A3'],
... 'B': ['B0', 'B1', 'B2', 'B3'],
... 'C': ['C0', 'C1', 'C2', 'C3'],
... 'D': ['D0', 'D1', 'D2', 'D3']
... }
>>> dados2 = {
... 'A': ['A4', 'A5', 'A6', 'A7'],
... 'B': ['B4', 'B5', 'B6', 'B7'],
... 'C': ['C4', 'C5', 'C6', 'C7'],
... 'D': ['D4', 'D5', 'D6', 'D7']
... }
>>> dados3 = {
... 'A': ['A8', 'A9', 'A10', 'A11'],
... 'B': ['B8', 'B9', 'B10', 'B11'],
... 'C': ['C8', 'C9', 'C10', 'C11'],
... 'D': ['D8', 'D9', 'D10', 'D11']
... }
>>> df1 = pd.DataFrame(dados1, index=[0, 1, 2, 3])
>>> df2 = pd.DataFrame(dados1, index=[4, 5, 6, 7])
>>> df3 = pd.DataFrame(dados1, index=[8, 9, 10, 11])
>>> frames = [df1, df2, df3]
>>> resultado = pd.concat(frames)
>>> resultado
     A   B   C   D
0   A0  B0  C0  D0
1   A1  B1  C1  D1
2   A2  B2  C2  D2
3   A3  B3  C3  D3
4   A0  B0  C0  D0
5   A1  B1  C1  D1
6   A2  B2  C2  D2
7   A3  B3  C3  D3
8   A0  B0  C0  D0
9   A1  B1  C1  D1
10  A2  B2  C2  D2
11  A3  B3  C3  D3

O método concat() recebe uma lista ou dicionário e os junta de acordo com o eixo.
Sem contesto o exemplo acima deve ficar um pouco mais complicado de entender. Vamos voltar a ele. Digamos que você queira associar um rótulo para cada dataframe que foi concatenado. Assim fica mais fácil de saber onde cada dataframe foi posicionado. Podemos fazer isso passando os rótulos para o parâmetro keys.

>>> resultado = pd.concat(frames, keys=['X', 'Y', 'Z'])
>>> resultado
       A   B   C   D
X 0   A0  B0  C0  D0
  1   A1  B1  C1  D1
  2   A2  B2  C2  D2
  3   A3  B3  C3  D3
Y 4   A0  B0  C0  D0
  5   A1  B1  C1  D1
  6   A2  B2  C2  D2
  7   A3  B3  C3  D3
Z 8   A0  B0  C0  D0
  9   A1  B1  C1  D1
  10  A2  B2  C2  D2
  11  A3  B3  C3  D3

Adicionando esses rótulos criamos um índice hierárquico. Com essa vantagem podemos selecionar cada bloco pelo seu rótulo usando o atributo loc.

>>> resultado.loc['X']
    A   B   C   D
0  A0  B0  C0  D0
1  A1  B1  C1  D1
2  A2  B2  C2  D2
3  A3  B3  C3  D3

Com esse exemplo, você já pode ver como esse atributo pode ser útil. Para saber mais sobre o atributo loc, veja nosso tutorial sobre Seleção de subconjuntos de dados.

Definindo o critério de concatenação dos eixos

Os DataFrames pandas possuem dois eixos, o eixo de índice (eixo 0) e o eixo das colunas (eixo 1). Quando juntamos dois ou mais dataframes temos que decidir o que fazer com os outros eixos (além do que estamos concatenando). As opções que temos são as seguintes:

  • join='outer': Fazer a união de todos os eixos. Esse é o valor padrão. Essa opção evita perdas de dados.
  • join='inner': Fazer uma interseção dos eixos.

Vamos ver exemplos de como funciona cada uma das opções.

>>> dados4 = {
...     'B': ['B2', 'B3', 'B6', 'B7'],
...     'D': ['D2', 'D3', 'D6', 'D7'],
...     'F': ['F2', 'F3', 'F6', 'F7']
... }
>>> df4 = pd.DataFrame(dados4, index=[2, 3, 6, 7])
>>> resultado = pd.concat([df1, df4], axis=1, join='outer')
>>> resultado
     A    B    C    D    B    D    F
0   A0   B0   C0   D0  NaN  NaN  NaN
1   A1   B1   C1   D1  NaN  NaN  NaN
2   A2   B2   C2   D2   B2   D2   F2
3   A3   B3   C3   D3   B3   D3   F3
6  NaN  NaN  NaN  NaN   B6   D6   F6
7  NaN  NaN  NaN  NaN   B7   D7   F7

Nesse exemplo, usamos dois dataframes com o número de índices e colunas diferentes. Isso para ficar claro que passando o valor ‘outer’, para o parâmetro join, o método concat() vai tentar organizar os dois dataframes de uma forma que nenhum dado seja perdido. Se você olhar atentamente vai ver que o segundo dataframe (df4) começa no índice dois e na coluna segunda coluna B. Como o primeiro dataframe (df1) possui o mesmo índice não foi preciso criar um novo. Isso não acontece com a coluna B, não da pra encaixar essas colunas. Então novas são criadas. Por isso temos elementos vazios no dataframe resultado. Se o parâmetro join for omitido, o resultado é o mesmo porque o valor padrão desse parâmetro é ‘outer’.
Agora, vamos ver um exemplo passando o valor ‘inner’:

>>> resultado = pd.concat([df1, df4], axis=1, join='inner')
>>> resultado
    A   B   C   D   B   D   F
2  A2  B2  C2  D2  B2  D2  F2
3  A3  B3  C3  D3  B3  D3  F3

Nesse exemplo, podemos ver que perdemos muitos dados. Isso porque, quando passamos o valor ‘inner’ para o parâmetro join, estamos dizendo para o método que queremos apenas os dados dos índices que os dois dataframes tem em comum (no caso, 2 e 3). Você pode verificar que todos os dados dos índices 2 e 3, dos dois dataframes (df1 e df4), foram concatenados e os dados dos outro índices foram perdidos.
Nos dois últimos exemplos, concatenamos os dataframes no eixo 1 (colunas), mas podemos usar o eixo 0 (índice). Nesse caso, no primeiro exemplo o método concat() vai tentar unir as colunas. E no segundo exemplo, apenas as colunas em comum seriam concatenadas (B e D).

>>> resultado = pd.concat([df1, df4], axis=0, join='outer')
>>> resultado
     A   B    C   D    F
0   A0  B0   C0  D0  NaN
1   A1  B1   C1  D1  NaN
2   A2  B2   C2  D2  NaN
3   A3  B3   C3  D3  NaN
2  NaN  B2  NaN  D2   F2
3  NaN  B3  NaN  D3   F3
6  NaN  B6  NaN  D6   F6
7  NaN  B7  NaN  D7   F7
>>> resultado = pd.concat([df1, df4], axis=0, join='inner')
>>> resultado
    B   D
0  B0  D0
1  B1  D1
2  B2  D2
3  B3  D3
2  B2  D2
3  B3  D3
6  B6  D6
7  B7  D7

O nosso dataframe, resultante da concatenação de dois dataframes, pode ter a mesma quantidade de linhas do primeiro dataframe, e o excedente (do segundo dataframe) é perdido.

>>> resultado = pd.concat([df1, df4], axis=1).reindex(df1.index)
>>> resultado
    A   B   C   D    B    D    F
0  A0  B0  C0  D0  NaN  NaN  NaN
1  A1  B1  C1  D1  NaN  NaN  NaN
2  A2  B2  C2  D2   B2   D2   F2
3  A3  B3  C3  D3   B3   D3   F3

Usando o método append do pandas

Se você deseja concatenar dataframes somente no eixo 0 (índice), uma escolha interessante é o método append. Esse método concatena as series e os dataframes ao longo do eixo 0.

>>> resultado = df1.append(df2)
>>> resultado
    A   B   C   D
0  A0  B0  C0  D0
1  A1  B1  C1  D1
2  A2  B2  C2  D2
3  A3  B3  C3  D3
4  A0  B0  C0  D0
5  A1  B1  C1  D1
6  A2  B2  C2  D2
7  A3  B3  C3  D3

Você pode passar uma lista de objetos para serem concatenados.

>>> resultado = df1.append([df2, df3])
>>> resultado
     A   B   C   D
0   A0  B0  C0  D0
1   A1  B1  C1  D1
2   A2  B2  C2  D2
3   A3  B3  C3  D3
4   A0  B0  C0  D0
5   A1  B1  C1  D1
6   A2  B2  C2  D2
7   A3  B3  C3  D3
8   A0  B0  C0  D0
9   A1  B1  C1  D1
10  A2  B2  C2  D2
11  A3  B3  C3  D3

Ignorando índices

Você pode ignorar os índices completamente passando o valor True para o parâmetro ignore_index (Se esses dados não forem importantes). Desse modo, o método vai concatenar os dataframes de acordo com as colunas. Por exemplo, se os dois dataframes têm uma coluna chamada B, essas colunas ficaram uma embaixo da outra.

>>> resultado = pd.concat([df1, df4], ignore_index=True)
>>> resultado
     A   B    C   D    F
0   A0  B0   C0  D0  NaN
1   A1  B1   C1  D1  NaN
2   A2  B2   C2  D2  NaN
3   A3  B3   C3  D3  NaN
4  NaN  B2  NaN  D2   F2
5  NaN  B3  NaN  D3   F3
6  NaN  B6  NaN  D6   F6
7  NaN  B7  NaN  D7   F7

O mesmo parâmetro funciona no método append().

Concatenando séries com dataframes

Você pode concatenar séries com dataframes. O método concat() pode trabalhar com os dois tipos. Nesse exemplo, vamos criar uma série e concatená-la com um dataframe. Essa série será adicionada como uma coluna no final do dataframe.

>>> s1 = pd.Series(['X0', 'X1', 'X2', 'X3'], name='X')
>>> resultado = pd.concat([df1, s1], axis=1)
>>> resultado
    A   B   C   D   X
0  A0  B0  C0  D0  X0
1  A1  B1  C1  D1  X1
2  A2  B2  C2  D2  X2
3  A3  B3  C3  D3  X3

Se a série não tiver um nome, será atribuído uma sequencia de números.

>>> s2 = pd.Series(['_0', '_1', '_2', '_3'])
>>> resultado = pd.concat([df1, s2, s2, s2], axis=1)
>>> resultado
    A   B   C   D   0   1   2
0  A0  B0  C0  D0  _0  _0  _0
1  A1  B1  C1  D1  _1  _1  _1
2  A2  B2  C2  D2  _2  _2  _2
3  A3  B3  C3  D3  _3  _3  _3

Podemos usar o parâmetro ignore_index e definir os rótulos das colunas com uma sequencia numérica desde a primeira coluna.

>>> resultado = pd.concat([df1, s2, s2, s2], axis=1, ignore_index=True)
>>> resultado
    0   1   2   3   4   5   6
0  A0  B0  C0  D0  _0  _0  _0
1  A1  B1  C1  D1  _1  _1  _1
2  A2  B2  C2  D2  _2  _2  _2
3  A3  B3  C3  D3  _3  _3  _3

Concatenando com um grupo de keys

O padrão é o novo dataframe herdar os nomes das colunas dos dataframes pais. Podemos mudar isso com o parâmetro keys. O uso mais comum do parâmetro keys é exatamente esse: substituir o nome das colunas.

>>> pd.concat([s3, s4, s5], axis=1)
   um  dois  tres
0   0     0     1
1   1     1     4
2   2     1     1
3   3     3     4
>>> pd.concat([s3, s4, s5], axis=1, keys=['X', 'Y', 'Z'])
   X  Y  Z
0  0  0  1
1  1  1  4
2  2  1  1
3  3  3  4

Podemos fazer a mesma coisa com um dicionário.

>>> dicio = {'X':s3, 'Y':s4, 'Z':s5}
>>> resultado = pd.concat(dicio, axis=1)
>>> resultado
   X  Y  Z
0  0  0  1
1  1  1  4
2  2  1  1
3  3  3  4

Referência:
Função concat()
Função append()