https://school.programmers.co.kr/learn/courses/30/parts/17042
CAR_RENTAL_COMPANY_CAR
테이블CAR_ID
CAR_TYPE
: 세단, SUV, 승합차, 트럭, 리무진DAILY_FEE
OPTIONS
: 주차감지센터, 스마트키, 네비게이션, 통풍시트, 결선시트, 후방카메라, 가죽시트 (콤마로 구분하여 여러개 가능)CAR_TYPE
= ‘SUV’ 평균 DAILY_FEE
출력SELECT ROUND('수치값', '반올림 자릿수')
, ROUND(123.567, 2) //123.570
AVERAGE_FEE
→ SELECT에서 ‘as’ 사용하기SELECT ROUND(AVG(daily_fee), 0) as AVERAGE_FEE
FROM CAR_RENTAL_COMPANY_CAR as C
WHERE C.car_type = 'SUV'
SELECT ROUND(AVG(DAILY_FEE)) AS AVERAGE_FEE
FROM CAR_RENTAL_COMPANY_CAR
WHERE CAR_TYPE = 'SUV'
💡 배운 점
1. SELECT … as ~ : 칼럼명 변경
2. ROUND : 반올림
BOOK
테이블BOOK_ID
CATEGORY
: 경제, 인문, 소설, 생활, 기술AUTHOR_ID
PRICE
PUBLISHED_DATE
: DATElike
Select name
from professor
where name like '%j_';
//_ : 길이가 1인 임의 스트링(한 문자)
//% : 길이에 무관한 임의 스트링
BOOK_ID
, PUBLISHED_DATE
출력DATE_FORMAT(PUBLISHED_DATE, '%Y-%m-%d')
SELECT BOOK_ID, PUBLISHED_DATE
FROM BOOK
WHERE PUBLISHED_DATE like '2021%' and CATEGORY = '인문'
ORDER BY PUBLISHED_DATE
SELECT BOOK_ID,
DATE_FORMAT(PUBLISHED_DATE, '%Y-%m-%d') AS PUBLISHED_DATE
FROM BOOK
WHERE CATEGORY = '인문' AND YEAR(PUBLISHED_DATE) = 2021
ORDER BY PUBLISHED_DATE;
💡 배운 점
1. DATE 포맷 수정 : DATEFORMAT(…, ‘%Y-%m-%d’)
2. 정렬 : ORDER_BY
3. String 비교 연산 : like ‘%’
4. DATE 자료형 특정 부분 추출 : YEAR(), MONTH(), DAY(), HOUR(), MINUTE(), SECOND(), TIME()
PATIENT
테이블PT_NO
환자 번호PT_NAME
환자 이름GEND_CD
성별 코드AGE
나이TLNO
전화번호, NULL 가능WHERE AGE <= 12
WHERE GEND_CD = 'W'
SELECT PT_NAME, PT_NO, GEND_CD, AGE, TLNO
SELECT CASE
WHEN TLNO IS NULL THEN 'NONE'
ELSE TLNO
END AS TLNO
SELECT IFNULL(TLNO, 'NONE') AS TLNO
ORDER BY AGE desc
ORDER BY AGE desc, PT_NAME
SELECT PT_NAME, PT_NO, GEND_CD, AGE,
IFNULL(TLNO, 'NONE') as TLNO
FROM PATIENT
WHERE AGE <= 12 and GEND_CD = 'W'
ORDER BY AGE desc, PT_NAME
💡 배운 점
1. IFNULL : NULL값 대체
2. ORDER BY A, B : A가 같다면 B 기준
MEMBER_PROFILE
테이블MEMBER_ID
MEMBER_NAME
TLNO
GENDER
DATE_OF_BIRTH
: DATEWHERE MONTH(DATE_OF_BIRTH) = 3
WHERE GENDER = 'W'
SELECT MEMBER_ID, MEMBER_NAME, GENDER, DAT_FORMAT(...)
WHERE TLNO != 'NULL'
ORDER BY MEMBER_ID
SELECT MEMBER_ID, MEMBER_NAME, GENDER,
DATE_FORMAT(DATE_OF_BIRTH, '%Y-%m-%d') as DATE_OF_BIRTH
FROM MEMBER_PROFILE
WHERE MONTH(DATE_OF_BIRTH) = 3
and GENDER = 'W'
and TLNO != 'NULL' // TLNO IS NOT NULL
ORDER BY MEMBER_ID
💡 배운 점
IS NOT NULL, IS NULL : NULL 여부 확인
FIRST_HALF
아이스크림 가게의 상반기 주문 정보 테이블SHIPMENT_ID
출하 번호FLAVOR
아이스크림 맛TOTAL_ORDER
총주문량SELECT FLAVOR
FROM FIRST_HALF
ORDER BY TOTAL_ORDER desc, SHIPMENT_ID
DOCTOR
종합병원에 속한 의사 정보 테이블DR_NAME
의사이름DR_ID
의사IDLCNS_NO
면허번호HIRE_YMD
고용일자, DATEMCDP_CD
진료과코드, NULLTLNO
전화번호, NULLSELECT DR_NAME, DR_ID, MCDP_CD,
DATE_FORMAT(HIRE_YMD, '%Y-%m-%d') as HIRE_YMD
FROM DOCTOR
WHERE MCDP_CD = 'CS' or MCDP_CD = 'GS'
ORDER BY HIRE_YMD desc, DR_NAME
USED_GOODS_BOARD
중고거래 게시판 정보 테이블BOARD_ID
게시글 IDWRITER_ID
작성자 IDTITLE
게시글 제목CONTENTS
게시글 내용PRICE
가격CREATED_DATE
작성일, DATESTATUS
거래상태VIEWS
조회수USED_GOODS_REPLY
중고거래 게시판 첨부파일 정보 테이블REPLY_ID
댓글 IDBOARD_ID
게시글 IDWRITER_ID
작성자 IDCONTENTS
댓글 내용, NULLCREATED_DATE
작성일, DATESELECT B.TITLE, B.BOARD_ID, R.REPLY_ID, R.WRITER_ID, R.CONTENTS, DATE_FORMAT(R.CREATED_DATE, '%Y-%m-%d') as CREATED_DATE
FROM USED_GOODS_BOARD as B, USED_GOODS_REPLY as R
WHERE YEAR(R.CREATED_DATE) = 2022 and MONTH(R.CREATED_DATE) = 10
ORDER BY R.CREATED_DATE, B.TITLE
Select *
from professor, teaches;
FROM
USED_GOODS_BOARD as B
INNER JOIN
USED_GOODS_REPLY as R ON B.BOARD_ID = R.BOARD_ID
💡 배운 점
INNER JOIN .. ON ~ : 카테시안 곱이 되지 않게 주의!
FIRST_HALF
상반기 주문 정보 테이블SHIPMENT_ID
출하 번호FLAVOR
아이스크림 맛, pkTOTAL_ORDER
총주문량ICECREAM_INFO
성분에 대한 정보를 담은 테이블FLAVOR
아이스크림 맛, pk(FIRST_HALF
테이블 FLAVOR
의 외래키)INGREDIENT_TYPE
성분 : sugar_based, fruit_basedINGREDIENT_TYPE
= ‘fruit_based’SELECT H.FLAVOR
FROM FIRST_HALF as H
INNER JOIN ICECREAM_INFO as I on H.FLAVOR = I.FLAVOR
WHERE H.TOTAL_ORDER > 3000
and I.INGREDIENT_TYPE = 'fruit_based'
ORDER BY H.TOTAL_ORDER desc
REST_INFO
식당 정보 테이블REST_ID
식당 IDREST_NAME
식당 이름FOOD_TYPE
음식 종류, NULLVIEWS
조회수, NULLFAVORITES
즐겨찾기 수, NULLPARKING_LOT
주차장 유무, NULLADDRESS
주소, NULLTEL
전화번호, NULLREST_REVIEW
식당 리뷰 정보 테이블REVIEW_ID
리뷰 IDREST_ID
식당 ID, NULLMEMBER_ID
회원 ID, NULLREVIEW_SCORE
점수, NULLREVIEW_TEXT
리뷰 텍스트, NULLREVIEW_DATE
리뷰 작성일, DATE, NULLREST_INFO.ADDRESS
= ‘서울%’WHERE I.ADDRESS like '서울%'
SELECT I.REST_ID, I.REST_NAME, I.FOOD_TYPE,
I.FAVORITES, I.ADDRESS, AVG(R.REVIEW_SCORE)
SELECT ROUND(..., 2)
SELECT I.REST_ID, I.REST_NAME, I.FOOD_TYPE, I.FAVORITES, I.ADDRESS,
ROUND(AVG(R.REVIEW_SCORE), 2) as SCORE
FROM REST_INFO as I
INNER JOIN REST_REVIEW as R on I.REST_ID = R.REST_ID
WHERE I.ADDRESS like '서울%'
ORDER BY ROUND(AVG(R.REVIEW_SCORE), 2) desc, I.FAVORITES desc
SELECT I.REST_ID, I.REST_NAME, I.FOOD_TYPE, I.FAVORITES, I.ADDRESS,
ROUND(AVG(R.REVIEW_SCORE), 2) as SCORE
FROM REST_INFO as I
INNER JOIN REST_REVIEW as R on I.REST_ID = R.REST_ID
GROUP BY I.REST_ID
HAVING I.ADDRESS like '서울%'
ORDER BY SCORE desc, I.FAVORITES desc
SELECT REST_ID, REST_NAME, FOOD_TYPE, FAVORITES, ADDRESS,
ROUND(AVG(REVIEW_SCORE), 2) as SCORE
FROM REST_INFO
NATURAL JOIN REST_REVIEW
WHERE ADDRESS like '서울%'
GROUP BY REST_ID
ORDER BY SCORE desc, FAVORITES desc
SELECT I.REST_ID, I.REST_NAME, I.FOOD_TYPE, I.FAVORITES, I.ADDRESS,
ROUND(AVG(R.REVIEW_SCORE), 2) as SCORE
FROM REST_INFO as I
INNER JOIN REST_REVIEW as R on I.REST_ID = R.REST_ID
WHERE I.ADDRESS like '서울%'
ORDER BY ROUND(AVG(R.REVIEW_SCORE), 2) desc, I.FAVORITES desc
WHERE 절 사용: WHERE
절이 JOIN
연산 후에 적용됩니다. 즉, 먼저 REST_INFO
와 REST_REVIEW
를 JOIN
한 후, 그 결과에서 ADDRESS
가 '서울%'인 레코드를 필터링합니다.
그룹화 없음: 이 쿼리에는 GROUP BY
절이 없습니다. 이는 SQL 표준에 따르면 잘못된 접근 방식일 수 있으며, 일부 SQL 시스템에서는 오류를 일으킬 수 있습니다. GROUP BY
절 없이 집계 함수(AVG
)를 사용하고 있으며, 이는 일반적으로 전체 결과 세트에 대한 단일 평균을 계산합니다.
SELECT A.REST_ID, B.REST_NAME, B.FOOD_TYPE, B.FAVORITES, B.ADDRESS, ROUND(AVG(A.REVIEW_SCORE),2) AS SCORE
FROM REST_REVIEW A
JOIN REST_INFO B ON A.REST_ID = B.REST_ID
GROUP BY A.REST_ID
HAVING B.ADDRESS LIKE '서울%'
ORDER BY SCORE DESC, B.FAVORITES DESC
GROUP BY 절 사용: 이 쿼리는 GROUP BY
절을 사용하여 REST_REVIEW
테이블의 REST_ID
별로 그룹화합니다. 이는 각 식당에 대한 평균 점수를 계산하는 올바른 방법입니다.
HAVING 절 사용: HAVING
절은 그룹화 후에 필터링을 적용합니다. REST_INFO
의 ADDRESS
필터링은 그룹화된 데이터에 적용됩니다.
그룹화의 유무와 위치: 첫 번째 쿼리는 GROUP BY
절을 사용하지 않고 있으며, 이는 기술적으로 문제가 될 수 있습니다. 두 번째 쿼리는 올바르게 GROUP BY
를 사용하여 각 식당에 대한 리뷰 점수의 평균을 계산합니다.
필터링의 위치: 첫 번째 쿼리는 WHERE
절을 사용하여 조인 전에 필터링을 적용합니다. 두 번째 쿼리는 HAVING
절을 사용하여 그룹화 후에 필터링을 적용합니다.
일반적으로, 집계 함수를 사용할 때는 GROUP BY
절을 사용하는 것이 적절하며, 이를 통해 그룹별로 정확한 집계 결과를 얻을 수 있습니다. 첫 번째 쿼리의 경우, 특정 SQL 시스템에서는 오류를 발생시킬 수 있거나 예상치 못한 결과를 반환할 수 있습니다.
💡 배운 점
1. GROUP BY : JOIN 테이블에 대한 집계연산 시에는 HAVING으로 필터링
2. WHERE : 필터링 후에 JOIN 연산 진행됨.
3. SELECT에서 as로 명명한 것 ORDER BY 절 등에서 재사용 가능
FOOD_FACTORY
식품공장 정보 테이블FACTORY_ID
공장 IDFACTORY_NAME
공장 이름ADDRESS
주소TLNO
전화번호, NULLWHERE ADDRESS like '강원도%'
SELECT FACTORY_ID, FACTORY_NAME, ADDRESS
ORDER BY FACTORY_ID
ONLINE_SALE
테이블ONLINE_SALE_ID
온라인 상품 판매 IDUSER_ID
회원 IDPRODUCT_ID
상품 IDSALES_AMOUNT
판매량SALES_DATE
판매일GROUP BY USER_ID, PRODUCT_ID
HAVING count(*) >= 2
USER_ID
, PRODUCT_ID
출력SELECT USER_ID, PRODUCT_ID
USER_ID
기준 오름차순 정렬 → 같다면 PRODUCT_ID
기준 내림차순 정렬ORDER BY USER_ID, PRODUCT_ID desc
SELECT USER_ID, PRODUCT_ID
FROM ONLINE_SALE
GROUP BY USER_ID, PRODUCT_ID
HAVING count(*) >= 2
ORDER BY USER_ID, PRODUCT_ID desc
💡 배운 점
그룹별로 HAVING 조건을 만족하는 것만 SELECT
ANIMAL_INS
동물 보호소에 들어온 동물 정보 테이블ANIMAL_ID
동물의 아이디ANIMAL_TYPE
생물 종DATETIME
보호 시작일, DATETIMEINTAKE_CONDITION
보호 시작 시 상태NAME
이름, NULLSEX_UPON_INTAKE
성별 및 중성화 여부SELECT *
FROM ANIMAL_INS
ORDER BY ANIMAL_ID
ONLINE_SALE
온라인 상품 판매 정보 테이블ONLINE_SALE_ID
온라인 상품 판매 IDUSER_ID
회원 IDPRODUCT_ID
상품 IDSALES_AMOUNT
판매량SALES_DATE
판매일, DATEOFFLINE_SALE
오프라인 상품 판매 정보 테이블OFFLINE_SALE_ID
오프라인 상품 판매 IDPRODUCT_ID
상품 IDSALES_AMOUNT
판매량SALES_DATE
판매일, DATEWHERE SALES_DATE like '2022-03%'
union
활용SELECT IFNULL(USER_ID, "NULL") as USER_ID
ORDER BY SALES_DATE, PRODUCT_ID, USER_ID
SELECT DATE_FORMAT(SALES_DATE, '%Y-%m-%d') as SALES_DATE,
PRODUCT_ID,
IFNULL(USER_ID, "NULL") as USER_ID,
SALES_AMOUNT
FROM (SELECT SALES_DATE, PRODUCT_ID, USER_ID, SALES_AMOUNT
FROM ONLINE_SALE
union all
SELECT SALES_DATE, PRODUCT_ID, NULL, SALES_AMOUNT
FROM OFFLINE_SALE
) as SUB
WHERE SALES_DATE like '2022-03%'
ORDER BY SALES_DATE, PRODUCT_ID, USER_ID
SELECT DATE_FORMAT(SALES_DATE, '%Y-%m-%d') as SALES_DATE,
PRODUCT_ID, USER_ID,
SALES_AMOUNT
FROM (SELECT SALES_DATE, PRODUCT_ID, USER_ID, SALES_AMOUNT
FROM ONLINE_SALE
union all
SELECT SALES_DATE, PRODUCT_ID, NULL, SALES_AMOUNT
FROM OFFLINE_SALE
) as SUB
WHERE SALES_DATE like '2022-03%'
ORDER BY SALES_DATE, PRODUCT_ID, USER_ID
union
도 가능💡 배운 점
1. "NULL” ≠ NULL
2. JOIN이 아닌 UNION도 있다! (vstack 같은 거)
ANIMAL_INS
동물 보호소에 들어온 동물 정보 테이블ANIMAL_ID
동물의 아이디ANIMAL_TYPE
생물 종DATETIME
보호 시작일, DATETIMEINTAKE_CONDITION
보호 시작 시 상태NAME
이름, NULLSEX_UPON_INTAKE
성별 및 중성화 여부SELECT NAME, DATETIME
FROM ANIMAL_INS
ORDER BY ANIMAL_ID desc
SELECT ANIMAL_ID, NAME
WHERE INTAKE_CONDITION = "Sick"
ORDER BY ANIMAL_ID
SELECT ANIMAL_ID, NAME
FROM ANIMAL_INS
WHERE INTAKE_CONDITION = "Sick"
ORDER BY ANIMAL_ID
WHERE INTAKE_CONDITION != "AGED"
SELECT ANIMAL_ID, NAME
FROM ANIMAL_INS
WHERE INTAKE_CONDITION != "AGED"
ORDER BY ANIMAL_ID
SELECT ANIMAL_ID, NAME
FROM ANIMAL_INS
ORDER BY ANIMAL_ID
SELECT ANIMAL_ID, NAME, DATETIME
FROM ANIMAL_INS
ORDER BY NAME, DATETIME desc
SELECT NAME
FROM ANIMAL_INS
ORDER BY DATETIME desc
LIMIT 1
💡 배운 점
limit 3 offset 1 : 최고를 제외한 3인 검색
USER_INFO
의류 쇼핑몰에 가입한 회원 정보 테이블USER_ID
회원 IDGENDER
성별, NULL, 0남자 1여자AGE
나이, NULLJOINED
가입일, DATEWHERE JOINED like '2021%'
WHERE AGE >= 20 and AGE <= 29
SELECT count(*)
SELECT count(*) as USERS
FROM USER_INFO
WHERE AGE >= 20 and AGE <= 29 and JOINED like '2021%'