λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
  • μž₯원읡 κΈ°μˆ λΈ”λ‘œκ·Έ
πŸ”¬web application/- Database

[SQL] Window ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ©΄ Group By κ°€ λͺ»ν•˜λŠ” 것을 ν•  수 μžˆλ‹€

by Wonit 2025. 2. 9.

λ°°κ²½

νšŒμ‚¬μ—μ„œ μ–΄λ–€ 업무λ₯Ό μ²˜λ¦¬ν•˜λ˜ 도쀑 μ•„λž˜μ™€ 같은 μš”κ΅¬μ‚¬ν•­μ„ λ§Œμ‘±ν•˜λŠ” SQL 을 μ§œμ•Όν–ˆλ‹€.

 

νŠΉμ • 일에 λ°œμƒν•œ transaction 쀑 κ°€μž₯ μ΅œμ‹  transaction 데이터λ₯Ό 뽑아라

 

이 tx λ°μ΄ν„°λŠ” λ§€μΌλ§ˆλ‹€ λŒ€λŸ‰μœΌλ‘œ λ°œμƒν•˜λŠ” λ°μ΄ν„°λ‘œ append only ν…Œμ΄λΈ”μ΄λ‹€.

 

userId κ°€ key 둜 μž‘ν˜€μžˆκ³  n 개의 tx 데이터가 μžˆλŠ”λ° λ‚ μ§œ 순으둜 μ •λ ¬λœ μ΅œμ‹ μ˜ 데이터λ₯Ό 뽑기 μœ„ν•΄ distinct κ°€ ν•„μš”ν–ˆλ‹€.

 

λ‚˜λŠ” μžμ—°μŠ€λŸ½κ²Œ group by 쿼리λ₯Ό μ§°λŠ”λ° μƒκ°ν•΄λ³΄λ‹ˆ λ‹Ήμ—°νžˆ group by λ‘œλŠ” ν•΄λ‹Ή μš”κ΅¬μ‚¬ν•­μ„ λ§Œμ‘±ν•  수 μ—†μ—ˆλ‹€. κ·ΈλŸ¬λ‹€ 데이터 μ—”μ§€λ‹ˆμ–΄λ§ νŒ€μ—κ²Œ 도움을 μš”μ²­ν•˜μ˜€κ³  window ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ©΄ λ‚΄κ°€ μ›ν•˜λŠ” 상황을 100% λ§Œμ‘±μ‹œν‚¬ 수 μžˆμ—ˆκ³  이에 λŒ€ν•œ λ‚΄μš©μ„ κ³΅μœ ν•΄λ³΄λ € ν•œλ‹€.

 

Group By 의 ν•œκ³„

이야기 ν•  λ‚΄μš©μ€ Group By 의 ν•œκ³„λΌκΈ° 보닀 νŠΉμ„±μ— 가깝닀.

 

Group By λŠ” 데이터λ₯Ό Grouping ν•˜μ—¬ 각 group λ³„λ‘œ κ³„μ‚°λœ κ²°κ³Όλ₯Ό 생성할 λ•Œ μ‚¬μš©ν•œλ‹€.

 

μ˜ˆμ‹œλ‘œ 봐보자

 

μ•žμœΌλ‘œ 예제둜 μ‚¬μš©ν•  μ•„λž˜ ν…Œμ΄λΈ” μŠ€ν‚€λ§ˆλŠ” μ•„μ£Ό κ°„λ‹¨ν•˜λ‹€

 

 

id, userId 그리고 μƒνƒœλ₯Ό λ‚˜νƒ€λ‚΄λŠ” status 와 주문일이 μžˆλ‹€

 

그리고 5개의 dummy data κ°€ λ“€μ–΄μžˆλ‹€

 

 

Group By ν•¨μˆ˜λ₯Ό μ΄μš©ν•œλ‹€λ©΄ user 와 μΌμžλ³„ 주문의 합계λ₯Ό λ‚˜νƒ€λ‚Ό 수 μžˆλ‹€

 

SELECT user_id, ordered_at::DATE, count(1)
FROM orders
GROUP BY user_id, ordered_at::DATE;

 

이외에도 λ‹€μ–‘ν•˜κ²Œ sum μ΄λ‚˜ avg λ₯Ό μ΄μš©ν•΄ μ‚°μˆ κ³„μ‚°μ„ ν•  μˆ˜λ„ μžˆμ§€λ§Œ μ€‘μš”ν•œ 사싀은 Group By 에 λͺ…μ‹œλ˜μ§€ μ•Šμ€ ν•„λ“œλŠ” select ν•  수 μ—†λ‹€λŠ” 것이닀.

 

Group By λŠ” κ·Έλ£Ήν™”λœ κ²°κ³Όλ₯Ό κΈ°μ€€μœΌλ‘œ μž‘μ—…μ„ μˆ˜ν–‰ν•˜κΈ° λ•Œλ¬Έμ— λͺ…μ‹œλ˜μ§€ μ•Šμ€ λ‚˜λ¨Έμ§€ ν•„λ“œλ₯Ό Result Set 쀑 어디에 ν‘œν˜„ 할지 μ• λ§€ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

 

이λ₯Ό μœ„ν•΄μ„œλŠ” WITH ν˜Ήμ€ Sub query λ₯Ό μ΄μš©ν•΄μ•Ό ν•˜λŠ”λ° 이 λ§ˆμ €λ„ νŠΉμ • μΌ€μ΄μŠ€μ—μ„œλ§Œ κ°€λŠ₯ν•˜λ‹€λŠ” 것이닀.

 

μœˆλ„μš° ν•¨μˆ˜

 

μ•žμ„  문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•œ 것이 Window ν•¨μˆ˜μ΄λ‹€.

 

postgresql Window Function docs μ—μ„œ μžμ„Έν•œ λ‚΄μš©μ„ 확인할 수 μžˆλ‹€.

 

핡심은 λ‹€μŒκ³Ό κ°™λ‹€.

 

ν…Œμ΄λΈ”μ—μ„œ ν–‰ 그룹을 μ •μ˜ν•˜κ³ , κ·Έλ£Ή λ‚΄μ—μ„œ 계산을 μˆ˜ν–‰ν•˜λ„λ‘ μ„€κ³„λœ ν•¨μˆ˜λ‘œ λ ˆμ½”λ“œλ³„ 계산을 μˆ˜ν–‰ν•˜λ©΄μ„œ 데이터λ₯Ό 전체 μ°Έμ‘°ν•˜κ³  싢은 κ²½μš°μ— μ‚¬μš©λœλ‹€. μžμ„Έν•œ λ‚΄μš©μ€ PostgreSQL Window Function Docs μ—μ„œ 확인할 수 μžˆλ‹€.

 

ν–‰μ˜ 그룹을 μ •μ˜ν•œλ‹€λŠ” λ©΄μ—μ„œλŠ” Group By 와 λ™μΌν•˜μ§€λ§Œ 전체 데이터λ₯Ό μ°Έμ‘°ν•  수 μžˆλ‹€λŠ” μ μ—μ„œ λ‹€λ₯΄λ‹€.

 

ν•¨μˆ˜μ˜ μ‚¬μš©λ²•μ€ λ‹€μŒκ³Ό κ°™λ‹€.

 

SELECT WINDOW_FUNCTION (ARGUMENTS) OVER

( [PARTITION BY 컬럼] [ORDER BY 컬럼] [WINDOWING 절] )

FROM ν…Œμ΄λΈ”λͺ… ;
  • OVER(): μœˆλ„μš°λ₯Ό μ •μ˜
  • PARTITION BY: κ·Έλ£Ήν™” κΈ°μ€€ 지정
  • ORDER BY: μ •λ ¬ κΈ°μ€€ 지정

window ν•¨μˆ˜λŠ” over 와 partition by λ₯Ό ν•¨κ»˜ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

 

partition by λ₯Ό μ΄μš©ν•΄μ„œ Grouping ν•  기쀀을 μ •ν•΄μ£Όκ³  windowing ν•  기쀀을 μ •ν•΄μ£Όλ©΄ λœλ‹€.

 

Window ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄μ„œ 2024-11-17 에 λ°œμƒν•œ μ£Όλ¬Έ 쀑 κ°€μž₯ μ΅œμ‹ μ˜ 주문을 μ‘°νšŒν•˜λΌ λΌλŠ” μš”κ±΄μ„ λ§Œμ‘±μ‹œν‚€λ©΄ λ‹€μŒκ³Ό κ°™λ‹€

 

1. window ν•¨μˆ˜λ‘œ μΌμžλ³„ μ •λ ¬ν•˜κΈ°

 

SELECT *,
       row_number() over (partition by user_id order by ordered_at desc)
FROM orders

 

μ—¬κΈ°μ„œ μ‚¬μš©ν•œ row_number() ν•¨μˆ˜λŠ” windowing 기쀀에 따라 μˆœμ„œλŒ€λ‘œ 번호λ₯Ό λΆ€μ—¬ν•œλ‹€.

 

2. WITH ꡬ문을 μ΄μš©ν•˜μ—¬ ν…Œμ΄λΈ” 검색

 

with TMP as (SELECT *,
                    row_number() over (partition by user_id order by ordered_at desc) as RN
             FROM orders)
SELECT *
FROM TMP
WHERE RN = 1;

 

κ°€μž₯ μ΅œμ‹ μ˜ 주문을 μ‘°νšŒν•˜κΈ° μœ„ν•΄ μ•žμ„œ μ‘°νšŒν•œ κ²°κ³Όλ₯Ό Sub query 둜 λ„£κ³  row number 에 쑰건을 μΆ”κ°€ν•˜λ©΄ μ•„λž˜μ™€ 같이 μ›ν•˜λŠ” κ²°κ³Όκ°€ μ‘°νšŒλœλ‹€

 

λ‹€μ–‘ν•œ Window ν•¨μˆ˜λ“€

 

μ•žμ„œ μ‚¬μš©ν•œ window ν•¨μˆ˜λŠ” row_number() 둜 windowing 쑰건에 따라 μˆœμ„œλ§Œ λΆ€μ—¬ν•œ 것이닀.

 

이외에도 λ‹€μ–‘ν•œ ν•¨μˆ˜λ“€μ΄ μ‘΄μž¬ν•œλ‹€

 

  • νŠΉμ • 값을 ν•©μ‚°ν•˜λŠ” SUM OVER
  • ν˜„μž¬ 행을 κΈ°μ€€μœΌλ‘œ 이전 ν–‰μ˜ 값을 κ°€μ Έμ˜€λŠ” LAG OVER
  • μˆœμœ„λ₯Ό λΆ€μ—¬ν•˜λŠ” RANK OVER

 

λ“±λ“±

λŒ“κΈ€