Registrering af fortløbende kalenderdage
Brug datoregning og rækkenumre til at finde ubrudte serier af dage
Registrering af fortløbende kalenderdage er en gratis Forberedelse til kodeinterviews-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Forberedelse til kodeinterviews, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Forberedelse til kodeinterviews-kurset indeholder 4 lektioner i alt.
Interviewopgaven
Interviewere holder af spørgsmål om sammenhængende serier, fordi de viser, om du virkelig forstår vinduesfunktioner og datoberegning. En typisk opgaveformulering er: "Givet en tabel med brugeres login-datoer, find hver ubrudt serie af sammenhængende kalenderdage."
Den umiddelbare tanke er et selvjoin, der sammenligner hver række med den næste, men det bliver hurtigt voldsomt på store tabeller og er besværligt at udtrykke. Det professionelle svar bruger teknikken med huller og øer. I denne lektion lærer du at finde sammenhængende dage på en enkel måde med rækkenumre og subtraktion af datoer.
Eksempeldataene
Gennem hele lektionen bruger vi en tabel med navnet logins, hvor der er én række pr. bruger pr. dag, hvor brugeren var aktiv. Det antages, at dubletter allerede er fjernet (ét login pr. kalenderdag).
user_id— hvem der loggede indlogin_date— en DATE-værdi
For bruger 1 er datoerne 1., 2. og 3. januar, derefter et hul og så 6. og 7. januar. Vi forventer to serier: en serie på 3 dage og en serie på 2 dage.
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-07Den centrale indsigt
Her er det greb, der løser alle problemer med sammenhængende dage. Hvis du ordner rækkerne efter dato og tildeler hver af dem et fortløbende rækkenummer, er forskellen mellem datoen og rækkenummeret konstant for enhver serie af sammenhængende dage.
Hvorfor? Både datoen og rækkenummeret øges præcis med 1 for hver sammenhængende dag, så deres forskel ændrer sig ikke. Når der opstår et hul, springer datoen frem, mens rækkenummeret ikke gør – dermed brydes konstanten, og en ny gruppe begynder.
Se forskellen
Lad os gennemgå det manuelt for bruger 1. ROW_NUMBER tæller 1, 2, 3, 4, 5. Træk rækkenummeret (som antal dage) fra datoen, og se resultatet.
- 1. jan. − 1 = 31. dec.
- 2. jan. − 2 = 31. dec.
- 3. jan. − 3 = 31. dec.
- 6. jan. − 4 = 2. jan.
- 7. jan. − 5 = 2. jan.
De første tre deler 31. dec., mens de sidste to deler 2. jan. Denne fælles forankringsværdi er vores grupperingsnøgle.
Tilføjelse af ROW_NUMBER
Det første konkrete trin er at tilføje et rækkenummer, opdelt pr. bruger, så serier aldrig krydser brugergrænser, og sorteret efter datoen.
PARTITION BY user_id genstarter tælleren for hver bruger; ORDER BY login_date sikrer, at rækkefølgen følger kalenderen.
SELECT
user_id,
login_date,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY login_date
) AS rn
FROM logins;Beregning af gruppeforankringen
Træk nu rn dage fra login_date. I PostgreSQL kan du direkte trække et heltalsantal dage fra en dato. Resultatet er den konstante forankring, der identificerer hver ø.
Bemærk, at vi ikke kan referere til aliaset rn i den samme SELECT, som definerer det – derfor indlejrer vi først den foregående forespørgsel i en CTE eller en underforespørgsel.
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;Gruppering af øerne
Når forankringen er på plads, har hver sammenhængende serie den samme værdi i grp. Gruppér efter user_id og grp, og aggregér derefter for at få begyndelsen, slutningen og længden af hver serie.
MIN(login_date)— seriens første dagMAX(login_date)— seriens sidste dagCOUNT(*)— antal dage i serien
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;Forskelle mellem SQL-dialekter
Syntaksen for datoberegning varierer. Nævn dette i interviews for at vise din bredde.
- PostgreSQL:
login_date - rn(dato minus hele dage) - MySQL:
DATE_SUB(login_date, INTERVAL rn DAY) - SQL Server:
DATEADD(day, -rn, login_date)
Logikken er identisk; kun funktionsnavnene ændres. Den portabile mentale model er "forskyd hver dato bagud med dens placering, så en ubrudt serie samles til én konstant."
-- SQL Server version of the anchor
DATEADD(day, -1 * rn, login_date) AS grpHvorfor ikke et selvjoin?
En interviewer kan spørge, hvorfor du undgik et selvjoin som l1.login_date = l2.login_date + 1. Du kan give disse grunde:
- Et selvjoin tester kun sammenhæng, ikke hele serien – det kræver stadig gruppering at samle komplette serier.
- Det kan give mange kombinationer og har kompleksiteten O(n²) uden gode indekser.
- Metoden med rækkenumre er én sorteret gennemgang og skalerer langt bedre.
Vinduesfunktioner er det moderne og forventede svar på disse problemer.
Beskyttelse mod dubletter
Hele teknikken forudsætter én række pr. bruger pr. dag. Hvis kilden indeholder flere login pr. dag, får to rækker på samme dato forskellige rækkenumre, hvilket ødelægger forankringen.
Undgå det ved først at fjerne dubletter – konvertér tidsstempler til datoer og brug DISTINCT, eller brug DENSE_RANK på datoen i stedet for ROW_NUMBER, så ens datoer får samme nummer.
WITH days AS (
SELECT DISTINCT user_id, login_ts::date AS login_date
FROM raw_logins
)
SELECT * FROM days;Den komplette løsning
Når alle dele samles, får du et rent svar, der er klar til interviewet, og som viser hver serie af sammenhængende dage med begyndelse, slutning og længde.
Den samme grundstruktur – fjern dubletter, nummerér, træk fra, gruppér – løser næsten alle spørgsmål om "sammenhængende" værdier, du får stillet.
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;Hurtigt tjek
Tjek, om du har forstået det centrale greb.
Opsummering
Du har lært det grundlæggende mønster for sammenhængende dage:
- Fjern dubletter, så der kun er én række pr. bruger pr. dag.
- ROW_NUMBER sorteret efter dato og partitioneret efter bruger.
- Træk fra: Træk rækkenummeret fra datoen for at få en konstant forankring pr. serie.
- GROUP BY forankringen, og aggregér for at få begyndelse, slutning og længde.
Denne grundstruktur for huller og øer skalerer i én gennemgang og er bedre end selvjoins. Dernæst bruger du den til at beregne den længste serie pr. bruger.
Lær Forberedelse til kodeinterviews med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 90
- Lektioner
- 360
Ofte stillede spørgsmål
Er lektionen “Registrering af fortløbende kalenderdage” gratis?
Ja — hele teksten til “Registrering af fortløbende kalenderdage” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Forberedelse til kodeinterviews-kurset, skal du opgradere til CoddyKit PRO. Forberedelse til kodeinterviews-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Registrering af fortløbende kalenderdage”?
Brug datoregning og rækkenumre til at finde ubrudte serier af dage Du øver dig i Forberedelse til kodeinterviews med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Forberedelse til kodeinterviews?
Der kræves ingen tidligere erfaring. Forberedelse til kodeinterviews på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.
Hvor lang tid tager lektionen “Registrering af fortløbende kalenderdage”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Forberedelse til kodeinterviews-lektion?
Ja. Alle Forberedelse til kodeinterviews-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Registrering af fortløbende kalenderdage
- Længste serie pr. bruger
- N sammenhængende rækker, der opfylder en betingelse
- Aktuel aktiv serie pr. i dag