0Pricing
Coding Interview Prep · 강의

사용자별 최장 연속 기록

각 그룹 안에서 최대로 연속된 실행 길이를 계산합니다.

사용자별 최장 연속 기록은(는) CoddyKit의 무료 Coding Interview Prep 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Coding Interview Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

문제

연속 날짜 감지에 이어 자주 나오는 질문은 다음과 같습니다. “각 사용자에게 연속해서 활동한 날짜 중 가장 긴 구간은 무엇인가요?” 제품 및 성장 팀은 참여도를 측정하기 위해 이 질문을 끊임없이 사용합니다.

이제 각 구간을 식별하는 방법은 알고 있습니다. 새로 해야 할 일은 사용자별로 최대 구간 길이를 찾고, 흔히 그 최장 구간의 날짜도 함께 반환하는 것입니다. 이 과정은 공백과 구간 뼈대 위에 바로 구축됩니다.

구간 생성기 되짚기

이전 과정에서는 login_date - ROW_NUMBER()를 구간 기준값으로 사용해 구간별로 그룹화했습니다. 한 사용자는 여러 구간을 가질 수 있으므로, 먼저 구간마다 한 행을 계산한 뒤 사용자마다 한 행으로 줄입니다.

다음의 두 단계 계획을 기억하세요. 먼저 구간을 만들고, 그다음 구간을 집계합니다.

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 - rn AS grp
FROM numbered;

구간당 한 행

각 구간을 길이와 날짜 범위를 담은 하나의 요약 행으로 통합합니다. 사용자와 기준값으로 그룹화하고 지표를 계산합니다.

이 CTE의 이름을 islands로 지정하면 다음 단계에서 깔끔하게 읽어 올 수 있습니다.

WITH numbered AS (
  SELECT user_id, login_date,
    ROW_NUMBER() OVER (
      PARTITION BY user_id ORDER BY login_date
    ) AS rn
  FROM logins
),
islands AS (
  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
)
SELECT * FROM islands;

간단한 답: MAX 길이

면접관이 길이만 원한다면 마지막 단계는 한 줄이면 됩니다. 사용자별로 구간을 그룹화하고 최대 길이를 구합니다.

시작 날짜와 끝 날짜가 필요하지 않을 때 가장 깔끔한 답입니다.

-- ...numbered and islands CTEs as before...
SELECT
  user_id,
  MAX(streak_len) AS longest_streak
FROM islands
GROUP BY user_id
ORDER BY user_id;

날짜도 반환하기

면접관이 “그 연속 구간이 언제였는지도 보여 주세요”라고 덧붙이는 경우가 많습니다. 단순한 MAX만으로는 어떤 구간이 선택되었는지 알 수 없습니다. 사용자별로 구간의 순위를 매긴 뒤 1위를 남겨야 합니다.

길이 내림차순으로 정렬한 ROW_NUMBER를 사용하면 각 사용자의 최장 구간이 1위를 차지합니다. 동률을 결정할 기준도 추가하여 결과가 항상 같은 순서로 나오게 하세요.

ROW_NUMBER() OVER (
  PARTITION BY user_id
  ORDER BY streak_len DESC, streak_start ASC
) AS rnk

순위 매기기 및 필터링

순위 계산을 CTE로 감싼 다음 rnk = 1만 남깁니다. 윈도 함수를 WHERE에서 직접 필터링할 수 없으므로 이 추가 단계가 반드시 필요합니다.

WITH numbered AS (
  SELECT user_id, login_date,
    ROW_NUMBER() OVER (
      PARTITION BY user_id ORDER BY login_date
    ) AS rn
  FROM logins
),
islands AS (
  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
),
ranked AS (
  SELECT *,
    ROW_NUMBER() OVER (
      PARTITION BY user_id
      ORDER BY streak_len DESC, streak_start
    ) AS rnk
  FROM islands
)
SELECT user_id, streak_start, streak_end, streak_len
FROM ranked
WHERE rnk = 1;

동률 처리에서 RANK와 ROW_NUMBER 비교

사용자에게 최대 길이가 같은 연속 구간이 두 개 있고 면접관이 두 구간을 모두 반환하기를 원한다면 어떻게 해야 할까요? ROW_NUMBER를 RANK로 바꾸고 rnk = 1을 유지하세요.

  • ROW_NUMBER — 사용자마다 정확히 하나의 결과를 선택합니다. 동률 결정 기준을 추가하지 않으면 동률일 때 임의로 선택됩니다.
  • RANK — 가장 긴 구간이 동률이면 모두 1위를 공유하며 모두 남습니다.

어떤 동작을 원하는지 명확히 하세요. 이는 예외 상황까지 주의 깊게 고려했다는 신호가 됩니다.

RANK() OVER (
  PARTITION BY user_id
  ORDER BY streak_len DESC
) AS rnk  -- keep all rnk = 1

예제로 살펴보기

사용자 7이 1월 1일부터 4일까지 로그인하고, 이어서 1월 10일부터 11일까지, 그다음 1월 20일부터 23일까지 로그인했다고 가정해 보겠습니다. 길이가 각각 4, 2, 4인 세 구간입니다. 최장 길이는 4이고 동률이 있습니다.

  • ROW_NUMBER와 동률 결정 기준 streak_start를 사용하면 1월 1일부터 4일까지의 구간만 반환됩니다.
  • RANK를 사용하면 1월 1일부터 4일까지와 1월 20일부터 23일까지의 두 구간이 모두 반환됩니다.

이 내용을 소리 내어 설명하면 중복 상황을 고려해 추론했음을 보여 줄 수 있습니다.

로그인 기록이 없는 사용자 처리

면접관은 다음과 같이 물을 수 있습니다. "한 번도 로그인하지 않은 사용자는 어떻게 하나요?" 이러한 사용자는 logins에 행이 없으므로 결과에서 사라집니다. 이들을 연속 기록이 0인 상태로 표시해야 한다면 전체 users 테이블을 LEFT JOIN하고 COALESCE를 사용합니다.

SELECT u.user_id,
  COALESCE(MAX(i.streak_len), 0) AS longest_streak
FROM users u
LEFT JOIN islands i ON i.user_id = u.user_id
GROUP BY u.user_id;

성능 참고 사항

이 패턴은 데이터에 대해 정렬된 단일 순회와 그룹화를 수행합니다. 빠르게 유지하려면 다음을 따르십시오.

  • (user_id, login_date)에 인덱스를 만들어 윈도 함수의 ORDER BY가 정렬을 피하도록 합니다.
  • 원본에 하루에 여러 이벤트가 있다면 일찍 중복을 제거합니다.
  • ORDER BY에서 login_date를 함수로 감싸지 마십시오. 인덱스 사용이 막힐 수 있습니다.

매우 큰 테이블에서는 이 방식이 어떤 자체 조인 방식보다도 훨씬 빠르게 동작합니다.

면접 답변 완성본

각 사용자의 날짜와 함께 가장 긴 연속 기록을 반환하는 완성된 쿼리입니다 — 화이트보드에 작성할 버전입니다.

WITH numbered AS (
  SELECT user_id, login_date,
    ROW_NUMBER() OVER (
      PARTITION BY user_id ORDER BY login_date
    ) AS rn
  FROM logins
),
islands AS (
  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
),
ranked AS (
  SELECT *,
    ROW_NUMBER() OVER (
      PARTITION BY user_id
      ORDER BY streak_len DESC, streak_start
    ) AS rnk
  FROM islands
)
SELECT user_id, streak_start, streak_end, streak_len
FROM ranked
WHERE rnk = 1
ORDER BY user_id;

빠른 확인

요구 사항에 맞는 도구를 고르십시오.

복습

사용자별 가장 긴 연속 기록을 계산하려면 다음과 같이 합니다.

  • login_date - ROW_NUMBER() 앵커로 구간을 만듭니다.
  • 각 구간을 길이와 날짜 범위로 축약합니다.
  • 길이만 필요하다면 사용자별로 그룹화하여 MAX(streak_len)을 구합니다.
  • 날짜도 필요하다면 사용자별로 구간의 순위를 매기고 순위 1만 남깁니다 — 동률을 포함하려면 RANK를, 하나의 승자만 선택하려면 ROW_NUMBER를 사용합니다.
  • 연속 기록이 0인 사용자를 표시하려면 사용자 목록에 LEFT JOIN합니다.

다음 주제: 조건을 충족하는 N개의 연속 행 찾기

자주 묻는 질문

“사용자별 최장 연속 기록” 강의는 무료인가요?

네 — “사용자별 최장 연속 기록” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Coding Interview Prep 강의 전체를 잠금 해제할 수 있습니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

“사용자별 최장 연속 기록”에서 뭘 배우나요?

각 그룹 안에서 최대로 연속된 실행 길이를 계산합니다. 브라우저에서 직접 실행하는 실습 코드로 Coding Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Coding Interview Prep을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Coding Interview Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“사용자별 최장 연속 기록” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Coding Interview Prep 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Coding Interview Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 연속된 달력 날짜 감지하기
  2. 사용자별 최장 연속 기록
  3. 조건을 충족하는 N개의 연속 행
  4. 오늘 기준 현재 활성 연속 기록
← Coding Interview Prep(으)로 돌아가기