Write a solution to show the unique ID of each user, If a user does not have a
unique ID replace just show null. Return the result table in any order.
두 테이블을 LEFT JOIN을 진행하면 되는 간단한 문제였다.
SELECT
uni.unique_id,
e.name
FROM Employees e
LEFT JOIN EmployeeUNI uni
ON e.id = uni.id
Write a solution to report the distance traveled by each user.
Return the result table ordered by travelled_distance in descending order,
if two or more users traveled the same distance, order them by their name in
ascending order.
각 유저(Users)별로 차량 탑승 기록(Rides)의 총 이동 거리를 구하는 문제이다.
SELECT
u.name,
IFNULL(SUM(r.distance), 0) AS travelled_distance
FROM
Users u
LEFT JOIN
Rides r ON u.id = r.user_id
GROUP BY
u.id
ORDER BY
travelled_distance DESC,
u.name ASC;
IFNULL vs COALESCE위 1407번 문제에서 탑승 기록이 없어 distance 합계가 null이 되는 경우를 0으로 바꿔주기 위해 IFNULL을 사용했다. 다른 분들의 풀이를 보니 COALESCE를 많이 사용하길래, 두 함수의 차이점을 정리했다.
| 비교 항목 | IFNULL | COALESCE |
|---|---|---|
| 작동 원리 | 1번째 값이 NULL이면 2번째 값 반환 | 괄호 안의 값 중 첫 번째로 NULL이 아닌 값 반환 |
| 인자 개수 | 딱 2개 (값, 대체값) | 2개 이상 제한 없음 (값1, 값2, 값3...) |
| 표준 여부 | MySQL 등 일부 DB 전용 함수 | 표준 SQL (Oracle, PostgreSQL 등 대부분 지원) |
IFNULL):IFNULL을 써도 무방하다.COALESCE)SQL-- COALESCE 활용 예시
SELECT COALESCE(NULL, NULL, 10, 20); -- 결과: 10
Write a solution to find for each date the number of different products sold and
their names. The sold products names for each date should be sorted lexicographically.
Return the result table ordered by sell_date.
날짜별로 판매된 상품들을 그룹화하고, 하나의 문자열로 묶어서 출력하는 문제이다.
제가 처음에 작성했던 오답 쿼리와 발생한 문제점은 다음과 같다.
[초기 쿼리]
SELECT
sell_date,
COUNT(product) as num_sold,
GROUP_CONCAT(product) as products
FROM Activities
GROUP BY sell_date
ORDER BY
sell_date ASC,
products ASC;
[문제점]
중복 값이 그대로 출력됨:
동일한 날짜에 'Mask'가 두 번 팔렸다면, num_sold도 1개가 더 카운트되고 products 문자열에도 'Mask'가 두 번 들어가게 된다.
문자열 내부 정렬 안 됨:
쿼리 맨 마지막에 있는 ORDER BY products ASC는 결과로 나온 행(Row) 전체의 순서를 정렬할 뿐, GROUP_CONCAT으로 묶인 단어들 내부의 순서(사전순)를 정렬해 주지 못한다.
이 문제를 해결하려면 COUNT()와 GROUP_CONCAT() 함수 내부에서 직접 DISTINCT와 ORDER BY를 사용해야 한다.
num_sold: COUNT(DISTINCT product)를 사용하여 중복을 제거한 고유 상품 개수만 센다.
products: GROUP_CONCAT(DISTINCT product ORDER BY product ASC)를 사용하여 결합하기 전에 미리 중복을 제거하고 사전순으로 정렬되도록 처리한다.
SELECT
sell_date,
COUNT(DISTINCT product) AS num_sold,
GROUP_CONCAT(DISTINCT product ORDER BY product ASC SEPARATOR ',') AS products
FROM
Activities
GROUP BY
sell_date
ORDER BY
sell_date ASC;
MySQL에서 여러 행의 데이터를 하나의 문자열로 결합할 때 사용하는 굉장히 유용한 함수이다. 데이터를 요약해서 보여주어야 할 때 자주 활용된다.
1. 기본 문법
GROUP_CONCAT(
[DISTINCT] 컬럼명
[ORDER BY 정렬할_컬럼명 ASC/DESC]
[SEPARATOR '구분자']
)
2. 주요 옵션 설명
DISTINCT: 결합할 데이터 중 중복되는 값을 하나로 합쳐준다.
ORDER BY: 문자열로 결합되기 전에 괄호 안에서 데이터들의 순서를 먼저 정렬한다. (전체 쿼리 끝에 쓰는 ORDER BY와는 적용 시점과 대상이 다르다!)
SEPARATOR: 단어와 단어 사이를 이어줄 구분자를 지정한다.
기본값은 쉼표(,)이다. 만약 띄어쓰기나 슬래시 등으로 연결하고 싶다면 SEPARATOR ' / ' 와 같이 지정할 수 있다.
3. 활용 예시 비교
만약 묶어줄 데이터가 [A, B, A, C] 순서로 존재한다고 가정.
GROUP_CONCAT(col) "A,B,A,C"GROUP_CONCAT(DISTINCT col) "A,B,C"GROUP_CONCAT(DISTINCT col ORDER BY col DESC) "C,B,A"GROUP_CONCAT(col SEPARATOR ' | ') "A | B | A | C"Write a solution to find the users who have valid emails.
A valid e-mail has a prefix name and a domain where:
The prefix name is a string that may contain letters (upper or lower case), digits,
underscore '_', period '.', and/or dash '-'. The prefix name must start with a letter.
The domain is '@leetcode.com'. Return the result table in any order.
정규표현식(Regular Expression)을 활용하여 복잡한 문자열 조건을 필터링하는 문제이다.
정규식 문법뿐만 아니라, MySQL의 대소문자 구분(Case Sensitivity) 처리와 버전별 호환성 에러까지 해결해야 하는 꽤 까다롭고 배울 점이 많은 문제였다.
유효한 이메일인지 판별하기 위해 문제에서 제시한 규칙을 정규표현식 기호로 하나씩 치환했다
^[a-zA-Z] : ^는 문자열의 시작을 의미한다.@ 앞에는 문자, 숫자, 특수문자(_, ., -)만 허용[a-zA-Z0-9_.-]* : 괄호 안의 문자들이 0개 이상(*) 올 수 있음을 의미한다.@leetcode.com으로 끝날 것@leetcode\\.com$ : 마침표(.)는 정규식에서 '아무 문자 1개'를 의미하므로, 진짜 마침표를 표현하기 위해 이스케이프(\\) 처리한다. $는 문자열의 끝을 의미한.[완성된 기본 정규표현식]
'^[a-zA-Z][a-zA-Z0-9_.-]*@leetcode\\.com$'
위에서 만든 정규표현식을 WHERE mail REGEXP 구문에 넣고 돌렸더니, winston@leetcode.COM 이라는 오답 데이터가 검색 결과에 포함되었다.
REGEXP 연산자는 기본적으로 대소문자를 구분하지 않는다(Case-insensitive). 따라서 소문자로 @leetcode.com이라 적었어도 대문자인 COM까지 모두 유효하다고 판단한 것이다.REGEXP 뒤에 BINARY 키워드를 추가하였다. (WHERE mail REGEXP BINARY '정규식')BINARY 키워드를 넣었더니 이번에는 아래와 같은 끔찍한 에러가 발생했습니다.
Character set 'utf8mb3_general_ci' cannot be used in conjunction with 'binary' in call to regexp_like.
utf8mb3_general_ci)을 사용 중인데, 쿼리에서는 바이트 단위로 비교하는 BINARY를 강제로 적용하려고 하니 MySQL 내부에서 충돌이 일어난 것이다.최신 MySQL 8.0 이상 환경에서는 이 문제를 아주 깔끔하게 해결할 수 있는 REGEXP_LIKE 전용 함수와 매칭 옵션을 제공한다.
REGEXP_LIKE(컬럼명, '정규식', '매칭옵션')'c' (Case-sensitive)를 주면, 대소문자를 아주 엄격하게 구분하여 매칭해주고, 반대로 대소문자를 무시하고 싶다면 'i'를 사용한다.SELECT
user_id,
name,
mail
FROM
Users
WHERE
REGEXP_LIKE(mail, '^[a-zA-Z][a-zA-Z0-9_.-]*@leetcode\\.com$', 'c');