๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

SQL

[SQL] MySQL - Window Function์œผ๋กœ ๋ฌธ์ œํ’€๊ธฐ(Leetcode - 180, 184, 185๋ฒˆ)

๋ฐ˜์‘ํ˜•

๐Ÿ”Š ๋ณธ ํฌ์ŠคํŒ…์—์„œ ์‚ฌ์šฉ๋˜๋Š” ํ…Œ์ด๋ธ”์˜ ์ž๋ฃŒ์™€ ์ถœ์ฒ˜๋Š” Leetcode์ž„์„ ๋ฐํž™๋‹ˆ๋‹ค. ๋” ๋‹ค์–‘ํ•œ SQL ๋ฌธ์ œ๋ฅผ ํ’€์–ด๋ณด์‹œ๋ ค๋ฉด Leetcode ์‚ฌ์ดํŠธ๋ฅผ ๋ฐฉ๋ฌธํ•ด ๋ณด์„ธ์š”!

 

Leetcode ์‚ฌ์ดํŠธ

 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” MySQL 8.0 ๋ฒ„์ „๋ถ€ํ„ฐ ํ˜ธํ™˜์ด ๊ฐ€๋Šฅํ•œ Window Function์„ ์ด์šฉํ•ด ๋ฌธ์ œ๋ฅผ ํ’€์–ด๋ณด์ž. ๋งŒ์•ฝ MySQL 8.0 ์ดํ•˜ ๋ฒ„์ „์ด๋ผ๋ฉด MSSQL์„ ํ™œ์šฉํ•ด Window Function์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. Window Function์— ๋Œ€ํ•ด์„œ๋Š” ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•ด๋ณด์ž. ์ƒ๋‹นํžˆ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ๋“ค์ด ๋งŽ๋‹ค.

 

์ด๋ฒˆ์—๋Š” ์ด 3๊ฐœ์˜ ๋ฌธ์ œ๋ฅผ ํ’€์–ด๋ณผํ…๋ฐ ๋‚œ์ด๋„๊ฐ€ ์–ด๋Š ์ •๋„ ์žˆ๋Š” ๋ฌธ์ œ๋“ค์ด๋‹ค. ๋”ฐ๋ผ์„œ ์ž์„ธํ•œ ๋ฌธ์ œ๋Š” ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญํ•ด 180๋ฒˆ, 184๋ฒˆ, 185๋ฒˆ ๋ฌธ์ œ๋ฅผ ์ž์„ธํžˆ ์ฝ์–ด๋ณด์ž.

 

# 180๋ฒˆ

๋จผ์ € ๋ฌธ์ œ์™€ ๋ฌธ์ œ์—์„œ ์š”๊ตฌํ•˜๋Š” ์ถœ๋ ฅํ˜•ํƒœ๋ฅผ ์‚ดํŽด๋ณด์ž.

 

Write an SQL query to find all numbers that appear at least three times consecutively. Return the result table in any order.
(์ ์–ด๋„ ์—ฐ์†์œผ๋กœ 3๋ฒˆ ๋‚˜ํƒ€๋‚˜๋Š” ๋ชจ๋“  ์ˆซ์ž๋“ค์„ ์ฟผ๋ฆฌํ•ด๋ผ. ๊ฒฐ๊ณผ๊ฐ’์˜ ๋ฐ์ดํ„ฐ๋“ค ์ •๋ ฌ ์ˆœ์„œ๋Š” ๋”ฐ๋กœ ์ง€์ •ํ•ด์ฃผ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.)

Leetcode 180๋ฒˆ ๋ฌธ์ œ

 

ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ ํ’€๊ธฐ ์œ„ํ•ด์„œ๋Š” Window Function ์ค‘ ๊ฐ’์„ ๋ฐ€์–ด๋‚ด๋Š” LAG(๊ฐ’, ์–ผ๋งˆ๋‚˜ lag์‹œํ‚ฌ์ง€) ์™€ ๊ฐ’์„ ๋•ก๊ฒจ์˜ค๋Š” LEAD(๊ฐ’, ์–ผ๋งˆ๋‚˜ lead์‹œํ‚ฌ์ง€) ๋ฅผ ์ด์šฉํ•œ 2๊ฐ€์ง€ ํ’€์ด๊ฐ€ ์žˆ๋‹ค. ์šฐ์„  LAG() ๋ฅผ ์‚ฌ์šฉํ•œ ํ’€์ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

-- LAG๋ฅผ ์ด์šฉํ•œ ํ’€์ด
SELECT DISTINCT sub.Num AS ConsecutiveNums
FROM (SELECT Num,
             LAG(Num, 1) OVER (ORDER BY Id) AS next,
             LAG(Num, 2) OVER (ORDER BY Id) AS nextAfter
      FROM Logs) sub
WHERE sub.Num = sub.next AND sub.next = sub.nextAfter

 

์œ„ ์ฟผ๋ฆฌ ์ค‘ ์„œ๋ธŒ์ฟผ๋ฆฌ์— ์žˆ๋Š” SELECT ๋ฌธ๋งŒ ์ฟผ๋ฆฌํ–ˆ์„ ๋•Œ ํ…Œ์ด๋ธ” ์ถœ๋ ฅ ํ˜•ํƒœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

Id Num next nextAfter
1 1 null null
2 1 1 null
3 1 1 1
4 2 1 1
5 1 2 1
6 2 1 2
7 2 2 1

 

๊ทธ๋ž˜์„œ ์œ„ ํ…Œ์ด๋ธ”์„ FROM ์ดํ›„์˜ ์„œ๋ธŒ์ฟผ๋ฆฌ๋กœ ๋„ฃ์–ด์ฃผ์–ด์„œ ์œ„ ํ…Œ์ด๋ธ”์˜ ๊ฒฐ๊ณผ๊ฐ’ ์ค‘ Num, next, nextAfter ์นผ๋Ÿผ ๊ฐ’์ด ๋ชจ๋‘ ์ผ์น˜ํ•˜๋Š” Num๊ฐ’์„ ์ฐพ์œผ๋ฉด ์ ์–ด๋„ 3๋ฒˆ ์—ฐ์†์œผ๋กœ ๋‚˜์˜ค๋Š” ๋ฐ์ดํ„ฐ๋“ค์„ ์ถ”์ถœํ•˜๊ฒŒ ๋œ๋‹ค.

 

๋‹ค์Œ์€ LEAD() ๋ฅผ ์‚ฌ์šฉํ•œ ์ฟผ๋ฆฌ๋ฌธ์ด๋‹ค. 

 

-- LEAD๋ฅผ ์‚ฌ์šฉํ•œ ํ’€์ด
SELECT DISTINCT sub.Num AS ConsecutiveNums
FROM (SELECT Num,
        LEAD(Num, 1) OVER (ORDER BY Id) AS next,
        LEAD(Num, 2) OVER (ORDER BY Id) AS nextAfter
      FROM Logs) sub
WHERE sub.Num = sub.next AND sub.next = sub.nextAfter

 

LEAD๋ฅผ ์ˆ˜ํ–‰ํ•œ ์„œ๋ธŒ์ฟผ๋ฆฌ์ ˆ์„ ํ…Œ์ด๋ธ”๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

Id Num next nextAfter
1 1 1 1
2 1 1 2
3 1 2 1
4 2 1 2
5 1 2 2
6 2 2 null
7 2 null null

# 184๋ฒˆ

184๋ฒˆ ๋ฌธ์ œ์™€ ๋ฌธ์ œ์—์„œ ์š”๊ตฌํ•˜๋Š” ํ˜•ํƒœ๋ฅผ ์‚ดํŽด๋ณด์ž.

 

๋ฌธ์ œ์—์„œ ์ฃผ์–ด์ง„ 2๊ฐœ์˜ ํ…Œ์ด๋ธ”

Write a SQL query to find employees who have the highest salary in each of the departments. For the above tables, your SQL query should return the following rows (order of rows does not matter).
(๊ฐ ๋ถ€์„œ์˜ ๊ฐ€์žฅ ๋†’์€ ๋ด‰๊ธ‰์„ ๋ฐ›๋Š” ์ง์›๋“ค์„ ์ฟผ๋ฆฌํ•ด๋ผ. ๋‹จ ์ถœ๋ ฅ๋˜๋Š” ๋ฐ์ดํ„ฐ์˜ ์ •๋ ฌ ๊ธฐ์ค€์€ ์ƒ๊ด€์ด ์—†๋‹ค.)

๋ฌธ์ œ์—์„œ ์š”๊ตฌํ•˜๋Š” ์ถœ๋ ฅ์˜ ํ˜•ํƒœ

 

์ด๋ฒˆ ๋ฌธ์ œ๋Š” Window Function์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์ผ๋ฐ˜์ ์ธ ์ฟผ๋ฆฌ๋ฌธ๊ณผ Window Function ์ค‘ ์ง‘๊ณ„ํ•จ์ˆ˜์ธ MAX() ๋ฅผ ์‚ฌ์šฉํ•ด ํ•ด๊ฒฐํ•˜๋Š” 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. ์šฐ์„  ์ผ๋ฐ˜ ์ฟผ๋ฆฌ๋ฌธ ํ’€์ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. 

 

-- 1. ๋ถ€์„œ๋ณ„ ๊ฐ€์žฅ ๋†’์€ ๋ด‰๊ธ‰์„ ์šฐ์„  ์ฟผ๋ฆฌ
-- 2. 1๋ฒˆ ์ฟผ๋ฆฌํ•œ ๊ฒฐ๊ณผ ํ…Œ์ด๋ธ”๊ณผ ๋ถ€์„œ์ด๋ฆ„์ด ์ ํ˜€์žˆ๋Š” Department ํ…Œ์ด๋ธ” ์กฐ์ธ
-- 3. 2๋ฒˆ์—์„œ ์กฐ์ธํ•œ ํ…Œ์ด๋ธ”์„ Employee ํ…Œ์ด๋ธ”๊ณผ DepartmentId ๊ธฐ์ค€์œผ๋กœ ์กฐ์ธ
-- 4. Employee์˜ Salary(์ง์›๋“ค์ด ๋ฐ›์€ ๋ด‰๊ธ‰)๊ณผ max_Salary(๋ถ€์„œ๋ณ„ ์ตœ๋Œ€ ๋ด‰๊ธ‰)์ด ๊ฐ™์€ ๋ฐ์ดํ„ฐ ์ฟผ๋ฆฌ
-- ์ผ๋ฐ˜ ์ฟผ๋ฆฌ

SELECT sub2.Name AS Department, e.Name AS Employee, e.Salary
FROM Employee AS e
 INNER JOIN (SELECT *
             FROM (SELECT DepartmentId, MAX(Salary) AS max_Salary
                   FROM Employee 
                   GROUP BY DepartmentId) sub1
              INNER JOIN Department AS d ON sub1.DepartmentId = d.Id) sub2 ON e.DepartmentId = sub2.DepartmentId
WHERE e.Salary = sub2.max_Salary

 

์ฟผ๋ฆฌ๋ฌธ์„ ์ž‘์„ฑํ•  ๋•Œ ๊ณ ๋ คํ•œ ์‚ฌํ•ญ์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ๊ตฌ์ƒํ–ˆ๋Š”์ง€ ์œ„ ์ฝ”๋“œ์˜ ์ฃผ์„์œผ๋กœ ์ž‘์„ฑํ•ด๋ณด์•˜๋‹ค. ์„œ๋ธŒ์ฟผ๋ฆฌ ํ•˜๋‚˜์”ฉ ์ผ์ผ์ด ๊ฒฐ๊ณผํ™”๋ฉด์„ ๊ด€์ฐฐํ•˜๋ฉด ์ดํ•ด๊ฐ€ ์ˆ˜์›”ํ•  ๊ฒƒ์ด๋‹ค.

 

๋‹ค์Œ์€ Window Function ์˜ MAX() ๋ฅผ ์‚ฌ์šฉํ•œ ํ’€์ด์ด๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด Window Function ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ์„œ๋ธŒ์ฟผ๋ฆฌ๊ฐ€ 1๊ฐœ ๊ฐ์†Œํ•˜๋Š” ์ฝ”๋“œ์˜ ๊ฐ„๊ฒฐ์„ฑ์ด ํ–ฅ์ƒ๋˜์—ˆ์Œ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

-- 1.์šฐ์„  ๋ถ€์„œ id - ๋ถ€์„œ์ด๋ฆ„ ๋งค์นญ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ๋‘ ๊ฐœ์˜ ํ…Œ์ด๋ธ” ์กฐ์ธ
-- 2.์œˆ๋„์šฐ ํŽ‘์…˜์‚ฌ์šฉํ•ด์„œ ๋ถ€์„œ๋ณ„ ์ตœ๊ณ  ๋ด‰๊ธ‰ ํ…Œ์ด๋ธ” ๋งŒ๋“ค๊ธฐ
-- 3.2๋ฒˆ ๊ฒฐ๊ณผ ํ…Œ์ด๋ธ”์„ Employee ํ…Œ์ด๋ธ”๊ณผ ์กฐ์ธ์‹œํ‚ค๊ธฐ
-- 4.๋ด‰๊ธ‰๊ณผ ์ตœ๋Œ€๋ด‰๊ธ‰์ด ๊ฐ™์€ ๋ฐ์ดํ„ฐ ์ฟผ๋ฆฌ
-- Window Function ์‚ฌ์šฉ - ๋ถ€์„œ๋ณ„ ~ ์ตœ๋Œ€๋ด‰๊ธ‰

SELECT sub.Department, E.name AS Employee, E.Salary
FROM Employee AS E
 INNER JOIN (SELECT DISTINCT d.Id AS DepartmentId, d.name AS Department, MAX(Salary) OVER (PARTITION BY DepartmentId) AS max_Salary
             FROM Employee AS e
              INNER JOIN Department AS d ON e.DepartmentId = d.Id) sub ON E.DepartmentId = sub.DepartmentId
WHERE E.Salary = sub.max_Salary

# 185๋ฒˆ

๋‹ค์Œ์€ 185๋ฒˆ์˜ ๋ฌธ์ œ์™€ ์š”๊ตฌํ•˜๋Š” ์ถœ๋ ฅ ํ˜•ํƒœ๋ฅผ์‚ดํŽด๋ณด์ž.

 

๋ฌธ์ œ์—์„œ ์ฃผ์–ด์ง„ 2๊ฐœ์˜ ํ…Œ์ด๋ธ”

Write a SQL query to find employees who earn the top three salaries in each of the department. For the above tables, your SQL query should return the following rows (order of rows does not matter).
(๊ฐ ๋ถ€์„œ๋ณ„๋กœ ๊ฐ€์žฅ ๋†’์€ ์ƒ์œ„ ๋ด‰๊ธ‰ 3๋ฒˆ์งธ ๊นŒ์ง€ ๋ฐ›๋Š” ์ง์›๋“ค์„ ์ฟผ๋ฆฌํ•ด๋ผ. ์ถœ๋ ฅ๊ฒฐ๊ณผ์˜ ๋ฐ์ดํ„ฐ ์ˆœ์„œ๋Š” ์ƒ๊ด€์ด ์—†๋‹ค.

๋ฌธ์ œ์—์„œ ์š”๊ตฌํ•˜๋Š” ์ถœ๋ ฅ ํ˜•ํƒœ

 

ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ ํ’€๊ธฐ ์œ„ํ•ด์„œ๋Š” Window Function ์ค‘ DENSE_RANK() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋งค์šฐ ์œ ์šฉํ•˜๋‹ค. DENSE_RANK()๋Š” ํŠน์ • ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ๋‚ด๋ฆผ(๋˜๋Š” ์˜ค๋ฆ„) ์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌ ํ›„ ์ˆœ์œ„๋ฅผ ๋ถ€์—ฌํ•˜๋Š” ํ•จ์ˆ˜์ธ๋ฐ,  DENSE_RANK() ์ด์™ธ์— ROW_NUMBER(), RANK() ํ•จ์ˆ˜๋„ ์žˆ๋Š”๋ฐ, ๊ฐ„๋‹จํ•˜๊ฒŒ ์ด 3๊ฐ€์ง€์˜ ์ฐจ์ด์ ์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์ž. ์˜ˆ๋ฅผ ๋“ค์–ด ๋งŒ์•ฝ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ…Œ์ด๋ธ”์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

 

Name Level
John 154
Mike 122
Eliot 122
Sara 115
Laura 200

 

์œ„ ํ…Œ์ด๋ธ”์—์„œ 3๊ฐ€์ง€ ํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•œ ๊ฐ๊ฐ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. ์ฐธ๊ณ ๋กœ Level์„ ๊ธฐ์ค€์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌํ•œ๋‹ค๋Š” ๊ฐ€์ •ํ•˜๊ฒŒ ROW_NUMBER(), RANK(), DENSE_RANK()๋ฅผ ์‚ฌ์šฉํ•ด ๋งŒ๋“  ํŒŒ์ƒ๋ณ€์ˆ˜๋“ค์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•„์ง„๋‹ค.

 

Name Level ROW_NUMBER() RANK() DENSE_RANK()
Laura 200 1 1 1
John 154 2 2 2
Mike 122 3 3 3
Eliot 122 4 3 3
Sara 115 5 5 4

 

์ด์ œ ์–ด๋–ค ๊ฐœ๋…์ธ์ง€ ํ”ผ๋ถ€์— ํ™• ์™€๋‹ฟ์„ ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿผ ์ด์ œ ์ด๋ฅผ ํ™œ์šฉํ•ด 185๋ฒˆ ๋ฌธ์ œ๋ฅผ ํ’€์ดํ•œ SQL ๊ตฌ๋ฌธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

-- 1.์šฐ์„  ๋ถ€์„œid, ๋ถ€์„œ์ด๋ฆ„ ๋งค์นญ์‹œํ‚ค๊ธฐ
-- 2.๋ถ€์„œ๋ณ„ ๋ด‰๊ธ‰ ์ˆœ์œผ๋กœ ๋Œ„์Šค๋žญํฌ ์ •๋ ฌํ•œ ํ…Œ์ด๋ธ” ๋งŒ๋“ค๊ธฐ
-- 3.2๋ฒˆ์—์„œ ๋งŒ๋“  ํ…Œ์ด๋ธ”์„ FROM ์„œ๋ธŒ์ฟผ๋ฆฌ๋กœ ๋„ฃ์–ด์ฃผ๊ณ  ์ˆœ์œ„๊ฐ€ 3์œ„ ์ดํ•˜์ธ ๋ฐ์ดํ„ฐ๋“ค๋งŒ ์ฟผ๋ฆฌ
-- ์ฐธ๊ณ ๋กœ ์œˆ๋„์šฐ ํŽ‘์…˜์œผ๋กœ ๋งŒ๋“  ๋ณ€์ˆ˜๋ฅผ ์ƒˆ๋กœ์šด ์นผ๋Ÿผ์œผ๋กœ ๋„ค์ด๋ฐํ•ด์ค„๋•Œ๋Š” '' ๋”ฐ์˜ดํ‘œ ๋ถ™์—ฌ์ฃผ์–ด์•ผ ํ•จ!!

SELECT sub.Department, sub.Employee, sub.Salary
FROM (SELECT d.Name AS Department,
             e.Name AS Employee,
             e.Salary AS Salary,
             DENSE_RANK() OVER (PARTITION BY d.Name ORDER BY e.Salary DESC) AS 'Rank'
      FROM Employee AS e
       INNER JOIN Department AS d ON e.DepartmentId = d.Id) sub
WHERE sub.Rank <= 3

 

์œ„์—์„œ ์ฃผ์˜ํ•ด์•ผ ํ•  ๋ถ€๋ถ„(ํ•„์ž๋„ ์ฒ˜์Œ ์•Œ์•˜๋‹ค..)์€ ๋ฐ”๋กœ DENSE_RANK()๋กœ ๋งŒ๋“  ์ƒˆ๋กœ์šด ์นผ๋Ÿผ์„ ํ•˜๋‚˜์˜ ์ƒˆ๋กœ์šด ์ด๋ฆ„์œผ๋กœ ๋„ค์ด๋ฐํ•ด์ค„ ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ' ' (๋”ฐ์˜ดํ‘œ)๋ฅผ ๋ถ™์—ฌ์•ผ ํ•œ๋‹ค.(์Œ๋”ฐ์˜ดํ‘œ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค) 

DENSE_RANK() OVER (PARTITION BY columnA  ORDER BY columnB  DESC|ASC) AS 'New column name'

 

๋ฐ˜์‘ํ˜•