Closed Jewan1120 closed 1 week ago
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
WHERE DATE_FORMAT(SALES_DATE, '%Y-%m') = '2022-03'
UNION ALL
SELECT SALES_DATE, PRODUCT_ID, NULL AS USER_ID, SALES_AMOUNT
FROM OFFLINE_SALE
WHERE DATE_FORMAT(SALES_DATE, '%Y-%m') = '2022-03'
) A
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 AS USER_ID, SALES_AMOUNT
FROM OFFLINE_SALE
) A
WHERE DATE_FORMAT(SALES_DATE, '%Y-%m') = '2022-03'
ORDER BY SALES_DATE, PRODUCT_ID, USER_ID
UNION ALL
사용
UNION과 UNION ALL
필터링과 인덱스
서브쿼리에 별칭 붙이기
Mysql
에선 서브쿼리엔 별칭을 붙여줘야 함A
로 별칭을 붙여줌자동차 종류가 '세단' 또는 'SUV' 인 자동차
,
2022년 11월 1일부터 2022년 11월 30일까지 대여 가능
,
30일간의 대여 금액이 50만원 이상 200만원 미만인 자동차
,
에 대해서 자동차 ID, 자동차 종류, 대여 금액(컬럼명: FEE) 리스트를 출력하는 SQL문을 작성WITH CTE AS (
SELECT
*
FROM
CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE
START_DATE <= '2022-11-30'
AND END_DATE >= '2022-11-01'
)
SELECT
A.CAR_ID
, A.CAR_TYPE
, FLOOR(30 * (A.DAILY_FEE * (1 - C.DISCOUNT_RATE / 100))) FEE
FROM
CAR_RENTAL_COMPANY_CAR A
LEFT JOIN CTE B
ON A.CAR_ID = B.CAR_ID
INNER JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN C
ON A.CAR_TYPE = C.CAR_TYPE
WHERE
A.CAR_TYPE IN ('SUV', '세단')
AND B.CAR_ID IS NULL
AND C.DURATION_TYPE LIKE '30%'
AND FLOOR(30 * (A.DAILY_FEE * (1 - C.DISCOUNT_RATE / 100))) BETWEEN 500000 AND 2000000
ORDER BY
FEE DESC
, A.CAR_TYPE
, A.CAR_ID
자동차 종류가 '세단' 또는 'SUV' 인 자동차
: WHERE절을 이용해서 간단하게 필터링2022년 11월 1일부터 2022년 11월 30일까지 대여 가능
를 구하기 위해서는 11월에 대여를 하고 있는 차량은 제외를 해줘야함
따라서 JOIN들 중 A - (A ∩ B)
집합을 이용해서 A 테이블에서 B테이블의 11월에 대여를 하고 있는 차량을 제외FLOOR
: 대여 비용은 정수 부분만 출력하기 위해 버림 함수 사용30일간의 대여 금액이 50만원 이상 200만원 미만인 자동차
: 계산한 FEE
값을 BETWEEN
함수를 이용해서 필터링ANIMAL_OUTS
테이블이 있을 때, 0~23시 별로 입양 건 수 출력하기WITH RECURSIVE TMP AS (
(SELECT 0 HOUR)
UNION ALL
(SELECT HOUR + 1 FROM TMP WHERE HOUR < 23)
)
SELECT A.HOUR, IFNULL(B.COUNT, 0) `COUNT`
FROM TMP A LEFT JOIN (
SELECT HOUR(DATETIME) `HOUR`, COUNT(HOUR(DATETIME)) `COUNT`
FROM ANIMAL_OUTS
GROUP BY HOUR(DATETIME)
) B ON A.HOUR = B.HOUR
TMP
)을 만듦TMP
)과 HOUR(DATETIME)
으로 GROUP BY
한 ANIMAL_OUTS
테이블을 조인하여 원하는 결과를 추출함RECURSIVE WITH절 간단 설명
- RECURVICE WITH절은 초기 설정값과 RECURSIVE 할 쿼리를
UNION ALL
로 엮는 쿼리와 같음- 필요 요소: 초기 서브 쿼리, RECURSIVE 서브 쿼리, 기저조건
WITH RECURSIVE WITH CTE AS ( # 재귀쿼리 세팅
SELECT 0 AS HOUR # 초기값 설정
UNION ALL # 위 쿼리와 아래 쿼리의 값을 계산
SELECT HOUR + 1 FROM CTE # 반복 실행
WHERE HOUR < 23 # 반복을 멈추는 용도
)
프로시저와 변수(@)를 사용해서 풀이할 수 있음!
1) 처음에 작성한 코드
SELECT
CASE
WHEN MONTH(DIFFERENTIATION_DATE) BETWEEN 1 AND 3 THEN '1Q'
WHEN MONTH(DIFFERENTIATION_DATE) BETWEEN 4 AND 6 THEN '2Q'
WHEN MONTH(DIFFERENTIATION_DATE) BETWEEN 7 AND 9 THEN '3Q'
WHEN MONTH(DIFFERENTIATION_DATE) BETWEEN 10 AND 12 THEN '4Q'
END AS QUARTER, -- CASE문 닫고 컬럼명 지정
COUNT(*) AS ECOLI_COUNT
FROM ECOLI_DATA
GROUP BY QUARTER
ORDER BY QUARTER;
2) 다른 사람의 풀이를 참고 : QUARTER 함수, CONCAT 함수 사용
SELECT CONCAT(QUARTER(DIFFERENTIATION_DATE), 'Q') AS QUARTER, COUNT(ID) AS ECOLI_COUNT
FROM ECOLI_DATA
GROUP BY QUARTER
ORDER BY QUARTER;
1) 처음에 작성한 코드
2) QUARTER 함수
, CONCAT 함수
를 사용한 코드
(1) QUARTER 함수
: 주어진 날짜에서 몇 분기인지 숫자(1, 2, 3, 4)로 반환하는 함수
SELECT QUARTER('2020/09/01'); -- 출력 : 3
(2) CONCAT 함수
: 여러 문자열을 하나의 문자열로 연결해주는 함수
CONCAT(문자열1, 문자열2, ... , 문자열N)
SELECT CONCAT('Hello', ' ', 'World', '!'); -- 결과: 'Hello World!'
SELECT CONCAT(사원명, ' - ', 부서) AS 사원_정보 FROM 사원; -- 컬럼에도 사용 가능
서브쿼리 활용
SELECT YEAR(SALES_DATE) AS YEAR, MONTH(SALES_DATE) AS MONTH
, COUNT(DISTINCT B.USER_ID) AS PURCHASED_USERS
, ROUND(COUNT(DISTINCT B.USER_ID) / (SELECT COUNT(*) FROM USER_INFO WHERE YEAR(JOINED) = 2021), 1) AS PURCHASED_RATIO
FROM USER_INFO A
JOIN ONLINE_SALE B
ON A.USER_ID = B.USER_ID
WHERE YEAR(JOINED) = 2021
GROUP BY YEAR(SALES_DATE), MONTH(SALES_DATE)
ORDER BY YEAR, MONTH
가상테이블 활용
WITH TOTAL_USERS_2021 AS (
SELECT COUNT(DISTINCT USER_ID) AS T_COUNT
FROM USER_INFO
WHERE YEAR(JOINED) = 2021
)
SELECT YEAR(SALES_DATE) AS YEAR, MONTH(SALES_DATE) AS MONTH
, COUNT(DISTINCT B.USER_ID) AS PURCHASED_USERS
, ROUND(COUNT(DISTINCT B.USER_ID) / (SELECT * FROM TOTAL_USERS_2021), 1) AS PURCHASED_RATIO
FROM USER_INFO A
JOIN ONLINE_SALE B
ON A.USER_ID = B.USER_ID
WHERE YEAR(JOINED) = 2021
GROUP BY YEAR(SALES_DATE), MONTH(SALES_DATE)
ORDER BY YEAR, MONTH
LIKE 활용
SELECT YEAR(SALES_DATE) AS YEAR, MONTH(SALES_DATE) AS MONTH
, COUNT(DISTINCT B.USER_ID) AS PURCHASED_USERS
, ROUND(COUNT(DISTINCT B.USER_ID) / (SELECT COUNT(*) FROM USER_INFO WHERE JOINED LIKE "2021%"), 1) AS PURCHASED_RATIO
FROM USER_INFO A
JOIN ONLINE_SALE B
ON A.USER_ID = B.USER_ID
WHERE JOINED LIKE "2021%"
GROUP BY YEAR(SALES_DATE), MONTH(SALES_DATE)
ORDER BY YEAR, MONTH
가입한 전체 회원 수
는 서브쿼리 or 가상테이블로 구함 2021년에 가입한 회원 중, 상품을 구매한 회원 수
LIKE
또는 YEAR()
활용GROUP BY
활용
총 개수를 구하거나 값을 구할 때는 JOIN된 쿼리, GROUP BY쿼리와 분리된 서브쿼리를 활용하기,,!
SELECT
COUNT(ID) COUNT
FROM ECOLI_DATA
WHERE (GENOTYPE & 2 != 2) AND GENOTYPE & 5 > 0
이진수의 두번째 자리가 1이 아니고
, 첫번째나 세번째 자리가 1이 존재
하는 개체 수 찾기비트 연산자의 AND(&)
연산자 사용
둘 다 1 이라면 1, 아니면 0
8&5 연산
1000
101
----
000
따라서 000 을 10진수로 변환하면 0
GENOTYPE & 2 != 2
2번 형질을 제외하기 위해 &2
비트연산 시 2 가 나온다는 뜻은 2진수 두번째 자리에 1 이 있다는 뜻GENOTYPE & 5 > 0
5 의 이진수는 101
로 AND 비트연산 시 첫번째와 세번째 자리에 1이 존재한다면 0 이상이 나오게 됨ID | GENOTYPE | GENOTYPE 별 이진수 | 5의 이진수 | GENOTYPE & 5 |
---|---|---|---|---|
1 | 8 | 1000 | 101 | 0 |
2 | 15 | 1111 | 101 | 5 |
3 | 1 | 1 | 101 | 1 |
4 | 13 | 1101 | 101 | 5 |
CONV(데이터, 원본 진법, 변환할 진법)
CONV(GENOTYPE,10,2)
(수빈님이 작성해주신 내용 가져왔습니다 ㅎㅎ...)
WITH TMP AS (
SELECT SALES_DATE, PRODUCT_ID, NULL `USER_ID`, SALES_AMOUNT
FROM OFFLINE_SALE
WHERE SALES_DATE LIKE '2022-03%'
UNION ALL
SELECT SALES_DATE, PRODUCT_ID, USER_ID, SALES_AMOUNT
FROM ONLINE_SALE
WHERE SALES_DATE LIKE '2022-03%'
)
SELECT DATE_FORMAT(SALES_DATE, '%Y-%m-%d') `SALES_DATE`, PRODUCT_ID, USER_ID, SALES_AMOUNT
FROM TMP
WHERE SALES_DATE LIKE '2022-03%'
ORDER BY SALES_DATE, PRODUCT_ID, USER_ID
UNION ALL
연산을 하기 위해 테이블의 컬럼을 맞춰줌UNION ALL
연산을 하기 전에 WHERE
절에서 필터링을 진행LIKE '2022-03%'
을 통해 정해진 날짜의 데이터를 추출할 수 있도록 함
LIKE
연산을 사용함으로서 WHRE
절에서 데이터 가공 없애줌 (예진님이 저번에 말씀해주신 부분) UNION
과 UNION ALL
UNION ALL
은 중복 제거를 하지 않기 때문에 UNION
보다 성능이 좋음UNION ALL
사용이 효율적일 수 있음SELECT cp.CART_ID
FROM CART_PRODUCTS cp
INNER JOIN CART_PRODUCTS sub
ON cp.CART_ID = sub.CART_ID
WHERE cp.NAME = 'Milk' AND sub.NAME = 'Yogurt'
GROUP BY cp.CART_ID, sub.CART_ID
ORDER BY cp.CART_ID
한 테이블에서 서로 다른 조합만들기 문제
CART_ID
로 GROUP BY
SELECT ID, FISH_NAME, LENGTH
FROM FISH_INFO A JOIN FISH_NAME_INFO B
ON A.FISH_TYPE = B.FISH_TYPE
WHERE (A.FISH_TYPE, LENGTH) IN(
SELECT FISH_TYPE, MAX(LENGTH)
FROM FISH_INFO
GROUP BY FISH_TYPE
)
ORDER BY ID;
서브쿼리
사용 : 그룹(물고기 종류)별 최댓값 구하기 WHERE IN
사용 : IN 조건으로 사용하여 원하는 조건을 만족하는 행 필터링기초적인 질문이긴한데 혹시 다들 서브쿼리 사용하는 문제 푸실 때 메인쿼리랑 서브쿼리 중에 뭐 먼저 작성해보시나요..?! 2주차에 지영님이 푸셨던 문제 입니다
REST_REVIEW
테이블이 있을 때, 리뷰를 가장 많이 작성한 회원의 리뷰들을 조회하는 SQL 문 작성하기-- 코드를 입력하세요
-- 가장 많은 리뷰 적은 회원 찾기
SELECT m.MEMBER_NAME, r.REVIEW_TEXT, DATE_FORMAT(r.REVIEW_DATE, '%Y-%m-%d') AS REVIEW_DATE
FROM MEMBER_PROFILE m
JOIN REST_REVIEW r ON m.MEMBER_ID = r.MEMBER_ID
WHERE m.MEMBER_ID = (
SELECT MEMBER_ID
FROM REST_REVIEW
GROUP BY MEMBER_ID
ORDER BY COUNT(MEMBER_ID)
DESC LIMIT 1
)
ORDER BY REVIEW_DATE, r.REVIEW_TEXT;
서브쿼리 이용
DESC LIMIT 1
COUNT(*)가 아닌 COUNT(MEMBER_ID)를 사용했습니다!!
8주차에 다혜님이 푼 문제입니다!!
USED_GOODS_BOARD
와 USED_GOODS_USER
테이블에서 중고 거래 게시물을 3건 이상 등록한 사용자의 사용자 ID, 닉네임, 전체주소, 전화번호를 조회하는 SQL문을 작성SELECT
B.USER_ID
, B.NICKNAME
, CONCAT(B.CITY, ' ', B.STREET_ADDRESS1, ' ', B.STREET_ADDRESS2) `전체주소`
, REGEXP_REPLACE(B.TLNO, '(.{3})(.{4})(.{4})', '$1-$2-$3') `전화번호`
FROM
USED_GOODS_BOARD A
INNER JOIN USED_GOODS_USER B
ON A.WRITER_ID = B.USER_ID
GROUP BY
B.USER_ID
HAVING
COUNT(B.USER_ID) >= 3
ORDER BY
B.USER_ID DESC
CONCAT
을 이용한 주소 만들기REGEXP_REPLACE
: B.TLNO를 (.{3})(.{4})(.{4})
에 따라 그룹화하여 B.TLNO를 $1-$2-$3
로 대체
(.{n})
: n개의 문자를 그룹으로 묶음$n
: n번째 그룹을 참조 2주차 수빈님이 풀이하신 문제였습니다!
SELECT ID,
(CASE
WHEN (ROWNUM / CNT) <= 0.25 THEN "CRITICAL"
WHEN (ROWNUM / CNT) <= 0.5 THEN "HIGH"
WHEN (ROWNUM / CNT) <= 0.75 THEN "MEDIUM"
ELSE "LOW"
END) AS COLONY_NAME
FROM (SELECT ID, SIZE_OF_COLONY,
ROW_NUMBER() OVER(ORDER BY SIZE_OF_COLONY DESC) AS ROWNUM
FROM ECOLI_DATA) R,
(SELECT COUNT(*) AS CNT FROM ECOLI_DATA) C
ORDER BY ID
SELECT COUNT(*) AS CNT FROM ECOLI_DATA
2) ROW_NUMBER()
활용
: 중복값에 대해서도 순차적인 순위를 표시
SELECT ID, SIZE_OF_COLONY,
ROW_NUMBER() OVER(ORDER BY SIZE_OF_COLONY DESC) AS ROWNUM
FROM ECOLI_DATA
3주차 예진님이 풀이하신 문제입니다!
문제 리스트