Coding Challenges/LeetCode

[SQL50] 1141. User Activity for the Past 30 Days I

기록해연 2025. 3. 4. 10:15

난이도: EASY

 

문제 링크: https://leetcode.com/problems/user-activity-for-the-past-30-days-i/description/?envType=study-plan-v2&envId=top-sql-50


문제

더보기

Table: Activity
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| user_id       | int     |
| session_id    | int     |
| activity_date | date    |
| activity_type | enum    |
+---------------+---------+
This table may have duplicate rows.
The activity_type column is an ENUM (category) of type ('open_session', 'end_session', 'scroll_down', 'send_message').
The table shows the user activities for a social media website. 
Note that each session belongs to exactly one user. 

Write a solution to find the daily active user count for a period of 30 days ending 2019-07-27 inclusively. A user was active on someday if they made at least one activity on that day.
Return the result table in any order.
The result format is in the following example.

Example 1:
Input: 
Activity table:
+---------+------------+---------------+---------------+
| user_id | session_id | activity_date | activity_type |
+---------+------------+---------------+---------------+
| 1       | 1          | 2019-07-20    | open_session  |
| 1       | 1          | 2019-07-20    | scroll_down   |
| 1       | 1          | 2019-07-20    | end_session   |
| 2       | 4          | 2019-07-20    | open_session  |
| 2       | 4          | 2019-07-21    | send_message  |
| 2       | 4          | 2019-07-21    | end_session   |
| 3       | 2          | 2019-07-21    | open_session  |
| 3       | 2          | 2019-07-21    | send_message  |
| 3       | 2          | 2019-07-21    | end_session   |
| 4       | 3          | 2019-06-25    | open_session  |
| 4       | 3          | 2019-06-25    | end_session   |
+---------+------------+---------------+---------------+
Output: 
+------------+--------------+ 
| day        | active_users |
+------------+--------------+ 
| 2019-07-20 | 2            |
| 2019-07-21 | 2            |
+------------+--------------+ 
Explanation: Note that we do not care about days with zero active users.

각 세션은 30일간 보관됨.

2019-07-27에도 유효한 세션을 가진 유저의 수를 구하기.

 


 

시도 1

SELECT activity_date AS day
     , COUNT(DISTINCT user_id) AS active_users
FROM Activity 
WHERE activity_date 
    BETWEEN TO_DATE('2019-07-27','YYYY-MM-DD') - 29 
    AND TO_DATE('2019-07-27','YYYY-MM-DD')
GROUP BY activity_date

...? 왜 시간이 같이 나오지... 

=> TO_CHAR 처리


 

나의 최종 제출 답안:

SELECT TO_CHAR(activity_date, 'YYYY-MM-DD') AS day
     , COUNT(DISTINCT user_id) AS active_users
FROM Activity 
WHERE activity_date 
    BETWEEN TO_DATE('2019-07-27','YYYY-MM-DD') - 29 
    AND TO_DATE('2019-07-27','YYYY-MM-DD')
GROUP BY activity_date

 


 

chatGPT에게 더 간단하고 효율적이게 수정해달라고 요청한 답안:

 

 

현재 작성하신 쿼리도 문제를 해결하는 데는 전혀 문제가 없지만, 날짜만 그룹화하고 결과를 날짜 형식으로 출력하려면 일반적으로 다음과 같은 방식을 권장합니다:

  1. 시분초를 버린 날짜(즉, TRUNC)로 그룹화
  2. 날짜 출력 시 TO_CHAR를 사용하여 원하는 포맷으로 표시
  3. WHERE 절에서는 가급적 함수 호출을 피하고(인덱스가 있다면 활용하기 위함) 날짜 리터럴(예: DATE 'YYYY-MM-DD')을 사용
SELECT TO_CHAR(TRUNC(activity_date), 'YYYY-MM-DD') AS day,
       COUNT(DISTINCT user_id) AS active_users
FROM Activity
-- 30일 범위를 '날짜 리터럴'로 지정
WHERE activity_date >= DATE '2019-06-28'
  AND activity_date <= DATE '2019-07-27'
GROUP BY TRUNC(activity_date)
ORDER BY TRUNC(activity_date);

 

왜 이 방식이 조금 더 나을까?

  1. 가독성: DATE '2019-06-28'처럼 날짜 리터럴을 쓰면, TO_DATE 함수 없이도 직관적으로 날짜를 표현할 수 있습니다.
  2. 인덱스 활용: 만약 activity_date 컬럼에 인덱스가 있다면, WHERE activity_date >= ... AND activity_date <= ... 조건은 인덱스 범위 스캔을 유도할 수 있습니다.
    반면 TRUNC(activity_date)를 WHERE 절에 직접 쓰면 함수 기반 인덱스가 없을 경우 인덱스를 잘 활용하지 못할 수도 있습니다.
  3. 출력 포맷 분리: TRUNC(activity_date)로 그룹화하고, 실제 출력 시에는 TO_CHAR로 원하는 형식을 지정하는 것이 일반적인 패턴입니다.
    • 그룹화: GROUP BY TRUNC(activity_date)
    • 출력: SELECT TO_CHAR(TRUNC(activity_date), 'YYYY-MM-DD') AS day

하지만 데이터가 많지 않다면 지금 쓰신 쿼리(TO_CHAR(activity_date, 'YYYY-MM-DD') AS day로 직접 그룹화)도 충분히 잘 동작합니다.
결국, 인덱스 활용과 가독성을 조금 더 신경 쓴다면 위와 같이 날짜 리터럴 + TRUNC + TO_CHAR를 분리하는 방법을 권장드립니다.