Arquivo do autor:Fernando Melo

Sobre Fernando Melo

Fernando Melo é cientista da computação especializado em Data Management, e é apaixonado pelas tecnologias que mudam positivamente a vida das pessoas. Atualmente trabalha como Engenheiro de Solução para a Área de Gestão de Dados, e escreve este blog. Você pode segui-lo no Twitter / Instagram / Facebook @fmelodb e YouTube no canal Universo dos Dados. Nota: os textos e interesses neste blog refletem minha opinião pessoal, e não necessariamente representam as visões do meu empregador.

Com Reduzir em 100% seus Problemas com Armazenamento de Dados (não é propaganda)

Cientistas descobrem uma forma de armazenar dados que levam os desenvolvedores à loucura!


Os recursos mais interessantes dos provedores de Cloud no mercado, sem dúvida, são todos os tipos de Cloud Storage que você pode escolher e provisionar com alguns cliques. Neste post vou falar sobre o principal deles: Object Storage.

Francamente, o nome “Object Storage” não é amigável, e tampouco inspira algo moderno. Esse nome é nauseante, nauseabundo, nauseoso, ascoso, e todos os outros 28 sinônimos de repugnante. Se você ainda não conhece Object Storage, certamente é por causa do nome dele. Soa técnico demais, e excessivamente “coisa de infra”. Mas acredite, é “coisa de developer“.

Os nomes dos seus pares também não são dos melhores: diretórios e sistemas de arquivos [filesystems]. Esses nomes me lembram aquelas gavetas do meu escritório que abro uma vez a cada década. A verdade é que os nomes das coisas que referem-se às formas de persistência sempre me lembram armários, gavetas, pastas, baldes e caixas.

… de volta à dissertativa: há muitos anos, ou melhor, em todos os anos desde o início da era dos bancos de dados relacionais, quase TODAS as informações têm sido armazenadas em diretórios e sistemas de arquivos. Mas os anos passam, e alguém sempre inventa algo melhor. Continue lendo.

A Vida antes do Big Data

Antes de entrar no fantástico mundo do Object Storage, que apesar do nome, é realmente fantástico, tenho que desconstruir os filesystems. Rebaixá-los. É! Este post é propositalmente enviesado para enaltecer o poder do Object Storage em detrimento de outras formas de persistência mais convencionais. Desista aqui se não concorda.

Filesystems são ruins. Muito ruins. Eles são fixos. Têm limites. São cheios de regras. Não entendem HTTP, e pior: eles se falam por SAN e NAS [high-tech da época que seu pai ouvia Roupa Nova].

Filesystems são carentes de software inteligente e precisam de alguém para tomar conta – e fazer coisas totalmente zero business value: criar LUNs, formatar e montar discos, e configurar RAID.

Filesystems tornam-se naturalmente lentos enquanto crescem. Tento não acreditar que “ficar lento” faz parte da arquitetura deles, mas a verdade é que com o tempo eles ficam lentos por definição. Esse é o principal motivo pelo qual eles não suportam a escala da internet.

Explicando escala da internet: pense em uma Vespa. Sim, o inseto. Ela está parada, e de repente vai a 100 km/h, e para [freia no ar]. Depois ela faz uma curva de 90 graus, e acelera instantaneamente até 100 km/h de novo. De zero a 100 km/h em 0,00001 segundo. Agora ela faz outra curva de 90 graus sem parar, ignorando as forças da gravidade e da inércia. Agora troque a “Vespa” por “quantidade de usuários que acessam o seu sistema“. Pronto! Você já tem uma ideia do que é a escala da internet!

Obrigado Filesystems! Obrigado por nos ajudar no mundo dos megabytes e das coisas pequenas. Não nos veremos mais, pois agora as coisas são grandes demais pra vocês.

O Novo Centro do Universo

Há também aqueles que dizem que os bancos de dados também não suportam a escala da internet. Nenhum deles. Nécas. Nadicas. E isso inclui a maioria dos NoSQL. Sim, nem eles escapam.

O principal argumento é: dados não-estruturados [fotos, vídeos e textos] são complexos demais para os banco de dados. Se eu concordo? Em relação aos filesystems, sim, claro. Eles realmente não escalam. Mas e os bancos de dados? Prossiga na leitura.

Passaram por cima de todas as formas de persistência que existia, e criaram uma coisa que mais parece a pia da minha cozinha depois daquele almoço de domingo, e eles chamam de Object Storage. Agora esse treco é o centro do universo. Você coloca tudo lá, e ele se resolve [o Object Storage, não a pia da minha cozinha!].

Bom, mas e os bancos de dados?

O que diz minha chatbot Alexandra?

Fernando: Alexandra, o que você usava para armazenar os dados das suas aplicações web?
Alexandra: filesystems e bancos de dados.
Fernando: e o que você usa agora?
Alexandra: Object Storage.
Fernando: mas e o ACID, o 2-Phase Commit, a consistência forte, o teorema CAP que levei uma vida pra entender?
Alexandra: Não precisa.
Fernando: Precisamos ter uma conversa franca sobre coisas limitadas, a pia da minha cozinha, e a volta daqueles que não foram.

Uma Conversa Franca sobre Coisas Limitadas

Há dois tipos de filesystems: os tradicionais, e os que ainda não perderam as esperanças, mas todo mundo já sabe que eles já não têm mais futuro.

Os Filesystems Tradicionais

O primeiro são os filesystems tradicionais, baseados em SAN e NAS, que você provavelmente tem no seu data center on premise. Eles falham feio quando precisam escalar no nível da internet [Petabytes] – e se você acha que Petabyte é muito e nunca vai lhe atingir, lembre-se que há 20 anos um gigabyte era mais inimaginável do que você receber uma cesta de Lindt diretamente das mãos do Coelhinho da Páscoa, fora da Páscoa, no meio de deserto do Saara.

Filesystems precisam de uma tabela de lookup para localizar os arquivos. E essa tabela cresce. Imagine um banco de dados sem particionamento, compressão, paralelismo, índices… é isso. A tabela de lookup dos filesystems me lembra, carinhosamente, o DBU do Clipper.

Essas tabelas de lookup crescem até ao ponto que nada mais funciona. E então neste momento os administradores de storage fazem algo incrível:

Eles criam outra LUN.

Pense na LUN como a cauda de uma lagartixa. As lagartixas soltam sua cauda para confundir o predador e facilitar a fuga. Depois a cauda se regenera.

Quase a mesma coisa acontece com as LUNs. Quando a tabela de lookup fica grande, cria-se outra LUN para confundir o sistema operacional, deixando tudo mais rápido, mas depois a LUN cresce novamente e o processo se repete.

O grande problema é que as LUNs não fogem. Elas ficam lá para sempre e você tem que administrar! E quando você tem muitas LUNs e precisa dar um reboot no servidor, começa a ficar mais claro a definição de “anos-luz” que os cosmonautas usam para medir o tempo que leva para percorrer as viagens no espaço.

Por causa disso os filesystems tradicionais são os grandes perdedores na era do Big Data. Eles simplesmente não existem nesse mundo.

Os Filesystems Scale-Out

Hadoop Distributed Filesystem, HDFS, esse é o nome dele.

HDFS é independente de hardware. Os arquivos, cujos tamanhos não têm limite, são armazenados em blocos que se espalham entre vários discos do mesmo servidor, e entre discos de vários servidores. É scale-out afinal, isso já diz tudo: escala para os lados.

Ele parece melhor que os filesystems tradicionais, certo? Certo! Continue lendo.

HDFS resolve o problema de escalabilidade, mas … você precisa de um high-skilled high-salary high-high-high engenheiro de dados para mantê-lo no ar.

E complementado o “para mantê-lo no ar”: HDFS faz parte de um ecossistema que mais parece um zoológico composto por Zookeeper, Pig, Hive, Flume, HBase, YARN, Ambari … e o que mais mesmo ?! Divirta-se!

HDFS é muito labor-intensive. Sem falar na tripla redundância padrão que faz você gastar 3 PBytes para cada 1 PByte de dado útil. E você ainda acredita que esses filesystems são low cost porque, afinal de contas, o hardware é commodity, certo?

Risos.

HDFS é um filesystem em larga escala. Filesystem é um problema. Logo, HDFS é um problema em larga escala. HDFS é o máximo que se pode obter em larga escala de um filesystem no mundo on premise.

A nuvem chegou, e a festa vai começar. Não pare de ler.

A Pia da Minha Cozinha depois do Almoço

Nesta parte do texto meu objetivo é explicar para você, com um nível bastante aprofundado de detalhes, o que é Object Storage, e quais são seus benefícios.

Se você acha que um nível aprofundado de detalhes é como 100 páginas, esqueça!

Vou gastar um esforço colossal para tentar colocar 20 palavras numa frase. Não há muito segredo. Object Storage é simples por mais que eu tente criar alguma complexidade. Ele é flat, não tem diretórios ou hierarquias. Você coloca o arquivo lá, e ele vai. Você pede o arquivo, e ele vem.

Object Storage, um dos tipos de Cloud Storage, armazena porções de dados que podem ser identificadas individualmente. Essas porções podem ter metadados associados, e são acessados por meio de APIs. Os provedores de Cloud fazem a gestão do Object Storage, não você.

Arquivos nos filesystems são “objetos” no Object Storage.

O Object Storage também é composto por discos, como os filesystems, mas a diferença é que há “serviços” que fazem o gerenciamento dos arquivos, ao invés de deixar que essa gestão seja feita no nível do sistema operacional.

Esses serviços apresentam os discos como se fossem uma coisa só, formando uma camada de abstração maior, um pool. Cada arquivo que você coloca lá tem um ID, e é tudo que você precisa saber.

O Object Storage expõe seus objetos [arquivos] através de uma interface REST. Então você pode colocar ou recuperar arquivos usando o protocolo HTTP.

No exemplo abaixo eu faço três operações. Vou usar o curl [que é uma espécie de browser em formato texto] para executar operações HTTP:

curl -X PUT -H "X-Auth-Token: codigo_token" https://url/piaCozinha

curl -X PUT -H "X-Auth-Token: codigo_token" https://url/piaCozinha -T meuArquivo.txt

curl -X GET -H "X-Auth-Token: codigo_token" https://url/piaCozinha

Em todas as chamadas precisamos passar um token para segurança, que é gerado antes. E depois usamos comandos HTTP, como PUT, POST, DELETE e GET sobre uma URL [endpoint], que é fornecido pelo seu provedor de cloud para operar sobre um Object Storage.

Na linha 1 eu criei um container chamado piaCozinha, que é onde vão ficar meus arquivos [é como se fosse um diretório].

Na linha 3 eu coloquei um arquivo chamado meuArquivo.txt no container piaCozinha.

Na linha 5 eu busquei todos os arquivos no container piaCozinha, através da operação HTTP GET.

Simples, certo? Acredite, não tem mais nada.

Qual é o lado ruim e perverso desse tipo de persistência?  Imutabilidade [não pode alterar], e segue o AP do teorema CAP [disponibilidade e particionamento, e não consistência].

Como o caso de uso principal do Object Storage é armazenar dados não-transacionais, então esse lado perverso é menos perverso do que parece.

No final do dia, por causa das APIs, Object Storage significa que os desenvolvedores não precisam da benção do pessoal de infraestrutura para alocar mais espaço para a aplicação.

Está ficando quente. Continue lendo.

A Volta Daqueles que Não Foram

É inegável que SQL é a forma mais human-friendly para buscar qualquer coisa. Apache Hive sabe disso. Então melhor do que armazenar arquivos em Object Storage, é processá-los utilizando SQL.

Quais são os melhores engines de SQL? Os bancos de dados. 

Agora vai começar a era onde os bancos de dados processam os dados que armazenam, e também os dados que não armazenam.

Os bancos de dados se tornarão sistemas gerenciadores de dados, não importa onde os dados estejam.  E não estou falando de BLOBs. Estou falando de Object Storage, e em qualquer formato: Parquet, Avro, CSV ou whatever.

Eu tenho uma visão que o padrão do mercado será armazenar boa parte dos dados, não importa o formato, em Object Storage, e esses dados serão acessados nativamente com SQL por sistemas gerenciadores de dados.

Obrigado ecossistema Hadoop/HDFS! Obrigado por aumentarem um pouco a escala das coisas no mundo On Premise, mas no mundo Cloud é a vez dos Object Storages.

Publicidade

Análise Market Basket

Não sou nenhum Vanderbilt, mas esse trem que vou mostrar faz fumaça!


Estou começando um hobby nesse negócio de câmeras fotográficas, e comprei na internet uma Canon T7i [me falaram que era boa], com uma lente adicional e um flash.

Aí o site vem e me oferece um tal de Difusor Rebatedor Universal, porque quem comprou aquele flash, também comprou esse difusor. What the heck isso faz? Não tenho a mínima… mas era tão barato que [já que tô aqui…] comprei, pois fiquei com peso na consciência de não comprar algo – por míseros reais – que poderá me quebrar um galho depois.

Aproveitei e comprei também um cartão de memória, uma bolsa e um tripé – tudo na base do “quem comprou isso, também comprou aquilo”. 

No final, eles conseguiram me tirar alguns reais além do que eu havia planejado. Se bobear esse difusor estava encalhado no estoque, e eles conseguiram desovar [comigo!]. O lado positivo é que tudo que comprei valeu a pena, e acabei qualificando a compra com cinco estrelas.

Todo mundo saiu feliz.


O fato é que eu gastei mais dinheiro, e eles me venderam produtos que, é importante registrar, EU NUNCA TERIA COMPRADO SE NÃO FOSSEM AS RECOMENDAÇÕES DO SITE.

O site usou um modelo de machine learning, e um dos mais simples! Você também consegue implementá-lo com poucas linhas de código. Há variações e outras abordagens, claro, mas vou lhe mostrar uma delas, bem popular, porém bastante eficiente.

Figura 1 – Exemplo de recomendação de produtos em tempo real

Esse modelo não é somente para recomendação de produtos em tempo real no mundo online. Pode ser usado também para previnir incêndios em florestas, descobrir uma pré-disposição a diabetes, detectar reações adversas a drogas, verificar a efetividade de um treinamento de corrida de rua, analisar estudantes admitidos em universidades, só para citar alguns.

Mas a maioria das aplicações deste modelo tem a ver com vendas mesmo. O caso de uso mais comum no mundo offline é o Market Basket, e pode gerar as seguintes ações para aumentar as vendas: colocar dois produtos na mesma prateleira, então quem comprar um item poderá comprar o outro; descontos promocionais poderiam ser aplicados somente a um dos itens; anúncios e propagandas para um produto podem direcionar a compra de outro produto; dois produtos poderiam ser combinamos em uma única oferta. Essas foram só para citar algumas.

Enfim, o que você puder imaginar nesse sentido de promover uma venda que outrora não ocorreria.

Neste artigo vou utilizar dados reais de um ponto de venda no comércio [uma padaria]. Vou carregar esses dados em um banco de dados, e executar o modelo de machine learning dentro deste mesmo banco de dados. Depois vou construir uma aplicação simples em Python que faz recomendações em tempo real, usando as regras geradas por esse modelo. O modelo de machine learning que usaremos é do tipo Associação de Regras, e o algoritmo utilizado chama-se Apriori. Você poderá reproduzí-lo integralmente com os códigos que vou deixar aqui neste artigo.

Mas há um problema [você realmente achou que ia ser fácil?!]. Esse modelo, apesar de simples, é compute intensive. Isto é, quanto mais dados MELHOR, porém, quanto mais dados MAIS LENTO: eu tenho uma solução para esse problema, e vou comentar sobre isso mais pra frente.

Os Dados de uma Padaria

A padaria não é igual àquela do seu bairro! É uma bakery très chic, crème de la crème, da Europa. Por isso os nomes dos itens vendidos não estão em português.

Fiz o download dos dados de transações desta bakery no site Kaggle: este link lhe coloca direto na página.

O Kaggle, se você não conhece, disponibiliza dados reais de empresas para competições de machine learning e data science. É muito útil pra aprender e treinar.

O arquivo chama-se BreadBasket_DMS.csv. Ele é bem simples e correto, o que nos poupa de fazer data wrangling: são 4 colunas – date, time, transaction e item.

Transaction é um código sequêncial que agrupa os itens vendidos. Por exemplo, se a primeira venda teve apenas um item, este recebe o código 1. Se a segunda venda teve três itens, as próximas três linhas recebem o código 2. Isto é, cada transaction representa a venda de um conjunto de itens, a cestinha de compras de uma pessoa:

Figura 2 – Dataset da Bakery obtido no Kaggle

Em nosso caso, o arquivo já está preparado para o processamento do modelo. Quando aplicar ao seu caso, aos seus dados, você deverá organizá-los de forma semelhante: agrupe itens relacionados, e atribua um código para o modelo saber que alguma vez esses itens ocorreram juntos.

O próximo passo é carregar os dados no banco de dados. Eu vou usar o Oracle XE. Podemos criar uma tabela externa, e através dela carregar os dados para uma tabela dentro do banco:


-- Tabela que armazenaremos as transações
CREATE TABLE transactions
(DT_DIA VARCHAR2(10), DT_HORA VARCHAR2(8), TRANS_ID NUMBER(38), ITEM VARCHAR2(60));

-- Local em disco onde está o arquivo BreadBasket_DMS.csv
CREATE DIRECTORY my_directory AS '/tmp';

-- Tabela externa para carregar as transações
CREATE TABLE transactions_ext
( DT_DIA VARCHAR2(10), DT_HORA VARCHAR2(8), TRANS_ID NUMBER(38), ITEM VARCHAR2(60) )
ORGANIZATION EXTERNAL ( 
TYPE ORACLE_LOADER DEFAULT DIRECTORY my_directory
ACCESS PARAMETERS 
(RECORDS DELIMITED BY '\r\n' 
SKIP 1  FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"' AND '"' LRTRIM  )
LOCATION ('BreadBasket_DMS.csv')
);

-- Carga da tabela externa para a tabela de transações
INSERT INTO transactions 
SELECT * FROM transactions_ext ;

COMMIT;

OK, então criei uma tabela chamada transactions, e depois criei um diretório chamado my_directory, que é a forma de falar para o Oracle em qual caminho está nosso arquivo que baixei do Kaggle.

Criei também uma tabela externa chamada transactions_ext, que serve apenas para configurar [delimitação, nome do arquivo, etc] a carga do arquivo para a tabela transactions. No final, o comando insert select coloca os dados da tabela externa, ou seja, do arquivo, na tabela transactions.

Agora vou preparar o algoritmo Apriori. Há dois requisitos:

[1] Criar uma view chamada trans_view com a coluna que agrupa as transações [trans_id], e a coluna com o nome dos itens [item]. Daqui pra frente, vamos utilizar a view ao invés da tabela transactions.

[2] Criar uma tabela de parâmetros para calibrar o algoritmo. Perceba que o nome da tabela de parâmetros pode ser qualquer um, mas o nome das colunas e os datatypes devem ser iguais aos que coloquei no script abaixo.

-- Criamos uma view com o grupo de transações e os itens
CREATE VIEW trans_view AS SELECT trans_id, item FROM transactions;

-- Criamos uma tabela para armazenar os parâmetros do algoritmo
CREATE TABLE parametros
(setting_name VARCHAR2(30), setting_value VARCHAR2(4000));

-- Inserimos os parâmetros na tabela
begin 
 insert into parametros values (DBMS_DATA_MINING.ASSO_MAX_RULE_LENGTH, 3);
 insert into parametros values (DBMS_DATA_MINING.ASSO_MIN_CONFIDENCE, 0.01);
 insert into parametros values (DBMS_DATA_MINING.ASSO_MIN_SUPPORT, 0.01);
 insert into parametros values (DBMS_DATA_MINING.ODMS_ITEM_ID_COLUMN_NAME, 'ITEM');
 commit;
end;
/

É importante entender que o algoritmo Apriori gera regras. Por exemplo, vamos supor que se alguém comprar pão e café, muito provavelmente também comprará manteiga. A parte antecedente, a regra, é “pão e café”, e a parte consequente, aquela que o algoritmo calcula, é a manteiga.

O algoritmo Apriori gera uma lista de regras, cada uma com sua relevância, e também as respectivas consequências, isto é, a associação correspondente [o que ele recomenda]. Para isso existe também uma série de parâmetros que precisam ser calibrados para o algoritmo não gerar muitas regras inúteis [o modelo não considera as regras que não estão de acordo com esses parâmetros]. Alguns desses parâmetros nós inserimos na tabela de parâmetros:

O parâmetro MAX_RULE_LENGTH determina qual é o tamanho da maior regra [até quantos itens ela poderá ter], por exemplo, a compra de “um pão” é uma regra com 1 item, a compra de “um pão e um café” tem 2 itens, e a compra de “um pão, um café e um bolo” tem 3 itens. MIN_CONFIDENCE é o porcentual mínimo de confiança da regra, isto é, são as chances de alguém que comprou pão também comprar café. MIN_SUPPORT determina a popularidade mínima dos itens da regra no conjunto total de dados. Por exemplo, se a regra “pão e café” participou de 10 transações num total de 100, o suporte desta regra é 10%. ITEM_ID_COLUMN_NAME é o nome da coluna que corresponde aos itens.

Eu utilizei 3 para max rule length, e 1% para min confidence e support. Isso significa que qualquer regra que não estiver de acordo com esses parâmetros, não será considerada no conjunto de regras que o modelo vai gerar.

1% é um valor baixo – normalmente você poderia utilizar de 10% a 30% para o mínimo de confiança e suporte. Mas como não temos muitos dados, decidi colocar um valor baixo. Assim eu gero mais regras, e decido qual é a restrição ideal mais pra frente quando embutir as regras na aplicação, como veremos mais adiante.

Obs: com muitos dados é importante não gerar muitas regras, às vezes inúteis [baixa confiança e pouco suporte podem gerar associações irrelevantes]. Esse algoritmo é caro em termos de processamento, como comentei antes.

Para uma explicação mais aprofundada sobre o algoritmo Apriori e seus parâmetros [que não é o objetivo deste artigo], você pode ver neste link.

Dados carregados, parâmetros calibrados. Agora vou criar o modelo. Como já falei, o modelo de machine learning utiliza o algoritmo Apriori para gerar as regras, com base nos dados, e nos parâmetros.


BEGIN 
DBMS_DATA_MINING.CREATE_MODEL(
model_name            => 'MODELO_PADARIA',
mining_function       =>  DBMS_DATA_MINING.ASSOCIATION,
data_table_name       => 'TRANS_VIEW',
case_id_column_name   => 'TRANS_ID',
target_column_name    =>  NULL,
settings_table_name   => 'PARAMETROS');
END;
/

A criação do modelo é feita através de uma API no banco de dados, chamada DBMS_DATA_MINING. Primeiro temos que dar um nome ao modelo [para identificá-lo e utilizá-lo na aplicação] com o atributo MODEL_NAME. Temos que selecionar o algoritmo no atributo MINING_FUNCTION, que em nosso caso é ASSOCIATION [este modelo utiliza o algoritmo Apriori]. No atributo DATA_TABLE_NAME temos que informar o nome da view que criamos anteriormente. No atributo CASE_ID_COLUMN_NAME temos que informar qual é a coluna da view que agrupa as transações [a cesta de compras]. O atributo TARGET_COLUMN_NAME só é usado para modelos supervisionados, e como ASSOCIATION é não-supervisionado podemos deixar nulo. SETTINGS_TABLE_NAME é o nome da tabela de parâmetros que definimos anteriormente.

Bom, agora que o modelo está criado, vamos ver o resultado [as regras criadas]. Uma observação importante é que podemos gravar as regras em uma tabela, ou podemos consultar direto de uma view de resultado, chamada DM$VR + Nome do Modelo, que em nosso caso é DM$VRMODELO_PADARIA, criada automaticamente quando criamos o modelo.

Você pode recriar o modelo com uma peridiocidade, por exemplo, uma vez por dia, ou uma vez por semana, ou mês. Depende muito do caso uso, se os dados tem muita variação ou não.

Vamos ver os resultados. Não se assuste com essa query abaixo. Eu explico.


SELECT list_of_antecedents, consequent, lift, confidence, support 
FROM (
   SELECT ROW_NUMBER() OVER (PARTITION BY rule_id ORDER BY ant_items.piece) as row_number,
          LISTAGG(ant_items.item, ' AND ') WITHIN GROUP (ORDER BY ant_items.piece) OVER (PARTITION BY RULE_ID) as list_of_antecedents,
          consequent,
          support, confidence, lift
   FROM (SELECT rule_id,
                antecedent, 
                consequent_name as consequent, 
                round(to_number(rule_support)*100,4) as support, 
                round(to_number(rule_confidence)*100,4) as confidence, 
                round(to_number(rule_lift),4) as lift
         FROM DM$VRMODELO_PADARIA) rules,
                   XMLTABLE ('/itemset/item'  PASSING rules.ANTECEDENT
                   COLUMNS item varchar2(30)  PATH 'item_name',
                           piece for ordinality ) ant_items
         WHERE lift > 1 AND confidence > 10 AND support > 1
   ORDER BY confidence DESC, support DESC
) WHERE row_number = 1

As linhas mais importantes são as 13 e 17. Na linha 13, temos a view que guarda os resultados da criação do modelo, isto é, onde estão as nossas regras e vários indicadores para cada uma delas [support, confidence, lift, e mais alguns outros]. Na linha 17, eu coloco as restrições, por exemplo, somente as regras com lift maior que 1, confidence maior que 10%, e support maior que 1%.

Normalmente alguns itens muito frequentes [support é maior] participam de regras com itens menos frequentes [support é menor]. Por exemplo: pão aparece em 70 de 100 transações, enquanto que manteiga aparece em 5 de 100. Certamente haverá uma regra pão e manteiga, e outra manteiga e pão. Quando há regras assim com o suporte dos itens tão discrepantes, isso pode acabar inflando o indicador de confiança [no meu exemplo, quando alguém comprar manteiga]. Desta forma, podemos usar um indicador adicional, o Lift, que nos mostra o quão provável é a compra da manteiga se alguém comprar o pão. Se for maior que 1, é provável que compre. Se for 1, é indiferente. E se for menor que 1, é improvável que compre. Por isso na query filtrei lift maior que 1.

A query realmente é o que está entre as linhas 7 e 17. Todo o entorno eu fiz para melhorar o output, e colocá-lo depois na aplicação simples que fiz em Python. Quando uma regra tem mais de um item, a linha se repete na view. Por isso usei LISTAGG e concatenei com um AND para ficar algo como “Bread AND Coffee”. E a função analítica ROW_NUMBER() utilizei para retornar só uma linha, a primeira, pois “Bread AND Coffee” iria retornar duas vezes. Se você executar esse código SQL aos poucos, primeiro o que está entre as linhas 7 e 17, e depois o entorno, vai entender melhor o que estou dizendo.

Esse é o resultado da query:

Figura 3 – Resultado da query no SQL Developer

Em uma aplicação, quando alguém seleciona por exemplo, Toast, você recomenda Coffee. Há 70% de confiança de que essa regra seja verdadeira.

Nota: todos esses códigos até aqui poderiam ter sido feitos no SQL Developer, com a feature Data Miner: é como um wizard que lhe permite implementar os algoritmos de machine learning com drag & drop.

Não Pare! É uma Vergonha Você Chegar até Aqui e Não Colocar esse Código na sua Aplicação

Vamos colocar esse código em uma aplicação simples para simular como seria.

Veja. Esse é só um blog. Não pense que vou usar algum design pattern com altos requintes de polimorfismo, um framework robusto, uma infraestrutura de kubernetes, microserviço, e tal… vai no Python script mode mesmo. Você vai entender!


import sys
import cx_Oracle

# itemsList é uma lista de itens que o usuário informa
# se for mais que um, um AND é incluído para cada par de itens
itemsList = ''
for args in sys.argv[1:]:
    if len(itemsList) == 0:
        itemsList = args
    else:
        itemsList = itemsList + ' AND ' + args

# Conecta no banco de dados Oracle
conn = cx_Oracle.connect("demouser", "demopass", "localhost/XE")
cur = conn.cursor()

# É a vez daquela query que retorna os resultados
# Mas desta vez estou filtrando com itemsList

sql = """SELECT rownum, consequent FROM (
       SELECT ROW_NUMBER() OVER (PARTITION BY rule_id ORDER BY ant_items.piece) as row_number,
          LISTAGG(ant_items.item, ' AND ')
          WITHIN GROUP (ORDER BY ant_items.piece)
             OVER (PARTITION BY RULE_ID) as list_of_antecedents,
          consequent, support, confidence, lift
   FROM (SELECT rule_id,
                antecedent, 
                consequent_name as consequent, 
                round(to_number(rule_support)*100,4) as support, 
                round(to_number(rule_confidence)*100,4) as confidence, 
                round(to_number(rule_lift),4) as lift
         FROM DM$VRANALISE_ASSOCIACAO) rules,
                   XMLTABLE ('/itemset/item'  PASSING rules.ANTECEDENT
                   COLUMNS item varchar2(30)  PATH 'item_name',
                           piece for ordinality ) ant_items
         WHERE lift > 1 AND confidence > 10 AND support > 1
   ORDER BY confidence DESC, support DESC
) WHERE row_number = 1 and list_of_antecedents = :antecedentList"""

# Executa a query e mostra os itens que devemos recomendar
cur.execute(sql, antecedentList=itemsList)
res = cur.fetchall()
print(res)

A aplicação app.py recebe um argumento que é a lista de antecedentes. Poderia ser por exemplo um clique do usuário, ou os produtos que ele coloca num carrinho de compras. A aplicação retornar uma lista de consequentes por ordem de confiança.

Veja alguns exemplos de execução deste código:

Quando o usuário entra com Coffee, a aplicação recomenda Cake. Quando o usuário entra com Bread e Coffee, a aplicação recomenda primeiro Pastry, e depois Cake. Quando o usuário entra com Tea, a aplicação recomenda Cake e depois Sandwich. E quando entra com Cake, a aplicação recomenda primeiro Coffee, depois Tea, e depois Hot Chocolate.

Essa é a vantagem de ter os algoritmos de machine learning dentro do banco de dados. Você aproveita os recursos do servidor do banco, que entrega bastante performance, e também a linguagem SQL, que é muito fácil de usar.

Se Você é um DBA Ocupado que Não Gosta de Ler, Leia Isto!

Duas pessoas se encontraram à meia-noite naquela sala que fica no final do corredor de um prédio antigo, numa cidade estranha…


Eram dois administradores de banco de dados, de uma mesma consultoria, e estavam prestando serviço para um cliente. Eles viajaram por horas para estarem lá naquele momento.

Ambos eram qualificados, certificados, e com boa experiência em lidar com ambientes de alta criticidade. Mas havia uma certa tensão, como sempre. Era uma janela de manutenção importante que envolvia várias atividades complicadas [daquelas que se você fizer bem leva um tapinha nas costas, e se fizer errado leva um chute no traseiro – você sabe do que estou falando!].

Houve alguns problemas. Os pré-requisitos para aplicar um patch não foram seguidos corretamente. Houve falha no planejamento de capacidade e o espaço em disco acabou durante a execução de um processo batch, resultando em erros e reprocessamentos. O backup de uma tabela precisou ser restaurado, mas não funcionou de primeira [culpa do time de storage]. Eles tiveram que fazer muitas ações manuais e usar a expertise, mas no final deu tudo certo.

Esta foi só mais uma noite de um final de semana. Não muito diferente de outras que ocorrem durante o ano.


Dez anos se passaram.

Aquelas duas pessoas estavam trabalhando juntas na mesma empresa, porém em uma outra consultoria.

E havia uma diferença. Um deles ainda era DBA, porém MUITO MAIS EXPERIENTE do que antes, mas com a vida de sempre. O outro era o SÓCIO-FUNDADOR da consultoria que eles trabalhavam. E ele estava tranquilo, pois tinha acabado de fechar mais um negócio milionário com um cliente novo.

Você Encara os Fatos – Ou Se Esquiva?

O fato é que hoje as empresas buscam ser data-driven, e aquelas que já são, precisam muito de profissionais na área de dados com skill em cloud, engenharia e ciência de dados, e menos de DBAs.

Como um engenheiro de dados, você terá que ser capaz de desenvolver, construir e manter arquiteturas que suportam sistemas em qualquer escala, e explorações e análises em uma grande quantidade de dados, feitas por cientistas de dados.

Você não terá que instalar e configurar uma infraestrutura de banco de dados: você irá apenas criar um serviço de persistência na nuvem; Você não vai dimensionar pelo pico e projetar o crescimento para os próximos 3 anos: você vai provisionar pelo uso, e otimizar a utilização; Você não vai criar scripts de monitoração em shell para os bancos de dados e os backups: você vai carregar arquivos dos mais diversos formatos usando Python e SQL; Você não vai sentar aqui e os desenvolvedores lá: vocês vão sentar juntos e colocar os algoritmos pra máquina aprender; Você não vai reportar quais são as queries lentas quando houver um problema de performance: você vai resolver o problema de performance!

O sócio-fundador, e ex-DBA, percebeu que a área dele estava passando por um estágio de grande automação. Ele deixou de lado seu foco em infraestrutura e administração de banco de dados, e aprendeu Python, REST, JSON, Spark, integração, cloud, DevOPs, melhorou muito seus conhecimentos em SQL, conheceu técnicas de data wrangling, e os diferentes tipos de banco de dados. Familiarizou-se com IoT, microserviços, machine learning, IA e blockchain.

Ele ainda trabalha até as 4 AM, mas a diferença é que ele ama cada minuto: ele abriu essa startup de consultoria que só cresce, e hoje é dono do seu tempo.


Fazendo isso você pode não chegar aonde ele chegou – fundar uma startup [talvez nem queira isso]. Mas você estará preparado para a evolução tecnológica que estamos passando, e vai trabalhar nas melhores oportunidades com os melhores salários do mercado.

Há um trem em alta velocidade passando. Você pode encarar os fatos [da evolução tecnológica] e aceitar o desafio de entrar nesse trem, como o sócio-fundador fez, ou então ver ele passar.

Um Panorama Sobre os Tipos de Banco de Dados para Você Ficar por Dentro

Sistemas de banco de dados são como vinhos, queijos e árvores. Eles melhoram enquanto envelhecem.


Em um projeto complexo, com requisitos de alta performance, escalabilidade e disponibilidade para a camada de persistência, de todas as decisões que você deve tomar, nenhuma é mais desafiadora – e nenhuma outra tem recebido mais atenção – do que ESCOLHER O BANCO DE DADOS MAIS ADEQUADO [a engenharia do Uber que o diga]. O que torna tal decisão tão difícil não é só a quantidade de opções que existe [são mais de 300 bancos de dados para os mais diversos fins].

Os sistemas Relacionais são os mais populares, suportam nativamente SQL [que é a melhor linguagem para consultar e manipular dados], e são os melhores em integridade – mas sabidamente eles não escalam de forma eficiente, e têm baixa flexibilidade. Os sistemas NoSQL escalam horizontalmente, são extremamente flexíveis e suportam dados variados, mas pecam na consistência [que é eventual]. Os sistemas NewSQL são um avanço dos NoSQL, têm maior consistência, maior suporte ao SQL, porém com menor disponibilidade em relação ao anterior. Os sistemas Multi-Model são uma combinação do Relacional com o NoSQL, porém não absorvem todas as características de arquitetura de cada um. Multi-Model é uma capacidade que todos os anteriores podem ter.

A GRANDE DIFICULDADE está em escolher aquele que atenda aos requisitos atuais, MAS QUE TAMBÉM ATENDA AOS REQUISITOS FUTUROS que você ainda não sabe quais são. Isso pode te deixar na mão se o sistema de banco de dados escolhido não tiver a capacidade de se adequar ao dinamismo que estamos vivenciando, como desenvolvimento ágil, microserviços, aplicações cloud native, machine learning, blockchain e IoT.

Pare por um momento e responda: Você Tem Dúvidas para Decidir Qual é a Camada de Persistência Mais Adequada para seu Próximo Projeto? Espero que este panorama lhe ajude a ter uma visão mais clara.

Relacional é SQL, e SQL é Relacional: uma Simbiose dos Anos 70 que é Atual até Hoje

SQL e o sistema de banco de dados Relacional transformaram em museu rapidamente tudo que existia antes. Inovações como acesso client/server, joins, locks no nível de linha, leitura consistente, transações locais e distribuídas, e constraints, só para citar alguns, foram capazes de aliviar os desenvolvedores da época, e substituir milhares de linhas de código por uma simples frase: SELECT FROM WHERE. A evolução do SQL foi um dos acontecimentos mais importantes na década de 80 na área de tecnologia, pois mudou a forma como a informação era utilizada, e como os sistemas eram construídos.

A linguagem SQL e o sistema Relacional surgiram, e milhares de linhas de código foram substituídas por uma simples frase: SELECT FROM WHERE.

A linguagem SQL, utilizada para buscar e manipular dados em um banco Relacional, é amplamente “simulada” sob outros formatos NoSQL, porque, no final do dia, SQL é uma das linguagens mais simples do universo!

Hoje em dia SQL é extremamente robusto: ele permite não só consultar e manipular dados, mas também permite fazer data wrangling de forma muito eficiente, fornece capacidade nativa para reconhecimento de padrões, expressões regulares, aprendizado de máquina, funções analíticas, e possui uma biblioteca de APIs bastante abrangente em todas as implementações de banco de dados.

A Força Bruta de Consistência

Outra característica marcante nos sistemas Relacionais é a INTEGRIDADE. Quando o objetivo é integridade, nada construído até hoje é mais rápido e mais simples do que o sistema de banco de dados Relacional. Há quase 40 anos ele tem sido o padrão para armazenamento na maioria dos sistemas de informação, porque a maioria dos sistemas precisa de integridade.

As propridades ACID [Atomicidade, Consistência, Isolamento e Durabilidade] são os fundamentos da integridade nos sistemas Relacionais, e ao lado da linguagem SQL, são vistas como as principais características deste modelo de persistência.

ATOMICIDADE: se uma transação tem vários comandos, ou faz tudo, ou não faz nada; CONSISTÊNCIA: a transação respeita a unicidade da chave primária, o valor que não pode ser nulo, a chave estrangeira, os datatypes e quaisquer outras regras definidas na tabela; ISOLAMENTO: uma transação não enxerga a outra que atua no mesmo dado. Um produto tem 1 unidade e duas compras são realizadas ao mesmo tempo: uma deve executar primeiro. Se a primeira efetivar, a segunda não consegue comprar o produto [por falta de estoque]. Se a primeira falhar, a segunda efetiva a compra do produto; DURABILIDADE: o dado alterado por uma transação persiste em disco, e este sempre será o mesmo até que outra transação válida o altere.

Para garantir a integridade em transações distribuídas, os sistemas Relacionais utilizam o protocolo Two-Phase Commit, conhecido como 2PC, para garantir a atomicidade [A do ACID].

Quando uma transação é distribuída entre vários bancos de dados, quando um deles faz commit, todos votam, e caso todos votem ‘sim’, eles registram o commit em logs locais – ou rollback, caso pelo menos um vote ‘não’, ou haja qualquer falha [fase 1]. Supondo que todos votem ‘sim’, a confirmação é retornada para todos, e a transação é efetivada em todos os bancos de dados [fase 2]. O log de confirmação local garante que todos podem exercer o commit, caso haja uma falha durante fase 2.

O modelo relacional é schema-full. Isto significa que a estrutura das tabelas [colunas, datatypes e constraints] precisa estar definida antes dos dados serem gravados. É como um contrato entre quem vai ler e quem escrever no banco de dados.

A característica schema-full, as propriedades ACID, e o protocolo 2PC são os alicerces da integridade no sistema de banco de dados Relacional. Há outros acessórios [como as leituras consistentes e o MVCC] para melhorar a experiência do usuário diante da força bruta de consistência disponível.

Há Certas Coisas que os Sistemas Relacionais Não Fazem por Você

Os TRADE-OFFS mais importantes dos sistemas Relacionais são, em primeiro lugar, A FALTA DE FLEXIBILIDADE DO FORMATO DE DADOS, e depois, A BAIXA CAPACIDADE DE ESCALAR no nível da internet.

A FALTA DE FLEXIBILIDADE foi na verdade uma vantagem do modelo Relacional nos anos 80-90 [quando os desenvolvedores tinham que fazer o schema na mão]: o modelo de dados é schema-full. Isto é, primeiro é necessário criar a estrutura dos dados, para depois inserí-los, e não o oposto [como ocorrem com os NoSQL]. Há vantagens na abordagem schemaless, principalmente nos tempos atuais, e portanto, hoje, não tê-la é um ponto negativo [mais adiante eu falo sobre Multi-Model, que torna o sistema Relacional mais moderno].

A BAIXA CAPACIDADE DE ESCALAR ocorre porque, em geral, nos sistemas Relacionais, os dados das tabelas não são distribuídos entre servidores distintos e independentes [shared-nothing], e por isso eles apenas escalam verticalmente [até a capacidade de um servidor, e não de vários] – esse é um forte limitador de escalabilidade.

Há, entretanto, uma implementação de software ainda única, da Oracle [chamada Oracle RAC], que permite que vários servidores ativos e distintos acessem os arquivos de um mesmo banco de dados [shared-disk], mantendo a consistência forte. No teorema CAP que explico mais adiante, o RAC recebe a classificação CA [consistency e availability]: essa tecnologia é a que mais oferece escalabilidade para um sistema de banco de dados Relacional, mas ainda é inferior aos níveis de escalabilidade oferecidos pelos NoSQL.

VOCÊ DEVE CONSIDERAR O SISTEMA DE BANCO DE DADOS RELACIONAL PARA DADOS QUE PRECISAM DE INTEGRIDADE.

Os Sistemas NoSQL são como Albatrozes: Ágeis no Ar, mas Desajeitados em Terra

Apesar de tanta inovação e robustez para gerenciar dados, por causa da internet, o modelo de infraestrutura que só escala verticalmente [para cima], como ocorre com os bancos de dados Relacionais, demonstrou não atender a escalabilidade necessária para suportar aplicações web de OLTP intenso: milhões de usuários com demandas variáveis e imprevisíveis.

No ano 2000 surgiram as primeiras tecnologias NoSQL que ganharam escala. Fizeram muito sucesso como forma de persistência nas redes sociais, e começaram a ganhar espaço nas empresas.

Os bancos de dados NoSQL, de forma geral, são baseados em estruturas de dados schemaless, em arquitetura shared-nothing [totalmente distribuído, nada é compartilhado], e por isso conseguem escalar horizontalmente [para os lados] e armazenar qualquer estrutura de dados. Há muitas implementações de NoSQL, sendo que as principais são Chave-Valor, Documentos, Grafos e Orientado a Coluna.

A implementação Chave-Valor é indicada para leitura ou escrita intensiva de qualquer coisa, não importa o formato, desde que seja com o maior throughput possível. A implementação de Documentos é indicada para manipular informações que são agrupadas e relacionadas, como um catálogo de produtos, por exemplo. Grafos é indicada para dados altamente conectados, para encontrar conexões entre pessoas e coisas. Orientado a Coluna é um híbrido do formato linha e coluna, e é indicado para altos volumes de dados, em especial para escrita [não tanto para consulta].

Você precisa entender o Teorema CAP [Consistency, Availability, Partition Tolerance] e sua importância no mundo dos sistemas distribuídos. Ele prova que um sistema que distribui seus dados em servidores diferentes [sem compartilhar qualquer componente de infraestrutura], não pode ter disponibilidade e consistência ao mesmo tempo. No advento de uma falha na rede ou em um servidor qualquer, a CONSISTÊNCIA É GARANTIDA [letra C do CAP, onde um leitor enxerga todas as escritas completadas previamente], ou a DISPONIBILIDADE É GARANTIDA [letra A do CAP, onde o sistema sempre estará disponível para leitura/escrita], e não ambos.

Em geral, os sistemas NoSQL suportam AP [A de Availability, e P de Partition Tolerance]: sempre que há escrita em um servidor, ela é replicada de forma assíncrona para outros servidores espelho para manter a disponibilidade da informação – todos os servidores, os que escrevem, e os que recebem as escritas replicadas, ficam disponíveis para leitura.

Como exemplo, em uma configuração de cluster com 10 servidores, em geral 5 permitem escrita, e os outros 5 permitem apenas leitura. Nesta configuração, cada servidor-escritor replica de forma assíncrona para seu par que só permite leitura. Cada conjunto escritor/leitores são completamente independentes de outros conjuntos escritor/leitores, e cada um desses conjuntos armazena uma parte da informação.

Se houver falha em um servidor, os demais servidores espelho continuam lendo ou escrevendo, mesmo que possa haver inconsistência: a falha impede que um servidor replique seus dados para as outros servidores espelho. Desta forma, quando se lê um dado nesta situação, ele estará eventualmente consistente. Isso ocorre porque o dado poderá ter sido alterado, mas esta alteração pode não ter sido propagada para as réplicas. Além disso, mesmo sem uma falha, como as replicações são assíncronas, é possível que uma leitura em uma réplica possa não estar enxergando a versão mais recente da informação. Logo, haveria também uma leitura eventualmente consistente.

Ao contrário dos sistemas AP, os sistemas CP privilegiam a consistência forte. Um servidor-escritor envia os dados de forma síncrona para os seus respectivos servidores espelho. Então, se você está lendo a partir do servidor que escreve, ou da réplica, você sempre estará lendo a informação mais recente. Isso é consistência forte. Entretanto, se um servidor que escreve tem uma falha e fica indisponível, os seus pares ficam impossibilitados de escrever para evitar um cenário de inconsistência. Por isso, uma parte da informação torna-se inacessível, porém não o sistema todo: apenas os servidores com falha.

Esse é o trade-off entre AP e CP. E não é possível um sistema de banco de dados ter C, A e P ao mesmo tempo, conforme prova o teorema.

Tanto os sistemas AP, como os CP, dizem ter Consistência. Mas é importante entender que existem dois tipos de Consistência: a Forte, onde um leitor enxerga todas as escritas feitas previamente; e a Eventual, onde um leitor pode ou não, isto é, eventualmente, enxergar as escritas feitas previamente. Além disso, existe a Consistência no ACID, que tem a ver com integridade do dado escrito [datatypes, null ou not null, constraints, etc], e também existe a Consistência no CAP, que indica se a informação que um leitor lê é, ou não, a versão mais recente.

Consistência eventual para as redes sociais, ok! Para os batches… humm, talvez. Para as aplicações online? Complicado…! Neste caso, se necessário, o desenvolvedor que deve garantir a consistência forte em um sistema AP, já que ela não está disponível nesse tipo de banco de dados – e vale lembrar que é muito complexo codificar consistência: não é exatamente para qualquer um! É uma codificação altamente suscetível a bugs [lembra quando falei sobre as inovações do sistema Relacional? Os NoSQL descartam várias delas, a consistência forte é um exemplo].

Quanto mais os bancos de dados NoSQL se aproximam das aplicações online nas empresas, maior é a necessidade de transações, ACID, e consistência forte.

Não é exatamente uma desilução dos NoSQL que suportam AP, pois eles ainda têm seu lugar em diversos casos de uso. Mas está cada vez mais comum a introdução de capacidades CP [consistência] nas tecnologias que outrora iniciaram sua implementação com AP.

Um outro ponto relevante é que nenhum sistema garante efetivamente 100% de disponibilidade [o A do CAP]. Na prática há alguns 9s de disponibilidade, mas nunca 100% [você pode ter 20 réplicas, mas embora difícil, não é impossível perder todas elas]. Então o A do CAP é apenas “teórico”, mesmo para os NoSQL AP, que notadamente sacrificam a consistência para supostamente garantir 100% da disponibilidade. De fato, tanto a consistência quanto a disponibilidade são sacrificadas nos sistemas NoSQL AP.

E para complicar um pouco mais, AP sempre sacrifica a consistência, pois em virtude da necessidade de baixa latência [lê-se replicação assíncrona], os dados em geral sempre estarão eventualmente consistentes, mesmo sem uma falha ou falta de comunicação na rede.

VOCÊ DEVE CONSIDERAR UM SISTEMA DE BANCO DE DADOS NOSQL QUANDO ESCALABILIDADE FOR MUITO MAIS IMPORTANTE DO QUE INTEGRIDADE, DO CONTRÁRIO, CONSIDERE O RELACIONAL.

Obrigado pela Ajuda NoSQL: Agora é com a Gente!

A necessidade real nas empresas para atender as demandas de escalabilidade atuais é Consistência Forte [do SQL] com Escalabilidade Horizontal Distribuída [do NoSQL]. Eis então que surgem os bancos de dados NewSQL!

O título desta parte do meu artigo é originalmente do post de um blog do MemSQL, que faz uma sátira com os bancos de dados NoSQL, em favor do deles, um banco de dados NewSQL. E eles ainda completam: “Tá na hora de admitirmos o que todos nós já sabíamos por muito tempo: NoSQL é a ferramenta errada para muitos casos de usos nas aplicações modernas, e está na hora de seguirmos em frente.

O texto do post começa explicando a ascensão da tecnologia NoSQL diante das supostas limitações da tecnologia SQL para os sistemas mais modernos, digitais e cloud native. Na sequência é introduzida a tecnologia NewSQL, que emerge dos pontos positivos do SQL e NoSQL combinados, o que acaba por colocar em decadência as tecnologias só-NoSQL, e também só-SQL.

De fato, SQL fez emergir NoSQL, que fez emergir NewSQL. E os sistemas NewSQL são então, mais do que nunca, gratos pela contribuição dos NoSQL, que os fez surgir, daí o título ligeiramente escrachado do post.

A diferença mais notável entre um NoSQL e um NewSQL é que o último suporta consistência forte, e na melhor da boa vontade, SQL e outras características do sistema Relacional. Os sistemas de informação nas empresas não são Facebooks, Instagrams, Twitters. Elas precisam de consistência. Então sim, NewSQL ascende. NoSQL, decai.

Os sistemas NewSQL representam uma evolução em relação aos NoSQL, oferecendo suporte a CP do CAP. NewSQL tenta ser um SQL, e tolera partições de rede, a grande diferença. A tolerância a partição permite escalar horizontalmente, como os NoSQL, e entrega escalabilidade ao nível da internet.

Mas francamente, quase todos os NewSQL, ou todos realmente, não suportam SQL plenamente. Não há como ignorar quase 4 décadas de desenvolvimento. Todos os NewSQL suportam apenas uma porção do que os sistemas SQL tradicionalmente suportam: compliance ao ANSI plenamente, extensões proprietárias, ACID, 2PC, Multi-Model, segurança abrangente de dentro pra fora, backups e restores granulares, monitoração robusta com grande poder de instrumentação, e milhares de outras características.

É apreciável o esforço de construir uma consistência nativa bem elaborada numa arquitetura dominada por Albatrozes que voam bem, mas pousam terrivelmente mal [aqui eu me refiro aos NoSQL].

VOCÊ DEVE CONSIDERAR UM SISTEMA DE BANCO DE DADOS NEWSQL QUANDO ESCALABILIDADE FOR MUITO RELEVANTE, TÃO QUANTO INTEGRIDADE, DO CONTRÁRIO, CONSIDERE ALGUM DOS ANTERIORES.

Não é um Pato, é Multi-Model

Os sistemas de banco de dados Multi-Model são aqueles que permitem nativamente vários formatos ao mesmo tempo, como Relacional, Grafos, Chave-valor, Documentos, Colunar e qualquer outro que se torne relevante.

A característica Multi-Model se torna relevante principalmente nos tempos atuais, onde as aplicações podem requerer persistência poliglota, isto é, um sistema de banco de dados diferente para cada parte da aplicação, já que cada um é melhor em algum caso específico.

A verdade é que sempre existe aquele sistema de banco de dados “cutting-edge“, de ponta, de última geração, crème de la crème. Aquele que só ele faz aquilo que ele faz. Mas o que também ocorre é que se aquilo que só ele faz se torna relevante, os sistemas de banco de dados multi-model o absorvem. Tem sido assim nos últimos anos, e provavelmente sempre será.

As inovações dos sistemas Single-Model são incorporadas nos sistemas Multi-Model

Desenvolver um banco de dados é muito complexo. Um exemplo disso é o bem difundido PostgreSQL [com suas limitações de escalabilidade], que fez por exemplo o Uber deixar de usá-lo, conforme mencionei no início deste artigo. É mais simples e rápido incorporar features novas do que construir todo um core, e é por isso que os sistemas de banco de dados Multi-Model ainda dominam.

VOCÊ DEVE CONSIDERAR UM SISTEMA DE BANCO DE DADOS MULTI-MODEL QUANDO FLEXIBILIDADE DE SCHEMA E CONSISTÊNCIA FOREM AMBOS MUITO IMPORTANTES.

Document Stores?

Eles são bancos de dados Relacionais ao contrário, porque ao invés de criar as tabelas e escrever os dados, você cria os dados e escreve as tabelas. Eles vieram, provavelmente, da mente de algum desenvolvedor de front-end que se cansou do pragmatismo dos Relacionais, e se revoltou. Não farão nada com a gente: os bancos Multi-Model já o absorveram! 


Documento é uma forma alternativa de persistência que se popularizou com o formato XML há alguns anos, e hoje está mais presente com o formato JSON, que tem melhor usabilidade.

Opinião sincera e desnecessária sobre a comparação entre XML e JSON para a narrativa de quando usar o que, como, e onde: esqueça XML, use JSON. Não é o objetivo deste post comparar os dois, mas vamos lá: além de ridículo, antigo, e ocupar muito espaço, XML me lembra muito HTML, e eu detesto HTML! Quando pessoas fazem o trabalho sujo de desenvolver ferramentas como o DreamWeaver, que nos permitem NÃO VER códigos HTML, eu definitvamente me sinto no topo da cadeia alimentar…

Normalmente utilizado para troca de dados entre sistemas, um documento JSON também pode ser utilizado como um formato de armazenamento [sempre surge alguém com uma ideia brilhante]. Então vou compará-lo com o modelo Relacional, o mais popular.

No modelo Relacional, uma tabela é um conjunto de linhas, e cada linha é um conjunto de colunas. No modelo de Documentos, uma coleção é um conjunto de documentos, e cada documento possui atributos. Então uma coleção é como se fosse uma tabela, um documento é como se fosse uma linha, e seus atributos é como se fossem colunas.

A diferença é que cada documento pode ser diferente em uma coleção, e em cada documento, seus atributos também podem ser diferentes, e ainda cada um desses atributos pode conter outros documentos, que pode conter outros documentos, que pode conter outros documentos, que pode conter outros documentos, que pode conter outros documentos, que pode conter outros documentos [não estou repetindo só pro meu post ter mais palavras. É que, de fato, rola um lance recursivo aqui, e incrivelmente eles não entram em loop].

Leia o parágrafo anterior 3 vezes. Eu sei que ficou confuso, e você não entendeu. Na terceira re-leitura você vai entender, e também vai notar que fiz o melhor que pude. A confusão faz parte desse tipo de banco de dados. Eu mesmo li três vezes pra entender o que escrevi.

No modelo Relacional, a estrutura deve ser definida antes de escrever os dados, enquanto que no modelo de Documentos, você pode escrever sem ter uma estrutura. A aplicação que escreve é que define a estrutura dos dados.

Se você conhece os bancos de dados Orientados a Objeto [faz parte do passado, e ninguém sente saudade], um banco de dados de Documentos vai lhe parecer bastante familiar, pois ambos permitem estruturas aninhadas [um atributo dentro de outro]. Porém, as semelhanças param por aí. O banco de dados de Documentos tem característica schemaless, onde você não especifica a estrutura antes de escrever os dados.

Reforçando os conceitos para você não esquecer nunca mais: 

Schema-full, ou Schema on Write

Quando a estrutura da tabela e as regras são definidas no banco de dados, e o código da aplicação deve respeita-las. Típico do banco de dados Relacional.

Schemaless, ou Schema ou Read

Quando a estrutura da tabela e as regras são definidas na aplicação, e no banco de dados se define apenas o nome da tabela, ou coleção. Típico do banco de dados de Documentos.

Neste post vou explorar as principais características de um banco de dados de Documentos para você utilizar quando for necessário.

Ele é necessário, e você precisa conhecer. É sério.

Vamos Olhar por Dentro: Coloque as Crianças pra Dormir!

O formato Java Script Object Notation [JSON] surgiu por volta do ano 2000, e logo se tornou popular principalmente pela sua simplicidade. Ele é mais compacto do que XML e consegue representar estruturas complexas, que não são possíveis na forma tabular comum nos bancos relacionais. 

{
 id: 100, 
 nome: “Suco”, 
 dept: “Bebidas”, 
 qtd: 10, 
 vl: 4.50
}

O documento JSON acima é um dos mais simples, e nota-se que ele se parece muito com uma estrutura chave-valor. O documento é tudo que está entre as chaves {}, e os atributos estão separados por vírgulas.

Ao buscar um atributo, obtem-se o valor correspondente: nome, por exemplo, retorna Suco.

Vamos ver um exemplo no Banco de Dados Oracle.

No Oracle 21c, criamos uma tabela comum, e os documentos são armazenados em uma coluna desta tabela, que representa a coleção, quando especificamos o datatype JSON.

Abaixo, eu crio uma tabela produtos que armazena uma coleção na coluna prodDocument, que é o documento JSON acima. Depois consulto o atributo nome:

CREATE TABLE produtos
(id INT, prodDocument JSON);

INSERT INTO produtos VALUES (1,
'{id: 100, nome: "Suco", dept: "Bebidas", qtd: 10, vl: 4.50}');

SELECT p.prodDocument.nome FROM produtos p;

É possível também colocar documentos dentro de documentos:

{"Produtos":
[
{id: 100, nome: “Suco”, dept: “Bebidas”, qtd: 10, vl: 4.50},
{id: 200, nome: “Chá”, dept: “Bebidas”, qtd: 31, vl: 6.75},
{id: 300, nome: “Água”, dept: “Bebidas”, qtd: 17, vl: 1.70}
]
}

Então basta abrir colchetes [observação desnecessária: eu definitivamente substitui os parenteses por eles nos meus textos, você já deve ter percebido] na parte do valor de um atributo para aninhar outros documentos JSON.

Na mesma tabela produtos vou incluir outra linha, desta vez com 3 documentos:

INSERT INTO produtos VALUES (2,
'{"Produtos":[
{id: 100, nome: "Suco", dept: "Bebidas", qtd: 10, vl: 4.50},
{id: 200, nome: "Chá",  dept: "Bebidas", qtd: 31, vl: 6.75},
{id: 300, nome: "Água", dept: "Bebidas", qtd: 17, vl: 1.70}
]}');

SELECT p.prodDocument.Produtos.nome
FROM produtos p
WHERE id = 2;

Perceba que a primeira linha que inseri [id = 1] tem um documento JSON com 5 atributos. E a segunda linha [id = 2] tem 3 documentos JSON com 5 atributos. Isto é, a segunda linha é aninhada, pois tem vários documentos. Agora veja este outro exemplo mais interessante:   

{OrdemVenda: 
[
 {id: 1,
  loja: "SP-A",
  data: "01-10-2018",
  items: [
    {id: 100, nome: "Suco", dept: "Bebidas", qtd: 10, vl: 4.50},
    {id: 200, nome: "Chá",  dept: "Bebidas", qtd: 31, vl: 6.75},
    {id: 300, nome: "Água", dept: "Bebidas", qtd: 17, vl: 1.70}
   ]},
 {id: 2,
  loja: "SP-B",
  data: "01-10-2018",
  items: [
    {id: 300, nome: "Água", dept: "Bebidas", qtd: 1, vl: 1.70},
    {id: 110, nome: "Café", dept: "Bebidas", qtd: 2, vl: 3.20}
   ]}
]}

No exemplo acima temos um documento com Ordens de Venda. Precisamente temos duas Ordens, sendo que a primeira tem três itens vendidos, e a segunda tem dois itens.

Basicamente modelamos em um documento o que seria equivalente a 5 tabelas em um modelo Relacional Normalizado em 3NF [Ordem, ItemOrdem, Loja, Produto e Departamento].

Agora quero retornar a quantidade total de itens vendidos em todas as Ordens.

Mas antes veja que este documento se lê assim: ele é composto por Ordens de Venda com os atributos [id, loja, data e items], que por sua vez, items é composto por [id, nome, dept, qtd e vl]. Então para somar a quantidade, eu tenho que percorrer as ordens, depois os items, e então buscar [qtd].

Vou usar a função JSON_TABLE para converter o JSON com Arrays para Relacional para poder somar.

Para cada array [OrdemVenda e Items] precisamos incluir a cláusula NESTED com o PATH do atributo que queremos retornar. $ indica a raíz do documento, ou o ponto onde eu parei no caso da cláusula NESTED. O asterisco entre colchetes é para retornar todos os itens do array – poderia passar 0 para retornar o primeiro item, 1 para o segundo, e assim por diante:

INSERT INTO produtos VALUES (3,
'{OrdemVenda: [
{id: 1,
loja: "SP-A",
data: "01-10-2018",
items: [
{id: 100, nome: "Suco", dept: "Bebidas", qtd: 10, vl: 4.50},
{id: 200, nome: "Chá",  dept: "Bebidas", qtd: 31, vl: 6.75},
{id: 300, nome: "Água", dept: "Bebidas", qtd: 17, vl: 1.70}
]},
{id: 2,
loja: "SP-B",
data: "01-10-2018",
items: [
{id: 300, nome: "Água", dept: "Bebidas", qtd: 1, vl: 1.70},
{id: 110, nome: "Café", dept: "Bebidas", qtd: 2, vl: 3.20}
]}
]}');

SELECT SUM(qtd)
FROM produtos p,
     JSON_TABLE(p.prodDocument, '$'
                 COLUMNS (
                          NESTED PATH '$.OrdemVenda[*]'
                          COLUMNS (
                                   NESTED PATH '$.items[*]'
                                   COLUMNS (qtd NUMBER PATH '$.qtd')
                                   )
                         )
                ) as prodDocument
WHERE id = 3

Em breve vou fazer um artigo somente sobre a manipulação de documentos JSON no banco de dados Oracle. Há muitas funcionalidades que podemos explorar.

Fiz um post há um tempo sobre algumas funções poderosas no Oracle para fazer transformação de dados. Algumas delas são para transformar documentos JSON. Veja aqui.

Já Passa da Meia-Noite, Vamos Falar dos Benefícios, Comparando com o Modelo Relacional

DOCUMENTOS SÃO SCHEMA ON READ. A principal característica de um Documento JSON no banco de dados é o fato de ele ser schema on read. Isto é, você não precisa criar uma estrutura rígida que uma tabela tem [colunas, datatypes, constraints] para depois colocar os dados: a aplicação que determina como os dados são escritos, sem a especificar previamente a estrutura – veja claramente nos exemplos que criei mais cedo neste artigo.

Essa característica permite um desenvolvimento mais flexível, por exemplo: em uma tabela “Clientes” você pode ter clientes com diferentes tipos e quantidades de contato, como telefone, celular, email, Facebook, Whatsapp, Linkedin, etc. Mas nem todos os clientes precisam ter todos os contatos.

Em um modelo Relacional, a tabela Clientes poderia ter muitas colunas com valores nulos [porque nem todos teriam todas essas formas de contato], ou teríamos que normalizar as tabelas, criando outras, e usando joins nas consultas, podendo eventualmente deixa-las mais lentas.

{"nome": "João",  "telefone" : "911112222"}
{"nome": "Mario", "telefone" : "922223333", "twitter": "@mario"}

Com a tabela [coleção] armazenando os clientes desta forma, uma query que busca “twitter” para um cliente que não tenha esta informação receberia um null.

COM DOCUMENTOS AS MUDANÇAS NO BANCO DE DADOS SÃO MAIS ÁGEIS. Não é necessário fazer DDL [ALTER TABLE] para alterar a estrutura de uma coleção, porque a estrutura na prática está nos documentos [schema on read, lembra?]. Se a aplicação quiser incluir um “Complemento de Endereço” na coleção “Clientes” por exemplo, basta incluir a nova informação na aplicação, sem precisar alterar o banco de dados. De fato essa característica torna as mudanças mais ágeis. Entretanto, alguns bancos relacionais, como o Oracle por exemplo, permite alterar uma estrutura relacional sem downtime – ainda que, também, suporta o modelo de Documentos, conforme observamos nos exemplos.

OS DOCUMENTOS MINIMIZAM O USO DE JOINS, E POR ISSO AS CONSULTAS SÃO MAIS RÁPIDAS. Uma outra característica interessante do JSON é o fato de armazenar os “relacionamentos” no mesmo documento, sem precisar recorrer a joins como ocorre no modelo Relacional. No exemplo da Ordem de Venda mais acima, temos os pais [ordens] e os filhos [itens das ordens] juntos no mesmo documento. Isto indica que ao modelar você pode pensar na forma como a aplicação vai funcionar, e então especificar um Documento que terá todas as informações contidas nele.

OS DOCUMENTOS TRABALHAM MELHOR COM API REST, A SENSAÇÃO DO MOMENTO. JSON é o formato mais comum quando se utiliza as populares APIs REST [chamadas por http]. Consultar uma coleção retorna nativamente um JSON, sem necessitar de conversão.

Diga-me o que Você Fala só para seus Amigos em Particular

SCHEMA ON READ REQUER CUIDADO. A característica schema on read promove agilidade no desenvolvimento, mas impõe maior governança por parte do desenvolvedor. Veja o seguinte exemplo:

{cliente_id:100, desc_cep: "11111-100"}
{cliente_id:101, desc_cep: "22222-100"}
{cliente_id:102, desc_cep: "33333-100"}
{cliente_id:103, cep: "44444-100"}
{cliente_id:"texto", cep: "55555-100"}

A coleção utiliza como padrão desc_cep para indicar o CEP, mas em algum momento a informação foi escrita como cep. Como o atributo cep ou desc_cep não tem a integridade exercida no banco de dados, a informação é gravada errada. O mesmo ocorre com client_id: o último está como texto ao invés de número. Um find para buscar a informação certamente não traria o resultado correto.

LEITURA DE MUITOS DOCUMENTOS DE UMA SÓ VEZ. O fato de não fazer joins porque toda a informação está contida em um mesmo documento só é vantagem quando a maioria das buscas forem para um ou poucos documentos. Suponha que uma consulta retorne 10 mil documentos: um I/O vai trazer muito mais dados no formato Relacional do que no formato JSON, mesmo fazendo joins. Isso ocorre porque no modelo Relacional as linhas estão mais próximas fisicamente, pois as tabelas são normalizadas, e portanto faz menos I/O. No JSON não se normaliza, então existe muita redundância e isto implica em maior consumo de armazenamento [menos “dados” por I/O].

COMPRESSÃO MAIS OU MENOS. Há um trade-off entre compressão e performance, em especial para o formato de Documentos. Em geral os bancos de dados de Documentos não possuem compressões colunares ou deduplicações. Eles recorrem a algoritmos mais lentos como zlib que podem até gerar uma boa redução, mas ao custo de impactar no desempenho das leituras.

CONTROLE TRANSACIONAL PRECÁRIO. Os sistemas de banco de dados puramente baseados em Documentos JSON, em geral, não suportam ACID nativamente, ou suportam com alguma restrição. Isto significa que se você transacionar sobre documentos terá que gerenciar as transações manualmente na aplicação ou por meio de alguma API. Boa sorte. 

RELACIONAMENTOS SÃO UM PARTO. Os defensores deste formato argumentam que os Documentos não precisam de relacionamentos, pois eles estão todos auto-contidos no mesmo Documento. Mas o mundo é feito de relacionamentos, e eles são complexos em especial quando são do tipo muitos-para-muitos. Veja um exemplo: em um sistema nós temos papéis e usuários. Você pode ter uma coleção “Papéis” cujos documentos contém para cada papel todos os seus usuários, ou uma coleção “Usuários” cujos documentos contém para cada usuário todos os seus papéis. Qualquer atributo de Papéis ou Usuários, como “descrição do Papel” ou “Nome do Usuário”, vai se repetir nos documentos, criando redundância e margem para bugs… é possível, entretanto, utilizar IDs ao invés de “embeddar” um documento no outro, mas aí é como no Relacional, só que desta vez com menos performance, pois o Relacional já é otimizado para relacionamentos.

Então modelar relacionamentos em um banco de dados de Documentos exige maior skill do desenvolvedor para não implementar alguma prática que depois se torne um problema — e não há estrutura schemaless que salve o esforço de mudança depois.

Para te deixar Menos Relacional por Entender Melhor como os Bancos de Documentos Funcionam. Vamos Finalmente para as Considerações Finais!

Uma coleção schemaless no banco de dados é na verdade um “schema implícito” porque o “schema” sempre existe em algum lugar: neste caso, no código da aplicação.

É necessário um schema para determinar se o correto é “DataNasc” ou “Data_Nascimento”. A flexibilidade no banco de dados vem ao custo de uma maior governança na aplicação.

Ter todas as informações contidas em um Documento para evitar joins não necessariamente é um benefício de performance. Consultas SQL com muitos joins são em geral uma deficiência de design da aplicação do que propriamente do modelo Relacional. Documentos JSON muito aninhados [documentos dentro de documentos] também podem ser um problema, e uma deficiência da aplicação.

Há restrições importantes quando armazenamos JSON em um banco de dados de Documentos, como controle transacional, tipos de leituras realizadas e inclusive tamanho do banco de dados.

O banco de dados de Documentos é um modelo bastante interessante que endereça alguns casos de usos. No entanto, na minha visão ele é um formato COMPLEMENTAR ao Relacional.

Recomendo ARMAZENAR DOCUMENTOS no banco de dados RELACIONAL quando:

[1] Houver a necessidade da aplicação criar atributos de forma ad-hoc, em runtime, com formatos diversos.

[2] A aplicação exigir que se normalize uma informação tal que gere muitas colunas com Nulos em várias linhas.

Recomendo ARMAZENAR DOCUMENTOS em um banco de dados DE DOCUMENTOS quando:

[3] A origem [quem gerou] e o fim [quem vai consumir] também forem JSON. Neste caso não faria sentido o meio [o banco de dados] ter outro formato, e em especial se o dado tem perfil transitório, isto é, ele é consumido rapidamente e fica como histórico, sem pós-processamentos [ex: IOT, logs, configurações, etc].

Em geral, minha primeira opção é optar por schema on write [Relacional], e complementar com schema on read [JSON] se for necessário.

Tenho uma preferência particular por bancos de dados Multi-Model, que permitem vários formatos nativamente ao mesmo tempo, em detrimento dos bancos de dados especializados. E por isso, em uma estrutura multi-model, você pode usar os formatos mais adequados para os mais diversos casos de uso sem muita complexidade.

Hoje em dia as aplicações tem necessidades de Dados mais poliglotas, e ter várias tecnologias especialistas aumenta bastante a complexidade.

Pense no caso de uso, e não na plataforma.

Se você pensar em “plataforma” para endereçar um caso de uso, vai acabar colocando um banco de dados muito especializado que só faz aquilo: se os requisitos mudarem, você ficará sem saída. Um banco de dados multi-model é capaz de atender vários casos de uso, e você não precisa se preocupar com a plataforma.

Você pode filtrar por Document Store no site DB-Engines, para ver quais são os bancos de dados especializados em Documentos mais populares. MongoDB é o banco de Documentos mais popular, mas é importante notar que os bancos mais populares da lista [1. Oracle e 2. MySQL] são multi-model, e aceitam Documentos JSON nativamente.

Há Uma Coisa em Comum entre os Duendes que Vivem Embaixo da sua Cama, e os Bancos de Dados Schemaless

Ambos não existem!


Do ponto de vista da aplicação, na prática, schemaless não existe.

Schemaless, ou schema on read, é quando não existe a estrutura de dados definida [colunas, datatypes, constraints] quando se escreve, pois quem a define é quem lê [a aplicação]. O oposto é Schema-full, ou schema on write, normalmente utilizado pelos bancos de dados Relacionais: define-se a estrutura, e depois insere os dados.

Provavelmente a fábula do schemaless parece ter sido criada por desenvolvedores de front-end que, supostamente, teriam muito mais agilidade para desenvolver e mudar as aplicações sem depender do banco de dados [e seus agregados, como DBAs, indisponibilidade, processos de mudança, etc].

Mas a realidade não é bem assim: tudo SEMPRE tem schema, e se tem schema, mudanças SEMPRE têm esforço.

Schema é o conjunto de regras de integridade que você define para organizar os dados. O sufixo full é quando o BANCO DE DADOS exerce o schema, e o sufixo less é quando VOCÊ exerce. SEMPRE ALGUÉM EXERCE O SCHEMA.

Nos últimos 30 anos os principais bancos de dados criaram mecanismos para implementar recursos de integridade com escalabilidade e disponibilidade. Em alguns bancos de dados é possível fazer alterações de estrutura com zero downtime para a aplicação, e mesmo assim garantir toda a integridade.

Não há como uma aplicação não ter schema. Se você utiliza um Document Store por exemplo, onde os dados persistem como documentos JSON [logo, schemaless], a aplicação tem que saber como ler esses documentos. Saber como ler significa ter schema. Quando você lê um documento JSON e extrai dele um valor numérico para fazer um cálculo, terá que convertê-lo para int, float ou Decimal [um type, logo, um schema]. A propósito, Python é uma linguagem dinamicamente tipada, e mesmo que você não especifique o type estaticamente enquanto programa, deve especifica-lo mentalmente [logo, um schema] para não gerar erro durante a execução.

No final do dia, quando você entende que sempre existe um schema, não há nada que um banco de dados schemaless faça com mais agilidade do que um banco de dados schema-full. A questão é se você quer deixar que o banco de dados exerça o schema por você, ou você adia o trabalho inevitável de VOCÊ exercer o schema depois.  

Eu tenho uma preferência particular por bancos de dados Multi-Model, pois há situações onde o uso conjunto da persistência Relacional e Documentos oferece o melhor da flexibilidade com o melhor da integridade.

O Erro Mais Caro da Sua Vida É Não Usar Python ou SQL pra Arrumar Toda Aquela Bagunça

Conhecer Python ou SQL para fazer data wrangling é igual gravidez: quanto antes você souber, melhor!


Boa parte do trabalho de um cientista de dados, e de um engenheiro de machine learning, é arrumar os dados. Você precisa organizar tudo antes de começar a explorar, e colocar os algoritmos pra máquina aprender.

Eu conheço SQL há muito tempo, e já fiz cada transformação que até a Álgebra Relacional dúvida!

Não é só a linguagem em si, mas o banco de dados também ajuda. Por exemplo, no Oracle você pode criar colunas virtuais, trocar partes da tabela com dados por partes sem dados, inserir em várias tabelas diferentes ao mesmo tempo em que lê a partir de uma query, utilizar funções PL/SQL que podem executar com paralelismo pipeline, persistir em vários formatos de dados, enfim, é uma miríade de capacidades de transformação, tanto para a performance, como para a funcionalidade.

Mais tarde eu aprendi como fazer essa arrumação com Python – já tendo conhecido outras linguagens, como Java por exemplo. Neste post vou comparar brevemente como é fazer esse trabalho de wrangling com SQL e Python [através de uma biblioteca com essa finalidade].

Definitivamente SQL e Python são as melhores ferramentas não-Nutella [sem o apoio de uma ferramenta de transformação própria pra isso] para limpar e arrumar dados.


Meu primeiro contato com Pandas ocorreu há mais ou menos um ano. Confesso que achei que estava re-construindo a roda. Praticamente tudo que eu fazia com essa biblioteca em Python eu acreditava que conseguia fazer em SQL com muito mais simplicidade. Bom, primeiro eu explico o que é o Pandas.

Pandas é uma biblioteca open-source em Python para análise de dados.

Pandas basicamente é composto por um objeto chamado Dataframe [uma planilha!], e este dispõe de vários métodos com uma boa usabilidade para manipular dados. Minha primeira impressão ao utilizar o dataframe foi a de utilizar o Excel em forma de código, mas com muito mais performance.

Para instalar, como todo pacote Python, basta executar o comando abaixo no sistema operacional [Terminal no Mac ou Linux, e Command Window no Windows]:

pip install pandas

Para utilizar, em um notebook [Jupyter ou Zeppelin] ou em um arquivo Python .py:

import pandas as pd
meuDataFrame = pd.read_csv('arquivo.csv')
meuDataFrame.describe()

O código acima importa o pacote Pandas, carrega um arquivo csv no dataframe meuDataFrame, e faz um describe nos dados. Describe() é fantástico! Ele retorna uma tabela com uma série de dados estatísticos para cada coluna, como count, avg, sum, stddev, min, max e percentis. Essa primeira exploração é bem interessante com Pandas em relação ao SQL. Mas a vantagem do Pandas começa e termina aqui. Quase que todo o resto do trabalho de wrangling [limpeza dos dados] eu achei muito mais simples e mais rápido com SQL, por isso a sensação de estar reinventando a roda.

Limpar e transformar dados sobre grandes volumes requer critério. Cada minuto pensando na estratégia de implementação equivale a horas de processamento.

A manipulação dos dados em si com dataframes consiste em criar dataframes a partir de dataframes, sendo que as cópias vão transformando os dados aos poucos, com colunas novas a mais, ou a menos. Há também agregações [groupby], filtros [query], unicidade [unique], ordenações [sort_values], e merges e joins [este último me lembra…. SQL!]. É necessário registrar também que há uma miríade de funções estatísticas para utilizar que são bem interessantes. 

Em Python com Pandas:

# Adicionar nova coluna somando 10 sobre uma coluna existente
meuDataFrame['nova_coluna'] = meuDataFrame['col1'] + 10

# Somar
meuDataFrame.col1.sum()

# Filtrar coluna maior que 100
meuDataFrame.query('(col1 > 100)')

# Eliminar duplicidade
meuDataFrame.col1.unique()

# Ordenar
meuDataFrame.sort_values(by='col1', ascending=False)

Em SQL:

# Adicionar nova coluna somando 10 sobre uma coluna existente
SELECT a.*, a.col1 + 10 as nova_coluna FROM tab a

# Somar
SELECT SUM(col1) FROM tab

# Filtrar coluna maior que 100
SELECT * FROM tab WHERE col1 > 100

# Eliminar duplicidade
SELECT DISTINCT col1 FROM tab

# Ordenar
SELECT * FROM tab ORDER BY col1 DESC

Pandas tem também uma excelente integração com o pacote Numpy para processamento de arrays e matrizes multidimencionais, e o Matplotlib, para visualização de gráficos

A documentação do Pandas está aqui neste link.

Pandas é facil, mas SQL é mais human-readable, na minha opinião. Com SQL a manipulação de dados é extremamente trivial. Ao invés de criar dataframes a partir de outros dataframes para incluir, transformar ou remover colunas, você faz subquery ou transforma na própria cláusula SELECT. Todas as APIs que você pode imaginar para trabalhar um dado existem nos sistemas gerenciadores de banco de dados que suportam o SQL ANSI. Desde funções descritivas simples, como SUM ou COUNT, até funções mais complexas como as Regressões Lineares, Testes de Hipótese e Análises de Variância. Adicionalmente, existem também as funções analíticas [esta última me lembra… Pandas!] e as de machine learning. Com Pandas, bom, para machine learning, teria que utilizar algum outro pacote [como scikit-learn, por exemplo].

Uma Conversa Franca sobre Não Perder Tempo

No final do dia eu prefiro SQL, mas depende.

Eu prefiro Pandas quando manipulo arquivos que não precisam persistir por muito tempo [análises pontuais] e não precisam de integração com outras fontes de dados, e não precisa de tanta performance.

Por exemplo: costumo fazer análises de infraestrutura com centenas de servidores, cada um com centenas métricas, e depois de tomar as conclusões, apago tudo – criar um banco, criar as tabelas, carregar os dados, e só então explorar os dados leva tempo… é melhor fazer esse tipo de análise com Pandas.

Quando preciso de um acabamento mais enterprise, isto é, algo que vai existir por mais tempo, com grande volume de dados, que vai ser utilizado por outras pessoas, que será reutilizado, compartilhado, que precisa de segurança, e claro, extrema performance e escalabilidade, não há dúvidas que SQL, na minha opinião, é melhor.

Tente conhecer os dois: SQL e Pandas. Use Pandas quando for mais conveniente, do contrário use SQL. Você escala mais conhecendo ambos.

3 Funções Super Poderosas para Transformar Dados no Oracle

Se você já se deparou alguma vez com a necessidade de desnormalizar um relacionamento entre duas ou mais tabelas para criar uma lista de strings, ou criar um mapeamento para popular uma estrutura de grafo ou documento JSON, ou ainda converter do Relacional para JSON, você precisa conhecer as funções LISTAGG, JSON_OBJECT e JSON_ARRAYAGG.

Como exemplo considere as seguintes tabelas:

ALUNO (id, nome)
DISCIPLINA (id, nome)
ALUNO_DISCIPLINA (id_aluno, id_disciplina)

A tabela ALUNO_DISCIPLINA é basicamente a tabela de ligação do muitos-para-muitos entre ALUNO e DISCIPLINA, já que um aluno pode cursar várias disciplinas, e cada disciplina pode ter muitos alunos.

Então a seguinte consulta retorna:

SELECT a.nome AS nome_aluno,
       b.nome AS nome_disciplinas
FROM aluno A,
     disciplina B,
     aluno_disciplina C
WHERE a.id = c.id_aluno
  AND b.id = c.id_disciplina
ORDER BY a.nome, b.nome

Transforma os Valores de Linhas Diferentes em uma String separada por Algum Delimitador

Para desnormalizar, basta agrupar pelas colunas que devemos manter como identificador [no meu exemplo nome do aluno] e depois na função LISTAGG coloque a coluna que queremos desnormalizar e o delimitador [neste caso, vírgula]. WITHIN GROUP permite ordenar o resultado:

SELECT a.nome AS nome_aluno,
       LISTAGG(b.nome, ',') WITHIN GROUP (ORDER BY b.nome) AS lista_disciplinas
FROM aluno A,
     disciplina B,
     aluno_disciplina C
WHERE a.id = c.id_aluno
  AND b.id = c.id_disciplina
GROUP BY a.nome ORDER BY a.nome

Mais simples do que parece, certo?! Se você quiser transformar dados no modelo Relacional para um modelo de Grafos, pode usar LISTAGG para ligar as arestas aos nós com apenas uma query. Há outras variantes do LISTAGG na documentação, neste link. No Oracle ela está disponível a partir da versão 11g, e há similares em outras tecnologias.

Na versão 19c foi incluída a opção DISTINCT no LISTAGG, para o caso de você querer eliminar os valores duplicados que são listados com LISTAGG. Veja este exemplo onde eu projeto o nome do departamento e listo ao lado, separado por vírgula, todos os cargos a partir da tabela de funcionários. Como vários funcionários podem ter o mesmo cargo, ele poderá se repetir na lista. Então com distinct nós eliminamos os valores duplicados:

Console do Oracle Live SQL

Transforma Relacional em JSON, usando Atributos Simples ou com Arrays

Voltando ao exemplo dos estudantes, ao invés de apenas desnormalizar, vamos também converter para JSON, e aninhar as disciplinas, pois quero montar um Documento com os alunos e seus respectivos cursos:

SELECT JSON_OBJECT('id'          VALUE a.id,
                   'nome'        VALUE a.nome,
                   'disciplinas' VALUE JSON_ARRAYAGG(b.nome order by b.nome)) JSON_DOCUMENT
FROM aluno A,
     disciplina B,
     aluno_disciplina C
WHERE a.id = c.id_aluno
  AND b.id = c.id_disciplina
GROUP BY a.id, a.nome ORDER BY a.nome

JSON_OBJECT transforma uma coluna em um atributo JSON em um documento, e JSON_ARRAYAGG cria um array para um atributo. Temos como resultado um documento JSON com os códigos dos alunos, nomes e um array com as disciplinas que cada um cursa. Há uma variedade de funções de conversão para JSON no Oracle. Especificamente a JSON_ARRAYAGG você pode verificar a referência neste link. JSON_ARRAYAGG está disponível a partir da versão 12c.

A partir da versão 19c do a função JSON_OBJECT foi simplificada. Basta listar as colunas separadas por vírgula, ou então colocar asterisco para retornar todas as colunas. Veja:

Console do Oracle Live SQL

Para criar um array com JSON_ARRAYAGG utilizando a nova forma do JSON_OBJECT podemos fazer como no exemplo abaixo. Eu retorno o departamento com o nome de todos os funcionários:

Console do Oracle Live SQL

Agora Faz o Contrário: Transforma do JSON para o Relacional

E para converter de JSON para Relacional, utilize NESTED sobre a coluna que representa o documento JSON e as colunas que você quer projetar [também somente na versão 19c]. Eu fiz uma sub-query utilizando o exemplo acima [transformando de Relacional para JSON] e a query superior faz o inverso, transformando o JSON no Relacional:

Console do Oracle Live SQL

Utilizei o Oracle XE 18c para fazer os testes, e o SQL Developer como front-end. Todos são gratuitos e estão disponíveis para download. Para os comandos que funcionam no 19c, utilizei Oracle SQL Live direto na internet [no momento que estou escrevendo este post, a versão 19c está disponível somente no SQL Live]. Update: você pode criar uma conta no free tier da Oracle Cloud (OCI) e utilizar gratuitamente o Oracle Autonomous Database.