O DBT tem sido utilizado por muitas empresas na área de Dados e acredito que podemos extrair bons insights neste post sobre ele. Esse vai ser um post prático mostrando como o DBT funciona e espero que vocês gostem.
O que é DBT?
DBT significa Data Build Tool e permite que equipes transformem os dados já carregados em seu warehouse através de operações de DML como um simples Select. DBT representa o T no processo de ELT, ou seja, ele não trabalha para extrair e carregar dados mas sim, para transformá-los.
Passo 1: Criando o projeto DBT
Agora, assumimos que o DBT já esteja instalado, mas se não estiver, recomendo consultar este link para mais informações.
Após a instalado, você pode criar um novo projeto usando CLI ou pode clonar este projeto do repositório DBT no Github.
Aqui para este post, vamos usar o modo CLI para criar nosso projeto e também para concluir as próximas etapas. Para criar um novo projeto, execute o comando abaixo no seu terminal.
dbt init
Depois de executar o comando acima, você precisa digitar o nome do projeto e qual warehouse ou banco de dados você vai usar conforme a imagem abaixo.
Para este post, vamos usar o adaptador do postgres. É muito importante que você tenha o banco de dados postgres já instalado ou você pode criar uma imagem postgres usando o docker.
Sobre os adaptadores, o DBT suporta vários deles e você pode conferir aqui.
Criei uma estrutura de tabela e também carreguei os dados simulando dados de uma plataforma de vídeo chamada wetube e vamos utilizá-los para entender como o DBT funciona. Acompanhe a estrutura:
Passo 2: Estrutura e mais sobre DBT
Após executar o comando dbt init para criar o projeto, uma estrutura de pastas e arquivos abaixo será criada.
Não vou falar sobre todos os diretórios do projeto, mas gostaria de focar em dois deles.
Sources
Antes de falarmos sobre os dois diretórios, vamos falar sobre os Sources, são basicamente os dados já carregados em seu warehouse. No processo DBT, as fontes têm o mesmo significado de dados brutos.
Não há pastas que representem dados Sources para este projeto, mas você precisa saber sobre este termo pois vamos configurar tabelas já criadas como Sources para as próximas seções.
Seeds
Seeds é um diretório que oferece um mecanismo interessante e útil para carregar dados estáticos em seu warehouse por meio de arquivos CSV. Se você deseja carregar esses dados, você precisa criar um arquivo CSV neste diretório e executar o comando abaixo.
dbt seed
Para cada campo no arquivo CSV, o DBT irá inferir os tipos e criará tabelas e suas colunas no warehouse ou banco de dados.
Models
O DBT funciona com o paradigma de Model, a ideia principal é que você pode criar modelos através da transformações utilizando instruções SQL baseadas em fontes de tabelas ou modelos existentes
Cada arquivo SQL localizado na pasta de model criará um modelo em seu warehouse ou banco de dados quando o comando abaixo for executado.
dbt run
Lembre-se que um modelo pode ser criado através de uma fonte ou outro modelo e não se preocupe com isso, vou mostrar mais detalhes sobre isso.
Passo 3: Configurando a configuração com o banco de dados
Com o projeto já criado, precisamos configurar a conexão com o banco de dados e aqui neste post vamos usar o postgres como banco de dados.
Depois de inicializar o projeto, vários arquivos são criados e um deles é chamado de profiles.yml.
profiles.yml é o arquivo é responsável por controlar os diferentes perfis/profiles para as diferentes conexões com os bancos de dados, como ambiente de desenvolvimento e produção. Se você notou, não podemos ver este arquivo na imagem acima porque este arquivo é criado fora do projeto para evitar credenciais que sejam confidenciais. Você pode encontrar esse arquivo no diretório ~/.dbt/.
Se você observar, temos um perfil chamado dbt_blog e um destino chamado dev, por padrão, o destino refere-se a dev com as configurações de conexão do banco de dados. Além disso, é possível criar um ou mais perfis e alvos(target), permitindo trabalhar com diferentes ambientes.
Outro detalhe importante é que o perfil dbt_blog deve ser especificado no arquivo dbt_project.yml como um perfil padrão. Nas próximas seções, discutiremos o que é e como o arquivo dbt_project.yml funciona.
Passo 4: Criando o arquivo dbt_project.yml
Todo projeto DBT possui um arquivo dbt_project.yml, você pode configurar informações como nome do projeto, diretórios, perfis e tipo de materialização.
name: 'dbt_blog'
version: '1.0.0'
config-version: 2
profile: 'dbt_blog'
model-paths: ["models"]
analysis-paths: ["analyses"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]
target-path: "target" # directory which will store compiled SQL files
clean-targets: # directories to be removed by `dbt clean`
- "target"
- "dbt_packages"
models:
dbt_blog:
# Config indicated by + and applies to all files under models/example/
mart:
+materialized: table
Observe que o campo de profile foi configurado como o mesmo profile especificado no arquivo profiles.yml e outro detalhe importante é sobre o campo materialized. Aqui foi configurado como um valor table, mas por padrão, é uma view.
O campo materialized permite que você crie modelos como uma tabela ou view em cada execução. Existem outros tipos de materialização, mas não vamos discutir aqui e eu recomendo ver a documentação do dbt.
Passo 5: Criando nosso primeiro modelo
Criando os primeiros arquivos
Vamos mudar um pouco e vamos criar uma subpasta no diretório do model chamada mart e dentro desta pasta vamos criar nossos arquivos .SQL e também outro arquivo importante que ainda não discutimos chamado schema.yml.
Criando o arquivo schema
Os arquivos de schema são usados para mapear fontes e documentar modelos como o nome do modelo, colunas e muito mais. Agora você pode criar um arquivo chamado schema.yml e preencher com as informações abaixo.
version: 2
sources:
- name: wetube
tables:
- name: account
- name: city
- name: state
- name: channel
- name: channel_subs
- name: video
- name: video_like
- name: user_address
models:
- name: number_of_subs_by_channel
description: "Number of subscribers by channel"
columns:
- name: id_channel
description: "Channel's ID"
tests:
- not_null
- name: channel
description: "Channel's Name"
tests:
- not_null
- name: num_of_subs
description: "Number of Subs"
tests:
- not_null
Sources: No campo source você pode incluir tabelas do seu warehouse ou banco de dados que serão utilizadas na criação do modelo.
models: No campo models você pode incluir o nome do modelo, colunas e suas descrições
Criando um modelo
Esta parte é onde podemos criar scripts .SQL que resultarão em nosso primeiro modelo.
Para o primeiro modelo, vamos criar uma instrução SQL para representar um modelo que podemos ver os números de inscritos do canal. Vamos criar um arquivo chamado number_of_subs_by_channel.sql e preenchê-lo com os scripts abaixo.
with source_channel as (
select * from
{{ source('wetube', 'channel') }}
),
source_channel_subs as (
select * from
{{ source('wetube','channel_subs') }}
),
number_of_subs_by_channel as (
select
source_channel.id_channel,
source_channel.name,
count(source_channel_subs.id_subscriber) num_subs
from source_channel_subs
inner join source_channel using (id_channel)
group by 1, 2
)
select * from number_of_subs_by_channel
Entendendo o modelo
Observe que temos vários scripts separados por expressão de tabela comum (CTE) que se torna útil para entender o código.
O DBT permite usar o template Jinja {{ }} trazendo uma maior flexibilidade ao nosso código.
O uso da palavra-chave source dentro do modelo Jinja significa que estamos nos referindo a tabelas de origem. Para referenciar um modelo, você precisa usar a palavra-chave ref.
A última instrução SELECT baseada nas tabelas de origem (source) irá gerar o modelo (model) como tabela no banco de dados.
Executando o nosso primeiro modelo
Execute o comando abaixo para criar nosso primeiro modelo baseado nos arquivos anteriores.
dbt run
Saída
Criando um novo modelo
Imagine que precisamos criar um novo modelo contendo as informações da conta e seus canais. Vamos voltar ao arquivo schema.yml para adicionar esse novo modelo.
- name: account_information
description: "Model containing account information and it's channels"
columns:
- name: id_account
description: "Account ID"
tests:
- not_null
- name: first_name
description: "First name of user's account"
tests:
- not_null
- name: last_name
description: "Last name of user's account"
tests:
- not_null
- name: email
description: "Account's email"
tests:
- not_null
- name: city_name
description: "city's name"
tests:
- not_null
- name: state_name
description: "state's name"
tests:
- not_null
- name: id_channel
description: "channel's Id"
tests:
- not_null
- name: channel_name
description: "channel's name"
tests:
- not_null
- name: channel_creation
description: "Date of creation name"
tests:
- not_null
Agora, vamos criar um novo arquivo SQL e nomeá-lo como account_information.sql e adicionar os scripts abaixo:
with source_channel as (
select * from
{{ source('wetube', 'channel') }}
),
source_city as (
select * from
{{ source('wetube','city') }}
),
source_state as (
select * from
{{ source('wetube','state') }}
),
source_user_address as (
select * from
{{ source('wetube','user_address') }}
),
source_account as (
select * from
{{ source('wetube','account') }}
),
account_info as (
select
account.id_user as id_account,
account.first_name,
account.last_name,
account.email,
city.name as city_name,
state.name as state_name,
channel.id_channel,
channel.name as channel,
channel.creation_date as channel_creation
FROM source_account account
inner join source_channel channel on (channel.id_account = account.id_user)
inner join source_user_address user_address using (id_user)
inner join source_state state using (id_state)
inner join source_city city using (id_city)
)
select * from account_info
Criando nosso último modelo
Para o nosso último modelo, vamos criar um modelo sobre quantas curtidas tem um vídeo. Vamos alterar novamente o schema.yml para descrever e documentar nosso futuro e último modelo.
- name: total_likes_by_video
description: "Model containing total of likes by video"
columns:
- name: id_channel
description: "Channel's Id"
tests:
- not_null
- name: channel
description: "Channel's name"
tests:
- not_null
- name: id_video
description: "Video's Id"
tests:
- not_null
- name: title
description: "Video's Title"
tests:
- not_null
- name: total_likes
description: "Total of likes"
tests:
- not_null
Crie um arquivo chamado total_likes_by_video.sql e coloque o código abaixo:
with source_video as (
select * from
{{ source('wetube','video') }}
),
source_video_like as (
select * from
{{ source('wetube','video_like') }}
),
source_account_info as (
select * from
{{ ref('account_information') }}
),
source_total_like_by_video as (
select source_account_info.id_channel, source_account_info.channel,
source_video.id_video, source_video.title, count(*) as total_likes
FROM source_video_like
inner join source_video using (id_video)
inner join source_account_info using (id_channel)
GROUP BY source_account_info.id_channel,
source_account_info.channel,
source_video.id_video,
source_video.title
ORDER BY total_likes DESC
)
select * from source_total_like_by_video
Executando novamente
Após a criação dos arquivos, vamos executar DBT novamente para criar os novos modelos
dbt run
Saída
Os modelos foram criados no banco de dados e você pode executar instruções select diretamente em seu banco de dados para verificá-lo. Perceba que além dos modelos criados, você pode notar as demais tabelas que foram mapeadas no arquivo schema.yml e que já existiam na estrutura do banco inicial. Lembre-se do mecanismo de criar tabelas estáticas através do diretório Seeds, pode ser uma boa escolha para uma carga inicial.
Modelo: account_information
Modelo: number_of_subs_by_channel
Modelo: total_likes_by_video
Passo 6: DBT Docs
Documentação
Depois de gerados nossos modelos, agora vamos gerar documentos com base nestes. O DBT gera uma documentação completa sobre modelos (models), sources e suas colunas, através de uma página da web.
Gerando as docs
dbt docs generate
Disponibilizando as docs no servidor Web
Após a geração dos documentos, você pode executar o comando abaixo no seu terminal para iniciar um servidor da Web na porta 8080 e consultar a documentação localmente utilizando o seu navegador. Caso o navegador não abra automaticamente, digite o seguinte endereço localhost:8080 no seu navegador.
dbt docs serve
Lineage
Outro detalhe sobre a documentação é que você pode ver através de um Lineage os modelos e suas dependências.
Código no Github
Você pode conferir esse código na nossa página do Github.
Curtiu? Eu espero que tenha gostado!
Comments