[Mysql] HAVING절, 정규표현식, IF문

dataong·2021년 1월 14일
3

SQL 스터디

목록 보기
1/5

🔎SQL스터디 시작 배경

데잇걸즈 과정을 끝마치고 그동안 쌓아온 SQL을 까먹지 않기 위해 구성원들과 스터디를 만들었다. 3주간 진행한 커리큘럼은 선미님의 백문이불여일타 SQL중급편에 있었던 중급 문제를 다시 풀어보는 것이었다. 주로 리트코드와 Hacker rank에 있는 문제였다. 매주 5문제를 풀고 수요일에 ZOOM에 모여 풀이를 리뷰하는 시간을 가졌다.이렇게 문제별 페이지에 자신의 코드를 남기고 리뷰하고 싶은 문제는 따로 표시해두는 방식으로 진행했다.

🔎기억에 남는 문제 풀이

1. Leetcode 182. Duplicate Emails

✍🏻 내 쿼리

SELECT A.Email
FROM
(SELECT Email, COUNT(*) AS Num
FROM Person
GROUP BY Email) A
WHERE A.Num > 1;

👏🏻 팀원의 개선된 쿼리

SELECT Email
FROM Person
GROUP BY Email
HAVING COUNT(Email) > 1

중복이 있는 이메일 주소를 뽑는 문제였다. 나는 GROUP BY의 조건문인 HAVING이 기억에 나지 않아 WHERE을 이용해 해결했다. 팀원의 쿼리를 보면 HAVING을 이용하면 서브쿼리가 없어도 간단하게 풀리는 문제였다.

2. Hackerrank. Weather Observation Station 11

✍🏻 내 쿼리

SELECT DISTINCT CITY
FROM STATION
WHERE UPPER(LEFT(CITY,1)) NOT IN ("A","E","I","O","U") 
OR UPPER(RIGHT(CITY,1)) NOT IN ("A","E","I","O","U")

👏🏻 팀원의 개선된 쿼리

SELECT DISTINCT city
FROM station
WHERE city NOT REGEXP '^[aeiouAEIOU].*' 
OR city NOT REGEXP '.*[aeiouAEIOU]$'

나는 NOT IN을 이용해 문제를 풀었고 팀원은 정규표현식을 이용해 풀었다. 둘다 쿼리 길이나 결과는 동일하지만 정규표현식을 이용하면 비슷한 다른 문제에서 더 간편할 것 같다.

정규표현식이란?

  • REGEXP: 정규식 표시방법
  • 사용기호:
    - .: 하나의 문자를 의미
    - *: 0개 이상의 앞의 문자 반복
    - []: [ ] 안의 문자열 중 어떤 문자라도 매치되면 된다
    - ^: 문장의 시작
    - $: 문장의 끝

예를들면 ^[aeiouAEIOU].*의 의미는 [ ]안의 문자로 시작(^)하면 된다는 뜻이다. 만약 ^[aeiouAEIOU]..이었다면 arg, ath, ery 등과 같이 [ ]안의 문자로 시작하고 뒤에 두 글자(..)가 있는 형태가 된다.

3. Hackerrank. Top Competitors

HAVING을 까먹었다😭

✍🏻 내 쿼리

SELECT hacker_id, name
FROM (SELECT DISTINCT table1.hacker_id, Hackers.name, table1.quiz_count
			FROM (SELECT Submissions.hacker_id, COUNT(Submissions.hacker_id) OVER(PARTITION BY Submissions.hacker_id) as quiz_count 
			FROM Submissions
					INNER JOIN Challenges ON Submissions.challenge_id = Challenges.challenge_id
					INNER JOIN Difficulty ON Challenges.difficulty_level = Difficulty.difficulty_level
			WHERE Submissions.score = Difficulty.score) table1 -- 서브쿼리1
INNER JOIN Hackers ON table1.hacker_id = Hackers.hacker_id) table2 -- 서브쿼리2
WHERE quiz_count > 1 
ORDER BY quiz_count DESC, hacker_id ASC;

👏🏻 팀원의 개선된 쿼리

SELECT s.hacker_id, h.name 
FROM Submissions s 
          JOIN Challenges c ON s.challenge_id = c.challenge_id 
          JOIN Difficulty d ON c.difficulty_level = d.difficulty_level  
          JOIN Hackers h ON s.hacker_id = h.hacker_id 
WHERE s.score = d.score
GROUP BY s.hacker_id, h.name 
HAVING COUNT(s.hacker_id) > 1 
ORDER BY COUNT(s.hacker_id) DESC, s.hacker_id ASC

한눈에 보아도 내 쿼리 길고 장황하다! 이 단순한 문제에 서브쿼리를 2개나 썼기 때문..! 팀원의 쿼리를 보면 HAVINGORDER BYCOUNT(변수)를 써서 서브쿼리 없이 쿼리를 완성시켰다. HAVINGORDER BY에서도 COUNT로 계산된 변수가 들어갈 수 있다는 것을 배웠다.

4. Hackerrank. Binary Tree Nodes

✍🏻 내 쿼리

SELECT B1.N, 
CASE WHEN B1.P IS NULL THEN 'Root' 
WHEN B3.P IS NOT NULL THEN 'Leaf'
 ELSE 'Inner' END AS Category
FROM BST B1 LEFT OUTER JOIN BST B2 ON B1.P = B2.N
LEFT OUTER JOIN BST B3 ON B2.P = B3.N 
ORDER BY B1.N ASC

👏🏻 팀원의 개선된 쿼리

SELECT b2.N, 
IF(P IS NULL, 'Root',
   IF((SELECT COUNT(b1.P) FROM BST AS b1 WHERE b1.P = b2.N) >=1 , 'Inner', 'Leaf'))
FROM BST AS b2
ORDER BY b2.N

내 쿼리는 이 문제에선 통과가 되었지만, 이 문제에서만 통과할 수 있다는 점에서 문제가 된다. 반면 팀원의 쿼리는 이 문제뿐만 아니라 데이터의 계층이 더 깊어져도 활용할 수 있다는 점에서 개선된 쿼리이다! IF문을 이용했고 조건으로 SELECT문을 이용했다는 것이 배울점이었다. 팀원의 쿼리 특히 IF절 안의 SELECT문의 WHERE절을 짚고 넘어가보자.

🔍팀원 쿼리 이해하기
① BST를 b1과 b2로 나눠서 지정
② Root: 규칙이 가장 간단한 Root는 미리 빼주기(P가 null일 때)
③ Inner: b1의 P가 b2의 N에 있다는 것은 제일 말단 부분인 Leaf가 아니라는 것, 즉 Inner
④ Leaf: 나머지 모두 다

같은 원리로 IF문 안에 SELECT COUNT(b1.P) FROM BST AS b1 WHERE b1.P = b2.N) >=1 대신 N in (SELECT DISTINCT P FROM BST)로 써도 같은 결과가 나온다!

🎉Takeaways

💡 HAVING절을 잘 활용하자
💡 정규표현식 지식 습득
💡 SQL문제 풀 때 다른 상황에서도 적용될 수 있는 쿼리짜기
💡 IF문을 활용해보자

마치며

약 3주간 스터디를 하면서 하늘 아래 같은 쿼리는 없다라고 느꼈다! 같은 문제여도 푸는 방식이 다양했고, 팀원들의 풀이를 보면서 좀 더 나은 쿼리를 작성하게 되었다. 다음 스터디는 고급 SQL문제 풀이인데, 특히 사용자 정의 함수 부분을 제대로 배워보고 싶다!

profile
올리고 보는 데이터 분석 TIL

0개의 댓글