Transformar dados com joins e operadores de conjunto

Concluído

Os fluxos de trabalho de engenharia de dados frequentemente exigem a combinação de informação de múltiplos conjuntos de dados. As junções combinam tabelas horizontalmente ao corresponder as linhas em colunas relacionadas, enquanto os operadores de conjunto combinam tabelas verticalmente, acrescentando, intersectando ou excluindo as linhas. Compreender quando usar cada abordagem ajuda-o a construir pipelines de transformação eficientes.

Nesta unidade, aprende a juntar tabelas com base em relações e a aplicar operadores de conjunto para combinar conjuntos de dados com esquemas correspondentes.

Combinar tabelas com junções

As junções fundem linhas de duas tabelas com base numa coluna relacionada. O tipo de junção determina quais as linhas que aparecem no resultado e como as linhas não emparelhadas são tratadas.

Diagrama que explica a combinação de tabelas com junções.

Compreender os tipos de junção

Cada tipo de junção serve um propósito específico na transformação de dados:

Diagrama que mostra os diferentes tipos de junção.

Tipo de junção Description Caso de uso
INNER Retorna apenas linhas correspondentes de ambas as tabelas Liga encomendas a clientes válidos
LEFT Devolve todas as linhas da tabela à esquerda; adiciona NULLs para a tabela direita quando não há correspondência Inclua todos os clientes, mesmo sem encomendas
RIGHT Retorna todas as linhas da tabela direita; adiciona NULLs na tabela esquerda quando não há correspondência encontrada. Inclua todos os departamentos, mesmo sem funcionários
FULL Devolve todas as linhas de ambas as tabelas, adiciona NULLs para não correspondências Combine conjuntos de dados para encontrar lacunas em ambos os lados
SEMI Devolve as linhas da tabela à esquerda que têm correspondências na tabela da direita Filtrar os clientes que fizeram encomendas
ANTI Devolve as linhas da tabela à esquerda que não têm correspondência na tabela da direita Encontre clientes que nunca fizeram encomendas
CROSS Retorna o produto cartesiano de todas as linhas Gerar todas as combinações possíveis

Juntar tabelas com SQL

A JOIN cláusula combina tabelas com base numa condição especificada com ON ou USING. Os seguintes exemplos utilizam tabelas de funcionários e departamentos:

CREATE TEMP VIEW employee(id, name, deptno) AS
    VALUES (105, 'Chloe', 5),
           (103, 'Paul',  3),
           (101, 'John',  1),
           (102, 'Lisa',  2),
           (104, 'Evan',  4),
           (106, 'Amy',   6);

CREATE TEMP VIEW department(deptno, deptname) AS
    VALUES (3, 'Engineering'),
           (2, 'Sales'),
           (1, 'Marketing');

Para alinhar os colaboradores com os seus departamentos, use uma junção interna:

-- Inner join: only employees with matching departments
SELECT e.id, e.name, e.deptno, d.deptname
FROM employee e
INNER JOIN department d ON e.deptno = d.deptno;

Uma junção interna devolve apenas três linhas — funcionários cujos números de departamento existem na tabela de departamentos. Os funcionários dos departamentos 4, 5 e 6 não aparecem porque esses departamentos não estão na tabela dos departamentos.

Quando precisar de incluir todos os colaboradores independentemente das correspondências nos departamentos, use uma junção à esquerda:

-- Left join: all employees, NULL for missing departments
SELECT e.id, e.name, e.deptno, d.deptname
FROM employee e
LEFT JOIN department d ON e.deptno = d.deptno;

Isto devolve todos os seis funcionários. Aqueles sem departamentos correspondentes mostram NULL na coluna deptname.

Junte DataFrames com PySpark

O método do join() PySpark oferece a mesma funcionalidade. O how parâmetro especifica o tipo de junção:

df_employee = spark.table("employee")
df_department = spark.table("department")

# Inner join
df_inner = df_employee.join(
    df_department,
    on=df_employee.deptno == df_department.deptno,
    how="inner"
)
display(df_inner)

Para junções exteriores à esquerda, direita ou completas, altere o parâmetro how.

# Left join: keep all employees
df_left = df_employee.join(
    df_department,
    on="deptno",
    how="left"
)

# Full outer join: keep all rows from both tables
df_full = df_employee.join(
    df_department,
    on="deptno",
    how="full"
)

Quando a coluna de junção tem o mesmo nome em ambos os DataFrames, passe o nome da coluna como uma cadeia. Esta abordagem evita colunas duplicadas no resultado.

Filtrar dados com semi-joins e anti-joins

Semi e anti joins filtram dados com base na existência noutra tabela. Uma semijunção devolve as linhas da tabela da esquerda que têm correspondências, sem incluir colunas da tabela da direita:

-- Find employees who belong to listed departments
SELECT *
FROM employee
LEFT SEMI JOIN department ON employee.deptno = department.deptno;

Um anti join devolve o oposto — linhas sem correspondências:

-- Find employees in departments not listed
SELECT *
FROM employee
LEFT ANTI JOIN department ON employee.deptno = department.deptno;

Em PySpark, use how="semi" ou how="anti":

# Employees with matching departments
df_semi = df_employee.join(df_department, on="deptno", how="semi")

# Employees without matching departments
df_anti = df_employee.join(df_department, on="deptno", how="anti")

Combinar linhas com operadores de conjunto

Os operadores de conjunto funcionam verticalmente, combinando linhas de diferentes consultas com a mesma estrutura de coluna. Ao contrário dos joins, os operadores de conjuntos não correspondem em condições — empilham ou comparam conjuntos de resultados inteiros.

Diagrama que explica como combinar linhas com operadores de conjunto.

Compreender operadores de conjuntos

O Azure Databricks suporta três operadores de conjunto:

Operator Description Comportamento
UNION Combina linhas de ambas as consultas Remove duplicados automaticamente; use ALL para mantê-los
INTERSECT Devolve as linhas que existem em ambas as consultas Apenas as linhas correspondentes aparecem
EXCEPT Devolve linhas da primeira consulta que não estão na segunda Remove duplicados por padrão

Ambas as consultas devem ter o mesmo número de colunas com tipos de dados compatíveis.

Fusão de conjuntos de dados com a UNION

UNION acrescenta linhas de uma consulta para outra. Por padrão, remove duplicados.

-- Combine active and archived customers (no duplicates)
SELECT customer_id, name, email FROM active_customers
UNION
SELECT customer_id, name, email FROM archived_customers;

Para preservar todas as linhas, incluindo as que são duplicadas, use UNION ALL:

-- Combine all rows, keeping duplicates
SELECT customer_id, name, email FROM active_customers
UNION ALL
SELECT customer_id, name, email FROM archived_customers;

Use UNION ALL quando souber que não há duplicados ou quando os duplicados são significativos. Funciona melhor porque salta a etapa de deduplicação.

Utiliza-se union() no PySpark para combinar DataFrames. Note que o union() do PySpark comporta-se como UNION ALL—ele mantém duplicados:

df_active = spark.table("active_customers")
df_archived = spark.table("archived_customers")

# Combine both tables (keeps duplicates)
df_combined = df_active.union(df_archived)

# Remove duplicates if needed
df_distinct = df_active.union(df_archived).distinct()

Encontre linhas comuns com INTERSECT

INTERSECT devolve apenas as linhas que aparecem em ambas as consultas. Isto ajuda a identificar dados sobrepostos:

-- Find customers in both active and archived tables
SELECT customer_id FROM active_customers
INTERSECT
SELECT customer_id FROM archived_customers;

Esta consulta identifica clientes que de alguma forma aparecem em ambas as tabelas — útil para verificações de qualidade de dados ou identificação de registos que necessitam de reconciliação.

Excluir linhas com EXCEPT

EXCEPT devolve linhas da primeira consulta que não existem na segunda. Isto ajuda a identificar lacunas ou registos únicos:

-- Find active customers not in the archived table
SELECT customer_id FROM active_customers
EXCEPT
SELECT customer_id FROM archived_customers;

Também pode usar MINUS como sinónimo para EXCEPT:

-- Same result using MINUS syntax
SELECT customer_id FROM active_customers
MINUS
SELECT customer_id FROM archived_customers;

Tal como UNION, tanto INTERSECT como EXCEPT removem duplicados por defeito. Adicione ALL para preservar duplicados quando necessário.

Sugestão

Ao encadear operações de conjuntos, lembre-se de que INTERSECT tem precedência superior a UNION e EXCEPT. Use parênteses para controlar a ordem de avaliação.

Escolha entre junções e operadores de conjunto

A escolha entre joins e operadores de conjunto depende do teu objetivo de transformação. As junções combinam colunas de tabelas relacionadas horizontalmente, enquanto os operadores de conjunto combinam linhas verticalmente.

Use joins quando precisar:

  • Enriquecer registos adicionando colunas de tabelas relacionadas
  • Registos de correspondência baseados em relações-chave
  • Filtrar dados com base na existência noutra tabela (junções semi/anti)

Use operadores de conjunto quando precisar:

  • Anexar registos de múltiplas fontes com esquemas idênticos
  • Encontre registos comuns entre conjuntos de dados
  • Identificar registos únicos para um conjunto de dados

Ambas as abordagens constituem a base de pipelines eficazes de transformação de dados. Na unidade seguinte, explora como carregar dados transformados em tabelas de alvo dentro do Unity Catalog.