O Desafio Técnico
Imagine o seguinte cenário: um grupo de cinco amigos deseja comprar ingressos para o cinema. O sistema precisa identificar todas as opções de cinco assentos consecutivos vazios na mesma fileira. Além de encontrar a disponibilidade, o sistema deve classificar essas opções com base em uma métrica de "melhor lugar", priorizando a proximidade com o centro da sala e uma visão otimizada (geralmente localizada a dois terços da distância da tela).
Para este problema, utilizaremos uma tabela que registra o status de ocupação de cada assento. O objetivo é resolver o problema usando uma única abordagem SQL robusta, utilizando funções de janela (Window Functions).
Estrutura dos Dados
A tabela de entrada, denominada status_assentos_cinema, possui a seguinte estrutura simplificada:
| Coluna | Tipo | Descrição |
|---|---|---|
cod_assento |
string | Identificador do assento (Formato: 'Fila-Coluna', ex: '5-12') |
esta_vendido |
int | Status de ocupação (0: Disponível, 1: Vendido) |
Critérios de Otimização
Para definir o "melhor assento", utilizaremos o cálculo da Distância Euclidiana Ponderada em relação a um ponto ideal na sala. Definimos o ponto de visualização perfeita como:
- Linha ideal: 65% do total de fileiras (fundo da sala).
- Coluna ideal: 50% da largura da fileira (centro).
- Peso: Atribuiremos peso 3 para o deslocamento vertical (fileira) e peso 2 para o horizontal (coluna).
Solução em SQL (Hive/Spark SQL)
A consulta abaixo utiliza CTEs (Common Table Epxressions) para decompor o problema em etapas: extração de coordenadas, identificação de sequências e cálculo de ranqueamento.
WITH base_processada AS (
-- Extrai coordenadas numéricas do identificador de assento
SELECT
cod_assento,
CAST(SPLIT(cod_assento, '-')[0] AS INT) AS num_fila,
CAST(SPLIT(cod_assento, '-')[1] AS INT) AS num_coluna,
esta_vendido
FROM dw_cinema.status_assentos_cinema
),
mapeamento_sequencias AS (
-- Identifica janelas de 5 assentos e verifica disponibilidade
SELECT
cod_assento,
num_fila,
num_coluna,
SUM(CASE WHEN esta_vendido = 0 THEN 1 ELSE 0 END) OVER (
PARTITION BY num_fila
ORDER BY num_coluna ASC
ROWS BETWEEN CURRENT ROW AND 4 FOLLOWING
) AS contagem_vazios,
COLLECT_LIST(cod_assento) OVER (
PARTITION BY num_fila
ORDER BY num_coluna ASC
ROWS BETWEEN CURRENT ROW AND 4 FOLLOWING
) AS lista_assentos,
MAX(num_fila) OVER () AS total_filas,
MAX(num_coluna) OVER (PARTITION BY num_fila) AS total_colunas_fila
FROM base_processada
),
calculo_score_conforto AS (
-- Aplica a fórmula de distância para ranqueamento
SELECT
cod_assento AS assento_inicio,
CONCAT(num_fila, '-', num_coluna, ' até ', num_fila, '-', num_coluna + 4) AS intervalo_reserva,
lista_assentos,
contagem_vazios,
-- Cálculo da distância Euclidiana ponderada em relação ao ponto ideal (0.65, 0.50)
SUM(
(3 * ABS(num_fila - (0.65 * total_filas))) +
(2 * ABS(num_coluna - (0.50 * total_colunas_fila)))
) OVER (
PARTITION BY num_fila
ORDER BY num_coluna ASC
ROWS BETWEEN CURRENT ROW AND 4 FOLLOWING
) AS score_proximidade
FROM mapeamento_sequencias
)
SELECT
assento_inicio,
intervalo_reserva,
lista_assentos,
score_proximidade
FROM calculo_score_conforto
WHERE contagem_vazios = 5
ORDER BY score_proximidade ASC;
Geração de Dados Sintéticos (Python)
Para validar a lógica, podemos utilizar o script Python abaixo para gerar um dataset simulando um cinema com assentos aleatoriamente ocupados.
import pandas as pd
import numpy as np
# Configurações da sala
filas_total = 10
colunas_total = 20
taxa_ocupacao = 0.40
# Gerar coordenadas
dados_cinema = []
for f in range(1, filas_total + 1):
for c in range(1, colunas_total + 1):
dados_cinema.append({
"cod_assento": f"{f}-{c}",
"esta_vendido": 0
})
df_assentos = pd.DataFrame(dados_cinema)
# Simular vendas aleatórias baseadas na frequência
indices_vendidos = np.random.choice(
df_assentos.index,
size=int(taxa_ocupacao * len(df_assentos)),
replace=False
)
df_assentos.loc[indices_vendidos, "esta_vendido"] = 1
print(df_assentos.head(10))
Análise Técnica da Lógica
A solução utiliza a cláusula ROWS BETWEEN CURRENT ROW AND 4 FOLLOWING. Esta técnica é superior a auto-joins, pois percorre os dados em uma única passagem (Single Pass), o que é computacionalmente eficiente em ambientes de Big Data como Hive ou Spark. A verificação contagem_vazios = 5 garante que a janela não inclua assentos vendidos ou que a janela não "quebre" no final de uma fileira, onde o tamanho da janela seria naturalmente menor que 5.