ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SQL 수준별학습
    [스파르타코딩클럽]데이터분석 과정/TIL,WIL 2024. 4. 8. 20:12

    SQL

    • Window Function
      • 행과 행의 관계를 정의
      • 순위, 합계, 평균, 행 위치 조작
      • GROUP BY 구문과 병행하여 사용할 수 없음
      • 결과 건수가 줄어들지 않음 (집계 제외)
      • 파티션을 분할
      • 서브쿼리에는 사용 가능
      • 종류
        • 순위 : RANK, DENSE_RANK, ROW_NUMBER
        • 집계 : SUM, MAX, MIN, AVG, COUNT
        • 순서 : FIRST_VALUE, LAST_VALUE, LAG, LEAD
        • 비율 : RATIO_TO_REPORT, PERCENT_RANK, CUME_DIST, NTILE
    # 윈도우 함수 기본문법 
    SELECT WINDOW_FUNCTION () OVER([PARTITION BY 컬럼] [ORDER BY 컬럼])
    FROM 테이블명

     

    • 순위
      • RANK : 특정 컬럼의 순위를 구하는 함수, 동일한 값에 대해서는 같은 순위를 부여하며 중간 순위를 비운 값 출력
      • DENSE_RANK : 동일한 값에 대해서는 같은 순위를 부여하고 중간 순위를 비우지 않음
      • ROW_NUMBER : 동일한 값이어도 고유한 순위를 부여
    • 순서 
      • FIRST_VALUE : 가장 먼저 나온 값을 구하여 출력, 공동 등수를 인정하지 않고, 처음 나온 행만 가져오며 MIN함수를 쓰는 것과 결과가 동일
      • LAST_VALUE : 가장 마지막에 나온 값을 구하여 출력, 공동 등수를 인정하지 않고, 나중에 나온 행만 가져오며 MAX함수를 쓰는 것과 결과가 동일
      • LAG : 이전 N번째의 행을 가져오는 함수
      • LEAD : 이후 N행의 값을 가져오는 함수
    • 비율
      • RATIO_TO_REPORT : 파티션 내 전체 SUM값에 대한 행별 백분율을 소수점으로 출력
      • PERCENT_RANK : 파티션별로 가장 먼저 나오는 값을 0, 가장 마지막에 나오는 값을 1로 정하여 행 순서별 백분율을 출력, 구간을 나누어 백분율로 출력
      • CUME_DIST : 파티션별 전체건수에서 현재 행보다 작거나 같은 건수에 대한 누적백분율을 출력
      • NTILE : 파티션별 전체 건수를 계산한 값을 N등분한 결과를 출력
    • STRING 함수
      • CONCAT : 문자열을 병합할 때 사용
      • SUBSTRING : 문자열을 자를 때 사용
      • SUBSTRING_INDEX : 문자열을 특정 구분기호를 통해 출력할 떄 사용
      • REVERSE : 문자열을 뒤집을 때 사용, 특정 문자를 찾을 때도 사용
      • LEFT, RIGHT :  문자열을 기준으로 N개 출력
    • MATH함수
      • ABS : 절댓값을 출력 
      • ROUND : 숫자를 소수점 이하 자릿수에서 올림하여 출력
      • CEILING : 소수점을 올림하여 출력
      • FLOOR : 소수점을 내림하여 출력
      • TRUNCATE : 숫자를 소수점 이하 자릿수에서 버림하여 출력
      • RAND : 지정 숫자 범위 중 하나를 랜덤하게 출력
    • 날짜함수
      • NOW / SYSDATE / CURRENT_TIMESTAMP : 현재시간과 날짜를 출력 
      • DATE_ADD : 날짜에서 기준값만큼 덧셈하여 출력
      • DATE_SUB : 날짜에서 기준값만큼 뺄셈하여 출력
      • DATEDIFF :  두 날짜를 뺄셈하여 출력
      • DATE_FORMAT : 날짜를 형식에 맞게 출력
      • UNIX_TIMESTAMP : 현재 시간을 UNIXTIME으로 구함
      • CURDATE / CURRENT_DATE : 현재 날짜 출력
      • CURTIME / CURRENT_TIME : 현재 시간 출력
      • YEAR : 날짜의 연도 출력
      • MONTH : 날짜의 월 출력
      • DAY : 날짜의 일을 출력
    # RANK
    RANK() OVER(PARTITION BY 컬럼1 ORDER BY 컬럼2)
    
    # DENSE_RANK
    DENSE_RANK() OVER(PARTITION BY 컬럼1 ORDER BY 컬럼2)
    
    # ROW_NUMBER
    ROW_NUMBER() OVER(PARTITION BY 컬럼1 ORDER BY 컬럼2)
    
    # FIRST_VALUE
    FIRST_VALUE(컬럼1) OVER(PARTITION BY 컬럼2 ORDER BY 컬럼3)
    
    # LAST_VALUE
    LAST_VALUE(컬럼1) OVER(PARTITION BY 컬럼2 ORDER BY 컬럼3)
    
    # LAG
    LAG(컬럼1) OVER (PARTITION BY 컬럼2 ORDER BY 컬럼3) 
    LAG(컬럼1, 숫자) OVER (PARTITION BY 컬럼2 ORDER BY 컬럼3)
    
    # LEAD
    LEAD(컬럼1) OVER (PARTITION BY 컬럼2 ORDER BY 컬럼3)
    LEAD(컬럼1, 숫자) OVER (PARTITION BY 컬럼2 ORDER BY 컬럼3)
    
    # RATIO_TO_REPORT
    RATIO_TO_REPORT(컬럼1) OVER (PARTITION BY 컬럼2 ORDER BY 컬럼3)
    
    # PERCENT_RANK
    PERCENT_RANK() OVER (PARTITION BY 컬럼1 ORDER BY 컬럼2)
    
    # CUME_DIST
    CUME_DIST() OVER (PARTITION BY 컬럼1 ORDER BY 컬럼2)
    
    # NTILE
    NTILE(숫자) OVER (PARTITION BY 컬럼1 ORDER BY 컬럼2)

     


    SQL 과제 

    row_number 윈도우 함수를 사용하여, 2023-01-01 이후 서버별로 경험치가 가장 많은 1위 유저의 serverno, game_account_id, exp, 순위(rown컬럼)를 출력
    • 2023-01-01 이후 :where >= 2023-01-01
    • row_number 윈도우 함수 : row_number() over(partition by order by) as rown
    • 서버별 :  row_number() over(partition by serverno order by) as rown 
    • 경험치가 가장 많은 1위 : where >= 1
    SELECT 
      serverno,
      game_account_id,
      exp,
      rown
    FROM (
      SELECT 
        serverno,
        game_account_id,
        exp,
        ROW_NUMBER() OVER(PARTITION BY serverno ORDER BY exp DESC) AS rown
      FROM basic.users
      WHERE date >= '2023-01-01'
    ) AS ranked_users
    WHERE rown = 1;

     

    1️⃣우리는 IP 주소를 통해 이상접속 유저를 감지하려고 합니다. ip 주소는 x1.x2.x3.x4 형태처럼, ’.’ 을 기준으로
    4개의 숫자조합으로 구성되어 있습니다. ip주소를 x1.x2 기준으로 자르고 컬럼이름을 ip2로 지정해주세요.
    그리고 그 값을 count 해 주세요. count 한 값은 컬럼이름을 ipcnt 로 지정해주세요.
    2️⃣ 이 ip 주소를 기반으로, count 수가 4개 이상인 경우를 추출해주세요.
    3️⃣ with 문을 사용하여, 해당 유저의 기존ip주소, ipcnt, ip2, game_account_id 를 추출
    • ip주소를 x1.x2 기준으로 자르 : substring_index(,'.',2)
    •  그 값을 count : count
    • 이 ip 주소를 기반 : join
    • count 수가 4개 이상 : where >= 4
    •  with 문 : with ip_counts
    WITH ip_counts AS (
      SELECT 
        SUBSTRING_INDEX(ip_addr, '.', 2) AS ip2,
        COUNT(*) AS ipcnt
      FROM basic.users
      GROUP BY ip2
    )
    SELECT 
      basic.users.ip_addr AS existing_ip,
      ipcnt,
      ip2,
      basic.users.game_account_id
    FROM basic.users
    JOIN ip_counts ON SUBSTRING_INDEX(basic.users.ip_addr, '.', 2) = ip_counts.ip2
    WHERE ipcnt >=4;

     

    1️⃣ basic.users 테이블에서, game_account_id 별로 level을 오름차순 정렬하여 이전 경험치를 lagexp 라는 컬럼
    2️⃣ 현재 가지고 있는 경험치와, 이전 경험치를 모두 표시해주시고, 현재경험치-이전경험치를 getexp(획득한 경험치) 라는 컬럼
    3️⃣ payment 테이블과 join하여(어떤 조인 방식이 좋을지 고민해주세요), 결제를 하지 않은 유저와 결제한 유저를 paygb 라는 컬럼
    4️⃣ 단 이때, 획득한 경험치는 10만 이상인 경우만 필터링해주세요.
    5️⃣ paygb를 기준으로 획득한 경험치가 많은 유저를 그룹별 3개씩 출력
    •  game_account_id 별 : partiton by game_account_id
    • level을 오름차순 정렬 : order by level
    • 이전 경험치 : LAG
    • 현재경험치-이전경험치 :  exp - lagexp
    • payment 테이블과 join : left join
    • 결제를 하지 않은 유저와 결제한 유저를 paygb 라는 컬럼 : case when
    • 획득한 경험치는 10만 이상인 경우 : where >= 100000
    •  paygb를 기준으로 획득한 경험치가 많은 유저 : row_number() over(partiton by paygb)
    • 경험치가 많은 유저 :  row_number() over(partiton by paygb order by getexp desc)
    • 그룹별 3개씩 : where rown >= 3
    WITH lag_exp AS (
      SELECT
        game_account_id,
        exp,
        LAG(exp) OVER(PARTITION BY game_account_id ORDER BY level) AS lagexp
      FROM basic.users
    ),
    pay_status AS (
      SELECT
        u.game_account_id,
        CASE WHEN p.game_account_id IS NOT NULL THEN '결제함' ELSE '결제안함' END AS paygb
      FROM basic.users u
      LEFT JOIN basic.payment p ON u.game_account_id = p.game_account_id
    ),
    filtered_exp AS (
      SELECT DISTINCT
        l.game_account_id,
        l.exp,
        l.lagexp,
        l.exp - l.lagexp AS getexp,
        p.paygb
      FROM lag_exp l
      JOIN pay_status p ON l.game_account_id = p.game_account_id
      WHERE l.exp - l.lagexp >= 100000
    ),
    ranked_exp AS (
      SELECT
        f.*,
        ROW_NUMBER() OVER(PARTITION BY f.paygb ORDER BY f.getexp DESC) AS rown
      FROM filtered_exp f
    )
    SELECT *
    FROM ranked_exp
    WHERE rown <= 3;

     

    '[스파르타코딩클럽]데이터분석 과정 > TIL,WIL' 카테고리의 다른 글

    코드카타  (0) 2024.04.05
    코드카타  (0) 2024.04.04
    코드카타  (0) 2024.04.03
    코드카타 / SQL  (0) 2024.04.02
    코드카타 / SQL  (0) 2024.04.01
Designed by Tistory.