[SQL Study] 쿼리 '잘' 짜는 PO되기 / SELF JOIN (Feat. 인프런-중급)
원래 매일 쿼리 짜려고 했는데 주말엔 노느라 못 짜고, 평일엔 일에 치여 힘들어서 못 짜고
강의 유효기간은 이틀 남았고, 오늘은 백신휴가고여서 하루 종일 쿼리 강의를 들어보려고 마음 먹었다.
SELF JOIN
- Self Join은 어렵게 생각할 필요 없이, 하나의 테이블을 둘로 쪼개서 값을 구하는 것이라고 이해하면 된다.
- 문제 - 181번 Inner Join 연습
The Employee table holds all employees including their managers. Every employee has an Id, and there is also a column for the manager Id.
| Id | Name | Salary | ManagerId |
| 1 | Joe | 70000 | 3 |
| 2 | Henry | 80000 | 4 |
| 3 | Sam | 60000 | NULL |
| 4 | Max | 90000 | NULL |
Given the Employee table, write a SQL query that finds out employees who earn more than their managers. For the above table, Joe is the only employee who earns more than his manager.
- 정답
SELECT
Employee.Name AS Employee
FROM Employee
INNER JOIN Employee AS Manager ON Employee.ManagerId = Manager.Id
WHERE Employee.Salary > Manager.Salary
사실 ID와 ManagerID를 매핑하는게 어려웠다. 그래서 중간까지 Inner Join 에서 막혀서 강의를 듣고 다음날 다시 풀어봤다.
역시나 Inner join 에서 막혔는데 특히 표를 상상하는게 어려웠다.
그래서 아래와 같이 코드를 짜고, 테이블을 살펴봤다.
SELECT
*
FROM Employee
INNER JOIN Employee AS Manager ON Employee.ManagerId = Manager.Id
ID | Name | Salary | ManagerId | Id | Name | Salary | MangerID |
1 | Joe | 70000 | 3 | 3 | Sam | 60000 | null |
2 | Henry | 80000 | 4 | 4 | Max | 90000 | null |
음, Employee 테이블의 ManagerId와 Manager 테이블의 ID를 InnerJoin 하면 이런 형태군.
그럼 Employee 테이블의 Id와 Manager테이블의 ManagerID를 바꿔서 조인하면 어떻게 나오지?
SELECT
*
FROM Employee
INNER JOIN Employee AS Manager ON Employee.Id = Manager.ManagerId
ID | Name | Salary | ManagerId | Id | Name | Salary | MangerID |
3 | Sam | 60000 | null | 1 | Joe | 70000 | 3 |
4 | Max | 90000 | null | 2 | Henry | 80000 | 4 |
테이블이 반대로 나와서 조금 헷갈린다.
그래도 이럴 경우, Where절을 테이블에 맞게 잘 셋팅하면 정답이 나올 것 같다.
아마 위와 같은 테이블에선 이런 where절을 쓰면 맞지 않을까?
SELECT
Manager.Name AS Employee
FROM Employee
INNER JOIN Employee AS Manager ON Employee.Id = Manager.ManagerId
WHERE Employee.Salary < Manager.Salary
위와 같이 쿼리를 짜면 정답은 나오지만, Join 할 때 테이블이 잘 상상되지 않아 쿼리 짜기가 어렵다.
Manager 테이블에 결국 Employee 가 배치되어 있고, 실제 매니저는 Employee 테이블에 존재하는 것 처럼 보이니
Select 문으로 원하는 값을 출력하거나 조건문을 짤 때 테이블이 잘 상상되지 않는 어려움이 있을 수 있을 것 같다.
그럼 Employee.Id와 ManagerId를 Inner Join하면 어떤 형태가 되지?
그냥 같은 테이블을 두 번 반복하는 결과가 나온다. 쓸데 없는 짓이었다는 것을 깨닫는다.
SELECT
*
FROM Employee
INNER JOIN Employee AS Manager ON Employee.Id = Manager.Id
ID | Name | Salary | ManagerId | Id | Name | Salary | MangerID |
1 | Joe | 70000 | 3 | 1 | Joe | 70000 | 3 |
2 | Henry | 80000 | 4 | 2 | Henry | 80000 | 4 |
3 | Sam | 60000 | null | 3 | Sam | 60000 | null |
4 | Max | 90000 | null | 4 | Max | 90000 | null |
또 궁금한 건 Employee와 Manager 테이블에서 MagerId로 Join 하면? (궁금한 건 다 해봐야 하는 편..)
ManagerId로 조인할 경우, 결국 똑같은 테이블을 똑같이 오른쪽에 붙여서 출력하라는 샘이라 위와 다를바 없다.
결국 어떤 KEY 로 조인을 할 건지가 명확해야 쿼리 짜기도 편하다는 결론이 나온다.
SELECT
*
FROM Employee
INNER JOIN Employee AS Manager ON Employee.MangerId = Manager.ManagerId
ID | Name | Salary | ManagerId | Id | Name | Salary | MangerID |
1 | Joe | 70000 | 3 | 1 | Joe | 70000 | 3 |
2 | Henry | 80000 | 4 | 2 | Henry | 80000 | 4 |
여하튼 여러 테이블을 출력하면서, Inner Join 이 어떤 형태로 Join 되는지에 대한 명확한 이해를 하기 위해 노력했다.
실제 업무를 하며 쿼리를 짤 때도 이런식으로 쿼리를 날려보며, 테이블을 보며 상상해보는게 가장 적합할 것 같다. (물론 사고가 빠른편이라면 쿼리도 한방에 짤 수 있지만 전 사고가 매우 느립니다. )
자 그럼 Join 과 관련된 다음 리트코드 문제 주세요!
Table: Weather
| Column Name | Type |
| id | int |
| recordDate | date |
| temperature | int |
id is the primary key for this table. This table contains information about the temperature in a certain day.
Write an SQL query to find all dates' id with higher temperature compared to its previous dates (yesterday).
Return the result table in any order.
The query result format is in the following example:
Weather
| id | recordDate | Temperature |
| 1 | 2015-01-01 | 10 |
| 2 | 2015-01-02 | 25 |
| 3 | 2015-01-03 | 20 |
| 4 | 2015-01-04 | 30 |
Result table:
| id |
| 2 |
| 4 |
In 2015-01-02, temperature was higher than the previous day (10 -> 25).
In 2015-01-04, temperature was higher than the previous day (20 -> 30).
문제만 봐도 읽고 싶지 않아요.. (피무룩...)
Weather라는 테이블이 있고, 온도 정보를 담고 있고 기본키는 ID라네요.
이전 날짜와 비교해서 더 높은 온도를 갖고 있는 행을 찾으래요. Weather테이블을 보니 2번과 4번이 이전 날짜보다 온도가 높네요.
id를 출력하라고 했으니, id만 출력하면 2,4가 나와야 해요. 이제 시작해볼까요.... (엄 두 안 남)
SELECT
*
FROM Weather
지금까지 배운 걸 바탕으로 Inner Join을 시도하려 했으나, 이전 날짜와 어떻게 비교해야할지 한참을 테이블만 보며 고민하다가
결국 강의 듣고 알아버렸다.. (혼자는 못 풀었다는 의미 흐흐...)
Inner Join으로 id에 +1을 더해 다음날과 비교하는 테이블을 짰다.
SELECT
Today.id
FROM Weather As Today
INNER JOIN Weather As Yesterday On Yesterday.id+1 = Today.id
그럼 아래와 같은 테이블이 나오는데, 여기서 Today와 Yesterday의 Temperature만 비교해서 더 높은 온도를 가진
ID를 출력하면 바로 정답이 나온다.
시간을 갖고 조금 더 고민했으면 혼자 충분히 풀 수 있을 법한 문제였는데 아직 못 들은 강의가 백개 정도 남아 마음이 급했다.
Id | RecordDate | Temperature | Id | RecoreDate | Temperature |
2 | 2015-01-02 | 25 | 1 | 2015-01-01 | 10 |
3 | 2015-01-03 | 20 | 2 | 2015-01-02 | 25 |
4 | 2015-01-04 | 30 | 3 | 2015-01-03 | 20 |
SELECT
Today.id
FROM Weather As Today
INNER JOIN Weather As Yesterday On Yesterday.id+1 = Today.id
WHERE Today.Temperature > Yesterday.Temperature
이렇게 풀고 submit을 눌렀는데 아니 이게 머선129
왜 틀렸는지 설명은 해주지만, 내 쿼리에서 뭘 수정해야 할 지 모르겠어서 또 강의 재생..ㅎ
아, 날짜에 테스트 값을 거꾸로 넣어놔서 Id기준으로 +1을 하면 오답이 나오는 경우가 발생해서 틀린 것이었다.
이럴 경우, Join 은 Date로 해야하는데 Date로 Join을 하려면 아래와 같이 함수를 사용해줘야 한다고 한다.
SELECT
Today.id
FROM Weather As Today
INNER JOIN Weather As Yesterday On DATE_ADD(Yesterday.recorddate, INTERVAL 1 DAY) = Today.recorddate
WHERE Today.Temperature > Yesterday.Temperature
조건문에 ON DATE_ADD 함수를 사용해야 한다.
하루의 차이가 나니, yesterday테이블의 recorddate를 기준값으로 두고 INTERVAL 1 DAY라는 함수를 써준다.
그리고 이 조건과 Today 테이블의 recorddate와 일치하는 값을 출력하라는 쿼리를 작성해주면 아래와 같다.
INNER JOIN Weather As Yesterday On DATE_ADD(Yesterday.recorddate, INTERVAL 1 DAY) = Today.recorddate
자, 여기까지 작성하고 다시 제출..!
통과되었다.
INNER JOIN을 과연 내가 실무에서 써먹을 수 있을지 자신이 없다.
오늘의 쿼리 공부는 여기까지 -