0Pricing
Coding Interview Prep · Aula

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 acesso
  • login_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-07

A 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ência
  • MAX(login_date) — último dia da sequência
  • COUNT(*) — 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 grp

Por 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

  1. Detectando dias consecutivos no calendário
  2. Maior sequência por usuário
  3. N linhas consecutivas que atendem a uma condição
  4. Sequência ativa atual até hoje
← Voltar para Coding Interview Prep