Detectando dias consecutivos no calendário
Use aritmética de datas e números de linha para encontrar sequências ininterruptas de dias.
Detectando dias consecutivos no calendário é uma aula grátis de Coding Interview Prep no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Coding Interview Prep, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Coding Interview Prep inclui 4 aulas no total.
A configuração da entrevista
Entrevistadores adoram perguntas sobre sequências porque elas revelam se você realmente entende funções de janela e aritmética de datas. Um enunciado típico: "Dada uma tabela com as datas de acesso dos usuários, encontre cada sequência ininterrupta de dias consecutivos do calendário."
A abordagem ingênua é fazer uma autojunção comparando cada linha com a seguinte, mas isso explode em tabelas grandes e é difícil de expressar. A resposta profissional usa a técnica de lacunas e ilhas. Nesta lição, você aprenderá a detectar dias consecutivos de forma clara, usando números de linha e subtração de datas.
Os dados de exemplo
Ao longo desta lição, usamos uma tabela logins com uma linha por usuário para cada dia em que esteve ativo. Presume-se que as duplicatas já tenham sido removidas (um acesso por dia do calendário).
user_id— quem fez acessologin_date— um valor DATE
Para o usuário 1, as datas são 1º, 2 e 3 de janeiro, depois há uma lacuna e, em seguida, 6 e 7 de janeiro. Esperamos duas sequências: uma de 3 dias e outra de 2 dias.
SELECT * FROM logins ORDER BY user_id, login_date;
-- user_id | login_date
-- 1 | 2024-01-01
-- 1 | 2024-01-02
-- 1 | 2024-01-03
-- 1 | 2024-01-06
-- 1 | 2024-01-07A ideia central
Aqui está o truque que resolve qualquer problema de dias consecutivos. Se você ordenar as linhas por data e atribuir a cada uma um número de linha sequencial, então, em qualquer sequência de dias consecutivos, a diferença entre a data e o número de linha permanece constante.
Por quê? Tanto a data quanto o número de linha aumentam exatamente 1 a cada dia consecutivo, portanto a diferença não muda. Quando surge uma lacuna, a data avança, mas o número de linha não — quebrando a constante e iniciando um novo grupo.
Visualizando a diferença
Vamos percorrer o exemplo manualmente para o usuário 1. ROW_NUMBER conta 1, 2, 3, 4, 5. Subtraia o número de linha (em dias) da data e observe o resultado.
- 1º de janeiro − 1 = 31 de dezembro
- 2 de janeiro − 2 = 31 de dezembro
- 3 de janeiro − 3 = 31 de dezembro
- 6 de janeiro − 4 = 2 de janeiro
- 7 de janeiro − 5 = 2 de janeiro
Os três primeiros compartilham 31 de dezembro; os dois últimos compartilham 2 de janeiro. Esse valor de âncora compartilhado é nossa chave de agrupamento.
Adicionando ROW_NUMBER
O primeiro passo concreto é anexar um número de linha, particionado por usuário para que as sequências nunca atravessem os limites entre usuários, e ordenado pela data.
PARTITION BY user_id reinicia o contador para cada usuário; ORDER BY login_date garante que a sequência siga o calendário.
SELECT
user_id,
login_date,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY login_date
) AS rn
FROM logins;Calculando a âncora de agrupamento
Agora subtraia rn dias de login_date. No PostgreSQL, você pode subtrair diretamente de uma data uma quantidade inteira de dias. O resultado é a âncora constante que identifica cada ilha.
Observe que não podemos referenciar o nome alternativo rn na mesma consulta SELECT que o define — por isso, primeiro envolvemos a consulta anterior em uma CTE ou subconsulta.
WITH numbered AS (
SELECT
user_id,
login_date,
ROW_NUMBER() OVER (
PARTITION BY user_id ORDER BY login_date
) AS rn
FROM logins
)
SELECT
user_id,
login_date,
login_date - rn AS grp
FROM numbered;Agrupando as ilhas
Com a âncora em mãos, toda sequência consecutiva compartilha o mesmo valor de grp. Agrupe por user_id e grp, depois agregue para obter o início, o fim e o comprimento de cada sequência.
MIN(login_date)— primeiro dia da sequênciaMAX(login_date)— último dia da sequênciaCOUNT(*)— número de dias da sequência
WITH numbered AS (
SELECT user_id, login_date,
ROW_NUMBER() OVER (
PARTITION BY user_id ORDER BY login_date
) AS rn
FROM logins
)
SELECT
user_id,
MIN(login_date) AS streak_start,
MAX(login_date) AS streak_end,
COUNT(*) AS streak_len
FROM numbered
GROUP BY user_id, login_date - rn
ORDER BY user_id, streak_start;Diferenças entre dialetos
A sintaxe da aritmética de datas varia. Mencione isso nas entrevistas para demonstrar sua amplitude.
- PostgreSQL:
login_date - rn(data menos dias inteiros) - MySQL:
DATE_SUB(login_date, INTERVAL rn DAY) - SQL Server:
DATEADD(day, -rn, login_date)
A lógica é idêntica; apenas os nomes das funções mudam. O modelo mental portável é: "desloque cada data para trás pela sua posição, para que uma sequência contínua se reduza a uma única constante".
-- SQL Server version of the anchor
DATEADD(day, -1 * rn, login_date) AS grpPor que não usar uma autojunção
Um entrevistador pode perguntar por que você evitou uma autojunção como l1.login_date = l2.login_date + 1. Eis os motivos:
- Uma autojunção testa apenas a adjacência, não a sequência completa — ainda é necessário agrupar para montar sequências inteiras.
- Ela pode gerar combinações excessivas e ter complexidade O(n²) sem bons índices.
- O método do número de linha faz uma única passagem ordenada e é muito mais escalável.
Funções de janela são a resposta moderna e esperada para esses problemas.
Protegendo-se contra duplicatas
Toda a técnica pressupõe uma linha por usuário por dia. Se a fonte tiver vários acessos no mesmo dia, duas linhas na mesma data receberão números de linha diferentes, corrompendo a âncora.
Defenda-se removendo as duplicatas primeiro — converta os registros de data e hora em datas e use DISTINCT, ou use DENSE_RANK na data em vez de ROW_NUMBER, para que datas iguais compartilhem o mesmo número.
WITH days AS (
SELECT DISTINCT user_id, login_ts::date AS login_date
FROM raw_logins
)
SELECT * FROM days;A solução completa
Ao reunir todas as partes, obtemos uma resposta clara e pronta para entrevistas, que lista cada sequência de dias consecutivos com seu início, fim e comprimento.
Esse mesmo esqueleto — remover duplicatas, numerar, subtrair, agrupar — resolve praticamente qualquer pergunta sobre "consecutivo" que apresentarem a você.
WITH days AS (
SELECT DISTINCT user_id, login_ts::date AS login_date
FROM raw_logins
),
numbered AS (
SELECT user_id, login_date,
ROW_NUMBER() OVER (
PARTITION BY user_id ORDER BY login_date
) AS rn
FROM days
)
SELECT user_id,
MIN(login_date) AS streak_start,
MAX(login_date) AS streak_end,
COUNT(*) AS streak_len
FROM numbered
GROUP BY user_id, login_date - rn
ORDER BY user_id, streak_start;Verificação rápida
Teste sua compreensão do truque central.
Recapitulação
Você aprendeu o padrão fundamental de dias consecutivos:
- Remover duplicatas para manter uma linha por usuário por dia.
- ROW_NUMBER ordenado por data e particionado por usuário.
- Subtrair o número de linha da data para obter uma âncora constante por sequência.
- GROUP BY a âncora e agregue para obter início, fim e comprimento.
Esse esqueleto de lacunas e ilhas escala em uma única passagem e supera as autojunções. Em seguida, você o usará para calcular a sequência mais longa por usuário.
Perguntas Frequentes
A aula “Detectando dias consecutivos no calendário” é grátis?
Sim — o texto completo de “Detectando dias consecutivos no calendário” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Coding Interview Prep, atualize para CoddyKit PRO. O curso de Coding Interview Prep inclui 4 aulas no total.
O que vou aprender em “Detectando dias consecutivos no calendário”?
Use aritmética de datas e números de linha para encontrar sequências ininterruptas de dias. Você pratica Coding Interview Prep com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Coding Interview Prep?
Nenhuma experiência prévia é necessária. Coding Interview Prep no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.
Quanto tempo leva a aula “Detectando dias consecutivos no calendário”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Coding Interview Prep?
Sim. Cada aula de Coding Interview Prep inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Detectando dias consecutivos no calendário
- Maior sequência por usuário
- N linhas consecutivas que atendem a uma condição
- Sequência ativa atual até hoje