본문 바로가기
Coding Test/SQL 공부

SQL 구문 급속 학습

by assidu 2026. 3. 30.

[1번 SQL. 전체 구문 ]

SELECT 
    B1.TYPE AS PARENT_TYPE,
    COUNT(F.MONSTER_ID) AS FUSION_COUNT,
    AVG(B1.COMBAT_POWER + B2.COMBAT_POWER) AS AVG_TOTAL_POWER,
    CASE 
        WHEN AVG(B1.COMBAT_POWER + B2.COMBAT_POWER) >= 2000 THEN '전설급'
        ELSE '영웅급'
    END AS GRADE_LABEL
FROM FUSION_MONSTERS F
LEFT JOIN BASIC_MONSTERS B1 ON F.MONSTER_ID_1 = B1.MONSTER_ID
LEFT JOIN BASIC_MONSTERS B2 ON F.MONSTER_ID_2 = B2.MONSTER_ID
GROUP BY B1.TYPE
HAVING AVG_TOTAL_POWER >= 1000
ORDER BY AVG_TOTAL_POWER DESC;

작동 순서대로(FROM ➡️ JOIN ➡️ GROUP ➡️ HAVING ➡️ SELECT ➡️ ORDER)

 

 

1. 데이터를 가져올 준비 (FROM, LEFT JOIN)

FROM FUSION_MONSTERS F
LEFT JOIN BASIC_MONSTERS B1 ON F.MONSTER_ID_1 = B1.MONSTER_ID
LEFT JOIN BASIC_MONSTERS B2 ON F.MONSTER_ID_2 = B2.MONSTER_ID

 

  • 의미: "가장 먼저 FUSION_MONSTERS 테이블(별명 F)을 펼쳐! 그리고 부모 1번과 부모 2번의 스펙을 알아야 하니까, BASIC_MONSTERS 테이블을 양옆에 각각 B1, B2라는 이름표를 붙여서 조인해 와."
  • 문법 포인트: JOIN 뒤의 ON은 두 테이블을 엮어줄 **'열쇠(Key)'**를 지정하는 곳입니다.
  • ON 뒤에 조건이라고 보면 된다. F.MONSTER_ID_1 = B1.MONSTER_ID 인 데이터를 BASIC_MONSTERS 테이블에서 가져온다.(별명으로 여기서 B1 테이블이라 부름) 

구문>>

FROM [테이블명] [별명]                 /                  [칼럼명] [별명]   

 

 

이름은 LEFT JOIN인데 합쳐진 표를 보면 나중에 부른 테이블이 오른쪽에 달라붙는다.

 

    (1) 'LEFT'의 진짜 의미는 "왼쪽을 절대 사수하라"

 

글을 적을 때 먼저 등장한 왼쪽(기준) 테이블의 데이터는 무조건 살려라! 라는 특명 의미함. => FROM 융합몬스터 (왼쪽에 적힌 기준 테이블!) LEFT JOIN 기본몬스터 (오른쪽에 적힌 보조 테이블!)

 

    (2) 글을 읽는 순서 = 표가 만들어지는 순서

 

데이터베이스는 쿼리에 적힌 순서대로 표의 기둥(칼럼)을 차례대로 세운다.

 

> FROM 융합테이블  => 가장 먼저 불렸으니 표의 왼쪽 자리 차지!

> LEFT JOIN 부모테이블 => 나중에 불렸으니 이미 세워진 융합 테이블의 오른쪽 빈자리에 기둥을 세우며 달라붙는다.

그래서 가로로 길게 표가 만들어짐.

 

 

 

2. 끼리끼리 묶기 (GROUP BY)

GROUP BY B1.TYPE

 

  • 의미: "방금 양옆으로 길게 합친 거대한 표를, 메인 부모(B1)의 속성(불, 물, 풀 등)을 기준으로 그룹지어 묶어!"
  • 문법 포인트: 이 구문이 들어가는 순간, 개별 몬스터의 이름이나 ID는 볼 수 없고 오직 **'그룹의 통계(합계, 평균, 개수)'**만 뽑아낼 수 있는 상태로 압축됩니다.

GROUP BY 이후에 살아남는 구문]

 

GROUP BY로 데이터를 압축한 이후에는 SELECT 구문 자리에 딱 2가지만 적을 수 있습니다.

  1. 그룹을 묶은 기준 자체: B1.TYPE (폴더 이름인 '불', '물', '풀'은 명확하니까 출력 가능)
  2. 그룹 전체를 뭉뚱그려 계산하는 통계 함수: * COUNT(*): "불 폴더 안에 문서(몬스터)가 몇 장 들어있어?"
    • SUM(전투력): "불 폴더 안에 있는 애들 전투력 다 더하면 몇이야?"
    • AVG(전투력): "불 폴더 애들 평균 전투력은?"

한 줄 요약: GROUP BY B1.TYPE까지만 실행하면, 수많은 데이터가 '속성별 폴더' 단위로 꽉 압축되며, 이때부터는 개별 데이터의 디테일(이름, ID 등)을 물어볼 수 없고 오직 그룹의 통계(몇 마리인지, 평균이 몇인지)만 물어볼 수 있는 상태가 됩니다!

 

 

3. 묶은 그룹 필터링하기 (HAVING)

HAVING AVG(B1.COMBAT_POWER + B2.COMBAT_POWER) >= 1000
-- (또는 MySQL 환경에서는 SELECT에서 지어준 별명인 HAVING AVG_TOTAL_POWER >= 1000 도 가능)

 

 

  • 의미: "방금 속성별로 묶어봤지? 그중에서 부모 전투력을 합친 평균값이 1000이 넘는 속성 그룹만 살려!"
  • 문법 포인트: 개별 데이터를 거르는 WHERE와 달리, HAVING은 GROUP BY로 묶인 결과물에 조건을 걸 때만 쓰는 전용 필터입니다. 반드시 GROUP BY 바로 밑에 와야 합니다.

 

4. 화면에 보여줄 칼럼 정하기 (SELECT, AS, CASE WHEN)

SELECT 
    B1.TYPE AS PARENT_TYPE,
    COUNT(F.MONSTER_ID) AS FUSION_COUNT,
    AVG(B1.COMBAT_POWER + B2.COMBAT_POWER) AS AVG_TOTAL_POWER,
    CASE
    	WHEN AVG(B1.COMBAT_POWER + B2.COMBAT_POWER) >= 2000 THEN '전설급'
        ELSE '영웅급'
    END AS GRADE_LABEL
  • 의미: "필터링까지 다 끝났지? 이제 화면에 딱 4개의 기둥(칼럼)만 띄울 거야.
    1. 부모 속성 (이름은 PARENT_TYPE으로 바꿔서)
    2. 그 속성에 속한 융합 몬스터 마리 수 (COUNT)
    3. 전투력 평균값 (AVG)
    4. 평균값이 2000 이상이면 '전설급', 아니면 '영웅급'이라는 새로운 라벨 (CASE WHEN)"
  • 문법 포인트: AS는 화면에 출력될 간판 이름을 내 마음대로 갈아 끼우는 역할. 쉼표(,)로 각각의 기둥을 구분해 줍니다.

 

5. 최종 결과물 정렬하기 (ORDER BY)

ORDER BY AVG_TOTAL_POWER DESC;
  • 의미: "마지막으로, 화면에 출력하기 직전에 전투력 평균값이 가장 높은 순서대로(내림차순, DESC) 표를 정렬해서 보여줘!"
  • 문법 포인트: DESC(Descending)는 큰 것부터 작은 것으로 내려가는 내림차순, ASC(Ascending)는 작은 것부터 커지는 오름차순(생략 가능)입니다.

 

 

[2번 SQL. 전체 구문]

1번이 통계를 내는 GROUP BY 위주였다면, 이번에는 "수많은 데이터 속에서 내가 원하는 특정 조건의 데이터만 현미경처럼 정밀하게 걸러내는(Filtering)" 핵심 구문들입니다.

 

목표: "2026년에 포획된 기록이 있는 몬스터 중에서, 속성이 '불' 또는 '물'이고 이름에 '드래곤'이 들어가는 가장 강력한 몬스터 딱 3마리만 보여줘!"

SELECT 
    NAME AS MONSTER_NAME, 
    TYPE, 
    COMBAT_POWER
FROM BASIC_MONSTERS
WHERE TYPE IN ('불', '물') 
  AND NAME LIKE '%드래곤%'
  AND MONSTER_ID IN (
      SELECT MONSTER_ID 
      FROM CAPTURE_LOGS 
      WHERE YEAR(CAPTURE_DATE) = 2026
  )
ORDER BY COMBAT_POWER DESC
LIMIT 3;

 

 

1. 여러 개를 한 번에 고르기 (IN)

WHERE TYPE IN ('불', '물')
  • 의미: "속성이 '불'이거나 '물'인 애들 다 나와!"
  • 문법 포인트: TYPE = '불' OR TYPE = '물'이라고 길게 써야 하는 코드를 아주 깔끔하게 압축해 줍니다. 괄호 안에 원하는 리스트를 콤마로 나열하기만 하면 됩니다.

2. 특정 단어 포함 여부 찾기 (LIKE)

AND NAME LIKE '%드래곤%'
  • 의미: "이름 앞뒤로 무슨 글자가 오든 상관없으니, 중간에 **'드래곤'**이라는 단어만 들어가 있으면 다 합격!" (예: 파이어드래곤, 드래곤슬라임 모두 검색됨)
  • 문법 포인트: % 기호가 핵심입니다. %는 '아무 글자나 와도 됨(또는 아예 안 와도 됨)'을 뜻하는 조커 카드입니다. 만약 드래곤%라고 쓰면 '드래곤'으로 시작하는 이름만 찾습니다.

3. 쿼리 안의 쿼리 (서브쿼리 + IN)

AND MONSTER_ID IN (
    SELECT MONSTER_ID FROM CAPTURE_LOGS WHERE YEAR(...)
)
  • 의미: 괄호 안의 새끼 쿼리(Subquery)가 먼저 실행되어 **'2026년에 잡힌 몬스터들의 ID 리스트(예: 1번, 5번, 9번)'**를 몰래 만들어냅니다. 그 후 밖의 메인 쿼리가 "내 ID가 저 리스트 안에 들어있나?" 하고 검사하는 구조입니다.
  • 문법 포인트: 굳이 복잡하게 JOIN으로 테이블을 합치지 않아도, 다른 테이블의 조건을 끌어와서 필터링할 수 있는 코테 최고의 치트키입니다.

4. 날짜에서 원하는 부분만 쏙 뽑기 (YEAR) (날짜함수!!!)

WHERE YEAR(CAPTURE_DATE) = 2026
  • 의미: 2026-03-30 14:22:00 처럼 복잡하게 생긴 날짜/시간 데이터에서 '연도(Year)' 부분만 숫자 2026으로 깔끔하게 잘라내서 비교합니다.
  • 문법 포인트: 날짜 함수는 코테 단골입니다. 달만 뽑고 싶으면 MONTH(), 일만 뽑고 싶으면 DAY()를 쓰면 됩니다.

5. 원하는 개수만큼만 자르기 (LIMIT)

ORDER BY COMBAT_POWER DESC
LIMIT 3;
  • 의미: "전투력 내림차순으로 정렬한 전체 리스트 중에서, 맨 위에 있는 1, 2, 3등까지만 화면에 보여주고 나머지는 잘라버려!"
  • 문법 포인트: 항상 쿼리의 가장 마지막 줄에 적습니다. "가장 강한 몬스터 1마리만 구해라"라고 하면 LIMIT 1을 쓰면 끝납니다.