0Pricing
Coding Interview Prep · Lezione

Rilevare giorni consecutivi del calendario

Usare l'aritmetica delle date e i numeri di riga per trovare sequenze ininterrotte di giorni

Rilevare giorni consecutivi del calendario è una lezione Coding Interview Prep gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Coding Interview Prep, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Coding Interview Prep include 4 lezioni in totale.

L'impostazione del colloquio

Gli intervistatori apprezzano le domande sulle sequenze perché rivelano se si comprendono davvero le funzioni finestra e l'aritmetica delle date. Una traccia tipica è: "Data una tabella con le date di accesso degli utenti, trovare ogni sequenza continua di giorni di calendario consecutivi."

L'istinto più immediato è usare un self-join per confrontare ogni riga con quella successiva, ma questa soluzione diventa molto onerosa sulle tabelle grandi ed è scomoda da esprimere. La risposta professionale usa la tecnica gaps-and-islands. In questa lezione imparerà a rilevare in modo chiaro i giorni consecutivi usando numeri di riga e sottrazioni tra date.

I dati di esempio

In questa lezione si utilizza una tabella logins con una riga per ogni utente e per ogni giorno in cui era attivo. Si presume che i duplicati siano già stati rimossi (un accesso per giorno di calendario).

  • user_id — l'utente che ha effettuato l'accesso
  • login_date — un valore DATE

Per l'utente 1 le date sono 1, 2 e 3 gennaio, poi c'è una lacuna, quindi 6 e 7 gennaio. Ci si aspettano due sequenze: una di 3 giorni e una di 2 giorni.

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

L'intuizione fondamentale

Ecco il trucco che permette di risolvere ogni problema sui giorni consecutivi. Se si ordinano le righe per data e si assegna a ciascuna un numero di riga progressivo, durante una sequenza di giorni consecutivi la differenza tra la data e il numero di riga rimane costante.

Perché? Sia la data sia il numero di riga aumentano esattamente di 1 a ogni giorno consecutivo, quindi la loro differenza non cambia. Quando compare una lacuna, la data aumenta bruscamente mentre il numero di riga no: la costante cambia e inizia un nuovo gruppo.

Osservare la differenza

Vediamo il procedimento a mano per l'utente 1. ROW_NUMBER conta 1, 2, 3, 4, 5. Si sottragga il numero di riga, considerato come numero di giorni, dalla data e si osservi il risultato.

  • 1 gennaio − 1 = 31 dicembre
  • 2 gennaio − 2 = 31 dicembre
  • 3 gennaio − 3 = 31 dicembre
  • 6 gennaio − 4 = 2 gennaio
  • 7 gennaio − 5 = 2 gennaio

Le prime tre righe condividono il 31 dicembre; le ultime due condividono il 2 gennaio. Questo valore di ancoraggio condiviso è la nostra chiave di gruppo.

Aggiungere ROW_NUMBER

Il primo passaggio concreto consiste nell'assegnare un numero di riga, partizionato per utente affinché le sequenze non oltrepassino mai i confini tra utenti, e ordinato per data.

PARTITION BY user_id riavvia il contatore per ogni utente; ORDER BY login_date garantisce che la sequenza segua il calendario.

SELECT
  user_id,
  login_date,
  ROW_NUMBER() OVER (
    PARTITION BY user_id
    ORDER BY login_date
  ) AS rn
FROM logins;

Calcolare l'ancoraggio del gruppo

Ora si sottraggano rn giorni da login_date. In PostgreSQL è possibile sottrarre direttamente a una data un numero intero di giorni. Il risultato è l'ancoraggio costante che identifica ogni isola.

Si noti che non è possibile fare riferimento all'alias rn nello stesso SELECT in cui viene definito: è quindi necessario racchiudere prima la query precedente in una CTE o in una subquery.

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;

Raggruppare le isole

Disponendo dell'ancoraggio, ogni sequenza consecutiva condivide lo stesso valore grp. Si raggruppi per user_id e grp, quindi si calcolino le aggregazioni per ottenere l'inizio, la fine e la durata di ogni sequenza.

  • MIN(login_date) — il primo giorno della sequenza
  • MAX(login_date) — l'ultimo giorno della sequenza
  • COUNT(*) — il numero di giorni della sequenza
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;

Differenze tra dialetti

La sintassi dell'aritmetica delle date varia. Lo menzioni nei colloqui per dimostrare la propria ampiezza di competenze.

  • PostgreSQL: login_date - rn (data meno un numero intero di giorni)
  • MySQL: DATE_SUB(login_date, INTERVAL rn DAY)
  • SQL Server: DATEADD(day, -rn, login_date)

La logica è identica; cambiano soltanto i nomi delle funzioni. Il modello mentale portabile consiste nello "spostare ogni data indietro di una quantità pari alla sua posizione, in modo che una sequenza continua si riduca a un'unica costante".

-- SQL Server version of the anchor
DATEADD(day, -1 * rn, login_date) AS grp

Perché non usare un self-join?

Un intervistatore potrebbe chiedere perché si è evitato un self-join come l1.login_date = l2.login_date + 1. Le motivazioni da fornire sono:

  • Un self-join verifica soltanto l'adiacenza, non l'intera sequenza: per ricostruire le sequenze complete serve comunque un raggruppamento.
  • Può generare molte combinazioni e ha complessità O(n²) in assenza di buoni indici.
  • Il metodo con i numeri di riga esegue un'unica scansione ordinata ed è molto più scalabile.

Le funzioni finestra sono la risposta moderna e attesa per questi problemi.

Gestire i duplicati

L'intera tecnica presuppone una riga per utente al giorno. Se la sorgente contiene più accessi nello stesso giorno, due righe con la stessa data ricevono numeri di riga diversi, falsando l'ancoraggio.

Per gestire il caso, deduplichi prima i dati: converta i timestamp in date e utilizzi DISTINCT, oppure usi DENSE_RANK sulla data al posto di ROW_NUMBER, così le date uguali condividono lo stesso numero.

WITH days AS (
  SELECT DISTINCT user_id, login_ts::date AS login_date
  FROM raw_logins
)
SELECT * FROM days;

La soluzione completa

Mettendo insieme tutti gli elementi si ottiene una risposta chiara, pronta per un colloquio, che elenca ogni sequenza di giorni consecutivi con inizio, fine e durata.

Questo stesso scheletro — deduplicare, numerare, sottrarre, raggruppare — risolve quasi ogni domanda sulle sequenze "consecutive" che potrebbe essere sottoposta.

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 rapida

Verifichi di aver compreso il trucco fondamentale.

Riepilogo

Ha appreso il modello fondamentale per i giorni consecutivi:

  • Deduplicare fino a ottenere una riga per utente al giorno.
  • ROW_NUMBER ordinato per data e partizionato per utente.
  • Sottrarre il numero di riga dalla data per ottenere un ancoraggio costante per ogni sequenza.
  • GROUP BY sull'ancoraggio e aggregazioni per ottenere inizio, fine e durata.

Questo scheletro di gaps-and-islands scala con un'unica scansione e supera i self-join. Nella prossima lezione verrà utilizzato per calcolare la sequenza più lunga per utente.

Domande Frequenti

La lezione «Rilevare giorni consecutivi del calendario» è gratuita?

Sì — il testo completo di «Rilevare giorni consecutivi del calendario» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Coding Interview Prep, passa a CoddyKit PRO. Il corso Coding Interview Prep include 4 lezioni in totale.

Cosa imparerò in «Rilevare giorni consecutivi del calendario»?

Usare l'aritmetica delle date e i numeri di riga per trovare sequenze ininterrotte di giorni Eserciti Coding Interview Prep con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Coding Interview Prep?

Non è richiesta alcuna esperienza precedente. Coding Interview Prep su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Rilevare giorni consecutivi del calendario»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Coding Interview Prep?

Sì. Ogni lezione Coding Interview Prep include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Rilevare giorni consecutivi del calendario
  2. La serie consecutiva più lunga per utente
  3. N righe consecutive che soddisfano una condizione
  4. Serie consecutiva attiva aggiornata a oggi
← Torna a Coding Interview Prep