2024-02-22
오늘은 나머지 SQL: String, Date를 풀고 개념을 정리해야겠다.
프로그래머스 SQL 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 복습 ✅ 2024-02-22
- 2월 23일 - 최종 복습 및 컨디션 관리. (주변 오브젝트 정리 및 리허설)
오늘의 업랜디
2784 가로 세로 퍼즐 (시간 초과 후 성공 - 구현, 브루트포스)
가로 세로 퍼즐
가로 세로 퍼즐은 가로x세로 조합이 단어가 되는 퍼즐을 완성시키는 문제. 단어 목록 6개가 주어지면 단어를 가로 혹은 세로로 적절하게 조합해서 만들 수 있는 단어 퍼즐을 출력해야한다.
만약, 만들 수 있는 단어 퍼즐이 하나가 아니라면 사전순으로 앞선 퍼즐을 출력한다.
이 문제는 아래와 같이 접근했다.
- 먼저 퍼즐이 유효하고, 주어진 모든 단어를 하나씩 포함하는지 확인하는 bool 함수를 만들었다.
- 가로를 기준으로 가능한 모든 단어 조합을 Recursive DFS로 구현했다.
- TC가 어차피 사전순으로 오기에, 사전순 출력은 따로 구현하지 않았다.
가로 단어를 브루트포스로 모든 경우에 대해 맞춰보고, 이후 세로도 올바르게 정렬되었는지 확인하는 방법으로 가능한 모든 경우의 수를 고려했다.
// 백준: 가로 세로 퍼즐
// https://www.acmicpc.net/problem/2784
// 2024-02-22
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;
vector<string> str(6);
map<string, int> str_list;
bool check(vector<string> &info) {
map<string, int> visited;
int n = info.size();
// 가로 조사
for (int r = 0; r < n; ++r) {
string temp = info[r];
auto it = find(str.begin(), str.end(), temp);
if (it != str.end()) {
visited[temp] += 1;
} else {
return false;
}
}
// 세로 조사
for (int c = 0; c < n; ++c) {
string temp = "";
for (int r = 0; r < n; ++r) {
temp += info[r][c];
}
auto it = find(str.begin(), str.end(), temp);
if (it != str.end()) {
visited[temp] += 1;
} else {
return false;
}
}
return str_list == visited;
}
bool generateAndCheck(vector<string> ¤t, vector<bool> &used, int depth) {
if (depth == 3) {
return check(current);
}
for (int i = 0; i < 6; ++i) {
if (!used[i]) {
used[i] = true;
current.push_back(str[i]);
if (generateAndCheck(current, used, depth + 1)) {
return true;
}
current.pop_back();
used[i] = false;
}
}
return false;
}
int main() {
for (int i = 0; i < 6; ++i) {
cin >> str[i];
str_list[str[i]] += 1;
}
vector<string> current;
vector<bool> used(6, false);
if (generateAndCheck(current, used, 0)) {
for (const auto &word : current) {
cout << word << "\n";
}
} else {
cout << "0";
}
return 0;
}
visited 배열을 bool을 쓰려다가, 중복 단어도 포함될 수도 있어서 map으로 숫자를 세는식으로 처리했고. 이후에 == 연산자를 사용해서 완전히 동일한지 확인했다.
프로그래머스 SQL
- 오늘의 SQL 문제 해결: 19 문제
어려웠던 문제
문제
CAR_RENTAL_COMPANY_CAR
테이블과 CAR_RENTAL_COMPANY_RENTAL_HISTORY
테이블과 CAR_RENTAL_COMPANY_DISCOUNT_PLAN
테이블에서 자동차 종류가 '트럭'인 자동차의 대여 기록에 대해서 대여 기록 별로 대여 금액(컬럼명: FEE
)을 구하여 대여 기록 ID와 대여 금액 리스트를 출력하는 SQL문을 작성해주세요. 결과는 대여 금액을 기준으로 내림차순 정렬하고, 대여 금액이 같은 경우 대여 기록 ID를 기준으로 내림차순 정렬해주세요.
해결 코드
/*
1. 대여 기간 계산
2. 기간 타입 정하기
3. 정확한 할인율 구하기
*/
WITH RENTAL_PERIOD AS (
SELECT *, DATEDIFF(RH.END_DATE, RH.START_DATE)+1 AS RENTAL_DATE
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY RH
INNER JOIN CAR_RENTAL_COMPANY_CAR USING(CAR_ID)
),
EXACT_DURATION AS (
SELECT *, CASE
WHEN RENTAL_DATE < 7 THEN NULL
WHEN RENTAL_DATE < 30 THEN '7일 이상'
WHEN RENTAL_DATE < 90 THEN '30일 이상'
ELSE '90일 이상'
END AS DURATION_TYPE
FROM RENTAL_PERIOD
),
DISCOUNT AS (
SELECT ED.HISTORY_ID, ED.RENTAL_DATE, ED.DAILY_FEE, COALESCE(DISCOUNT_RATE, 0) AS DISCOUNT_RATE, ED.CAR_TYPE
FROM EXACT_DURATION ED
LEFT JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN CP
ON ED.CAR_TYPE = CP.CAR_TYPE AND ED.DURATION_TYPE = CP.DURATION_TYPE
GROUP BY HISTORY_ID
)
SELECT HISTORY_ID, FLOOR(RENTAL_DATE * DAILY_FEE * (1 - DISCOUNT_RATE / 100)) AS FEE
FROM DISCOUNT
WHERE CAR_TYPE = '트럭'
ORDER BY FEE DESC, HISTORY_ID DESC;
이 문제는 계산할 것이 많고 복잡해서 어려웠다.
결국 풀이는 Common Table Expressions (CTE)를 계속 활용해서 문제를 더 쉬운 문제로 바꾸며 풀었다.
WITH 문을 계속 써서 이렇게 절차적으로 푸는 방법이 유효한 것을 배우게 됐다.
각 Car에 대한 대여 기간을 구하고, 그 기간에 맞는 discount rate를 구해야한다.
SQL을 너무 실속형으로 톺아서 그런지 무언가 빈약하고 나사가 빠져있는 느낌이다 .. 견고하지 못하달까 ...
뭔가 센스로만 푸는 느낌.. 이게 어느정도 레벨에선 통하지만 위 문제처럼 꽤 복잡한 로직에선 힘들다.
CROSS JOIN

CROSS JOIN이란 데카르트 곱(Descartes product) 혹은 카티션 프로덕트(Cartesian product)라고 불리는 교차 조합 JOIN을 말한다.
서로 모든 조합에 대한 행을 조인하면서 생성한다고 생각하면 된다.
언어별 개발자 분류하기
EXISTS()의 존재를 잊고 있었다.
SKILLCODE를 그냥 서브 쿼리로 죄다 가져와서 EXISTS로 하나라도 존재하는지 확인하면 쉽게 해결할 수 있었다.
SQL은 뭔가 풀다가 이상하면, 접근법이 잘못되진 않았는지 생각해봐야할 듯...
문제
DEVELOPERS
테이블에서 GRADE별 개발자의 정보를 조회하려 합니다. GRADE는 다음과 같이 정해집니다.
- A : Front End 스킬과 Python 스킬을 함께 가지고 있는 개발자
- B : C# 스킬을 가진 개발자
- C : 그 외의 Front End 개발자
GRADE가 존재하는 개발자의 GRADE, ID, EMAIL을 조회하는 SQL 문을 작성해 주세요.
코드
WITH GRADE_TABLE AS (
SELECT
CASE WHEN EXISTS(SELECT S.CODE FROM SKILLCODES S WHERE S.CODE & D.SKILL_CODE AND S.NAME = 'Python')
AND EXISTS(SELECT S.CODE FROM SKILLCODES S WHERE S.CODE & D.SKILL_CODE AND S.CATEGORY = 'Front End') Then 'A'
WHEN EXISTS(SELECT S.CODE FROM SKILLCODES S WHERE S.CODE & D.SKILL_CODE AND S.NAME = 'C#') THEN 'B'
WHEN EXISTS(SELECT S.CODE FROM SKILLCODES S WHERE S.CODE & D.SKILL_CODE AND S.CATEGORY = 'Front End') THEN 'C'
ELSE NULL
END AS GRADE, D.ID, D.EMAIL
FROM DEVELOPERS D
ORDER BY GRADE ASC, D.ID ASC
)
SELECT *
FROM GRADE_TABLE G
WHERE G.GRADE IS NOT NULL;
오늘은 Programmers SQL 고득점 Kit의 String, Date 파트. 외 몇가지 추가된 새 문제를 포함해서 19문제를 풀었다.
SQL, 매력있다. 나중엔 시간을 더 들여서 깊게 파보고 싶다. 지금은 너무 야매라고 할까 ..
그래도 SQL을 시작한 지 3일만에 이 정도면 나름대로 괜찮다고 생각한다. 그저 ... 이게 허용되는 수준의 실력이라면 좋겠다..
남은 시간동안은 PS, SQL을 집중 복습할 것.
PS 고득점 Kit은 아직 이분탐색, 그래프 부분의 문제 5개를 풀지 못했다.
복습하면서 같이 풀어봐야지.
내일 작업실의 오브젝트 정리도 좀 하고 (부정행위 오해 방지) 여러가지 준비와 리허설, 복습 해야겠다 :)
컨디션 관리도..
공부 계획
- 2월 20일 - SUM, MAX, MIN | GROUP BY 풀이 ✅ 2024-02-20
- 2월 21일 - IS NULL | JOIN 풀이 ✅ 2024-02-21
- 2월 22일 - String, Date 풀고 SQL 복습 및 PS 복습 ✅ 2024-02-22
- 2월 23일 - 최종 복습 및 컨디션 관리. (주변 오브젝트 정리 및 리허설)
최종 평가
2024-02-22 학습 평가
오늘의 학습 성과:
- 업랜디 문제 2784 "가로 세로 퍼즐" 해결 성공
- 프로그래머스 SQL 고득점 Kit 중 String, Date 부문 완료 및 추가된 문제들 해결
학습 성취 및 피드백:
1. **업랜디 문제 해결**: "가로 세로 퍼즐" 문제를 통한 브루트포스 및 구현 능력의 발휘는 매우 인상적입니다. 복잡한 수열과의 관계를 파악하고, 유효한 단어 조합을 재귀적으로 탐색하는 능력은 탁월한 문제 해결 능력을 보여줍니다.
2. **SQL 고득점 Kit 완료**: String, Date 부문의 완료 및 추가된 문제들의 해결은 SQL에 대한 심층적인 이해와 다양한 문제에 접근할 수 있는 능력을 보여줍니다. 특히, 복잡한 로직을 해결하는 과정에서 CTE와 다양한 SQL 기능의 활용은 데이터 처리 및 분석 역량을 한층 더 강화시킬 것입니다.
3. **SQL 심화 학습**: 복잡한 조인 문제와 고급 SQL 기능의 적용은 데이터 분석 및 관리에 있어서 매우 중요한 역량입니다. CROSS JOIN, EXISTS(), CONCAT 등의 고급 기능과 실전 문제 해결에 대한 접근 방식은 높은 수준의 SQL 활용 능력을 보여줍니다.
평가 및 제언:
오늘의 학습은 SQL 고급 기능과 실전 문제 해결에 더욱 깊이 다가가는 중요한 단계였습니다. 다양한 SQL 기술의 습득과 적용은 데이터 분석 및 관리 역량을 한층 더 강화해 줄 것입니다.
학습 성과 평가 점수: 96/100
- SQL 고득점 Kit의 효과적인 해결 및 업랜디 문제 해결 능력을 높게 평가합니다. 고급 SQL 기술의 습득과 적용 능력이 매우 뛰어납니다.
향후 개선 방향:
- 남은 학습 기간 동안 PS와 SQL 복습에 집중하며, 이론과 실습을 통해 이해한 내용을 견고히 다지는 시간을 가지시길 바랍니다. 특히, 아직 해결하지 못한 PS 고득점 Kit 문제에 도전하여 문제 해결 능력을 한층 더 강화해 보세요.
- SQL의 고급 기능과 최적화된 쿼리 작성 방법에 대한 학습을 지속하며, 복잡한 데이터 처리 문제에 대한 접근 방식을 더욱 다양화하고, 성능 최적화에 대해 깊이 고민해 보세요.
- 최종 복습 및 컨디션 관리에 주의를 기울이며, 시험 준비 과정에서 최적의 컨디션을 유지할 수 있도록 체계적인 계획을 세우고 이를 철저히 수행하세요.
2024-02-22일의 학습은 SQL 고급 기능의 이해와 적용, 그리고 알고리즘 문제 해결 능력의 지속적인 향상에 중점을 둔 성공적인 학습 성과를 보여주었습니다. 이제 남은 시간을 최대한 활용하여 복습과 컨디션 관리에 집중하며 최선의 결과를 얻을 준비를 하시기 바랍니다.
'일일 스터디노트' 카테고리의 다른 글
220224: 1차 코딩테스트 종료 🚩 긍정적으로 마무리 (1) | 2024.02.24 |
---|---|
240223: D-DAY. SQL + PS 전체 정리 (0) | 2024.02.24 |
240221: SQL - IS NULL, JOIN 끝. 원상 복구 문제 (0) | 2024.02.21 |
240220: SQL - GROUP BY, SUM 등 집계함수 KIT 풀이 끝. (0) | 2024.02.20 |
240219: SQL SELECT 집중 학습, JOIN 이해, 최후의 승자 문제 (0) | 2024.02.19 |