제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
접은 글을 통해 먼저 답변을 해보시고 제가 정리한 답을 확인해보시기 바라겠습니다!!

면접 리스트

소켓이란 무엇인가요?

더보기

응용 프로그램에서 TCP/IP 기반으로 생성하는 것으로 응용프로그램과 transport layer를 연결해주는 역할로, 두 머신이 네트워크를 통해 서로 통신할 수 있도록 양쪽에 생성되어 서로 다른 프로세스가 양방향, 실시간 통신을 할 수 있게 해주는 interface입니다.

소켓이 구현됨으로써, 네트워크 및 전송 계층의 캡슐화가 가능해집니다.

쿠키와 세션의 차이를 설명해주세요

더보기

HTTP 프로토콜은 connectionless를 지향하고 stateless protocol로써 리소스의 낭비를 줄여주지만 매 통신마다 클라이언트가 인증이 필요합니다.

 

쿠키와 세션은 이 단점을 보완하는 기술인데 두 기술의 가장 큰 차이는 저장위치로, 쿠키는 클라이언트세션은 서버에 저장됩니다. 그래서 쿠키는 빠르지만 보안에 취약하고, 세션은 느리지만 상대적으로 보안성이 좋습니다.

 

또 쿠키는 브라우저가 종료되어도 남아있지만 세션은 삭제된다는 차이가 있습니다. 따라서 보안성이 중요할때에는 세션을, 종료시에 유지되기 위해서는 쿠키를 사용해야합니다. 하지만 세션의 경우 서버의 자원을 사용하는 것이므로 사용자가 많아지면 자원 관리면에서 문제가 발생할 수 있습니다.

세션과 JWT Token을 비교해서 설명해주세요

더보기

세션은 서버에서 사용자의 id와 pw를 비교하여 세션 저장소에서 세션 id를 넘겨주고 사용자의 정보를 저장합니다. 클라이언트가 서버에게 정보를 보낼때 쿠키에 세션 id를 포함해서 같이 보내 서버는 세션 저장소에서 사용자임을 알 수 있고, 이전에 사용자가 했던 통신을 이어서 할 수 있게 합니다.

 

JWT는 서버에서 발급하는 것으로, 따로 저장소 없이 사용자의 고유 id 값을 보유하고 서버는 토큰을 검증 이후 조작 여부와 유효기간을 확인합니다. 검증이 완료되면 payload를 디코딩하여 사용자의 id에 맞는 데이터를 가져옵니다.

 

가장 큰 차이는 Session 저장소에 유저의 정보를 넣는 반면 JWT는 토큰 안에 유저의 정보를 넣습니다.

TCP가 어떻게 신뢰성을 보장하나요?

더보기
  1. 연결 설정: TCP 3방향 핸드셰이크는 송신자와 수신자 사이의 연결을 설정하는 데 사용됩니다. 여기에는 송신자의 SYN(동기화) 패킷, 수신자의 SYN-ACK(동기화-확인) 패킷 및 송신자의 ACK(확인) 패킷이 포함됩니다.
  2. 시퀀스 번호: TCP는 전송된 각 데이터 바이트에 고유한 시퀀스 번호를 할당합니다. 이렇게 하면 수신기가 올바른 순서로 데이터를 재구성할 수 있습니다.
  3. ACK: 수신기는 ACK 패킷을 전송하여 데이터의 성공적인 수신을 확인합니다. 보낸 사람이 지정된 시간 초과 기간 내에 ACK를 수신하지 않으면 승인되지 않은 데이터를 재전송합니다.
  4. Selective ack 및 Go-Back-N: TCP는 선택적 반복 또는 Go-Back-N 메커니즘을 사용하여 손실되거나 순서가 잘못된 패킷을 처리할 수 있습니다. Selective ack 을 통해 수신기는 특정 손실 패킷을 승인하고 재전송할 수 있는 반면 Go-Back-N손실된 패킷 이후 모든 후속 패킷을 재전송해야 합니다.

TCP가 어떻게 흐름제어를 구현하나요? 그리고 윈도우 사이즈는 무엇인가요?

더보기

TCP는 Sliding window를 이용하여 흐름제어를 구현합니다. 수신 측에서 설정한 윈도우 크기만큼 송신측에서 ACK 없이 세그먼트를 전송할 수 있게 데이터를 동적으로 조절하는 기법입니다. 수신측은 윈도우 사이즈가 바뀔때마다 송신측에게 윈도우 사이즈를 보낼 수 있고, 송신측은 그에 따른 데이터 바이트 크기만큼 확인 응답없이 계속해서 보낼 수 있습니다. 만약 ACK가 왔다면 그만큼 윈도우를 이동하면 됩니다.

 

윈도우 사이즈는 수신측에서 받을 수 있는 바이트 수를 의미하는데 호스트들은 데이터를 보내기 전에 윈도우 사이즈 크기를 수신 호스트의 윈도우 사이즈 크기에 맞춥니다.

 

따라서 TCP는 window size에 따라 sliding window로 흐름 제어를 구현합니다.

TCP 재전송 매커니즘을 설명해보세요

더보기

먼저 타임아웃이 있습니다. 송신측에서 설정한 RTO보다 RTT가 더 크면 패킷이 타임아웃되어 손실되었다고 생각하여 패킷을 다시 보냅니다. 해당 방법은 동작이 느리므로 네트워크의 지연을 초래할 수 있습니다.

 

그 대안으로 Fast Retransmission이 있습니다. Fast Retransmission은 중복된 ACK가 세개가 되었을 시에 송신측에서 패킷이 유실되었다고 하여 패킷을 다시 보내는 것을 말합니다. 또 패킷을 보낼때에는 Go-back-N 방식이 아닌 Selective Retransmission으로 네트워크 리소스를 절약하고 효율성을 향상 시킬 수 있습니다.

TCP 혼잡제어를 어떻게 관리하나요? 네트워크 혼잡을 예방하기 위한 메커니즘은 무엇인가요?

더보기

TCP는 혼잡제어를 AIMD와 slow start가 있습니다.

 

AIMD는 처음 패킷을 하나씩 보내고 문제없이 도착하면 window size를 1씩 증가시켜 전송하는 방식입니다. 만약 패킷 전송에 실패하면 그때의 패킷을 절반으로 줄이고 다시 window 사이즈를 1씩 증가합니다. 하지만 이 방법은 초기 네트워크에 많은 리소스를 사용하지 못하므로 많은 시간이 걸립니다.

 

slow start는 전송 속도를 늦게 올리는 AIMD와 다르게 window size를 지수형태로 증가합니다. 만약 혼잡현상이 발생하면 window 사이즈를 1로 떨어뜨리고 다음 1씩 증가합니다. fast recovery도 있는데 혼잡현상이 발생했을 때 window 사이즈를 1로 낮추는 것이 아닌, window 사이즈를 반으로 낮추어 그 window size에서 1씩 증가하는 방법입니다.

'기술면접 > 네트워크' 카테고리의 다른 글

[기술면접] 네트워크 5  (3) 2024.11.14
[기술면접] 네트워크 4  (4) 2024.11.10
[기술면접] 네트워크 3  (1) 2024.11.10
[기술면접] 네트워크 1  (2) 2024.11.06
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
접은 글을 통해 먼저 답변을 해보시고 제가 정리한 답을 확인해보시기 바라겠습니다!!

면접 리스트

OSI 7 계층과 TCP/IP 4 계층의 차이점은 무엇인가요?

더보기

TCP/IP 4 계층과 달리 OSI 7 계층애플리케이션 계층을 세개로 쪼개고 링크 계층을 데이터 링크 계층, 물리 계층으로 나눕니다. 또 OSI 7계층에서는 인터넷 계층을 네트워크 계층이라고 부릅니다.

HTTP/1.1와 HTTP/2를 설명하고 차이점은 무엇인가요?

더보기

HTTP/1.1은 매번 TCP 연결을 하는 것이 아닌 한번의 연결 이후 여러개의 파일을 송수신 할 수 있는 프로토콜입니다. 핸드셰이크가 없어져 오버헤드가 적어지지만 리소스가 많아질 수록 대기시간이 길어지는 단점이 있습니다.

HTTP/2는 HTTP/1.x보다 지연시간을 줄이고 응답시간을 더 빠르게 하는 프로토콜로 멀티 플렉싱, 헤더 압축 등 요청의 우선순위를 처리하는 프로토콜입니다. 장점으로는 멀티 플렉싱과 서버 푸시가 있습니다. 멀티 플렉싱은 여러개의 스트림을 사용하여 송수신하고 서버 푸시란 클라이언트의 요청 없이 서버가 클라이언트로 데이터를 먼저 줄 수 있습니다.

www.google.com을 주소창에 입력하면 어떻게 될까요?

더보기
  1. 브라우저에 www.goggle.com을 친다.
    1. 브라우저는 사용자가 주소창에 입력한 정보가 사이트 주소인지 검색어인지 확인한다.
  2. 브라우저는 캐싱된 DNS 기록을 통해 www.google.com에 대응되는 ip주소가 있는지 확인한다.
    1. DNS ⇒ 브라우저가 인터넷 자원을 로드할 수 있도록 도메인 이름을 IP주소로 변환한다.
    2. 과정
      1. 브라우저 캐시를 확인한다.
        1. 브라우저는 일정기간동안의 DNS 기록을 가지고 있다.
      2. 브라우저는 OS캐시를 확인한다.
        1. 브라우저 캐시에서 웹 사이트의 이름의 ip 주소가 발견이 안될경우 system call을 통해 OS가 저장하는 DNS 기록들의 캐시에 접근한다.
      3. Router 캐시를 확인한다.
        1. 컴퓨터에 DNS 기록을 찾지 못하면 브라우저는 DNS 기록을 캐싱하는 Router와 통신하여 찾는다
      4. ISP 캐시를 확인한다.
        1. DNS 서버를 구축하고 있는 ISP에서 DNS 기록을 찾는다.
    3. 캐시의 이유 ⇒ 네트워크 트래픽을 조절하고 데이터 전송시간을 줄일 수 있다.
  3. 요청한 URL 캐시가 없으면 ISP의 DNS 서버가 www.google.com을 호스팅하고 있는 서버의 IP 주소를 찾기 위해 DNS query를 보낸다.
    1. DNS query
      1. 여러 다른 서버들을 검색하여 특정 사이트의 IP 주소를 찾는다. ⇒ recursive search
      2. IP 주소를 찾을 때까지 DNS 서버에서 다른 DNS 서버를 오가면서 반복적으로 검색하던지 찾지 못해서 에러가 발생할 때까지 검색을 진행.
    2. ISP의 DNS 서버
      1. DNS recursor라고 부른다
      2. 인터넷을 통해 다른 DNS 서버들에게 물어 도메인 이름의 올바른 IP 주소를 찾는 책임을 가진다.
      3. 다른 DNS서버(name server)는 계층적으로 관리한다.
      4. www.google.com
        • DNS recursor가 root name server에 연락한다
        • root name server는 `.com 도메인 name server로 redirect한다
        • .com name server는 google.com name server로 redirect한다
        • google.com name server 는 DNS 기록에서 www.google.com 에 매칭되는 IP주소를 찾고, DNS recursor로 보낸다
          • 위 모든 요청들은 작은 데이터 패킷을 통해 보내진다.
            • 패킷 = 보내는 요청의 내용 + DNS recursor 의 IP 주소 으로 이뤄짐
            • 패킷들은 원하는 DNS 기록을 가진 DNS 서버에 도달할 떄까지 클라이언트와 서버를 여러 번 오간다
            • 이때 패킷들은 routing table에 기반하여 가장 빠른 경로로 움직인다.
            • 도중에 패킷이 loss되면 request fail error가 발생한다
  4. 브라우저가 서버와 TCP connection을 한다
    1. 브라우저는 올바른 IP 주소를 받게 되면 서버와 connection을 한다.
    2. 인터넷 프로토콜
      1. 브라우저는 인터넷 프로토콜 중 TCP를 사용하여 서버와 연결한다.
      2. TCP/IP 3-way handshake
        1. 클라이언트와 서버간의 데이터 패킷들이 오고 가기 위해 TCP-Connection이 되어야 한다.
        2. TCP/IP 3-way handshake 프로세스를 통해 클라이언트와 서버간의 connection이 이뤄진다.
        3. 클라이언트와 서버가 SYN과 ACK 메시지들로 3번의 프로세스를 거친 후 연결된다.
          1. 클라이언트 머신이 SYN 패킷을 서버에 보내고 connection을 open해달라고 요청한다
          2. 서버가 새로운 connection을 시작할 port가 있다면 SYN/ACK 패킷으로 응답한다
          3. 클라이언트는 SYN/ACK 패킷을 서버로부터 받게 되면 서버에게 잘 받았다는 의미로 ACK 패킷을 응답한다
  5. 브라우저가 웹 서버에 HTTP 요청을 한다.
    1. TCP connection이 완성되면 데이터를 전송한다
    2. Request
      1. 클라이언트의 브라우저는 GET 방식으로 www.google.com 웹 페이지를 요청한다
      2. 요청시 다른 부가적인 정보들도 함께 전달된다.
        1. Accept 헤더: 받아들이는 요청의 종류
        2. user-agent 헤더: browser identification
        3. connection 헤더: 추가적 요청을 위해 TCP Connection dbwlfmf dycjd
        4. 브라우저에서 얻은 쿠키 정보
  6. 서버가 요청을 처리하고 Response를 생성한다.
    1. 서버는 Apache와 같은 웹 서버를 가지고 있다.
      1. 웹 서버는 브라우저로부터 Request를 받고 Request Handler에게 전달하여 Request를 읽고 Response를 생성하게 된다.
    2. Request handler
      1. Request, request의 헤더, 쿠키를 읽어 요청을 파악한다.
      2. 필요에 따라 서버에 정보를 업데이트한다.
      3. Response를 특정 포맷으로 작성한다
  7. 서버가 HTTP Response를 보낸다,
    1. 첫 줄에는 Response의 상태를 의미하는 status code가 나타난다
    • 1xx은 정보만 담긴 메세지라는 것을 의미한다
    • 2xx response가 성공적이라는 것을 의미한다
    • 3xx 클라이언트를 다른 URL로 리다이렉트함을 의미한다
    • 4xx 클라이언트 측에서 에러가 발생했음을 의미한다
    • 5xx 서버 측에서 에러가 발생했음읠 의미한다
  8. 브라우저가 HTML content를 보여준다.

 

정리[네트워크 기초]

더보기

네트워크란 노드와 링크가 서로 연결되어 있으며 리소스를 공유하는 집합을 의미.

  • 노드: 서버, 라우터, 스위치 등 네트워크 장치
  • 링크: 유무선의 리소스가 흐르는 통로

처리량과 지연시간

좋은 네트워크 구축을 위해서는 많은 리소스를 처리하면서 지연시간이 짧고 장애 빈도가 적으며 좋은 보안을 갖춘 네트워크를 의미.

처리량(throughput)

  • 링크 내에서 성공적으로 전달된 트래픽 량 (많은 트래픽을 처리했다 = 처리량이 높다)
  • 초당 수신되는 비트 수를 의미하는 단위인 bps(bits per second)를 사용
  • 대역폭: 주어진 시간동안 네트워크 연결을 통해 흐를 수 있는 최대 비트 수

지연시간(latency)

  • 요청이 처리되는 시간. 메시지가 두 장치를 왕복하는데 걸리는 시간.
  • 매체 타입(유무선), 패킷 크기, 라우터의 패킷 처리 시간에 영향을 받음.

네트워크 토폴로지와 병목 현상

노드와 링크가 어떻게 배치 되어있는지에 대한 방식이자 연결 형태.

트리 토폴로지

트리 토폴로지
  • 계층형 토폴로지라고도 하며 트리 형태로 배치한 네트워크 구성.
  • 노드의 추가, 삭제가 쉬우며 특정 노드에 트래픽이 집중될 때 하위 노드에 영향을 미칠 수 있음.

버스 토폴로지

버스 토폴로지
  • 중앙 통신 회선 하나에 여러개의 노드가 연결되어 공유하는 네트워크 구성
  • LAN에서 사용
  • 설치 비용이 적고 신뢰성이 우수하며 노드 추가 삭제가 쉬움.
  • 스푸핑이 가능한 단점

스푸핑

  • LAN 상에서 송신부의 패킷을 캐치하여 악의적인 노드를 통해 수신하는 것.
    정상: src -> desc
    스푸핑: src -> hacker -> desc

스타 토폴로지

스타 토폴로지
  • 중앙에 있는 노드에 모두 연결된 네트워크 구성을 의미
  • 노드 추가 및 에러 탐지가 용이
  • 패킷 충돌 가능성이 낮고 에러를 쉽게 탐지 가능.
  • 중앙 노드에 장애가 발생하면 전체 네트워크를 사용할 수 없고 설치 비용이 고가.

링형 토폴로지

링형 토폴로지
  • 각 노드가 양 옆의 노드와 연결되어 고리처럼 하나의 연속된 길을 통해 통신을 하는 망 구성방식.
  • 데이터는 노드에서 노드로 처리
  • 노드 수가 증가되어도 네트워크 상의 손실이 거의 없고 충돌이 발생되는 가능성이 적고 노드의 고장을 쉽게 파악 가능.
  • 네트워크 구성 변경이 어렵고 회선에 장애가 생기면 전체 네트워크에 영향을 미침

메시 토폴로지

메시 토폴로지
  • 망형 토폴로지라고도 하며 그물망처럼 연결되어 있는 구조
  • 한 단말 장치에 문제가 생겨도 다른 경로를 통해 네트워킹 가능 -> 분산처리
  • 노드의 추가가 어려우며 구축 비용과 운용비용이 고가.

병목 현상

네트워크 토폴로지를 사용하는 주된 이유는 병목 현상을 줄이기 위함.
병목현상이 일어났을 때 네트워크 토폴로지를 확인해보면 답이 나오기도 함.

  • 전체 시스템의 성능이나 용량이 하나의 구성요소로 인해 제한을 받는 현상

네트워크 분류

네트워크는 규모를 기반으로 분류 가능

LAN

  • 사무실과 개인적으로 소유 가능한 규모
  • 근거리 통신망. 전송 속도가 빠르며 혼잡하지 않음.

MAN

  • 시 정도의 대도시 네트워크 규모
  • 전송 속도는 평균이며 LAN보다 혼잡

WAN

  • 세계적인 광역 규모
  • 전송 속도는 낮으며 MAN보다 혼잡

네트워크 성능 분석 명령어

병목 현상의 주된 원인

  • 네트워크의 대역폭
  • 네트워크 토폴로지
  • 서버 CPU, 메모리 사용량
  • 비효율적인 네트워크 구성

Ping

  • 네트워크 상태를 확인하려는 대상 노드를 향해 일정 크기의 패킷을 전송하는 명령어
  • 해당 노드의 패킷 수신 상태와 도달하기까지의 시간을 확인
  • ICMP protocol 기반
Ping

netstat

  • 접속되어 있는 서비스들의 네트워크 상태를 표시하는데 사용
  • 네트워크 접속, 라우팅 테이블, 네트워크 프로토콜 등 리스트를 보여줌
netstat

nslookup

  • DNS에 관련된 내용을 확인하기 위해 사용하는 명령어.
nslookup을 통해 naver의 ip 확인

tracert

  • 윈도우는 tracert 리눅스는 traceroute
  • 목적지 노드까지 네트워크 경로를 확인할 때 사용하는 명령어.
traceroute를 통해 google.com 경로 확인

네트워크 프로토콜 표준화

  • 네트워크 프로토콜: 다른 장치들끼리 데이터를 주고받기 위해 설정된 공통된 인터페이스

정리[TCP/IP 4계층 모델]

더보기

인터넷 프로토콜 스위트(Internet Protocol Suite)는 인터넷에서 컴퓨터들이 서로 정보를 주고 받는데 쓰이는 프로토콜 집합

  • TCP/IP 4 계층
  • OSI 7 계층이 존재

계층 구도

각 계층들은 특정 계층이 변경되었을 때 다른 계층이 영향을 받지 않도록 설계.

애플리케이션 계층

  • FTP, HTTP, SSH, SMTP, DNS 프로토콜이 존재
  • 응용 프로그램이 사용되는 프로토콜 계층
  • 웹 서비스, 이메일 등 서비스를 실질적으로 사용하는 계층

FTP

  • 장치와 장치 간의 파일을 전송하는데 사용되는 표준 통신 프로토콜

SSH

  • 보안되지 않은 네트워크에서 네트워크 서비스를 안전하게 운영하기 위한 암호화 네트워크 프로토콜

HTTP

  • World Wide Web을 위한 데이터 통신의 기초이자 웹 사이트를 이용하는데 쓰이는 프로토콜

SMTP

  • 전자 메일 전송을 위한 인터넷 표준 통신 프로토콜

DNS

  • 도메인 이름과 IP 주소를 매핑시켜주는 서버

전송 계층

  • TCP, UDP, QUIC 과 같은 프로토콜이 존재
  • 송신자와 수신자를 연결하는 통신 서비스를 제공
  • 연결 지향 데이터 스트림 지원, 신뢰성, 흐름 제어 제공

TCP

  • 패킷 사이의 순서를 보장
  • 연결 지향 프로토콜 사용
  • 수신 여부 확인을 통해 신뢰성 => 가상 회선 패킷 교환 방식 사용

가상 회선 패킷 교환 방식

  • 각 패킷에는 가상회선 식별자가 포함되어 모든 패킷을 전송하면 가상 회선이 해제되고 패킷들은 전송된 순서대로 도착하는 방식

UDP

  • 순서 보장 X
  • 수신 여부 확인 X => 데이터그램 패킷 교환 방식 사용

데이터그램 패킷 교환 방식

  • 패킷이 독립적으로 이동하며 최적의 경로를 선택하여 가는데 하나의 메시지에서 분할된 여러 패킷은 서로 다른 경로로 전송될 수 있으며 도착한 순서가 다를 수 있는 방식

TCP 연결 성립과정

  • 3way handshake 과정을 거침
  • SYN
    • 클라이언트가 서버로 ISN을 담아 SYN을 보냄
  • SYNACK
    • 서버는 SYN에 대한 대답으로 ISN + 1을 보냄 + 서버의 ISN도 담아 보냄
  • ACK
    • 클라리언트는 서버의 ISN + 1로 응답을 진행
  • UDP는 위와 같은 동작이 없음.

TCP 연결 해제 과정

  • 4way handshake
  • 1번
    • 클라이언트가 연결을 닫기 위해 FIN을 보냄. 클라이언트는 FIN_WAIT 단계로 도입
  • 2번
    • 서버는 FIN에 대한 대답 ACK를 보냄. 서버는 CLOSE_WAIT 단계로 도입
  • 3번
    • 서버는 클라이언트로 FIN 패킷을 보냄
  • 4번
    • 클라이언트는 TIME-WAIT 상태가 되고 서버로 ACK를 보내서 서버는 CLOSED가 됨.
    • 어느 정도의 시간을 대기한 후 연결을 닫음.
  • 어느정도의 시간 대기 후 close 하는 이유
    • 지연 패킷이 발생할 경우를 대비
    • 두 장치가 연결이 닫혔는지 확인하기 위해서

인터넷 계층

  • IP, ARP, ICMP와 같은 프로토콜
  • 장치로부터 받은 네트워크 패킷을 IP 주소로 지정된 목적지로 전송하기 위해 사용되는 계층
  • 상대방이 제대로 받았는지에 대해 보장하지 않는 비연결형적인 특징을 가지고 있음.

링크 계층

  • 이더넷
  • 실질적인 데이터를 전달하며 장치간에 신호를 주고받는 규칙을 정하는 계층

유선 LAN(IEEE802.3)

  • 전이중화 통신(full duplex)
  • 양쪽 장치가 동시에 송수신할 수 있는 방식

CSMA/CD

  • 반이중화 통신 중 하나
  • 데이터를 보낸 이후 충돌이 발생한다면 일정시간 이후 재전송 하는 방식

광섬유 케이블

  • 광섬유로 만들어진 케이블로 레이저를 이용하여 통신
  • 장거리 및 고속 통신 가능.
  • 유선 LAN을 이루는 케이블

무선 LAN(IEEE802.11)

  • 반이중화 통신
    • 양쪽 장치는 서로 통신할 수 있지만 동시에 통신할 수 없음.
    • 한번에 한 방향만 통신 가능
    • 응답하기 전까지 기다려야 하며 전송하면 충돌로 인해 데이터 손실 및 왜곡 발생 가능.

CSMA/CA

  • 반이중화 통신 중 하나로 데이터를 보내기 전에 일련의 과정을 기반으로 사전에 가능한 한 충돌을 방지하는 방식
  1. 사용중인 채널이 있다면 채널을 감지하다가 쉬는 채널을 발견
  2. 프레임간 공간 시간인 IFS(InterFrame Space) 시간만큼 기다린 후 랜덤 상수 기반으로 결정된 시간 이후 프레임을 보냄.
  3. 프레임을 보낸 후 ACK 패킷을 받았다면 마치지만 받지 못했으면 반복. 일정 한계를 넘으면 프레임 drop

PDU

Protocol Data Unit으로 헤더 + 페이로드로 구성. 각 계층에서 부르는 PDU가 다름

  • 애플리케이션 계층: 메시지
  • 전송계층: 세그먼트, 데이터그램
  • 인터넷 계층: 패킷
  • 링크 계층: 프레임

정리[네트워크 기기]

더보기

네트워크 기기의 처리 범위

네트워크 기기는 계층 별로 처리 범위를 나눌 수 있음.

  • 애플리케이션 계층: L7 스위치
  • 인터넷 계층: 라우터, L3 스위치
  • 데이터 링크 계층: L2 스위치, 브리지
  • 물리 계층: NIC, 리피터, AP

애플리케이션 계층을 처리하는 기기

L7 스위치

  • 스위치는 여러 장비를 연결하고 데이터 통신을 중재하며 목적지가 연결된 포트로만 전기 신호를 보내
    데이터를 전송하는 통신 네트워크 장비.
  • 로드 밸런서라고도 하며 서버의 부하를 분산하는 기기.
  • 트래픽 처리량의 증가를 목표
  • URL, 서버, 캐시, 쿠키들을 기반으로 트래픽을 분산.
  • 필터링 기능 + 트래픽 모니터링 가능
  • 헬스 체크를 통해 장애 탐지 가능

L4 스위치와 L7 스위치의 차이

  • L4 스위치는
    • 전송계층을 처리하는 기기로 스트리밍 관련 서비스에서는 사용 불가.
    • IP와 포트를 기반으로 트래픽을 분산
    • AWS에서의 NLB(Network Load Balancer)
  • L7 스위치는
    • IP, URL, HTTP 헤더, 쿠키 등을 기반으로 트래픽 분산
    • AWS에서의 ALB(Application Load Balancer)

헬스 체크

  • L4, L7 모두 헬스 체크를 통해 정상적인 서버 또는 비정상적인 서버를 판별
  • 전송주기와 재전송 횟수를 설정한 이후 반복적으로 서버에 요청을 보내는 것.

로드 밸런서를 이용한 서버 이중화

  • 서비스를 안정적으로 운용하기 위해서는 2대 이상의 서버는 필수적
  • 가상의 IP를 제공하고 이를 기반으로 안정적인 서비스 제공.

인터넷 계층을 처리하는 기기

라우터

  • 여러개의 네트워크를 연결, 분할, 구분시켜주는 역할
  • 패킷 소모를 최소화하고 경로를 최적화하고 최소 경로로 패킷을 포워딩

L3 스위치

  • L2 스위치 기능과 라우팅 기능을 갖춘 장비

데이터 링크 계층을 처리하는 기기

L2 스위치

  • MAC 주소를 기반으로 MAC 주소 테이블을 관리하며 패킷 전송을 담당
  • IP 주소를 기반으로 라우팅은 불가능하며 단순히 패킷의 MAC 주소를 읽어 스위칭하는 역할.

브리지

  • 두개의 근거리 통신망(LAN)을 상호 접속할 수 있도록 하는 통신망 연결 장치
  • 포트와 포트 사이의 다리역할을 하며 장치에서 받아온 MAC 주소를 MAC 주소 테이블로 관리.

물리 계층을 처리하는 기기

NIC

  • Network Interface Card는 2대 이상의 컴퓨터 네트워크를 구성하는데 사용
  • 네트워크와 빠른 속도로 데이터를 송수신할 수 있도록 컴퓨터 내에 설치하는 확장 카드
  • MAC 주소가 존재

리피터

  • 약해진 신호 정도를 증폭하여 다른쪽으로 전달하는 장치

AP

  • 패킷을 복사하는 기기
  • 유선 LAN을 연결한 후 다른 장치에서 무선 LAN 기술을 사용하여 무선 네트워크 연결 가능.

정리[IP 주소]

더보기

ARP

IP주소에서 ARP를 통해 MAC 주소를 찾아 MAC 주소를 기반으로 통신

  • ARP: IP 주소로부터 MAC 주소를 구하는 IP와 MAC 주소의 다리 역할을 하는 프로토콜
    • 가상 주소인 IP주소를 실제 주소인 MAC 주소로 변환.
    • RARP: 실제 주소인 MAC을 통해 IP주소로 변환하는 프로토콜
    • 브로드캐스트(Request) 후 응답받은 머신은 유니캐스트(Response)

홉바이홉 통신

  • IP 주소를 통해 통신하는 과정
  • 라우팅 테이블의 IP를 통해 시작 주소로부터 시작하여 다음 IP로 계속해서 이동하는 라우팅 과정을 통해
    최종 목적지로 도달하는 통신.

라우팅 테이블

  • 송신지에서 수신지까지 도달하기 위해 사용되며 라우터에 들어가있는 목적지 정보들과
    그 목적지로 가기 위한 방법이 들어있는 리스트

게이트웨이

  • 서로 다른 통신망, 프로토콜을 사용하는 네트워크 간의 통신을 가능하게 하는 관문 역할을 하는 컴퓨터나 소프트웨어

IP 주소 체계

IPv4 IPv6로 나뉨

  • IPv4는 32비트를 8비트 단위로 점을 찍어 표기
  • IPv6는 64비트를 16비트 단위로 점을 찍어 표기

클래스 기반 할당 방식

  • 처음에는 A, B, C, D, E 다섯개의 클래스를 구분하는 classful network addressing을 사용
  • 앞에있는 부분을 네트워크 주소, 그 뒤에 있는 부분을 컴퓨터에 부여하는 주소인 호스트 주소로 사용.

DHCP

  • IP 주소 및 기타 통신 매개 변수를 자동으로 할당하기 위한 네트워크 관리 프로토콜
  • 네트워크 장치의 IP 주소를 수동으로 설정할 필요없이 인터넷에 접속할 때마다 자동으로 IP 주소를 할당.

NAT

Network Address Translation은 패킷이 라우팅 장치를 통해 전송되는 동안 패킷의
IP 주소 정보를 수정하여 IP 주소를 다른 주소로 매핑하는 방법.

  • IPv4의 한계를 넘어 NAT으로 공인 IP와 사설 IP로 나누어 많은 주소를 처리
  • 사용이유
    • 여러대의 호스트가 하나의 공인 IP 주소를 사용하여 인터넷에 접속하기 위함.

NAT을 이용한 보안

  • 내부 네트워크에서 사용하는 IP 주소와 외부에 드러나는 IP 주소를 다르게 유지할 수 있기에 내부 네트워크에 대한 어느정도 보안이 가능
  • 단점: 접속하는 호스트의 숫자에 따라 접속 속도가 느려질 수 있음.

정리[HTTP]

더보기

기본적으로 HTTP는 웹 서비스 통신에서 사용.

HTTP/1.0

한 연결당 하나의 요청을 처리 ⇒ RTT의 증가.

서버로부터 파일을 가져올 때마다 TCP 3way handshake를 계속해서 해야하기 때문에 RTT가 증가

해결하기 위한 방법

  • 이미지 스플리팅
    • 많은 이미지를 다운로드 받게 되면 과부화가 걸리기 때문에 이미지가 합쳐있는 하나의 이미지를 다운받고 position을 이용하여 이미지를 표기하는 방법
  • 코드 압축
    • 개행 문자, 빈칸을 없애서 코드의 크기를 최소화하는 방법
  • 이미지 Base64 인코딩
    • 이미지 파일을 64진법으로 이루어진 문자열로 인코딩하는 방법
    • 크기가 37퍼센트 정도 증가

HTTP/1.1

  • 매번 TCP 연결을 하는 것이 아닌 한번 TCP 연결 이후에 keep-alive 옵션을 통해 여러개의 파일을 송수신할 수 있게 변경
  • 3way handshake가 한번만 발생
  • 무거운 헤더 구조를 가짐(헤더에 쿠키 등 많은 메타 데이터 포함)

HOL Blocking

  • Head Of Line Blocking: 네트워크에서 같은 큐에 있는 패킷이 첫번째 패킷에 의해 지연될 때 발생하는 성능 저하 현상.

HTTP/2

  • HTTP/1.x 보다 지연시간을 줄이고 응답시간을 더 빠르게 할 수 있으며 멀티 플렉싱, 헤더 압축, 서버 푸시, 요청의 우선 순위 처리를 지원하는 프로토콜

멀티 플렉싱

  • 여러개의 스트림을 사용하여 송수신
  • 이를 통해 특정 스트림의 패킷이 손실되어도 나머지 스트림은 멀쩡하게 동작 가능.
  • 병렬로 여러 요청을 받을 수 있고 응답을 줄 수 있음. ⇒ HOL Blocking 문제 해결

헤더 압축

  • HTTP/2에서는 헤더 압축을 하는데 허프만 코딩 압축 알고리즘 사용.
    • 허프만 코딩: 문자열을 문자 단위로 쪼개 빈도수를 세어 빈도가 높은 정보는 적은 비트수를 사용하여 표기, 빈도가 낮은 정보는 비트수를 많이 사용하여 전체 데이터의 표현에 필요한 비트양을 줄이는 원리

서버 푸시

  • 클라이언트의 요청없이 서버가 클라이언트에게 바로 푸시 가능

HTTPS

  • HTTPS위에서 HTTP/2가 동작.
  • 애플리케이션 계층과 전송 계층 사이에 신뢰계층인 SSL/TLS 계층을 넣은 신뢰할 수 있는 HTTP 요청 ⇒ 통신을 암호화

SSL/TLS

  • TLS 1.3을 기반으로 정리
  • 전송 계층에서 보안을 제공하는 프로토콜
  • 클라이언트와 서버가 통신을 할 때 SSL/TLS를 통해 제 3자가 메시지를 도청하거나 변조하지 못하도록.
  • 인증 메커니즘, 키 교환 알고리즘, 해싱 알고리즘이 주로 사용

보안 세션

  • 보안이 시작되고 끝나는 동안 유지되는 세션을 의미.
  • SSL/TLS는 핸드 셰이크를 통해 보안 세션을 생성하고 이를 기반으로 상태 정보등을 공유
  1. 클라리언트와 서버가 키를 공유하고 이를 기반으로 인증, 인증확인등의 작업이 일어나는 1RTT이후 데이터를 송수신
  2. 클라이언트에서 Cypher suites를 서버에 전달하면 서버는 받은 사이퍼 수트의 암호화 알고리즘 리스트를 제공할 수 잇는지 확인.
  3. 제공할 수 있다면 인증 매커니즘이 시작되고 암호화된 데이터의 송수신이 시작

인증 매커니즘

  • CA(Certificate Authorities)에서 발급한 인증서를 기반으로 이루어짐.
  • CA에서 발급한 인증서는 안전한 연결을 시작하는데 있어 필요한 공개키를 클라이언트에 제공하고 사용자가 접속한 서버가 신뢰할 수 있는 서버임을 보장.
  • 인증서는 서비스 정보, 공개키, 지문, 디지털 서명으로 이루어짐.

암호화 알고리즘

  • 타원 곡선 알고리즘 ECDHE
  • 디피 헬만 알고리즘 DHE
    • 암호키 교환 알고리즘

HTTP/3

  • QUIC 계층 위에서 돌아가며 UDP 기반
  • 멀티 플랙싱 + 초기 연결 설정 시 지연시간 감소
  • 3way handshake를 거치지 않음
    • QUIC은 1RTT만 소요
    • 클라이언트가 서버에 신호를 한번만 주고 서버도 거기에 응답하면 바로 통신 시작 가능
    • 순방향 오류 수정 매커니즘(FEC, Forword Error Correction)이 적용.
      • 전송한 패킷이 손실되었다면 수신 측에서 에러를 검출하고 수정하는 방식.
      • 낮은 패킷 손실률

 

 

 

참조

더보기
  • 면접을 위한 CS 전공지식 노트(주홍철 저) -길벗

'기술면접 > 네트워크' 카테고리의 다른 글

[기술면접] 네트워크 5  (3) 2024.11.14
[기술면접] 네트워크 4  (4) 2024.11.10
[기술면접] 네트워크 3  (1) 2024.11.10
[기술면접] 네트워크 2  (0) 2024.11.08
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers SQL 고득점 Kit(String, Date)입니다.

루시와 엘라 찾기

SELECT
    ANIMAL_ID,
    NAME,
    SEX_UPON_INTAKE
FROM ANIMAL_INS
WHERE NAME IN ('Lucy', 'Ella', 'Pickle', 'Rogan', 'Sabrina', 'Mitty')
ORDER BY ANIMAL_ID

 

  • 이름이 'Lucy', 'Ella', 'Pickle', 'Rogan', 'Sabrina', 'Mitty'인 동물을 조회하기 위해 조건을 걸어줍니다.
    WHERE NAME IN ('Lucy', 'Ella', 'Pickle', 'Rogan', 'Sabrina', 'Mitty')
  • 동물 ID를 기준으로 정렬합니다.

이름에 el이 들어가는 동물 찾기

SELECT 
    ANIMAL_ID, 
    NAME
FROM ANIMAL_INS
WHERE UPPER(NAME) LIKE '%EL%' AND ANIMAL_TYPE = 'Dog'
ORDER BY NAME;
  • 이름에 EL이 들어가는 개를 찾기 위해 UPPER(또는 LOWER)를 사용하여 Like절을 통해 조회합니다.
    WHERE UPPER(NAME) LIKE '%EL%' AND ANIMAL_TYPE = 'Dog'
  • 이름을 통해 정렬합니다.

중성화 여부 파악하기

SELECT
    ANIMAL_ID,
    NAME,
    CASE
        WHEN SEX_UPON_INTAKE LIKE 'Neutered%' Then 'O'
        WHEN SEX_UPON_INTAKE LIKE 'Spayed%' Then 'O'
        WHEN SEX_UPON_INTAKE LIKE 'Intact%' THEN 'X'
    END AS "중성화"
FROM ANIMAL_INS
ORDER BY ANIMAL_ID
  • 중성화 여부를 위한 CASE 절을 이용합니다.
    CASE
            WHEN SEX_UPON_INTAKE LIKE 'Neutered%' Then 'O'
            WHEN SEX_UPON_INTAKE LIKE 'Spayed%' Then 'O'
            WHEN SEX_UPON_INTAKE LIKE 'Intact%' THEN 'X'
        END AS "중성화"
  • 동물 ID를 통해 정렬합니다.

오랜기간 보호한 동물(2)

SELECT ANIMAL_ID, NAME
FROM (
    SELECT
        INS.ANIMAL_ID,
        INS.NAME,
        (OUTS.DATETIME - INS.DATETIME) AS PROTECTION_DAYS
    FROM
        ANIMAL_INS INS
        JOIN ANIMAL_OUTS OUTS ON INS.ANIMAL_ID = OUTS.ANIMAL_ID
    ORDER BY
        PROTECTION_DAYS DESC
)
WHERE ROWNUM <= 2;
  • 입양간 동물을 구하기 위해 JOIN 합니다.
    FROM ANIMAL_INS INS JOIN ANIMAL_OUTS OUTS ON INS.ANIMAL_ID = OUTS.ANIMAL_ID
  • 보호기간을 구하기 위해 PROTECTION_DAYS 컬럼을 생성하고 해당 컬럼을 통해 내림차순 정렬합니다.
    (OUTS.DATETIME - INS.DATETIME) AS PROTECTION_DAYS
  • 상위 두개의 레코드를 조회하기 위해 WHERE절을 이용합니다.
    WHERE ROWNUM <= 2;

카테고리 별 상품 개수 구하기

SELECT 
    SUBSTR(PRODUCT_CODE, 0, 2) AS CATEGORY, 
    COUNT(*)
FROM PRODUCT
GROUP BY SUBSTR(PRODUCT_CODE, 0, 2)
ORDER BY CATEGORY
  • 상품 카테고리 앞 두자리를 기준으로 그룹화를 진행합니다.
    GROUP BY SUBSTR(PRODUCT_CODE, 0, 2)
  • 카테고리를 기준으로 오름차순 정렬합니다.

DATETIME에서 DATE로 형변환

SELECT
    ANIMAL_ID,
    NAME,
    TO_CHAR(DATETIME, 'YYYY-MM-DD') AS "날짜"
FROM ANIMAL_INS
ORDER BY ANIMAL_ID
  • DATETIME을 YYYY-MM-DD로 형변환합니다.
  • 동물 ID를 기준으로 오름차순 정렬합니다.

연도 별 평균 미세먼지 농도 조회하기

SELECT
    YEAR(YM) AS YEAR, 
    ROUND(AVG(PM_VAL1),2) AS PM10, 
    ROUND(AVG(PM_VAL2),2) AS 'PM2.5'
FROM AIR_POLLUTION
WHERE LOCATION2 = '수원'
GROUP BY YEAR
ORDER BY YEAR;
  • 지역이 수원인 동네에 연도별 미세먼지 농도를 구하기 위해 GROUP BY를 사용합니다.
    WHERE LOCATION2 = '수원'
    GROUP BY YEAR
  • 연도를 기준으로 오름차순 정렬합니다.

한 해에 잡은 물고기 수 구하기

SELECT
    COUNT(*) AS FISH_COUNT
FROM FISH_INFO
WHERE YEAR(TIME) = 2021
  • 2021년에 잡은 물고기 수를 출력하기 위해 조건을 걸어줍니다.
    WHERE YEAR(TIME) = 2021

분기별 분화된 대장균의 개체수 구하기

SELECT
    E.QUARTER,
    COUNT(*) AS ECOLI_COUNT
FROM (
    SELECT 
        ID,
        CASE 
            WHEN MONTH(DIFFERENTIATION_DATE) <= 3 THEN "1Q"
            WHEN MONTH(DIFFERENTIATION_DATE) <= 6 THEN "2Q"
            WHEN MONTH(DIFFERENTIATION_DATE) <= 9 THEN "3Q"
            WHEN MONTH(DIFFERENTIATION_DATE) <= 12 THEN "4Q"
        END AS QUARTER
    FROM ECOLI_DATA
) E
GROUP BY QUARTER
ORDER BY E.QUARTER
  • 월을 기준으로 분기를 구하기 위해 CASE 절을 이용한 서브 테이블 E를 생성합니다.
    (
        SELECT 
            ID,
            CASE 
                WHEN MONTH(DIFFERENTIATION_DATE) <= 3 THEN "1Q"
                WHEN MONTH(DIFFERENTIATION_DATE) <= 6 THEN "2Q"
                WHEN MONTH(DIFFERENTIATION_DATE) <= 9 THEN "3Q"
                WHEN MONTH(DIFFERENTIATION_DATE) <= 12 THEN "4Q"
            END AS QUARTER
        FROM ECOLI_DATA
    ) E
  • QUARTER를 기준으로 그룹화와 정렬을 진행합니다
    GROUP BY QUARTER
    ORDER BY E.QUARTER
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers SQL 고득점 Kit(String, Date)입니다.

자동차 평균 대여 기간 구하기

SELECT 
    CAR_ID, 
    TO_CHAR(ROUND(AVG(END_DATE - START_DATE + 1), 1), 'FM9990.0') AS AVERAGE_DURATION
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
GROUP BY CAR_ID
HAVING AVG(END_DATE - START_DATE + 1) >= 7
ORDER BY ROUND(AVG(END_DATE - START_DATE + 1), 1) DESC, CAR_ID DESC
  • 자동차 평균 대여 기간을 구하기 위해서 자동차 ID를 기준으로 그룹화를 진행합니다.
    GROUP BY CAR_ID
  • 평균 대여 기간이 7일 이상인 레코드를 조회하기 위해 HAVING 절을 이용합니다.
    HAVING AVG(END_DATE - START_DATE + 1) >= 7
  • 소수점을 표현하기 위해 FM9990.0의 데이터 형식으로 표현해줍니다.
    TO_CHAR(ROUND(AVG(END_DATE - START_DATE + 1), 1), 'FM9990.0') AS AVERAGE_DURATION
  • 자동차 대여 기간, 자동차 ID를 기준으로 내림차순 정렬합니다.

조회수가 가장 많은 중고거래 게시판의 첨부파일 조회하기

SELECT
    CONCAT(CONCAT(
    	CONCAT(CONCAT(CONCAT('/home/grep/src/', B.BOARD_ID),'/'),file_id), 
    FILE_NAME),file_ext) AS FILE_PATH
FROM (
    SELECT
        BOARD_ID
    FROM USED_GOODS_BOARD
    ORDER BY VIEWS DESC
    FETCH NEXT 1 ROWS ONLY
) B JOIN USED_GOODS_FILE F ON B.BOARD_ID = F.BOARD_ID
ORDER BY F.FILE_ID DESC
  • 조회수가 가장 높은 레코드를 조회하기 위해 서브 테이블 B를 생성합니다.
    조회수를 기준으로 내림차순 정렬하고 첫번째 레코드를 조회합니다.
(
    SELECT
        BOARD_ID
    FROM USED_GOODS_BOARD
    ORDER BY VIEWS DESC
    FETCH NEXT 1 ROWS ONLY
) B
  • B 테이블과 USED_GOODS_FILE 테이블과 BOARD_ID를 기준으로 JOIN 합니다.
    FROM(...) B JOIN USED_GOODS_FILE F ON B.BOARD_ID = F.BOARD_ID
  • 첨부파일의 경로를 표시하기 위해 여러 컬럼을 더해주기 위해 CONCAT을 여러번 이용합니다.
    CONCAT(CONCAT(CONCAT(CONCAT(CONCAT('/home/grep/src/', B.BOARD_ID),'/'),file_id), FILE_NAME),file_ext) AS FILE_PATH
  • 파일 ID를 기준으로 내림차순 정렬합니다.

자동차 대여기록 별 대여 금액 구하기

SELECT 
    H.HISTORY_ID,
    C.DAILY_FEE * H.RENTAL_DATE * NVL(P.DISCOUNT_RATE, 1) AS FEE
FROM CAR_RENTAL_COMPANY_CAR C JOIN (
    SELECT 
        HISTORY_ID,
        CAR_ID,
        END_DATE - START_DATE + 1 AS RENTAL_DATE,
        CASE WHEN END_DATE - START_DATE + 1 >= 90 THEN '90일 이상'
            WHEN END_DATE - START_DATE + 1 >= 30 THEN '30일 이상'
            WHEN END_DATE - START_DATE + 1 >= 7 THEN '7일 이상'
            ELSE NULL
        END AS DURATION_TYPE
    FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
)H ON C.CAR_ID = H.CAR_ID LEFT JOIN (
    SELECT 
        DURATION_TYPE,
        (1 - (DISCOUNT_RATE / 100)) AS DISCOUNT_RATE
    FROM CAR_RENTAL_COMPANY_DISCOUNT_PLAN
    WHERE CAR_TYPE = '트럭'
)P ON H.DURATION_TYPE = P.DURATION_TYPE
WHERE C.CAR_TYPE = '트럭'
ORDER BY FEE DESC, H.HISTORY_ID DESC
  • 가격 정책과 조인하기 위해 서브 테이블 H을 생성합니다.
    대여 기간 컬럼(RENTAL_DATE)와 조인을 위한 컬럼(DURATION_TYPE) 컬럼을 추가해줍니다.
(
    SELECT 
        HISTORY_ID,
        CAR_ID,
        END_DATE - START_DATE + 1 AS RENTAL_DATE,
        CASE WHEN END_DATE - START_DATE + 1 >= 90 THEN '90일 이상'
            WHEN END_DATE - START_DATE + 1 >= 30 THEN '30일 이상'
            WHEN END_DATE - START_DATE + 1 >= 7 THEN '7일 이상'
            ELSE NULL
        END AS DURATION_TYPE
    FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
)H
  • 자동차 ID를 기준으로 JOIN합니다.
    FROM CAR_RENTAL_COMPANY_CAR C JOIN (...) H ON C.CAR_ID = H.CAR_ID
  • 트럭에 대한 자동차 대여 기록별 대여 금액을 구하기 위해 서브테이블 P를 생성하여 대여 기간을 기준으로 LEFT JOIN 합니다.
    LEFT JOIN을 하는 이유는 7일 이하로 렌트하는 레코드를 조회하기 위함입니다.
LEFT JOIN (
    SELECT 
        DURATION_TYPE,
        (1 - (DISCOUNT_RATE / 100)) AS DISCOUNT_RATE
    FROM CAR_RENTAL_COMPANY_DISCOUNT_PLAN
    WHERE CAR_TYPE = '트럭'
)P ON H.DURATION_TYPE = P.DURATION_TYPE
  • 자동차 타입이 트럭인 레코드를 조회하기 위한 조건을 걸어줍니다.
    WHERE C.CAR_TYPE = '트럭'
  • 대여 금액을 구하기 위해 해당 수식의 컬럼을 추가합니다.
    null인 것은 할인 대상이 아니기에 * 1을 해줍니다.
    C.DAILY_FEE * H.RENTAL_DATE * NVL(P.DISCOUNT_RATE, 1) AS FEE
  • 대여 금액과 대여 기록 ID를 기준으로 내림차순 조회합니다. 

조건에 부합하는 중고거래 상태 조회하기

SELECT 
    BOARD_ID, 
    WRITER_ID, 
    TITLE, 
    PRICE, 
    CASE WHEN STATUS = 'SALE' THEN '판매중'
         WHEN STATUS = 'RESERVED' THEN '예약중'
         WHEN STATUS = 'DONE' THEN '거래완료'
    END 
FROM USED_GOODS_BOARD
WHERE TO_CHAR(CREATED_DATE, 'YYYY-MM-DD') = '2022-10-05'
ORDER BY BOARD_ID DESC;
  • 2022년 10월 5일에 등록된 레코드를 조회하기 위해 조건을 걸어줍니다.
    WHERE TO_CHAR(CREATED_DATE, 'YYYY-MM-DD') = '2022-10-05'
  • STATUS를 통해 판매중/예약중/거래완료를 처리하기 위해 CASE 절을 사용합니다.
    CASE WHEN STATUS = 'SALE' THEN '판매중'
             WHEN STATUS = 'RESERVED' THEN '예약중'
             WHEN STATUS = 'DONE' THEN '거래완료'
        END 
  • 게시물 ID를 기준으로 내림차순 정렬합니다.

특정 옵션이 포함된 자동차 리스트 구하기

SELECT 
    CAR_ID,
    CAR_TYPE,
    DAILY_FEE,
    OPTIONS
FROM CAR_RENTAL_COMPANY_CAR
WHERE OPTIONS LIKE '%네비게이션%'
ORDER BY CAR_ID DESC
  • 네비게이션 옵션이 있는 레코드를 조회하기 위해 like 절을 사용합니다.
    WHERE OPTIONS LIKE '%네비게이션%'
  • 자동차 ID를 기준으로 내림차순 정렬합니다.

자동차 대여 기록에서 장기/단기 대여 구분하기

SELECT
    HISTORY_ID,
    CAR_ID,
    TO_CHAR(START_DATE, 'YYYY-MM-DD') AS START_DATE,
    TO_CHAR(END_DATE, 'YYYY-MM-DD') AS END_DATE,
    CASE WHEN END_DATE - START_DATE + 1 >= 30 THEN '장기 대여'
    ELSE '단기 대여'
    END AS RENT_TYPE
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE EXTRACT(MONTH FROM START_DATE) = 9
ORDER BY HISTORY_ID DESC
  • 대여 시작일이 9월인 레코드를 조회하기 위해 조건을 걸어줍니다.
    DATETIME 형식에서 월만 추출하기 위해 EXTRACT 절을 사용합니다.
    WHERE EXTRACT(MONTH FROM START_DATE) = 9
  • 30일 이상 대여하는 레코드에 대해서는 장기 대여 아닌 것에 대해서는 단기 대여를 위해 CASE 절을 사용합니다.
    CASE WHEN END_DATE - START_DATE + 1 >= 30 THEN '장기 대여'
        ELSE '단기 대여'
        END AS RENT_TYPE
  • 예약 ID를 기준으로 내림차순 정렬합니다.

조건별로 분류하여 주문상태 출력하기

SELECT
    ORDER_ID, 
    PRODUCT_ID, 
    TO_CHAR(OUT_DATE, 'YYYY-MM-DD') AS OUT_DATE,
    CASE WHEN TO_CHAR(OUT_DATE, 'YYYYMMDD') <= '20220501' THEN '출고완료'
         WHEN TO_CHAR(OUT_DATE, 'YYYYMMDD') > '20220501' THEN '출고대기'
         ELSE '출고미정'
    END AS 출고여부
FROM FOOD_ORDER
ORDER BY ORDER_ID
  • 2022년 5월 1일을 기준으로 출고 여부를 조회하기 위해 CASE 절을 사용합니다.
    CASE WHEN TO_CHAR(OUT_DATE, 'YYYYMMDD') <= '20220501' THEN '출고완료'
             WHEN TO_CHAR(OUT_DATE, 'YYYYMMDD') > '20220501' THEN '출고대기'
             ELSE '출고미정'
        END AS 출고여부
  • 주문 ID를 기준으로 오름차순 정렬합니다.

대여 기록이 존재하는 자동차 리스트 구하기

SELECT
    H.CAR_ID
FROM (
    SELECT
        DISTINCT CAR_ID
    FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
    WHERE EXTRACT(MONTH FROM START_DATE) = 10
) H JOIN (
    SELECT
        CAR_ID
    FROM CAR_RENTAL_COMPANY_CAR
    WHERE CAR_TYPE = '세단'
) C ON H.CAR_ID = C.CAR_ID
ORDER BY H.CAR_ID DESC
  • 10월에 대여 시작한 테이블을 조회하기 위해 서브 테이블 H를 생성합니다.
    중복을 제거하기 위해 DISTINCT 키워드를 사용합니다.
(
    SELECT
        DISTINCT CAR_ID
    FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
    WHERE EXTRACT(MONTH FROM START_DATE) = 10
) H
  • 자동차 종류가 세단인 자동차 데이터를 조회하기 위해 서브 테이블 C를 생성합니다.
(
    SELECT
        CAR_ID
    FROM CAR_RENTAL_COMPANY_CAR
    WHERE CAR_TYPE = '세단'
) C
  • 자동차 ID를 기준으로 H 테이블과 C 테이블을 JOIN합니다.
    FROM (...) H JOIN (...) C ON H.CAR_ID = C.CAR_ID
  • 자동차 ID를 기준으로 내림차순 정렬합니다.

조건에 맞는 사용자 정보 조회하기

SELECT
    U.USER_ID,
    U.NICKNAME,
    CONCAT(CONCAT(CONCAT(CONCAT(CITY, ' '), STREET_ADDRESS1), ' '), STREET_ADDRESS2) AS 전체주소,
    CONCAT(CONCAT(CONCAT(SUBSTR(TLNO,0, 3), '-'), CONCAT(SUBSTR(TLNO, 4, 4), '-')), SUBSTR(TLNO, 8)) AS 전화번호
FROM USED_GOODS_USER U JOIN (
    SELECT
        WRITER_ID
    FROM USED_GOODS_BOARD
    GROUP BY WRITER_ID
    HAVING COUNT(*) >= 3
) B ON U.USER_ID = B.WRITER_ID
ORDER BY U.USER_ID DESC
  • 중고 거래 게시물을 작성한 갯수를 구하기 위해 3건 이상 작성한 작성자 ID를 기준으로 그룹화한 서브 테이블 B를 생성합니다.
(
    SELECT
        WRITER_ID
    FROM USED_GOODS_BOARD
    GROUP BY WRITER_ID
    HAVING COUNT(*) >= 3
) B
  • USED_GOODS_USER 테이블과 USER_ID를 B.WRITER_ID를 기준으로 JOIN합니다.
    FROM USED_GOODS_USER U JOIN (...) B ON U.USER_ID = B.WRITER_ID
  • 전체 주소를 조회하기 위해 CITY와 도로명 주소 사이에 CONCAT 함수를 이용하여 띄어쓰기를 넣어줍니다.
    CONCAT(CONCAT(CONCAT(CONCAT(CITY, ' '), STREET_ADDRESS1), ' '), STREET_ADDRESS2) AS 전체주소,
  • 전화번호 11자 사이에 대쉬(-)를 넣어주기 위해 SUBSTR과 CONCAT 함수를 이용합니다.
    SUBSTR(A, B, C) => A를 B부터 C자리까지 자름
    SUBSTR(A, B) => A를 B이후 문자열
    CONCAT(CONCAT(CONCAT(SUBSTR(TLNO,0, 3), '-'), CONCAT(SUBSTR(TLNO, 4, 4), '-')), SUBSTR(TLNO, 8)) AS 전화번호
  • USER_ID를 기준으로 내림차순 정렬합니다.
     

취소되지 않은 진료 예약 조회하기

SELECT
    A.APNT_NO,
    P.PT_NAME,
    A.PT_NO,
    A.MCDP_CD,
    D.DR_NAME,
    A.APNT_YMD
FROM DOCTOR D JOIN (
    SELECT
        APNT_YMD,
        APNT_NO,
        PT_NO,
        MCDP_CD,
        MDDR_ID
    FROM APPOINTMENT
    WHERE APNT_CNCL_YN = 'N' AND TO_CHAR(APNT_YMD, 'YYYY-MM-DD') = '2022-04-13'
) A ON D.DR_ID = A.MDDR_ID JOIN PATIENT P ON A.PT_NO = P.PT_NO
WHERE A.MCDP_CD = 'CS'
ORDER BY A.APNT_YMD ASC
  • 2022년 4월 13일에 취소되지 않은 진료예약을 구하기 위해 서브테이블 A를 생성합니다.
(
    SELECT
        APNT_YMD,
        APNT_NO,
        PT_NO,
        MCDP_CD,
        MDDR_ID
    FROM APPOINTMENT
    WHERE APNT_CNCL_YN = 'N' AND TO_CHAR(APNT_YMD, 'YYYY-MM-DD') = '2022-04-13'
) A
  • 의사 ID와 환자 ID를 기준으로 JOIN합니다.
    FROM DOCTOR D JOIN (...) A ON D.DR_ID = A.MDDR_ID JOIN PATIENT P ON A.PT_NO = P.PT_NO
  • 흉부외과 데이터를 조회하기 위해 조건을 걸어줍니다.
    WHERE A.MCDP_CD = 'CS'
  • 진료 예약일시를 기준으로 오름차순 정렬합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers SQL 고득점 Kit(JOIN)입니다.

특정 기간동안 대여 가능한 자동차들의 대여 비용 구하기

SELECT
    H.CAR_ID,
    C.CAR_TYPE,
    C.DAILY_FEE * 30 * (1 - (DISCOUNT_RATE / 100)) AS FEE
FROM (
    SELECT DISTINCT CAR_ID
    FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY H
    GROUP BY CAR_ID
    HAVING MAX(TO_CHAR(END_DATE, 'YYYY-MM')) < '2022-11'
) H JOIN (
    SELECT CAR_ID, CAR_TYPE, DAILY_FEE
    FROM CAR_RENTAL_COMPANY_CAR C
    WHERE CAR_TYPE = '세단' OR CAR_TYPE = 'SUV'
) C ON H.CAR_ID = C.CAR_ID JOIN (
    SELECT CAR_TYPE, DURATION_TYPE, DISCOUNT_RATE
    FROM CAR_RENTAL_COMPANY_DISCOUNT_PLAN
    WHERE (CAR_TYPE = '세단' OR CAR_TYPE = 'SUV') AND (DURATION_TYPE = '30일 이상')
) P ON C.CAR_TYPE = P.CAR_TYPE
WHERE C.DAILY_FEE * 30 * (1 - (DISCOUNT_RATE / 100)) BETWEEN 500000 AND 2000000
ORDER BY FEE DESC, C.CAR_TYPE ASC, H.CAR_ID DESC
  • 2022년 11월 대여가 가능한 차를 찾아야 하기에 CAR_RENTAL_COMPANY_RENTAL_HISTORY에서 반납일이 11월보다 작은 차를 조회하기 위한 서브 테이블 H를 생성합니다.
(
    SELECT DISTINCT CAR_ID
    FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY H
    GROUP BY CAR_ID
    HAVING MAX(TO_CHAR(END_DATE, 'YYYY-MM')) < '2022-11'
) H
  • 자동차의 종류가 세단이나 SUV여야 하므로 서브테이블 C를 생성합니다.
(
    SELECT CAR_ID, CAR_TYPE, DAILY_FEE
    FROM CAR_RENTAL_COMPANY_CAR C
    WHERE CAR_TYPE = '세단' OR CAR_TYPE = 'SUV'
) C
  • H테이블과 C 테이블을 CAR_ID를 기준으로 조인합니다
    FROM (...) H JOIN (...)C ON H.CAR_ID = C.CAR_ID
  • 세단과 SUV의 30일 이상 대여할 시의 할인율을 구하기 위한 서브 테이블 P를 생성합니다.
(
    SELECT CAR_TYPE, DURATION_TYPE, DISCOUNT_RATE
    FROM CAR_RENTAL_COMPANY_DISCOUNT_PLAN
    WHERE (CAR_TYPE = '세단' OR CAR_TYPE = 'SUV') AND (DURATION_TYPE = '30일 이상')
) P
  • P 테이블과 C테이블의 CAR_TYPE을 기준으로 조인합니다.
    C.CAR_TYPE = P.CAR_TYPE
  • 30일 간의 대여 금액이 50만원 이상 200만원 미만인 자동차를 구하기 위해 BETWEEN 함수를 이용하빈다.
    DISCOUNT_RATE가 퍼센트로 표시되어있기에 해당 계산을 이용합니다.
    WHERE C.DAILY_FEE * 30 * (1 - (DISCOUNT_RATE / 100)) BETWEEN 500000 AND 2000000
  • 자동차 종류를 기준으로 오름차순 정렬, 자동차 ID를 기준으로 내림차순 정렬합니다.

5월 식품들의 총 매출 조회하기

SELECT
    P.PRODUCT_ID,
    P.PRODUCT_NAME,
    P.PRICE * O.AMOUNT AS TOTAL_SALES
FROM FOOD_PRODUCT P JOIN (
    SELECT PRODUCT_ID, SUM(AMOUNT) AS AMOUNT
    FROM FOOD_ORDER O
    WHERE TO_CHAR(PRODUCE_DATE, 'YYYY-MM') = '2022-05'
    GROUP BY PRODUCT_ID
) O ON P.PRODUCT_ID = O.PRODUCT_ID
ORDER BY TOTAL_SALES DESC, P.PRODUCT_ID ASC
  • 2022년 5월에 생산된 식품을 조회하기 위한 서브 테이블 O를 생성합니다.
    총 매출(TOTAL_ORDER * PRICE)을 구해야하기 때문에 식품ID를 기준으로 그룹화를 하고 SUM 함수를 사용합니다.
(
    SELECT PRODUCT_ID, SUM(AMOUNT) AS AMOUNT
    FROM FOOD_ORDER O
    WHERE TO_CHAR(PRODUCE_DATE, 'YYYY-MM') = '2022-05'
    GROUP BY PRODUCT_ID
) O

 

  • FOOD_PRODUCT 테이블과 PRODUCT_ID를 기준으로 JOIN합니다.
    FROM FOOD_PRODUCT P JOIN (...) O ON P.PRODUCT_ID = O.PRODUCT_ID
  • 총 매출을 구하기 위해 다음과 같이 TOTAL_SALES 컬럼을 구성합니다.
    P.PRICE * O.AMOUNT AS TOTAL_SALES
  • 총 매출을 기준으로 내림차순 정렬하고, 식품 ID를 기준으로 오름차순 정렬합니다.

주문량이 많은 아이스크림들 조회하기

SELECT
    H.FLAVOR
FROM (
    SELECT FLAVOR, TOTAL_ORDER
    FROM FIRST_HALF
) H JOIN (
    SELECT FLAVOR, SUM(TOTAL_ORDER) AS TOTAL_ORDER
    FROM JULY
    GROUP BY FLAVOR
) J ON H.FLAVOR = J.FLAVOR
ORDER BY H.TOTAL_ORDER + J.TOTAL_ORDER DESC
FETCH NEXT 3 ROWS ONLY
  • 상반기 아이스크림의 총 주문량을 구하기 위한 서브 테이블 H를 생성합니다.
(
    SELECT FLAVOR, TOTAL_ORDER
    FROM FIRST_HALF
) H
  • 7월에는 주문량이 많아 다른 두 공장에서 같은 맛을 출하를 진행하였으므로 FLAVOR를 기준으로 그룹화를 진행하고 총 주문량을 조회하는 서브테이르 J를 생성합니다.
(
    SELECT FLAVOR, SUM(TOTAL_ORDER) AS TOTAL_ORDER
    FROM JULY
    GROUP BY FLAVOR
) J
  • H 테이블과 J 테이블을 FLAVOR을 기준으로 JOIN 합니다.
    FROM (...) H JOIN (...) J ON H.FLAVOR = J.FLAVOR
  • 총 주문량에 대해 내림차순 정렬 후 상위 3개의 레코드를 조회합니다.
    ORDER BY H.TOTAL_ORDER + J.TOTAL_ORDER DESC
    FETCH NEXT 3 ROWS ONLY

조건에 맞는 도서와 저자 리스트 출력하기

SELECT 
    B.BOOK_ID, 
    A.AUTHOR_NAME, 
    TO_CHAR(B.PUBLISHED_DATE, 'YYYY-MM-DD') AS PUBLISHED_DATE
FROM BOOK B JOIN AUTHOR A ON B.AUTHOR_ID = A.AUTHOR_ID
WHERE B.CATEGORY = '경제'
ORDER BY B.PUBLISHED_DATE
  • 도서 테이블과 저자 테이블을 JOIN하기 위해 AUTHOR_ID를 기준으로 JOIN합니다.
    FROM BOOK B JOIN AUTHOR A ON B.AUTHOR_ID = A.AUTHOR_ID
  • 경제 카테고리에 해당하는 도서를 찾기 위해 조건을 걸어줍니다.
    WHERE B.CATEGORY = '경제'
  • 출판일을 기준으로 오름차순 정렬합니다.

그룹별 조건에 맞는 식당 목록 출력하기

WITH MemberReviewCount AS (
    SELECT
        MEMBER_ID,
        COUNT(*) AS REVIEW_COUNT
    FROM
        REST_REVIEW
    GROUP BY
        MEMBER_ID
),
MaxReviewMember AS (
    SELECT
        MEMBER_ID
    FROM
        MemberReviewCount
    WHERE
        REVIEW_COUNT = (SELECT MAX(REVIEW_COUNT) FROM MemberReviewCount)
)

SELECT
    MP.MEMBER_NAME,
    RR.REVIEW_TEXT,
    TO_CHAR(RR.REVIEW_DATE, 'YYYY-MM-DD')
FROM
    REST_REVIEW RR
    JOIN MEMBER_PROFILE MP ON RR.MEMBER_ID = MP.MEMBER_ID
    JOIN MaxReviewMember MRM ON RR.MEMBER_ID = MRM.MEMBER_ID
ORDER BY
    TO_CHAR(RR.REVIEW_DATE, 'YYYY-MM-DD') ASC,
    RR.REVIEW_TEXT ASC;
  • WITH 키워드를 이용하여 테이블을 미리 생성합니다.
    첫번째 테이블 MemberReviewCount는 MEMBER_ID를 기준으로 리뷰 횟수를 조회하는 테이블을 생성합니다.
WITH MemberReviewCount AS (
    SELECT
        MEMBER_ID,
        COUNT(*) AS REVIEW_COUNT
    FROM
        REST_REVIEW
    GROUP BY
        MEMBER_ID
),
  • 두번째 테이블은 리뷰를 가장 많이 작성한 회원 ID를 조회하기 위한 테이블을 생성합니다
MaxReviewMember AS (
    SELECT
        MEMBER_ID
    FROM
        MemberReviewCount
    WHERE
        REVIEW_COUNT = (SELECT MAX(REVIEW_COUNT) FROM MemberReviewCount)
)
  • 레스토랑 리뷰 테이블과 멤버 정보 테이블, MaxReviewMember 테이블을 Member_ID를 기준으로 JOIN 합니다.
    FROM
        REST_REVIEW RR
        JOIN MEMBER_PROFILE MP ON RR.MEMBER_ID = MP.MEMBER_ID
        JOIN MaxReviewMember MRM ON RR.MEMBER_ID = MRM.MEMBER_ID
  • 리뷰 작성일을 기준으로 오름차순, 리뷰 텍스트를 기준으로 오름차순 정렬합니다.

없어진 기록 찾기

SELECT
    O.ANIMAL_ID,
    O.NAME
FROM ANIMAL_INS I JOIN ANIMAL_OUTS O ON I.ANIMAL_ID(+) = O.ANIMAL_ID
WHERE I.ANIMAL_ID IS NULL
ORDER BY O.ANIMAL_ID, O.NAME;
  • 동물 보호소에 들어온 동물과 입양 보낸 동물들을 ANIMAL_ID를 기준으로 LEFT JOIN합니다.
    LEFT JOIN하는 이유는 입양 들어온 기록은 존재하는데 입양 보낸 기록이 없을 때를 찾기 위함입니다.
    FROM ANIMAL_INS I JOIN ANIMAL_OUTS O ON I.ANIMAL_ID(+) = O.ANIMAL_ID
  • 없어진 동물의 기록을 찾아야하기 때문에 I.ANIMAL_ID가 NULL인 데이터를 조회합니다.
    WHERE I.ANIMAL_ID IS NULL
  • 동물 ID와 이름을 기준으로 오름차순 정렬합니다.

있었는데요 없었습니다

SELECT
    I.ANIMAL_ID,
    I.NAME
FROM ANIMAL_INS I JOIN ANIMAL_OUTS O ON I.ANIMAL_ID = O.ANIMAL_ID
WHERE I.DATETIME > O.DATETIME
ORDER BY I.DATETIME ASC
  • 동물 보호소에 들어온 동물과 입양 보낸 동물들을 ANIMAL_ID를 기준으로 JOIN합니다.
    FROM ANIMAL_INS I JOIN ANIMAL_OUTS O ON I.ANIMAL_ID = O.ANIMAL_ID
  • 보호 시작일이 입양일보다 더 늦은 레코드를 조회하기 위해 조건을 걸어줍니다.
    WHERE I.DATETIME > O.DATETIME
  • 보호 시작일이 빠른 순으로 조회합니다.(오름차순으로 정렬합니다)

오랜기간 보호한 동물(1)

SELECT
    I.NAME, 
    I.DATETIME
FROM ANIMAL_INS I JOIN ANIMAL_OUTS O ON I.ANIMAL_ID = O.ANIMAL_ID(+)
WHERE O.ANIMAL_ID IS NULL
ORDER BY I.DATETIME ASC
FETCH NEXT 3 ROWS ONLY
  • 동물 보호소에 들어온 동물과 입양 보낸 동물들을 ANIMAL_ID를 기준으로 RIGHT JOIN합니다.
    RIGHT JOIN하는 이유는 입양을 아직 못간 동물을 찾기 위합니다.
    FROM ANIMAL_INS I JOIN ANIMAL_OUTS O ON I.ANIMAL_ID = O.ANIMAL_ID(+)
  • 입양을 못간 동물을 조회하기 위해 조건을 걸어줍니다.
    WHERE O.ANIMAL_ID IS NULL
  • 보호 시작일을 기준으로 오름차순으로 정렬하고 상위 3개의 레코드를 조회하기 위해 FETCH NEXT 절을 사용합니다.
    ORDER BY I.DATETIME ASC
    FETCH NEXT 3 ROWS ONLY

보호소에서 중성화한 동물

SELECT
    I.ANIMAL_ID, 
    I.ANIMAL_TYPE, 
    I.NAME
FROM ANIMAL_INS I JOIN ANIMAL_OUTS O ON I.ANIMAL_ID = O.ANIMAL_ID
WHERE I.SEX_UPON_INTAKE LIKE 'Intact%' 
    AND (O.SEX_UPON_OUTCOME LIKE 'Spayed%' OR O.SEX_UPON_OUTCOME LIKE 'Neutered%')
ORDER BY I.ANIMAL_ID
  • 동물 보호소에 들어온 동물과 입양 보낸 동물들을 ANIMAL_ID를 기준으로 JOIN합니다.
    FROM ANIMAL_INS I JOIN ANIMAL_OUTS O ON I.ANIMAL_ID = O.ANIMAL_ID
  • 보호소에서 중성화한 동물을 찾기 위해 조건을 걸어줍니다.
    들어왔을 때는 아니였지만 나갈때 중성화가 되어있으면 보호소에서 중성화 한 것입니다.
    WHERE I.SEX_UPON_INTAKE LIKE 'Intact%' 
        AND (O.SEX_UPON_OUTCOME LIKE 'Spayed%' OR O.SEX_UPON_OUTCOME LIKE 'Neutered%')
  • 동물 ID를 기준으로 오름차순 정렬합니다.

상품 별 오프라인 매출 구하기

SELECT
    P.PRODUCT_CODE,
    P.PRICE * S.TOTAL_SALES AS SALES
FROM PRODUCT P JOIN (
    SELECT PRODUCT_ID, SUM(SALES_AMOUNT) AS TOTAL_SALES
    FROM OFFLINE_SALE
    GROUP BY PRODUCT_ID
) S ON P.PRODUCT_ID = S.PRODUCT_ID
ORDER BY SALES DESC, P.PRODUCT_CODE ASC
  • 오프라인에서 상품별 판매량을 구하기 위해 상품 ID를 기준으로 그룹화한 테이블을 S로 생성합니다.
(
    SELECT PRODUCT_ID, SUM(SALES_AMOUNT) AS TOTAL_SALES
    FROM OFFLINE_SALE
    GROUP BY PRODUCT_ID
) S
  • 상품 테이블과 S를 PRODUCT_ID를 기준으로 JOIN합니다.
    FROM PRODUCT P JOIN (...) S ON P.PRODUCT_ID = S.PRODUCT_ID
  • 매출을 구하기 위해 다음과 같이 필드를 생성합니다.
    P.PRICE * S.TOTAL_SALES AS SALES
  • 총 매출량을 기준으로 내림차순, 상품 코드를 기준으로 오름차순 정렬합니다.

상품을 구매한 회원 비율 구하기

SELECT 
    TO_CHAR(O.SALES_DATE, 'YYYY') AS YEAR,
    EXTRACT(MONTH FROM O.SALES_DATE) AS MONTH,
    COUNT(DISTINCT U.USER_ID) AS PUCHASED_USERS,
    ROUND(COUNT(DISTINCT U.USER_ID)/(SELECT COUNT(*) FROM USER_INFO WHERE TO_CHAR(joined, 'YYYY') = '2021'), 1) AS PUCHASED_RATIO
FROM USER_INFO U JOIN ONLINE_SALE O ON U.USER_ID = O.USER_ID
WHERE TO_CHAR(U.JOINED, 'YYYY') = '2021'
GROUP BY TO_CHAR(O.SALES_DATE, 'YYYY'), EXTRACT(MONTH FROM O.SALES_DATE)
ORDER BY TO_CHAR(O.SALES_DATE, 'YYYY'), EXTRACT(MONTH FROM O.SALES_DATE)
  • 상품을 구매한 회원을 찾기 위해 USER_INFO 테이블과 ONLINE_SALE 테이블을 JOIN 합니다.
    FROM USER_INFO U JOIN ONLINE_SALE O ON U.USER_ID = O.USER_ID
  • 그 중 2021년에 가입한 회원을 구하기 위해 조건을 걸어줍니다.
    WHERE TO_CHAR(U.JOINED, 'YYYY') = '2021'
  • 연, 월별로 (구매 회원수 / 가입 회원) 을 구해야하기에 연, 월을 기준으로 그룹화를 진행합니다.
    GROUP BY TO_CHAR(O.SALES_DATE, 'YYYY'), EXTRACT(MONTH FROM O.SALES_DATE)
  • 상품을 구매한 회원의 비율을 소수점 두번째 자리에서 반올림해야 하기에 다음과 같이 컬럼을 구성합니다.
    2021년에 가입한 회원의 전체 수를 구하기 위해 서브 쿼리를 이용합니다.
    ROUND(COUNT(DISTINCT U.USER_ID)/(SELECT COUNT(*) FROM USER_INFO WHERE TO_CHAR(joined, 'YYYY') = '2021'), 1) AS PUCHASED_RATIO
  • 연과 월을 기준으로 오름차순 정렬합니다.

FrontEnd 개발자 찾기

SELECT
    D.ID,
    D.EMAIL,
    D.FIRST_NAME,
    D.LAST_NAME
FROM
    DEVELOPERS D
WHERE
    EXISTS (
        SELECT 1
        FROM SKILLCODES S
        WHERE S.CATEGORY = 'FRONT END'
          AND (D.SKILL_CODE & S.CODE) <> 0
    )
ORDER BY
    D.ID ASC;
  • 서브 쿼리를 사용합니다.
    SKILL이 FRONT END이고 DEVELOPERS의 SKILL_CODE와 비트 연산을 통해 0보다 큰(공통 비트가 존재하는) 컬럼을 조회합니다.
FROM
    DEVELOPERS D
WHERE
    EXISTS (
        SELECT 1
        FROM SKILLCODES S
        WHERE S.CATEGORY = 'FRONT END'
          AND (D.SKILL_CODE & S.CODE) <> 0
    )
  • 개발자 ID를 기준으로 정렬합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers SQL 고득점 Kit(IS NULL)입니다.

경기도에 위치한 식품창고 목록 출력하기

SELECT
    WAREHOUSE_ID,
    WAREHOUSE_NAME,
    ADDRESS,
    NVL(FREEZER_YN, 'N') AS FREEZER_YN
FROM FOOD_WAREHOUSE
WHERE ADDRESS LIKE '경기%'
ORDER BY WAREHOUSE_ID ASC
  • 경기도에 위치한 공장을 통해 like를 사용합니다.
    WHERE ADDRESS LIKE '경기%'
  • 냉동 여부가 null 것을 n으로 처리하기 위해 NVL을 사용합니다.
    NVL(FREEZER_YN, 'N') AS FREEZER_YN
  • 창고 ID를 기준으로 오름차순 정렬합니다.

이름이 없는 동물의 아이디

SELECT
    ANIMAL_ID
FROM ANIMAL_INS
WHERE NAME IS NULL
ORDER BY ANIMAL_ID ASC
  • 이름이 없는 동물의 아이디를 찾기 위해 IS NULL을 사용합니다.
    WHERE NAME IS NULL
  • ANIMAL_ID를 기준으로 오름차순 정렬합니다.

이름이 있는 동물의 아이디

SELECT
    ANIMAL_ID
FROM ANIMAL_INS
WHERE NAME IS NOT NULL
ORDER BY ANIMAL_ID;
  • 이름이 있는 동물의 아이디를 찾기 위해 IS NOT NULL을 사용합니다.
    WHERE NAME IS NOT NULL
  • ANIMAL_ID를 기준으로 오름차순 정렬합니다.

NULL 처리하기

SELECT
    ANIMAL_TYPE,
    NVL(NAME, 'No name'),
    SEX_UPON_INTAKE
FROM ANIMAL_INS
ORDER BY ANIMAL_ID
  • NAME이 null인 레코드를 No name으로 처리하기 위해 NVL을 사용합니다.
    NVL(NAME, 'No name')
  • ANIMAL_ID를 기준으로 오름차순 정렬합니다.

나이 정보가 없는 회원 수 구하기

SELECT
    COUNT(*) AS USERS
FROM USER_INFO
WHERE AGE IS NULL
  • 나이 정보가 없는 회원을 구하기 위해 IS NULL을 사용합니다.
    WHERE AGE IS NULL
  • 회원 수를 구하기 위해 COUNT 함수를 이용합니다.

ROOT 아이템 구하기

SELECT
    T.ITEM_ID,
    I.ITEM_NAME
FROM ITEM_TREE T JOIN ITEM_INFO I
ON T.ITEM_ID = I.ITEM_ID
WHERE PARENT_ITEM_ID IS NULL
ORDER BY T.ITEM_ID
  • ROOT ITEM을 구하기 위해 ITEM_TREE와 ITEM_INFO 테이블을 ITEM_ID를 기준으로 정렬합니다.
    FROM ITEM_TREE T JOIN ITEM_INFO I ON T.ITEM_ID = I.ITEM_ID
  • PARENT_ITEM_ID가 null인 것이 root 아이템이므로 해당 레코드를 조회합니다.
    WHERE PARENT_ITEM_ID IS NULL
  • ITEM_ID를 기준으로 오름차순 정렬합니다.

업그레이드 할 수 없는 아이템 구하기

SELECT
    I.ITEM_ID,
    I.ITEM_NAME,
    I.RARITY
FROM ITEM_INFO I LEFT OUTER JOIN ITEM_TREE T
ON I.ITEM_ID = T.PARENT_ITEM_ID
WHERE T.PARENT_ITEM_ID IS NULL
ORDER BY I.ITEM_ID DESC
  • 업그레이드 할 수 없는 아이템을 구하기 위해 ITEM_INFO ITEM_ID와 ITEM_TREE PARENT_ITEM_ID를 LEFT OUTER JOIN 후 PARENT_ITEM_ID가 null인 레코드를 조회합니다.
    FROM ITEM_INFO I LEFT OUTER JOIN ITEM_TREE T ON I.ITEM_ID = T.PARENT_ITEM_ID
    WHERE T.PARENT_ITEM_ID IS NULL
  • ITEM_ID를 기준으로 내림차순 정렬합니다.

잡은 물고기의 평균 길이 구하기

SELECT
    ROUND(AVG(IFNULL(LENGTH, 10)), 2) AVERAGE_LENGTH
FROM FISH_INFO
  • LENGTH가 null이면 10으로 처리하기 위해 IFNULL을 사용하여 평균값을 소수점 셋째자리에서 반올림합니다.
    SELECT ROUND(AVG(IFNULL(LENGTH, 10)), 2) AVERAGE_LENGTH
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers SQL 고득점 Kit(GROUP BY)입니다.

노선별 평균 역 사이 거리 조회하기

SELECT
    ROUTE,
    CONCAT(ROUND(SUM(D_BETWEEN_DIST), 1), 'km') AS TOTAL_DISTANCE,
    CONCAT(ROUND(AVG(D_BETWEEN_DIST), 2), 'km') AS AVERAGE_DISTANCE
FROM SUBWAY_DISTANCE
GROUP BY ROUTE
ORDER BY SUM(D_BETWEEN_DIST) DESC;
  • 노선 별로 구하기 위해 ROUTE를 그룹화합니다.
    GROUP BY ROUTE
  • 총 누계거리와 평균 역사이를 구하기 위해 SUM과 AVG 함수를 사용하고 문자열을 합성하기 위해 CONCAT 함수를 사용합니다.
    CONCAT(ROUND(SUM(D_BETWEEN_DIST), 1), 'km') AS TOTAL_DISTANCE,
    CONCAT(ROUND(AVG(D_BETWEEN_DIST), 2), 'km') AS AVERAGE_DISTANCE
  • 총 누계거리를 기준으로 내림차순 정렬합니다.

물고기 종류 별 잡은 수 구하기

SELECT COUNT(*) AS FISH_COUNT, FISH_NAME
FROM FISH_INFO A
JOIN FISH_NAME_INFO B
  ON A.FISH_TYPE = B.FISH_TYPE
GROUP BY FISH_NAME
ORDER BY 1 DESC
  • 잡은 물고기와 이름을 매핑하기 위해 JOIN 합니다.
    JOIN FISH_NAME_INFO B ON A.FISH_TYPE = B.FISH_TYPE
  • 물고기 종류를 위해 그룹화합니다.
    GROUP BY FISH_NAME
  • 물고기 잡은 수를 기준으로 내림차순 정렬합니다.

월별 잡은 물고기 수 구하기

SELECT
    COUNT(*) AS FISH_COUNT,
    EXTRACT(MONTH FROM TIME) AS MONTH
FROM FISH_INFO
GROUP BY EXTRACT(MONTH FROM TIME)
ORDER BY EXTRACT(MONTH FROM TIME) ASC
  • 월별로 잡은 물고기를 구하기 위해 그룹화합니다.
    GROUP BY EXTRACT(MONTH FROM TIME)
  • 월을 기준으로 오름차순 정렬합니다.

특정 조건을 만족하는 물고기 별 수와 최대 길이 구하기

SELECT
    COUNT(FISH_TYPE) AS FISH_COUNT,
    MAX(LENGTH) AS MAX_LENGTH,
    FISH_TYPE
FROM (
    SELECT
        ID,
        FISH_TYPE,
        IFNULL(LENGTH, 10) AS LENGTH
    FROM FISH_INFO
) F
GROUP BY FISH_TYPE
HAVING AVG(LENGTH) >= 33
ORDER BY FISH_TYPE ASC;
  • 길이가 10 이하면 null이므로 null을 10으로 지정하기 위해 IFNULL 함수를 사용한 서브 테이블을 F로 지정합니다.
    (
        SELECT
            ID,
            FISH_TYPE,
            IFNULL(LENGTH, 10) AS LENGTH
        FROM FISH_INFO
    ) F
  • 평균 길이가 33인 물고기 종류 별로 그룹화합니다.
    GROUP BY FISH_TYPE
    HAVING AVG(LENGTH) >= 33
  • 최대 길이와 물고기 잡은 수를 위해 COUNT 함수와 MAX 함수를 사용합니다.
  • 물고기 종류를 기준으로 오름차순 정렬합니다.
제가 공부한 내용을 정리하는 블로그입니다.
아직 많이 부족하고 배울게 너무나도 많습니다. 틀린내용이 있으면 언제나 가감없이 말씀해주시면 감사하겠습니다😁
Programmers SQL 고득점 Kit(GROUP BY)입니다.

고양이와 개는 몇마리 있을까

SELECT
    ANIMAL_TYPE,
    COUNT(*) AS COUNT
FROM ANIMAL_INS
GROUP BY ANIMAL_TYPE
ORDER BY ANIMAL_TYPE
  • 고양이와 개를 그룹화합니다.
    GROUP BY ANIMAL_TYPE
  • 개수를 구하기 위해 COUNT 함수를 사용하고 고양이가 먼저 나오기 위해 오름차순 정렬합니다.

동명 동물 수 찾기

SELECT
    NAME,
    COUNT(*) AS COUNT
FROM ANIMAL_INS
WHERE NAME IS NOT NULL
GROUP BY NAME
HAVING COUNT(*) > 1
ORDER BY NAME
  • 이름이 있으며, 이름을 기준으로 그룹화합니다.
    WHERE NAME IS NOT NULL
    GROUP BY NAME
  • 동일한 이름의 동물을 찾기 위해 HAVING 절을 통해 COUNT 함수를 사용합니다.
    HAVING COUNT(*) > 1
  • 동물 이름을 기준으로 오름차순 정렬합니다.

년, 월, 성별 별 상품 구매 회원 수 구하기

SELECT
    TO_CHAR(O.SALES_DATE, 'YYYY') AS YEAR, 
    EXTRACT(MONTH FROM O.SALES_DATE) AS MONTH, 
    U.GENDER AS GENDER,
    COUNT(DISTINCT U.USER_ID) AS USERS
FROM USER_INFO U JOIN ONLINE_SALE O
ON U.USER_ID = O.USER_ID
WHERE U.GENDER IS NOT NULL
GROUP BY TO_CHAR(O.SALES_DATE, 'YYYY'), EXTRACT(MONTH FROM O.SALES_DATE), U.GENDER
ORDER BY TO_CHAR(O.SALES_DATE, 'YYYY'), EXTRACT(MONTH FROM O.SALES_DATE), U.GENDER
  • 상품 구매한 회원을 구하기 위해 JOIN합니다.
    FROM USER_INFO U JOIN ONLINE_SALE O ON U.USER_ID = O.USER_ID
  • 성별 정보가 없는 데이터는 제외하기 위해 조건을 걸어줍니다.
    WHERE U.GENDER IS NOT NULL
  • 연도, 월, 성별을 기준으로 그룹화 및 정렬합니다.
    GROUP BY TO_CHAR(O.SALES_DATE, 'YYYY'), EXTRACT(MONTH FROM O.SALES_DATE), U.GENDER
    ORDER BY TO_CHAR(O.SALES_DATE, 'YYYY'), EXTRACT(MONTH FROM O.SALES_DATE), U.GENDER

입양 시각 구하기(1)

SELECT 
    TO_NUMBER(TO_CHAR(DATETIME, 'HH24') AS HOUR,
    COUNT(*) AS COUNT
FROM ANIMAL_OUTS
WHERE TO_CHAR(DATETIME, 'HH24') BETWEEN 09 AND 19
GROUP BY TO_CHAR(DATETIME, 'HH24')
ORDER BY HOUR ASC
  • 09시와 19시 사이의 데이터를 조회하기 위해 TO_CHAR와 BETWEEN 함수를 사용합니다.
    WHERE TO_CHAR(DATETIME, 'HH24') BETWEEN 09 AND 19
  • 입양 시각을 기준으로 그룹화합니다.
    GROUP BY TO_CHAR(DATETIME, 'HH24')
  • 정렬하기 위해 TO_NUMBER 함수를 사용하고 HOUR를 기준으로 오름차순 정렬합니다.

입양 시각 구하기(2)

SELECT 
    L.HOUR AS HOUR, 
    NVL(COUNT, 0) AS COUNT
FROM (
    SELECT TO_CHAR(DATETIME, 'HH24') AS HOUR, COUNT(*) AS COUNT 
    FROM ANIMAL_OUTS 
    GROUP BY TO_CHAR(DATETIME, 'HH24') 
    ORDER BY HOUR
) O, (
    SELECT LEVEL -1 AS HOUR 
    FROM DUAL 
    CONNECT BY LEVEL <= 24
) L      
WHERE L.HOUR = O.HOUR(+)
ORDER BY L.HOUR;
  • 입양 시각을 통해 그룹화를 진행합니다.
    (
        SELECT TO_CHAR(DATETIME, 'HH24') AS HOUR, COUNT(*) AS COUNT 
        FROM ANIMAL_OUTS 
        GROUP BY TO_CHAR(DATETIME, 'HH24') 
        ORDER BY HOUR
    ) O
  • 가상의 테이블와 계층 쿼리를 통해 LEVEL을 0부터 23까지 숫자를 반환합니다.
    ( SELECT LEVEL -1 AS HOUR FROM DUAL CONNECT BY LEVEL <= 24 ) L
  • O 테이블과 L 테이블을 LEFT OUTER JOIN합니다. 레코드가 없는 행을 0으로 표현하기 위함입니다.
    WHERE L.HOUR = O.HOUR(+)
  • NULL인 데이터를 0으로 표현하기 위해 NVL함수를 사용하고 HOUR를 기준으로 오름차순 정렬합니다.
    NVL(COUNT, 0) AS COUNT

가격대 별 상품 개수 구하기

SELECT
    FLOOR(PRICE / 10000) * 10000 AS PRICE_GROUP,
    COUNT(*)
FROM PRODUCT
GROUP BY FLOOR(PRICE / 10000) * 10000
ORDER BY PRICE_GROUP
  • 만원 단위의 가격대로 표현하기 위해 버림과 10000 곱하기를 통해 그룹화합니다.
    GROUP BY FLOOR(PRICE / 10000) * 10000
  • 가격대를 기준으로 오름차순 정렬합니다.

언어별 개발자 분류하기

WITH J AS (
    SELECT 
        ID,
        EMAIL,
        CASE -- 조건에 맞게 SCORE를 기록
           WHEN COUNT(CASE WHEN CATEGORY = 'Front End' THEN 1 END) > 0 AND 
                COUNT(CASE WHEN NAME = 'Python' THEN 1 END) > 0 
                THEN 3
           WHEN COUNT(CASE WHEN NAME = 'C#' THEN 1 END) > 0 THEN 2
           WHEN COUNT(CASE WHEN CATEGORY = 'Front End' THEN 1 END) > 0 THEN 1
        END AS SCORE
    FROM
        DEVELOPERS AS D
    JOIN -- 비트 논리 연산을 조건으로 JOIN
        SKILLCODES AS S ON D.SKILL_CODE & S.CODE
    GROUP BY 
        ID,EMAIL
    HAVING 
        SCORE > 0
)
SELECT 
    CASE 
        WHEN SCORE = 3 THEN 'A'
        WHEN SCORE = 2 THEN 'B'
        WHEN SCORE = 1 THEN 'C'
    END AS GRADE,
    ID,
    EMAIL
FROM 
    J
ORDER BY 
    1,2
  • DEVELOPERS 테이블의 SKILL_CODE와 SKILLCODES 테이블의 CODE 값을 비트 논리 AND 연산(&)을 통해 조건에 맞는 조인을 수행
    FROM DEVELOPERS AS D JOIN SKILLCODES AS S ON D.SKILL_CODE & S.CODE
  • ID와 EMAIL을 통해 그룹화 합니다.
    GROUP BY ID, EMAIL
  • SCORE는 0보다 큰 것들만 조회합니다.
    HAVING SCORE > 0
  • 개발자의 점수를 결정하기 위해 CASE 문을 사용합니다.
  • WITH 절을 이용합니다.
  • CASE 절을 통해 A, B, C를 결정합니다.
    CASE 
            WHEN SCORE = 3 THEN 'A'
            WHEN SCORE = 2 THEN 'B'
            WHEN SCORE = 1 THEN 'C'
        END AS GRADE,
  • GRADE와 ID를 통해 오름차순 정렬합니다.

조건에 맞는 사원정보 조회하기

SELECT
    G.SCORE,
    G.EMP_NO,
    E.EMP_NAME,
    E.POSITION,
    E.EMAIL
FROM (
    SELECT
        EMP_NO,
        SUM(SCORE) AS SCORE
    FROM HR_GRADE
    GROUP BY EMP_NO, YEAR
    ORDER BY SCORE DESC
    LIMIT 1
) G
JOIN HR_EMPLOYEES E ON G.EMP_NO = E.EMP_NO;
  • 사원 정보와 연도를 기준으로 GROUP화하여 SCORE가 가장 높은 사원을 조회하는 서브 테이블을 G로 설정합니다.
    (
        SELECT
            EMP_NO,
            SUM(SCORE) AS SCORE
        FROM HR_GRADE
        GROUP BY EMP_NO, YEAR
        ORDER BY SCORE DESC
        LIMIT 1
    ) G
  • HR_EMPLOYEES 와 회원 넘버를 통해 JOIN합니다.
    FROM (...) G JOIN HR_EMPLOYEES E ON G.EMP_NO = E.EMP_NO

연간 평가 점수에 해당하는 평가 등급 및 성과금 조회하기

SELECT
    G.EMP_NO,
    E.EMP_NAME,
    CASE
        WHEN G.SCORE / 2 >= 96 THEN 'S'
        WHEN G.SCORE / 2 >= 90 THEN 'A'
        WHEN G.SCORE / 2 >= 80 THEN 'B' 
        ELSE 'C'
    END AS GRADE,
    E.SAL * CASE
        WHEN G.SCORE / 2 >= 96 THEN 0.2
        WHEN G.SCORE / 2 >= 90 THEN 0.15
        WHEN G.SCORE / 2 >= 80 THEN 0.1
        ELSE 0
    END AS BONUS
FROM (
    SELECT
        EMP_NO,
        SUM(SCORE) AS SCORE
    FROM HR_GRADE
    GROUP BY EMP_NO, YEAR
    ORDER BY SCORE DESC
) G
JOIN HR_EMPLOYEES E ON G.EMP_NO = E.EMP_NO
ORDER BY G.EMP_NO;
  • 사원 정보와 연도를 기준으로 GROUP화하여 합산 SCORE 사원을 조회하는 서브 테이블을 G로 설정합니다.
    (
        SELECT
            EMP_NO,
            SUM(SCORE) AS SCORE
        FROM HR_GRADE
        GROUP BY EMP_NO, YEAR
        ORDER BY SCORE DESC
    ) G
  • HR_EMPLOYEES 와 회원 넘버를 통해 JOIN합니다.
    FROM (...) G JOIN HR_EMPLOYEES E ON G.EMP_NO = E.EMP_NO
  • CASE 절을 통해 성과금과 평가등급을 설정합니다.
    CASE
            WHEN G.SCORE / 2 >= 96 THEN 'S'
            WHEN G.SCORE / 2 >= 90 THEN 'A'
            WHEN G.SCORE / 2 >= 80 THEN 'B' 
            ELSE 'C'
        END AS GRADE,
        E.SAL * CASE
            WHEN G.SCORE / 2 >= 96 THEN 0.2
            WHEN G.SCORE / 2 >= 90 THEN 0.15
            WHEN G.SCORE / 2 >= 80 THEN 0.1
            ELSE 0
        END AS BONUS

부서별 평균 연봉 조회하기

SELECT
    E.DEPT_ID,
    D.DEPT_NAME_EN,
    E.AVG_SAL
FROM HR_DEPARTMENT D JOIN (
    SELECT
        DEPT_ID,
        ROUND(AVG(SAL)) AS AVG_SAL
    FROM HR_EMPLOYEES
    GROUP BY DEPT_ID
) E ON D.DEPT_ID = E.DEPT_ID
ORDER BY E.AVG_SAL DESC
  • 부서 아이디를 통해 그룹화하여 평균 연봉을 서브테이블 E로 구합니다.
    (
        SELECT
            DEPT_ID,
            ROUND(AVG(SAL)) AS AVG_SAL
        FROM HR_EMPLOYEES
        GROUP BY DEPT_ID
    ) E
  • HR_DEPARTMENT 테이블과 E 테이블을 부서 아이디를 통해 JOIN합니다.
    FROM HR_DEPARTMENT D JOIN (...) E ON D.DEPT_ID = E.DEPT_ID
  • 평균 연봉을 통해 내림차순 정렬합니다.

+ Recent posts