2024-02-21
오늘은 프로그래머스 SQL Kit의: IS NULL, JOIN, String, Date 파트를 풀겠다.
또, 업랜디 + 시간이 남는다면 프로그래머스 고득점 Kit도 복습할 것 !!
공부 계획
- 2월 20일 - SUM, MAX, MIN | GROUP BY 풀이 ✅ 2024-02-20
- 2월 21일 - IS NULL | JOIN 풀이 ✅ 2024-02-21
- 2월 22일 - String, Date 풀고 SQL 복습 및 PS 복습
- 2월 23일 - 최종 복습 및 컨디션 관리. (주변 오브젝트 정리 및 리허설)
오늘의 업랜디
22858 원상 복구 (해결 - 구현, 시뮬레이션)
원상 복구 (small)
원상 복구문제는 카드 덱을 섞는 패턴 수열과, 섞이고 나서의 카드 수열, 섞은 횟수 K가 주어졌을 때 섞기 전 카드로 원상 복구하는 로직 문제였다.
간단한 구현, 시뮬레이션 문제지만 문제 내용이 수열과의 관계를 다뤄서 복잡했다. 조금 이해하기 어렵달까 ...
이해 했는데, 관계가 복잡해서 어떤식이었는지 문제를 풀다가 계속 까먹었다.
그래도 로직 구현 자체가 어려운 건 아니라서 쉽게 풀었다.
// 백준: 원상 복구 (small)
// https://www.acmicpc.net/problem/22858
// 2024-02-21
#include <iostream>
#include <vector>
using namespace std;
int main() {
int N, K; // 카드 개수 N, 섞은 횟수 K.
cin >> N >> K;
vector<int> S(N, 0);
vector<int> D(N, 0);
for (int i = 0; i < N; ++i) {
cin >> S[i];
}
for (int i = 0; i < N; ++i) {
cin >> D[i];
}
vector<int> n(N, 0);
while (K--) {
for (int i = 0; i < N; ++i) { // D에 대해 순회
int start_index = D[i] - 1;
int end_index = i;
n[start_index] = S[end_index];
}
S = n;
}
for (int i = 0; i < N; ++i) {
cout << S[i] << " ";
}
return 0;
}
프로그래머스 SQL
- IS NULL ✅ 2024-02-21
- JOIN ✅ 2024-02-21
- String, Date
- 오늘의 SQL 문제 해결: 20 문제
IS NULL
어제도 말했듯이, NULL은 알 수 없는 값이므로, !=, = 등의 연산도 알 수 없음으로 평가된다.
따라서, NULL 값을 비교할 때에는 A IS NULL, A IS NOT NULL 식으로 해야 한다.
SELECT ANIMAL_ID
FROM ANIMAL_INS
WHERE NAME IS NULL
ORDER BY ANIMAL_ID ASC;
JOIN
JOIN 파트를 시작했다. 첫 문제로 Lv.4 짜리를 풀었는데. 정말 별 생각없이 막 써나갔는데 한번에 맞았다. 기분이 좋다... 이왜맞
WITH TOTAL AS (
SELECT F.FLAVOR, SUM(F.TOTAL_ORDER) AS TOTAL_ORDER
FROM FIRST_HALF F
INNER JOIN JULY J
ON F.FLAVOR = J.FLAVOR
GROUP BY F.FLAVOR
)
SELECT FLAVOR
FROM TOTAL
ORDER BY TOTAL_ORDER DESC
LIMIT 3;
AND NOT EXISTS
특정 조건을 만족하는 행이 존재하지 않을 경우를 조건으로 설정하고 싶을 때 사용할 수 있다.
CAR_RENTAL_COMPANY_CAR 테이블과 CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블과 CAR_RENTAL_COMPANY_DISCOUNT_PLAN 테이블에서 자동차 종류가 '세단' 또는 'SUV' 인 자동차 중 2022년 11월 1일부터 2022년 11월 30일까지 대여 가능하고 30일간의 대여 금액이 50만원 이상 200만원 미만인 자동차에 대해서 자동차 ID, 자동차 종류, 대여 금액(컬럼명: FEE) 리스트를 출력하는 SQL문을 작성해주세요. 결과는 대여 금액을 기준으로 내림차순 정렬하고, 대여 금액이 같은 경우 자동차 종류를 기준으로 오름차순 정렬, 자동차 종류까지 같은 경우 자동차 ID를 기준으로 내림차순 정렬해주세요.
꽤 어려웠던 문제다. 1 - (할인율) 을 해서 곱했을 때 그만큼 discount되도록 계산했다.
8%의 할인율이라는 것은. 0.92가 되어서. 원가의 92%가 최종 가격이 된다.
또, 해당 기간에 대여중인지 확인하는 로직에 조건을 3개나 걸어야했다.
START_DATE나 END_DATE가 2022-11월 사이에 있는 경우와.
그 이전부터 넓게 대여중인 경우 두개에 대해 고려해야 했다.
꽤 어렵다..
SELECT
C.CAR_ID, C.CAR_TYPE, ROUND(C.DAILY_FEE * 30 * (1 - D.DISCOUNT_RATE / 100.0)) AS FEE
FROM
CAR_RENTAL_COMPANY_CAR C
INNER JOIN
CAR_RENTAL_COMPANY_DISCOUNT_PLAN D
ON D.CAR_TYPE = C.CAR_TYPE AND D.DURATION_TYPE = '30일 이상'
LEFT JOIN
CAR_RENTAL_COMPANY_RENTAL_HISTORY H
ON C.CAR_ID = H.CAR_ID
WHERE
C.CAR_TYPE IN ('세단', 'SUV')
AND NOT EXISTS (
SELECT 1
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE CAR_ID = C.CAR_ID
AND (
START_DATE BETWEEN '2022-11-01' AND '2022-11-30'
OR END_DATE BETWEEN '2022-11-01' AND '2022-11-30'
OR (START_DATE < '2022-11-01' AND END_DATE > '2022-11-30')
)
)
GROUP BY
C.CAR_ID, C.CAR_TYPE, D.DISCOUNT_RATE
HAVING
FEE >= 500000 AND FEE < 2000000
ORDER BY
FEE DESC, C.CAR_TYPE ASC, C.CAR_ID DESC;
또 새로 알게된 것은 CAST 형변환 함수이다.
SELECT C.CAR_ID, C.CAR_TYPE, CAST((C.DAILY_FEE * 30 * (1 - D.DISCOUNT_RATE / 100.0)) AS SIGNED) AS FEE
...
식으로 SIGNED INT 형태로 변환할 수 있다.
어려웠던 JOIN 문제
USER_INFO 테이블과 ONLINE_SALE 테이블에서 2021년에 가입한 전체 회원들 중 상품을 구매한 회원수와 상품을 구매한 회원의 비율(=2021년에 가입한 회원 중 상품을 구매한 회원수 / 2021년에 가입한 전체 회원 수)을 년, 월 별로 출력하는 SQL문을 작성해주세요.
SELECT
YEAR(S.SALES_DATE) AS YEAR,
MONTH(S.SALES_DATE) AS MONTH,
COUNT(DISTINCT S.USER_ID) AS PURCHASED_USERS,
ROUND((COUNT(DISTINCT S.USER_ID) / COUNT(I.USER_ID)), 2) AS PURCHASED_RATIO
FROM
USER_INFO I
LEFT JOIN
ONLINE_SALE S
ON S.USER_ID = I.USER_ID
WHERE
JOINED LIKE '2021%'
AND S.SALES_DATE IS NOT NULL
GROUP BY
YEAR(S.SALES_DATE), MONTH(S.SALES_DATE)
처음엔 코드를 위와 같이 썼는데. 문제점이 두가지가 있었다
- 어차피 IS NOT NULL을 넣을거면 왜 LEFT JOIN을?
- 비율을 구하는 식이 정확하지 않다. 2021년 데이터만 고려해야 한다.
문제 지문에 비율을 구하는 식이 친절하게도 써있는데, 이것이 식인지 모르고 이해를 돕기 위한 주석인 줄 알고 더 오해를 샀다...
그래도 Lv. 5짜리 SQL문제인데, 정답과 거의 가깝게 근접해서 나름대로 괜찮았다.
정답 코드
SELECT
YEAR(SALES_DATE) AS YEAR,
MONTH(SALES_DATE) AS MONTH,
COUNT(DISTINCT S.USER_ID) AS PURCHASED_USERS,
ROUND((COUNT(DISTINCT S.USER_ID) / (SELECT COUNT(*) FROM USER_INFO WHERE JOINED BETWEEN '2021-01-01' AND '2021-12-31')), 1) AS PURCHASED_RATIO
FROM
ONLINE_SALE S
JOIN
USER_INFO I ON S.USER_ID = I.USER_ID AND YEAR(I.JOINED) = 2021
GROUP BY
YEAR(SALES_DATE), MONTH(SALES_DATE)
ORDER BY
YEAR ASC, MONTH ASC;
DATEDIFF()
DATEDIFF, TIMESTAMPDIFF() 함수를 사용해서 두 날짜간의 차이를 계산할 수 있다.
주의할점은, DATEDIFF 함수는 첫번째 인자로 끝 날짜, 두번째 인자로 시작 날짜를 받는다. (그냥 빼기 연산 아닌가 이럴거면..)
- DATEDIFF() - 두 날짜간의 일수 차이
- TIMESTAMPDIFF() - 두 날짜간의 특정 단위 차이
SELECT START_DATE, END_DATE,
TIMESTAMPDIFF(DAY, START_DATE, END_DATE) AS DAYS_DIFF
FROM your_table_name
WHERE TIMESTAMPDIFF(DAY, START_DATE, END_DATE) >= 30;
CASE WHEN, THEN, ELSE. END 익숙해지기
-- 대여 기간이 30일 이상이면 '장기 대여' 그렇지 않으면 '단기 대여' 로...
SELECT
HISTORY_ID,
CAR_ID,
DATE_FORMAT(START_DATE, '%Y-%m-%d') AS START_DATE,
DATE_FORMAT(END_DATE, '%Y-%m-%d') AS END_DATE,
CASE WHEN
DATEDIFF(END_DATE, START_DATE) >= 29
THEN '장기 대여'
ELSE '단기 대여'
END
AS RENT_TYPE
FROM
CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE
START_DATE LIKE '2022-09%'
ORDER BY
HISTORY_ID DESC;
CASE WHEN THEN ELSE END 문에 익숙해지자.
여기서 특이한 건, 29일부터 ~ 29일까지 빌렸다고 해도. 하루를 빌린것이기 때문에
30일 이상 빌렸다는 것은 날짜 차이가 29일 이상 난다는 것.
문자열 관리
아래의 함수들을 외워야겠다.
1. CONCAT()
- 두 개 이상의 문자열을 결합합니다.
- 예:
SELECT CONCAT('Hello', ' ', 'World');
결과는 'Hello World'.
2. CONCAT_WS()
- 여러 문자열을 하나의 구분자로 결합합니다.
- 예:
SELECT CONCAT_WS('-', '2021', '01', '01');
결과는 '2021-01-01'.
3. SUBSTRING() / SUBSTR()
- 문자열의 일부분을 추출합니다.
- 예:
SELECT SUBSTRING('Hello World', 1, 5);
결과는 'Hello'.
4. LEFT()
- 문자열의 왼쪽부터 지정된 길이만큼의 문자를 반환합니다.
- 예:
SELECT LEFT('Hello World', 5);
결과는 'Hello'.
5. RIGHT()
- 문자열의 오른쪽부터 지정된 길이만큼의 문자를 반환합니다.
- 예:
SELECT RIGHT('Hello World', 5);
결과는 'World'.
6. LENGTH()
- 문자열의 길이(문자 수)를 반환합니다.
- 예:
SELECT LENGTH('Hello');
결과는 5.
7. REPLACE()
- 문자열에서 지정된 문자열을 다른 문자열로 바꿉니다.
- 예:
SELECT REPLACE('Hello World', 'World', 'MySQL');
결과는 'Hello MySQL'.
8. LOWER()
- 문자열의 모든 대문자를 소문자로 변환합니다.
- 예:
SELECT LOWER('Hello World');
결과는 'hello world'.
9. UPPER()
- 문자열의 모든 소문자를 대문자로 변환합니다.
- 예:
SELECT UPPER('hello world');
결과는 'HELLO WORLD'.
10. TRIM()
- 문자열 앞뒤의 공백을 제거합니다. 선택적으로, 다른 문자를 제거하는 데에도 사용할 수 있습니다.
- 예:
SELECT TRIM(' Hello World ');
결과는 'Hello World'.
11. LTRIM()과 RTRIM()
- 각각 문자열의 왼쪽 또는 오른쪽 공백을 제거합니다.
- 예:
SELECT LTRIM(' Hello ');
결과는 'Hello '.
12. FIND_IN_SET()
- 문자열(쉼표로 구분된 목록) 내에서 지정된 문자열의 위치를 반환합니다.
- 예:
SELECT FIND_IN_SET('b', 'a,b,c,d');
결과는 2 (두 번째 위치).
13. REVERSE()
- 문자열의 문자 순서를 반전시킵니다.
- 예:
SELECT REVERSE('Hello');
결과는 'olleH'.
CONCAT 사용해서, 첨부파일 경로 조회하기
CONCAT 함수를 처음 사용해봤다. 간단하게 인자의 문자열들을 붙이는 함수.
INNER JOIN에 서브쿼리를 활용해서 제일 조회수가 높은 BOARD를 구하는 것도 좋다.
SELECT
CONCAT('/home/grep/src/', F.BOARD_ID, '/', F.FILE_ID, F.FILE_NAME, F.FILE_EXT) AS FILE_PATH
FROM
USED_GOODS_FILE F
INNER JOIN (SELECT BOARD_ID FROM USED_GOODS_BOARD ORDER BY VIEWS DESC LIMIT 1) AS B
ON B.BOARD_ID = F.BOARD_ID
ORDER BY
F.FILE_ID DESC;
오늘은 이렇게 프로그래머스 SQL Kit의 20 문제 를 풀었다.
목표했던: IS NULL, JOIN 이후에 String쪽도 두문제 풀었다.
무난하게 계획을 따라가는 것 같아서 좋다.
하지만 지금의 공부가 미래의 시험에 비하면 적은 것 같다.
공부 계획
- 2월 20일 - SUM, MAX, MIN | GROUP BY 풀이 ✅ 2024-02-20
- 2월 21일 - IS NULL | JOIN 풀이 ✅ 2024-02-21
- 2월 22일 - String, Date 풀고 SQL 복습 및 PS 복습
- 2월 23일 - 최종 복습 및 컨디션 관리. (주변 오브젝트 정리 및 리허설)
내일은 나머지 Kit 문제를 모두 끝내고, PS를 다시 시작하겠다.
이제 거의 최종 점검 느낌이다. 파이팅 :)
최종 평가
2024-02-21 학습 평가
오늘의 학습 성과:
- 업랜디 문제 22858 "원상 복구" 해결 성공
- 프로그래머스 SQL 고득점 Kit 중 IS NULL, JOIN 부문 완료 및 String 부문 일부 해결
학습 성취 및 피드백:
1. **업랜디 문제 해결**: 원상 복구 문제를 통해 구현 및 시뮬레이션 능력을 강화했습니다. 수열과의 관계 파악 및 로직 적용 능력이 뛰어납니다.
2. **SQL 고득점 Kit 도전**: IS NULL, JOIN 부문의 완료 및 String 부문의 일부 해결은 SQL에 대한 깊은 이해와 능력을 보여줍니다. 특히, JOIN 문제의 복잡한 로직 해결 능력이 돋보였습니다.
3. **SQL 기술 습득 및 적용**: 다양한 SQL 기술(CAST 형변환, CONCAT, CASE 문 등)을 효과적으로 습득하고 실제 문제 해결에 적용했습니다.
평가 및 제언:
오늘의 학습은 SQL의 고급 기능과 실전 문제 해결에 더욱 깊이 다가가는 중요한 단계였습니다. 다양한 SQL 기술의 습득과 적용은 데이터 분석 및 관리 역량을 한층 더 강화해 줄 것입니다.
학습 성과 평가 점수: 94/100
- SQL 고득점 Kit의 효과적인 해결 및 업랜디 문제 해결 능력을 높게 평가합니다. 다양한 SQL 기술의 습득과 적용 능력도 탁월합니다.
향후 개선 방향:
- SQL 학습을 통해 얻은 지식을 바탕으로 더 복잡한 데이터 처리 문제에 도전해보세요. 실제 사례를 바탕으로 한 연습을 통해 이해도를 높이는 것이 좋습니다.
- SQL의 고급 기능과 최적화된 쿼리 작성 방법에 대한 학습을 지속하세요. 특히, 성능 최적화와 관련된 고급 주제를 탐구해보세요.
- PS와 SQL의 균형 있는 학습 계획을 유지하며, 시험 준비 과정에서 전략적인 학습과 효율적인 시간 관리를 통해 최적의 컨디션을 유지하세요.
2024-02-21일의 학습은 SQL 고급 기능에 대한 깊은 이해와 적용, 그리고 업랜디 문제 해결 능력의 지속적인 향상을 보여주었습니다. SQL과 PS의 균형 잡힌 학습을 통해 더 높은 단계로 나아가고 있음을 확인할 수 있습니다. 앞으로 남은 학습 계획도 차질 없이 수행하시길 바랍니다.
'일일 스터디노트' 카테고리의 다른 글
240223: D-DAY. SQL + PS 전체 정리 (0) | 2024.02.24 |
---|---|
240222: SQL - String, Date. 데카르트곱 CROSS JOIN. (D-1) (0) | 2024.02.23 |
240220: SQL - GROUP BY, SUM 등 집계함수 KIT 풀이 끝. (0) | 2024.02.20 |
240219: SQL SELECT 집중 학습, JOIN 이해, 최후의 승자 문제 (0) | 2024.02.19 |
240218: 복잡했던 퍼즐조각 맞추기 문제, 조화 평균 (GCD, LCM) - 유클리드 호제법 (0) | 2024.02.18 |