https://school.programmers.co.kr/learn/courses/30/lessons/59045

 

습득한 점:

WHERE ~ IN () 처럼, LIKE IN () 도 가능할까 싶었는데, 불가능했다.

대신 아래처럼 AND를 전체로 묶은 뒤, 안쪽을 OR로 엮어줄 수는 있었다.

 

...

WHERE SEX_UPON_INTAKE LIKE 'Intact%'
AND (SEX_UPON_OUTCOME LIKE 'Spayed%' OR SEX_UPON_OUTCOME LIKE 'Neutered%')

...

 

 

 

문제:

보호소에 들어올 당시에는 중성화1되지 않았지만,

보호소를 나갈 당시에는 중성화된 동물의 아이디와 생물 종, 이름을 조회하는 아이디 순으로 조회하는 쿼리 작성.

 

즉, 들어올 땐, 중성화X, 나갈땐 중성화O 에 해당하는 데이터를 찾으면 된다.

 

 

 

문제파악:

처음 문제 딱 보고, 보호소에서 나가는 동물 중에 중성화 안된 동물이 있는지 궁금해서 찾아보니 있었다.

 

## 보호소에서 나가는 동물 중, 중성화 안된 동물 2마리 확인

 

SELECT *
FROM ANIMAL_OUTS
WHERE SEX_UPON_OUTCOME LIKE 'Intact%'

 

결국은 들어올 때의 SEX_UPON_INTAKE 컬럼은 "Intacts%" 이고,

동시조건으로 나갈 때의 SEX_UPON_OUTCOME 컬럼은 "Sprayed% 혹은 "Neutral%" 인 데이터를 찾으면 된다.

 

 

ANIMAL_ID 기준으로 테이블 2개 조인 후, WHERE 조건에 위의 2개 항목 엮어준 뒤 ORDER BY 까지 작성해주면 된다.

 

 

 

풀이:

 

보호소에서 나가는 동물 중, 중성화 안된 동물 2마리 확인

SELECT *
FROM ANIMAL_OUTS
WHERE SEX_UPON_OUTCOME LIKE 'Intact%'





찾는 데이터: 들어올 땐, 중성화X, 나갈땐 중성화 O

SELECT INS.ANIMAL_ID
     , INS.ANIMAL_TYPE
     , INS.NAME
#     , SEX_UPON_INTAKE
#     , SEX_UPON_OUTCOME
FROM ANIMAL_INS AS INS
     INNER JOIN ANIMAL_OUTS AS OUTS ON INS.ANIMAL_ID = OUTS.ANIMAL_ID


WHERE SEX_UPON_INTAKE LIKE 'Intact%'
AND (SEX_UPON_OUTCOME LIKE 'Spayed%' OR SEX_UPON_OUTCOME LIKE 'Neutered%')
ORDER BY ANIMAL_ID


혹은 AND SEX_UPON_OUTCOME NOT LIKE 'Intact%'도 가능할 것 같다.

 

https://school.programmers.co.kr/learn/courses/30/lessons/131116

 

습득한점:

 

 

*** LIMIT은 서브쿼리에 들어가면 쿼리 실행자체가 안된다는 점


*** GROUP BY된 상태로 SELECT 함수 인자로 MAX func 활용할 때,
     
각 그룹의 첫번째 값이 끌려올 뿐, MAX func가 적용되지 않는 점

 

 

추출 데이터
전체 데이터

 

 

 

문제:

FOOD_PRODUCT 테이블에서 식품분류별로 가격이 제일 비싼 식품의 분류, 가격, 이름을 조회하는 쿼리작성.

이때 식품분류가 '과자', '국', '김치', '식용유'인 경우만 출력.

결과는 식품 가격을 기준으로 내림차순 정렬.

 

 

 

문제파악:

각 카테고리(식품분류)에서 최대값을 나타내는 식품명(PRODUCT_NAME)을 찾아야 하는데,

'면' 그룹내 PRICE 값이 같은 항목이 존재함. 그룹별 최대값으로서 PRICE 값은 4950원으로 서로 같지만, 식품명은 다른 상황.

"이때 식품분류가 '과자', '국', '김치', '식용유'인 경우만 출력" 라는 조건이 없었다면, 이 문제는 잘못 설계된 문제

 

 

 

 

풀이:

처음에는 고유값 PRODUCT_ID를 활용해서 PRICE 값 기준 내림차순 정렬하여 첫번째 값만 찾은 뒤,
그렇게 찾은 PRODUCT_ID를 WHERE 절에 활용하고 싶었지만...

서브쿼리에는 LIMIT 를 사용할 수 없더라.

LIMIT을 억지로 감싸는 방법이 있는 것 같지만, 코드 가독성이 안좋을 것 같다.

 

 

 

결국 그룹별 PRICE 최대값을 찾아, 이를 조건에 넣어 매칭시키기로 했다.

 

각 식품분류(CATEGORY)별 MAX(PRICE) 값만 추출 후,

이를 첫번째 일치 조건으로 활용하기 위해 서브쿼리로 감싸서 WHERE 구문에 삽입

두번째 식품분류 일치 조건은 AND CATEGORY IN ( '과자', '국', 김치', '식용유' ) 으로 엮여주기

 

    조건1: 추출한 MAX(PRICE)값이 FOOD_PRODUCT 테이블의 PRICE 값과 같은지

    조건2: 식품 분류가 '과자', '국', '김치', '식용유' 해당하는지

 

마지막으로 내림차순 쿼리까지 잘 작성해주면 된다.

 

 

SELECT CATEGORY
      ,PRICE AS MAX_PRICE
      ,PRODUCT_NAME
FROM FOOD_PRODUCT
WHERE PRICE IN  
     ( 
     SELECT MAX(PRICE)
     FROM FOOD_PRODUCT
     GROUP BY CATEGORY
     )
AND CATEGORY IN ('과자', '국', '김치', '식용유')
ORDER BY MAX_PRICE DESC

아래 코드 삽입 후, 실행

 

 

#폰트 설정

 

import matplotlib.font_manager as fm

import os

 

# apt-get : 패키지 설치 명령어

# -qq : 에러외의 메세지 숨기는 옵션키

!apt-get -qq install fonts-nanum

 

 

fe = fm.FontEntry(

    fname=r'/usr/share/fonts/truetype/nanum/NanumGothic.ttf', # ttf 파일이 저장되어 있는 경로

    name='NanumGothic')                        # 폰트의 원하는 이름 설정

fm.fontManager.ttflist.insert(0, fe)              # Matplotlib 폰트 추가

plt.rcParams.update({'font.size': 9, 'font.family': 'NanumGothic'}) # 폰트 설정

 

 

font_setting0 = mpl.font_manager.FontProperties()

font_setting0.set_weight('bold')

✻   "데이터 스테이션" 님의 공정데이터 분석 강좌 내용을 개인 학습한 게시물임을 명시합니다 .
(원본 raw데이터 포함 X)

 

불량 분류모델 구성
https://colab.research.google.com/drive/1_pH3XE9apUDMjEAdH7TA0XDU70PdPbA1?usp=sharing 


특성공학 for 성능 향상
https://colab.research.google.com/drive/1q3c1uu0lB1ehg5V7ZvGS32yqlLrSu75O?usp=sharing 


알고리즘 성능비교를 통한 적정모델 찾기
https://colab.research.google.com/drive/15nrWUpyH8N1pyTYnRlvA8s8K1OycVOR1?usp=sharing 


Under Sampling을 통한 비율 맞추기
https://colab.research.google.com/drive/1tOAr35eL6Te84UlDwlOKlgmlhFHjQ3pV?usp=sharing 


예측 알고리즘 활용
https://colab.research.google.com/drive/1OGVz8Z8ZlYV0LqztjttUKLPJVfEYX284?usp=sharing 


예측 알고리즘 실현
https://colab.research.google.com/drive/1E8L8dcGvrh7S6LimCIaJSHig_u4rPaQj?usp=sharing 

 

✻   "데이터 스테이션" 님의 공정데이터 분석 강좌 내용을 개인 학습한 게시물임을 명시합니다 .
(원본 raw데이터 포함 X)

 

설비별 공정시간 차이
https://colab.research.google.com/drive/19_tmx2vJ7X4dj5yteeGxRWde8Qiwc4ll?usp=sharing 


설비별 공정안정여부 차이
https://colab.research.google.com/drive/12EgSWDq2LwmQgKPDiHxjCSqb9iZGlCTT?usp=sharing 


공정 경로별 불량갯수 차이
https://colab.research.google.com/drive/1xnUirZZipj83bPkEH679HbxzeP2f4jl2?usp=sharing 


오류 종류 별 설비 정지시간에 유의미한 차이가 있을까?
https://colab.research.google.com/drive/19KwfiIe_vah3T48yBouqeG6g6LG-BtIq?usp=sharing 

✻   "데이터 스테이션" 님의 공정데이터 분석 강좌 내용을 개인 학습한 게시물임을 명시합니다 .
(원본 raw데이터 포함 X)


주요인자 탐색
https://colab.research.google.com/drive/1dtzPl6kdigtuwCKgoNQS8CWylGlrPE-N?usp=sharing 


공정경로별 부하랑
https://colab.research.google.com/drive/1ZLP2FtsL08dR2ethBSwdU1FTDpKDfNOw?usp=sharing 


관리도
https://colab.research.google.com/drive/1Egne71KLGtIDE1wEmwS-t_2QUJZPpoJD?usp=sharing 


파레토
https://colab.research.google.com/drive/1X37PsEMt5B_q_YI_wn4DcQBxuWrHlmmt?usp=sharing 

✻   "데이터 스테이션" 님의 공정데이터 분석 강좌 내용을 개인 학습한 게시물임을 명시합니다 .
(원본 raw데이터 포함 X) 

 

인자탐색
https://colab.research.google.com/drive/1PrBjO4wg5Z6vAE4zv8DYLZ5HsALzwRJA?usp=sharing

 

 

이상치 제거
https://colab.research.google.com/drive/13jEHWP9mpOq5hWcw8a5hrYiW2Dd6bvxq?usp=sharing

 

파생 변수 생성
https://colab.research.google.com/drive/1Z66wzp8ONbT5HSRZSUMVo_saBFW37UpM?usp=sharing 

 

결측값 처리
https://colab.research.google.com/drive/1wH_y2UlR-vL4F6QE65xRYn_c4rzZajrx?usp=sharing 

 

데이터 병합
https://colab.research.google.com/drive/1FjagVsyMKN-p9eMdZFYX6rrIfRh8Yl5v?usp=sharing 

 

습득한점:

 

조회수의 최대값을 찾아야 할 때, 나는 당연히 최대값을 찾고, BOARD_ID를 MAX(VIEW) 를 SELECT 문에 함께 붙이면,
RDBMS이기에 자동으로 조회수가 최대값인 로우의 BOARD_ID를 뽑아낼 수 있을 줄 알았지만 아니었다.

그렇게 친절하지는 않은 듯 하다.

 

MAXVIEW는 MAXVIEW대로나오고,1줄짜리 row이니,

나머지 데이터들은 전체데이터의 첫번째열을 반환하는 것 같았다.

 

mysql의 새로운 특성을 파악했다.

 

SQL은 그렇게 친절하지 않았다.

 

 

 

문제:

USED_GOODS_BOARD와 USED_GOODS_FILE 테이블에서

조회수가 가장 높은 중고거래 게시물에 대한 첨부파일 경로를 조회하는 쿼리 작성.

 

첨부파일 경로는 FILE ID를 기준으로 내림차순 정렬

기본적인 파일경로는 /home/grep/src/ 이며, 게시글 ID를 기준으로 디렉토리가 구분되고,

파일이름은 파일 ID, 파일 이름, 파일 확장자로 구성되도록 출력

* 조회수가 가장 높은 게시물은 하나만 존재

 

 

문제파악:

게시물 서두에서 mysql의 새로운 특성을 파악했기에,

BOARD 테이블에서 VIEW의 최댓값만을 찾은 뒤,

이를 다시 BOARD 테이블에 WHERE조건으로 넣고 이에 해당하는 BOARD_ID를 찾으면 된다.

즉 서브쿼리를 겹겹으로 사용하면 된다.

 

또 이 문제에서는 새롭게, 컬럼의 내용을 이어붙여야 하는 조건이 있었다.

CONCAT(A, B, C) 이런식으로 필요한 컬럼을 이어 붙였다.

컬럼 외에 기재가 필요한 문자열의 경우 '' 혹은 ' 로 반드시 감싸줘야 한다.

 

DB언어 외에도 JAVA던 Python이던 마찬가지이다.

보통 문법적으로 의미를 가지는 경우에는 ' ' 를 활용하고

이외 별 의미가 없는 일반 문자열은 " "를 사용하면 된다.

 

사실 두개 중 어떤 것을 쓰던 상관이 없는데, 회사에 입사하면 개발자들끼리 관습적으로 정해놓는 룰이 있다.

이에 맞춰서, 혹은 혼자서만 사용한다면 개인 취향껏 사용하면 된다.

 

그 외는 문제가 시키는 데로 하면 된다.

 

 

풀이:

SELECT CONCAT('/home/grep/src/', BOARD_ID, '/', FILE_ID, FILE_NAME, FILE_EXT) AS FILE_PATH
FROM USED_GOODS_FILE
WHERE BOARD_ID = (

                SELECT BOARD_ID
                FROM USED_GOODS_BOARD
                WHERE VIEWS = (
                                WITH MAX_VIEWS AS (
                                     SELECT MAX(VIEWS) AS VIEW
                                     FROM USED_GOODS_BOARD   
                                                  )
                                SELECT *
                                FROM MAX_VIEWS   
                                )
                  )
ORDER BY FILE_ID DESC

 

*** WHERE 조건에 들어가는 WITH 문 대신에,

    SELECT MAX(VIEWS) FROM USED_GOODS_BOARD를 사용해도 된다.

 

 

습득한 점:

이제 Lv 4 문제도 3문제정도 밖에 남지 않아서, 벌써부터 난이도가 쉬워지고 있다.
그동안 5 난이도 부터 역순으로 문제풀어서, 3 난이도 문제 차례인데, 정답률 80%이하 인 것들만 풀어야 할듯 하다.

더불어, 윈도우 함수(LEAD, LAG, RANK, DENSE_RANK, ROW_NUMBER 등등) 혹은 사용자 정의 함수를 활용하는 문제가
프로그래머스에는 없는 듯해서, 3 난이도를 다 풀고나면 리트코드나 해커랭크 문제를 풀어야 할 것 같다.

아니면 Data Camp 라는 곳도 알아봐야겠다. (유료라 들었는데)

 

 

 

문제:

FOOD_PRODUCT와 FOOD_ORDER 테이블에서

생산일자가 2022년 5월인 식품들의 식품 ID, 식품 이름, 총매출을 조회하는 쿼리 작성

결과는 총매출을 기준으로 내림차순 정렬,

총매출이 같다면 식품 ID를 기준으로 오름차순 정렬

 

 

 

문제파악:

Product 테이블은 총 데이터 row 갯수가 40줄

Order 테이블은 총 데이터 row 갯구가 30줄 이었다.

 

Product 테이블의 상품 id가 고유값으로 적힌 테이블이라,

Order 테이블이 데이터 row 수가 더 많을 줄 알았는데, 아니었다.

 

따라서 전체 상품 중 일부 상품만 있는 경우 + 그 일부의 상품 중 상품 id값의 중복인 데이터가 있는 경우

이 2가지만 고려해서 문제를 풀면 된다.

 

LEFT 조인이 필요한 상황은 아니다.
INNER 조인 후, 2022년 5월의 데이터만 WHERE 조건에 넣고, 총 매출을 계산하는 "TOTAL_SALES" 컬럼을 새로 추가한다.

 

여기까지 작성 후, 코드를 실행하면 상품id가 중복되는 데이터 row가 존재 할 수 있는 결과값이 나온다.

 

문제에서 원하는 값은 상품 id가 중복되는 데이터들은 총 매출액을 합산해야 한다.

 

예를 들어,

5월 10일에 참치캔 총 매출액이 350,000원이고, 5월 21일에 참치캔 총 매출액이 200,000원 이라면

결과값으로 나와야 하는 5월의 참치캔 총 매출액은 550,000원으로 계산되어야 하는 식이다.

 

고로 GROUP BY로 PRODUCT_ID를 묶어주고, 이에 대한 합산을 위해

PRICE*AMOUNT를 SUM으로 감싸준다.   

 

PRICE*AMOUNT AS TOTAL_SALES 

>>> SUM(PRICE*AMOUNT) AS TOTAL_SALES

 

정렬조건은 문제에 쓰여진 그대로 기재하면 된다.

 

 

 

풀이:

SELECT FP.PRODUCT_ID
     , FP.PRODUCT_NAME
     , SUM(PRICE*AMOUNT) AS TOTAL_SALES
     
FROM FOOD_PRODUCT AS FP
     INNER JOIN FOOD_ORDER AS FO ON FP.PRODUCT_ID = FO.PRODUCT_ID
WHERE PRODUCE_DATE LIKE '2022-05%'

GROUP BY PRODUCT_ID
ORDER BY TOTAL_SALES DESC
       , PRODUCT_ID ASC

 

 

습득한 점:

서울에 위치한 식당들의 식당 ID, 식당 이름, 음식 종류, 즐겨찾기수, 주소, 리뷰 평균 점수를 조회하는 쿼리를 작성하는 것이니
조회되는 쿼리는 모두 서울에 위치한 식당이어야 하고,

 

예상되는 테이블 형태는 아래와 같으므로 (적기만 하면 되는 컬럼은 제외했음)

문제에서는 식당 이름에 대해서 그룹화 하란 말이 없었지만, 결국은 식당 별 리뷰 평균을 구하는 것과 같다.

 

고로, 문제에서 ~~~ 그룹 별 이라는 말이 없어도, 문제에서 원하는 테이블 형태를 예상해서

원하는 컬럼에 GROUP BY 를 붙여 계산해도 된다.

식당 이름 리뷰 점수 >>> 식당이름 리뷰평균
a 4.5 >>> a 4.50
b 3 >>> b 3.33
b 4 >>> c 3.50
b 3 >>>    
c 3 >>>    
c 4 >>>    

 

 

 

 

문제:

REST_INFO와 REST_REVIEW 테이블에서

서울에 위치한 식당들의 식당 ID, 식당 이름, 음식 종류, 즐겨찾기수, 주소, 리뷰 평균 점수를 조회하는 쿼리 작성.

 

리뷰 평균점수는 소수점 세 번째 자리에서 반올림,

결과는 평균점수를 기준으로 내림차순 정렬,

평균점수가 같다면 즐겨찾기수를 기준으로 내림차순 정렬

 

 

 

문제파악:

리뷰 평균점수를 구하는 산술계산 이외에는 크게 어려운 점이 없는 문제였다.

식당 별로 그룹화하라는 언급은 없었지만 REST_ID 별로 그룹화 한 뒤, REVIEW_SCORE에 대해 평균값을 계산하면 된다.

또한 테이블을 붙이는 겸, WITH 구문+ LEFT 함수 활용해서 미리 서울에 위치하는 식당만 뽑을 수 있도록 테이블을 새로 만들고 이를
REST_REVIEW 테이블과 조인했다.

 

WHERE 구문에는 LEFT를 사용했는데, ADDRESS 컬럼의 앞 글자 2개만 따기 위해 사용했다.

전체 문자열 중 "서울"이 들어가는 조건을 활용하는 경우에,

만약 행정구역 이름 (~구 혹은 ~로) 이후에 이어지는 명칭들에서 "서울"이 들어가 있으면

서울시는 아니지만 빌딩 이름 혹은 지점이름에 "서울"이 들어간다는 이유로 결과값이 걸려들 수 있어서 LEFT를 사용했다.

 

실제로 부산에도 서울빌딩이라는 건물이 있고,

서울에도 마곡 광안리라는 음식점이 있다.

아래의 아브뉴프랑이 아닌, "아브뉴서울" 이라고 적혀있으면 결과값에 걸려드는 것이다.

다소 억지스럽겠지만, 나는 절대 데이터를 믿으면 안된다고 생각한다.

ADDRESS
서울특별시 중구 다산로 149
서울시 강남구 신사동 627-27
서울특별시 강남구 언주로153길
경기도 수원시 영통구 센트럴타운로 85 아브뉴프랑 1F
경기도 수원시 영통구 센트럴타운로 85 아브뉴프랑 2F

 

 

 

 

풀이:

 

WITH SEOUL_REST AS (
    SELECT *
    FROM REST_INFO
    WHERE LEFT(ADDRESS,2) ='서울'
                )

SELECT SR.REST_ID
     , REST_NAME
     , FOOD_TYPE
     , FAVORITES
     , ADDRESS
     , ROUND(AVG(REVIEW_SCORE),2) AS SCORE
     
FROM SEOUL_REST AS SR
     INNER JOIN REST_REVIEW AS RR ON SR.REST_ID = RR.REST_ID

GROUP BY SR.REST_ID
ORDER BY SCORE DESC

 

 

 

습득한 점:

이전에는 테이블을 보이는데로 조인했다면,
지금은 테이블이 조인되었을 때의 형태를 머릿속으로 고려하면서 쿼리를 구성할 수 있게되었다.

 

 

문제:

USER_INFO 테이블과 ONLINE_SALE 테이블에서 년, 월, 성별 별로 상품을 구매한 회원수를 집계하는 쿼리문 작성

결과는 년, 월, 성별을 기준으로 오름차순 정렬.

이때, 성별 정보가 없는 경우 결과에서 제외.

 

문제파악:

크게 어려워 보이는 부분은 없었다.

다만 "동일한 날짜, 회원 ID, 상품 ID 조합에 대해서는 하나에 판매 데이터만 존재한다" 이 언급만 조금 더 고려하면 되었다.

 

결과 값은 이렇게 만들어져야 한다.

ONLINE_SALE만을 볼때, 날짜, 회원ID, 상품ID 조합에 대해서는 고유의 판매데이터만 존재한다고 했지만,
ONLINE_SALE 테이블과 USER_INFO 테이블이 합쳐졌을 때의 상황에서는 중복데이터가 생겨날 수 있음

(USER_ID 마다 성별은 다르게 설정될테니 제외, 나머지 AGE(나이)나 JOINED(가입일)로 인해 중복데이터가 발생될 수는 있을 것 같다)

 

해서, USER_ID를 카운트 할때는 DISTINCT가 필요하다.

 

풀이:

 

SELECT YEAR(sales_date) AS YEAR
     , MONTH(sales_date) AS MONTH
     , gender AS GENDER
     , COUNT(DISTINCT OS.USER_ID) AS USERS
     
FROM ONLINE_SALE AS OS
     INNER JOIN USER_INFO AS UI ON OS.USER_ID = UI.USER_ID
WHERE GENDER IS NOT NULL       
GROUP BY 1,2,3
ORDER BY 1 ASC ,2 ASC ,3 ASC

링크: https://school.programmers.co.kr/learn/courses/30/lessons/132204

 

 

습득한 점:

예시로 주어진 테이블을 반드시 보면서,

문제에서 주어진 말들을 오해하지 않고 빠르게 풀어야 겠다.

 

쉬워보인다고 해서 보지 않으면, 이렇게 쉬운 문제임에도 오히려 시간을 소모 할수 있다.

 

다음은 환자 정보를 담은 PATIENT 테이블과 의사 정보를 담은 DOCTOR 테이블, 그리고 진료 예약목록을 담은 APPOINTMENT에 대한 테이블입니다. PATIENT 테이블은 다음과 같으며 PT_NO, PT_NAME, GEND_CD, AGE, TLNO는 각각 환자번호, 환자이름, 성별코드, 나이, 전화번호를 의미합니다.

PT_NO VARCHAR(N) FALSE
PT_NAME VARCHAR(N) FALSE
GEND_CD VARCHAR(N) FALSE
AGE INTEGER FALSE
TLNO VARCHAR(N) TRUE

Column name Type Nullable

DOCTOR 테이블은 다음과 같으며 DR_NAME, DR_ID, LCNS_NO, HIRE_YMD, MCDP_CD, TLNO는 각각 의사이름, 의사ID, 면허번호, 고용일자, 진료과코드, 전화번호를 나타냅니다.

DR_NAME VARCHAR(N) FALSE
DR_ID VARCHAR(N) FALSE
LCNS_NO VARCHAR(N) FALSE
HIRE_YMD DATE FALSE
MCDP_CD VARCHAR(N) TRUE
TLNO VARCHAR(N) TRUE

Column name Type Nullable

APPOINTMENT 테이블은 다음과 같으며 APNT_YMD, APNT_NO, PT_NO, MCDP_CD, MDDR_ID, APNT_CNCL_YN, APNT_CNCL_YMD는 각각 진료 예약일시, 진료예약번호, 환자번호, 진료과코드, 의사ID, 예약취소여부, 예약취소날짜를 나타냅니다.

APNT_YMD TIMESTAMP FALSE
APNT_NO INTEGER FALSE
PT_NO VARCHAR(N) FALSE
MCDP_CD VARCHAR(N) FALSE
MDDR_ID VARCHAR(N) FALSE
APNT_CNCL_YN VARCHAR(N) TRUE
APNT_CNCL_YMD DATE TRUE

 

 

 

문제:

PATIENT, DOCTOR 그리고 APPOINTMENT 테이블에서

2022년 4월 13일 취소되지 않은 흉부외과(CS) 진료 예약 내역을 조회하는 쿼리 작성

진료예약번호, 환자이름, 환자번호, 진료과코드, 의사이름, 진료예약일시 항목이 출력되도록 작성

결과는 진료예약일시를 기준으로 오름차순 정렬

 

 

 

문제파악:

문제는 쉬워보였다.

그냥 테이블 2개 조인, 조인 반복하고, 조건 걸고, 정렬 후에 컬럼만 뽑아내면 되는 문제였고 그게 맞았다.

 

문제를 끝까지 제대로 잘 읽어야 한다.

취소되지 않은 진료 예약 내역을 조회하는 쿼리 작성이 아닌,

취소되지 않은 흉부외과(CS) 진료 예약 내역을 조회하는 쿼리 작성 이다.

 

당연히 이렇게 오해해 버리면 "흉부외과(CS) 진료 예약 내역" 이라는 예외조건을 빼먹고 쿼리를 작성하게 된다.


문제또한 모호하게 적혀있긴 하다.

 

"2022년 4월 13일 취소되지 않은 흉부외과(CS) 진료 예약 내역을 조회하는 쿼리 작성"

1) 나는 당연히 4월 13일에 취소되지 않은 진료예약내역을 조회하는 쿼리로 이해했는데,

 

문제에서 원하는 해석은,

2) 2022년 4월 13일의 예약건 중 취소되지 않은 건을 조회하는 것을 의미하는 것 같았다.

????? 저문장이 저렇게 해석된다고?

 

모호해도 상관없다.

저 문장이 어떻게 해석되든 나는 문제에 맞게 해석하고, 문제가 원하는 대로 풀어야만 한다.

완전히 다른 해석이라, 잘못 해석하면 괜히 시간이 끌릴 수 있으니 예시에 맞게 해석하는 것이 중요해 보였다.

 

글만 잘 읽고, 문장은 애매해도 예시에 맞게 잘 껴맞추면 충분히 쉽게 풀 수 있는 문제라 생각되었다.

 

 

 

문제풀이:

SELECT APNT_NO
     , PT_NAME
     , AP.PT_NO
     , AP.MCDP_CD
     , DR_NAME
     , AP.APNT_YMD

FROM APPOINTMENT AS AP
     INNER JOIN PATIENT AS PT ON PT.PT_NO = AP.PT_NO
     INNER JOIN DOCTOR AS DT ON DT.DR_ID = AP.MDDR_ID
     
WHERE AP.MCDP_CD ="CS"
AND APNT_YMD LIKE "2022-04-13%"
AND APNT_CNCL_YN = "N"
ORDER BY APNT_YMD ASC

 

링크: https://gooreumwave.tistory.com/43

 

습득한 점: 

단순히 코드를 붙여넣기 하기보단

문제를 풀기 위해 생각했었던 중간과정을 기록해보는 것이
나중에 다시 복습할 때, 좋을 것 같음.

또한 다음부턴 쿼리 작성 시간도 카운트 해보면 좋을 것 같다.

 

(쿼리 작성 속도가 점점 더 빨라지는 것 같아서 다행이다)

 

 

문제:

 

 

 

문제파악:

 

JULY 테이블의 strawberry 값이 2개 값으로 표출되어

FLAVOR 컬럼 기준으로 누적합 함수를 쓰면 풀 수 있지 않을까 했지만, 순간의 착각이었다.

FLAVOR 컬럼을 기준으로 누적합이 생성되는 것을 확인했다...

즉, JULY 테이블에서 한가지 맛을 거듭할때마다 아래 예시 처럼 CUM_SUM 컬럼에 누적합이 더해졌었다.

 

ex)

SHIPMENT_ID FLAVOR TOTAL_ORDER CUM_SUM
1 딸기 30 30
2 메론 40 70
3 사과 10 80
4 사과 20 100
5 수박 30 130

 

 

문제에서 원하는 부분은

출하번호는 다르지만, 같은 맛인 사과에 대한 TOTAL_ORDER만 합치고, 나머지 과일은 그대로 두면 되었었다.

 

그래서, 방법을 바꾸어 SELECT 문에 FLAVOR 별 그룹 합계를 구하고

FIRST_HALF 절과 INNER JOIN 이후에 이를 활용테이블로 삼아 합계 상위 3개만 뽑아냈다.

 

 

 

풀이:

 

1. JULY 테이블의 경우, 출하량이 많아 같은 맛의 아이스크림이라도 다른 출하번호를 갖게된다고 명시되어 있어서,
쿼리를 작성하기 전 이 부분을 직접 확인

 

SELECT COUNT(FLAVOR) = 7
FROM FIRST_HALF

 

SELECT COUNT(DISTINCT FLAVOR) = 7
FROM FIRST_HALF

SELECT COUNT(FLAVOR) = 8
FROM JULY

 

2. JULY 테이블에만 중복된 값(JULY 테이블의 딸기만 SHIPMENT_ID 가 2개)이 존재함을 파악할 수 있었고,
이 테이블에 대하여 그룹별 합계를 계산하고 이를 테이블로 활용하기 위해 WITH 문으로 감쌌다.

** FROM 절 or WITH 절 중 아무거나 사용해도 될 것 같음.


WITH COUNTER AS (
        SELECT FLAVOR
             , SUM(TOTAL_ORDER ) AS TOTAL_JULY
        FROM JULY
        GROUP BY FLAVOR
                )                           


3. 다음으로 새로 생성한 테이블에 FLAVOR 기준으로 FIRST_HALF를 조인했다.

 

이 과정에서 SELECT 문에 그룹별 FLAVOR 총 주문량이 합쳐진 컬럼이 필요했고,

SUM(TOTAL_JULY+TOTAL_ORDER) AS TOTAL_SUM 로 처리했다.

 

다시금 FLAVOR 별로 묶어 준 뒤, 총 주문량 기준으로 내림차순으로 정렬했고,

상위 3개의 총 주문량의 데이터만 나올 수 있도록 LIMIT 을 조정했다.


SELECT FIRST_HALF.FLAVOR
     , SUM(TOTAL_JULY+TOTAL_ORDER) AS TOTAL_SUM
FROM FIRST_HALF
     INNER JOIN COUNTER ON COUNTER.FLAVOR = FIRST_HALF.FLAVOR
GROUP BY FIRST_HALF.FLAVOR     
ORDER BY TOTAL_SUM DESC    # 내림차순
LIMIT 3

 

 

4. 문제에서 원하는 컬럼은 오직 FLAVOR 컬럼이었기에, TOTAL_SUM을 지우니 내림차순이 적용이 되지 않았다.

(정렬기준의 컬럼이 사라져 버리니까)

 

해서, 이를 다시 서브쿼리문으로 재활용하기 위해 괄호로 묶어서 FROM 절의 괄호에 넣었다.

 

풀이:

 

SELECT FLAVOR
FROM ( 
    
        WITH COUNTER AS (
                SELECT FLAVOR
                     , SUM(TOTAL_ORDER ) AS TOTAL_JULY
                FROM JULY
                GROUP BY FLAVOR
                        )                              

        SELECT FIRST_HALF.FLAVOR
             , SUM(TOTAL_JULY+TOTAL_ORDER) AS TOTAL_SUM
        FROM FIRST_HALF
             INNER JOIN COUNTER ON COUNTER.FLAVOR = FIRST_HALF.FLAVOR
        GROUP BY FIRST_HALF.FLAVOR     
        ORDER BY TOTAL_SUM DESC    # 내림차순
        LIMIT 3    

    )sub

 

 

 

 

 

습득한점:

코딩테스트 출제 문제 인 것 같은데, 어렵지는 않았으나 접근할 수 있는 방법이 생각보다 많아보였다.

조인으로도, WITH로도 풀 수 있을 것 같다.

 

 

문제:

데이터 분석 팀에서는 우유(Milk)와 요거트(Yogurt)를 동시에 구입한 장바구니가 있는지에 대한 이력 파악

우유와 요거트를 동시에 구입한 장바구니의 아이디를 조회하는 쿼리 작성

정렬은 아이디로

 

 

 

문제파악:

NAME 컬럼에
'Milk', 'Yogurt'가 모두 들어가는 값만 걸러낸다.

여기서 걸러낸 결과 NAME 카운트 값이 Milk =1 , Yogurt=1 이라고 당연하게 생각했던게 실수 였다.

2개인것도 3개 이상인 것도 고려를 해야했다.

 

아무튼 WHERE NAME IN ~으로는 2개 이상인 값들만 찾을 수 있으니, 이를 처리할 방법을 찾아야 했는데,
COUNT(DISTINCT NAME) = 2를 조건으로 걸면,

반드시 Milk 1개, Yogurt 1개의 구성으로 이루어진 CART_ID만 찾을 수 있고,

이 과정에서 예를들어 Yogurt만 있는 CART_ID,  Milk만 있는 CART_ID를 걸러낼 수 있다.

 

 

풀이:

SELECT CART_ID
FROM CART_PRODUCTS
WHERE NAME IN ('Milk', 'Yogurt')
GROUP BY CART_ID
HAVING COUNT(DISTINCT NAME)=2
ORDER BY CART_ID

 

 

 

습득한 점:

문제를 너무 느긋하게 풀고 있다.

다음 문제부터는 집중해서 최대한 빠르게 풀어보자

 

 

 

 

문제:

2022년 1월의 도서 판매 데이터를 기준으로 저자 별, 카테고리 별 매출액(TOTAL_SALES = 판매량 * 판매가) 구하기

저자 ID(AUTHOR_ID),

저자명(AUTHOR_NAME),

카테고리(CATEGORY),

매출액(SALES) 리스트를 출력하는 쿼리 작성
결과는 저자 ID를 오름차순으로, 저자 ID가 같다면 카테고리를 내림차순 정렬

 

 

문제파악:

문제 파악 할 것은 크게 없었다.

2022년 1월의 도서 판매 데이터를 기준으로 저자 별, 카테고리 별 매출액(TOTAL_SALES = 판매량 * 판매가) 구하기
- 위 조건만 유의하면 쉽게 풀 수 있다.

 

 

풀이:

SELECT B.AUTHOR_ID
     , AUTHOR_NAME
     , CATEGORY
     # , PRICE
     # , SALES
     , SUM(PRICE * SALES) AS TOTAL_SALES

FROM BOOK B
     INNER JOIN AUTHOR A ON B.AUTHOR_ID = A.AUTHOR_ID
     INNER JOIN BOOK_SALES S ON B.BOOK_ID = S.BOOK_ID
WHERE SALES_DATE LIKE '2022-01%'
GROUP BY AUTHOR_ID, CATEGORY
ORDER BY AUTHOR_ID, CATEGORY DESC

 

1. 막대 차트는, 수치 데이터 값들 간의 작은 양적 차이를 비교하는데 유용

        - 막대 차트를 사용할 때는비슷한 값들의 비교를 명확하게 하기 위해서 데이터를 정렬

 

2. 집계 형태에 따라 데이터가 어떻게 표현될 지 미리 예상해보기

        - ex) 합계, 평균

 

3. 생각보다 히트맵처럼 생긴 트리맵은 전체데이터를 한번에 보는 용도로는 좋은 수단이지만,
데이터의 양이 많아지면 많아질수록 시각화가 어려워 질 듯하다.

 

 

4. 시트의 우측 클릭을 통해 아래, scatter plot 처럼 추세선을 추가할 수있고,

분석 탭에서 집계형태 별로 가로 축, 세로 축 참조선 추가 가능

 

 

5. 도구설명 편집의 삽입을 통해, 현재시트에 다른 시트를 호버링하여 표현 할 수 있음

 

 

 

'프리온보딩 > 태블로 데이터 시각화' 카테고리의 다른 글

데이터 시각화 6  (0) 2023.09.08
데이터 시각화 5  (0) 2023.09.08
데이터 시각화 4  (0) 2023.09.08
데이터 시각화 3  (0) 2023.09.08
데이터 시각화 2  (0) 2023.09.08

인터넷 상에  죄다 LIMIT 1 풀이 투성이라, 조금 헤멧다.

리팩토링은 나중에...해봐야겠다.

 

링크: https://school.programmers.co.kr/learn/courses/30/lessons/131124

 

습득한 점:

WITH 구문을 사용 함과 동시에, 다른 2가지 이상의 테이블을 동시에 활용 할때,

FROM 절에 들어할 테이블이 2개 이상인 경우가 있어서

이를 어찌 처리해야 할지 난감했다.

 

결과적으로는 WITH 문 + SELECT 문 조합으로 계산을 끝낸 뒤,
이 전체를 새로운 테이블의 WHERE 값에 넣어버리는 식으로 쿼리를 작성했다.

 

이 과정에서, 아래 2가지의 차이를 알게되었다.

WHERE 컬럼 = (~~~~)

WHERE 컬럼 IN (~~~~)

 

서브쿼리에 해당하는 값이 1가지 이고, 이를 다른 테이블의 조건값으로서 매칭 시킬 때는 "=" 을 사용

서브쿼리에 해당하는 값이 2가지 이상이고, 이 때는 "IN" 을 활용해야

 

subquery returns more than 1 row 에러가 발생하지 않는다.

 

또한 내가 생각치 못한 경우의 수가 있는지 없는지, 테이블을 뜯어보는 습관은 좋은 것 같다.

(이러면 시간이 오래걸려서 실제 코테에선 맞는 방법일 지는 모르겠다)


 

문제:

MEMBER_PROFILE와 REST_REVIEW 테이블에서 리뷰를 가장 많이 작성한 회원의 리뷰들을 조회하는 쿼리 작성

결과값은 회원 이름, 리뷰 텍스트, 리뷰 작성일이 출력되도록 작성,

정렬기준은 리뷰 작성일을 기준으로 오름차순,

                 리뷰 작성일이 같다면 리뷰 텍스트를 기준으로 오름차순 정렬.

 

 

문제파악:

문제가 좀 이상하다.

문제 타이틀은 식당 목록을 출력하는 것인데,

정답의 결과는 아래와 같다.

 

또 하나, 문제를 처음 접근할 때 조인을 어떻게하면 좋을 지 처음엔 테이블을 하나하나 다 뜯어보다가,

고객을 정보를 담은 MEMBER_PROFILE 테이블의 member_id(이메일)는 14개인데,

리뷰정보를 담은 REST_REVIEW 테이블에서의 중복을 제거한 member_id(이메일)은 모두 15개 였다...

???

일단 테이블끼리 조인했다.

 

또한 리뷰 카운트를 세다보니, 리뷰 수의 최댓 값이 같은 사람이 한명이 아니었다.

리뷰를 가장 많이 작성한 사람이 3명 임에도,

아래의 쿼리와 같이 LIMIT 1을 조건으로 걸면, 저 위에 리뷰 3개를 작성한 사람 3명 중 1명만 조회가 된다.

 

LIMIT 1 을 조건으로 하고, 쿼리를 작성한 사람이 꽤 되었는데.. 이렇게 작성해도 정답처리가 되는 듯 했다.

여러모로 수정이 필요한 문제인듯하다. 시스템 자체 테스트 케이스의 갯수가 좀 적은 듯 하다.

 

SELECT A.MEMBER_NAME,B.REVIEW_TEXT,DATE_FORMAT(B.REVIEW_DATE, "%Y-%m-%d") AS REVIEW_DATE
from MEMBER_PROFILE A join REST_REVIEW B
on A.MEMBER_ID = B.MEMBER_ID
WHERE A.MEMBER_ID = (SELECT MEMBER_ID FROM REST_REVIEW
GROUP BY MEMBER_ID
ORDER BY COUNT(*) DESC LIMIT 1)
order by REVIEW_DATE asc, REVIEW_TEXT

 

 

풀이:

 

1. 리뷰 수 최대 값 찾기

WITH 문으로, MEMBER_ID를 그룹으로 묶은 뒤 MEMBER_ID별 카운트 값을 찾기

그 카운트 값들 중 최고인 값을 SELECT MAX(Review_NUMBER)로 찾고 Alias는 MAX_NUMBER로 지정

 

2. 리뷰 수가 최대값 인 아이디 찾기

REST_REVIEW 테이블에서 SELECT 문으로 MEMBER_ID, MEMBER_ID별 카운트 값을 찾는 쿼리 작성 후 이를 서브쿼리로 활용

만들어진 서브쿼리를 테이블로서 활용하고, MEMBER_ID값을 다시 구함.

이떄 WHERE 구문 조건으로 WHERE NUM = (1번 쿼리 전체) 을 조건으로 추가.

그럼 리뷰 카운트가 3인, MEMBER_ID값 만 뽑아낼 수 있다.

 

3. 찾은 아이디를 활용하여 정답 찾기

뽑아낸 MEMBER_ID 들을 WHERE 조건으로 활용하는 쿼리문을 작성

테이블 2가지를 조인 후, 정답에서 원하는 컬럼들을 다 가지고 온다.

이때는 WHERE 조건에 들어가는 값들이 1개 이상이므로 IN을 활용하여,
WHERE RR.MOMBER_ID IN (2번 쿼리 전체) 를 조건으로 활용.

뒤에 ORDER BY 정렬조건까지 잊지말고 작성.

 

 

SELECT MEMBER_NAME
     , REVIEW_TEXT
     , DATE_FORMAT(REVIEW_DATE, "%Y-%m-%d") AS REVIEW_DATE
     
FROM REST_REVIEW AS RR
     LEFT JOIN MEMBER_PROFILE AS MP ON RR.MEMBER_ID = MP.MEMBER_ID
WHERE RR.MEMBER_ID IN (

                    SELECT MEMBER_ID
                    FROM(
                        SELECT MEMBER_ID, count(*) AS NUM
                        FROM REST_REVIEW
                        GROUP BY MEMBER_ID
                        ) sub
                    WHERE NUM = (

                        WITH counter as (
                            SELECT MEMBER_ID, COUNT(*) AS Review_NUMBER
                            FROM REST_REVIEW
                            GROUP BY MEMBER_ID
                                         )

                        SELECT MAX(Review_NUMBER) AS MAX_NUMBER
                        FROM counter
                                )
                        
                       )
ORDER BY REVIEW_DATE
       , REVIEW_TEXT

 

쉬울 줄 알았는데, 어려운 문제

 

링크: https://school.programmers.co.kr/learn/courses/30/lessons/59413

 

습득한 점:

쉬운 줄 알았는데, 생각보다 어려운 문제였다. SET함수를 활용해 본적이 없었어서 그랬을 수도 있다.

풀이법을 외워버리면 될 것 같다. 이 문제를 통해 SET함수를 실제로 활용해 볼 수 있었음.

이 또한 With 구문으로 풀어볼려고 했지만, With 구문에 테이블 이름을 끌어옴을 명시하는데 있어서, 표기하기가 난해했다.

(내가 아직 부족한 걸 수도!!)

 

 

문제:

문제:

0시부터 23시까지, 각 시간대 별로 입양이 총 몇 건 일어났는지 파악하는 쿼리 작성.

결과는 시간대순으로 정렬.

 

 

 

문제파악:

열심히 case구문으로 풀었는데, 답이 나오지 않아 문제를 잘 살펴 보았더니
정답지에는 입양이 일어나지 않는 시간또한 나타나 있었다.
가령 0시 32분에 입양된 기록이 없다면, count=0으로 표시되어

hour count
0 0
1 0
2 3

 으로 표시되는 식이었다.

나의 쿼리는 당연히 입양이 일어난 시각만 셀 수 있도록 작성되어 있었다.

 

데이터베이스에는 없는 시간을 만드는 다른 방법을 생각해야만 했고,

SET 함수를 활용했다.

 

* SET함수의 경우, 어떤 변수에 특정 값을 넣어줄 때 사용한다. @와 변수명을 함께 사용하고,
* SET @HOUR = -1; 는, HOUR 변수 값을 -1 로 초기화 해주는 것과 같다.

다른 파이썬이나 자바에서의 변수초기화와 같다고 생각하면 될 것 같다.

* := 기호는 대입 연산자의 개념이다. HOUR = HOUR +1 >>> 우측에 계산된 값으로 좌측 값을 덮어 씌우는 형식.

 

* WHERE @HOUR < 23; 의 경우, 반복을 0부터 22까지 총 23번 한다고 생각하면 된다.

 

* 또한 아래 코드를 삽입해서, 

WHERE 조건에서 @HOUR 변수와 HOUR(DATETIME)의 값이 서로 같을때를 카운트하는 쿼리를 SELECT의 두번째 인자로 삽입한 뒤, COUNT라는 Alias를 붙인다.

(SELECT COUNT(*) 
FROM ANIMAL_OUTS 
WHERE HOUR(DATETIME) = @HOUR) AS COUNT

 

* @HOUR변수와 @HOUR(DATETIME)로 나타낸 시간이 같을 때를 카운트 하기때문에,

@HOUR(DATETIME)과 @HOUR변수 테이블이 순차적으로 함께 만들어지며 COUNT 더해나간다.

 

 

풀이: SET을 활용하는 쿼리를 작성 할때,    := 및 ;의 사용에 반드시 주의해야할 것 같다.

SET @HOUR = -1;
SELECT (@HOUR := @HOUR +1) AS HOUR
     , (SELECT COUNT(HOUR(DATETIME))
        FROM ANIMAL_OUTS
        WHERE HOUR(DATETIME)=@HOUR) AS COUNT
        FROM ANIMAL_OUTS
WHERE @HOUR < 23;

 

 

링크: https://school.programmers.co.kr/learn/courses/30/lessons/77487

 

습득한 점:

실제 코테라고 쫄지마, Lv 1 수준이야

 

 

문제:

이 서비스에서는 공간을 둘 이상 등록한 사람을 "헤비 유저"라고 부름.

헤비 유저가 등록한 공간의 정보를 아이디 순으로 조회하는 쿼리 작성

 

문제파악:

HOST_ID의 갯수가 2개 이상인 HOST_ID만 따로 뽑고, 원본데이터의 WHERE 조건절로 활용

 

 

풀이

-- 원하는 조건의 HOST_ID만 뽑은 뒤, 이를 원본데이터에 조건으로 넣기

# SELECT *
# FROM PLACES
# WHERE HOST_ID IN (
#              SELECT HOST_ID
#              FROM PLACES
#              GROUP BY HOST_ID
#              HAVING COUNT(HOST_ID)>=2
#             )
# ORDER BY ID            


-- 반대 풀이

SELECT *
FROM PLACES
WHERE HOST_ID NOT IN (
             SELECT HOST_ID
             FROM PLACES
             GROUP BY HOST_ID
             HAVING COUNT(HOST_ID)=1
            )
ORDER BY ID

 

 

링크: https://school.programmers.co.kr/learn/courses/30/lessons/131537

 

습득한 점: 

* 컬럼의 값을 NULL 로 표시해야 할 경우에는, 문자열 "NULL" 나타내는 것이 아닌 NULL 로만 기재

 

* UNION , UNION ALL?

     - UNION은 두 개의 테이블을 하나로 만드는 연산.

        두 개 테이블의 컬럼 수, 컬럼 데이터 형식이 모두 일치해야 함.

       UNION 연산은 두 개의 테이블을 하나로 합치면서 중복된 데이터를 제거한다. 정렬을 발생

 

     - UNION ALL은 중복을 제거하거나 정렬을 유발하지 않는다. 중복제거 없이 전부 보여줌

        두 개 테이블의 컬럼 수가 같아야 하는 것은 동일

 

- Reference

https://prinha.tistory.com/entry/MySQL-COALESCE-%ED%95%A8%EC%88%98

https://whitepro.tistory.com/580

https://gooreumwave.tistory.com/25


문제:

ONLINE_SALE 테이블과 OFFLINE_SALE 테이블에서

2022년 3월의 오프라인/온라인 상품 판매 데이터의 판매 날짜, 상품ID, 유저ID, 판매량을 출력

OFFLINE_SALE 테이블의 판매 데이터의 USER_ID 값은 NULL 로 표시

 

판매일을 기준으로 오름차순 정렬,

판매일이 같다면 상품 ID를 기준으로 오름차순,

상품ID까지 같다면 유저 ID를 기준으로 오름차순 정렬

 

문제 파악:

두개 테이블을 합쳐서, 날짜에 맞는 조건들만 건져내면 된다.

날짜만 포맷을 바꿔서 바꿔주면 되었음.

 

아래와 같이 NULL 값 표기가 필요할 때는,

문자열 "NULL" 나타내는 것이 아닌 NULL 로만 기재하는 것만 유의하면 쉽게 풀 수 있는 문제


* 아래처럼 With 구문을 쓰지 않고 테이블 2개를 한번에 묶고, 정렬해줘도 풀 수 있을 것 같다.

 

(SELECT *

FROM TABLE_1

 

UNION

 

SELECT *

FROM TABLE_2)

ORDER BY ~~~


 

풀이:

WITH COUNTER AS (

        SELECT DATE_FORMAT(SALES_DATE, "%Y-%m-%d") AS SALES_DATE
             , PRODUCT_ID
             , USER_ID
             , SALES_AMOUNT
        FROM ONLINE_SALE AS ONSALE

        UNION ALL

        SELECT DATE_FORMAT(SALES_DATE, "%Y-%m-%d") AS SALES_DATE
             , PRODUCT_ID
             , NULL AS USER_ID
             , SALES_AMOUNT
        FROM OFFLINE_SALE AS OFFSALE
                )

                
SELECT *
FROM COUNTER
WHERE SALES_DATE LIKE '2022-03%'
ORDER BY SALES_DATE,PRODUCT_ID,USER_ID

 

 

 

 

언젠가 활용될 지 모를 sql 고유값 만들기

아마 LEFT, INNER 등 조인을 통해 테이블 변환이 필요할 때 활용할 수 있을 것 같다.

CONCAT의 괄호 끝에 ' 넣어주고, 컬럼들 사이에 ,',', 넣어주면 끝이다.

전체 row갯수와 중복값을 제거한 row갯수가 같을 때만 활용해야한다.

 

ex)

SELECT ONLINE_SALE_ID
      ,USER_ID
      ,PRODUCT_ID
      ,SALES_AMOUNT
      ,concat(online_sale_id,',',user_id,',',PRODUCT_ID,',',SALES_AMOUNT) AS new
FROM ONLINE_SALE

 

 

 

전체 row 갯수 122

 

새로운 컬럼 new가 생성되고, 문자열 조합으로 이루어진 값을 확인할 수 있다.

'기존학습자료 > 기초 스킬' 카테고리의 다른 글

기초 함수 세부 활용  (0) 2023.06.25
사용자 함수  (0) 2023.06.25
정규표현식  (0) 2023.06.25


https://school.programmers.co.kr/learn/courses/30/lessons/157339

With counter AS(
    
        SELECT CAR.DAILY_FEE
             , CAR.CAR_ID
             , CAR.CAR_TYPE
             , START_DATE
             , END_DATE
    
        FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY AS HISTORY
              INNER JOIN CAR_RENTAL_COMPANY_CAR AS CAR ON CAR.CAR_ID = HISTORY.CAR_ID AND CAR.CAR_TYPE IN ('세단', 'SUV')
        WHERE (START_DATE > '2022-11-30' or END_DATE < '2022-11-01') 
                ) 

SELECT DISTINCT CAR_ID
     , counter.CAR_TYPE
     , ROUND((100-discount_rate)/100 * DAILY_FEE* 30) AS FEE     
      
FROM counter
     INNER JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN AS PLAN ON counter.CAR_TYPE = PLAN.CAR_TYPE AND DURATION_TYPE = '30일 이상'
     
     WHERE (100 - discount_rate)/100 * daily_fee * 30 >= 500000
     AND (100 - discount_rate)/100 * daily_fee * 30 < 2000000
     
     
ORDER BY 3 DESC, 2 ASC ,1 DESC

 

 

원래 CAR_TYPE의 SUV는 나오지 않아야 한다.

세단만이 나와야 하는데, 
조건에 해당되어서 일단은 suv값이 같이 나오고 있다.

 

일단 날짜 조건이 매우 까다롭다.

다들 까다로운 문제라고는 하지만 10시간 매달려도 못풀다니...
쿼리 하나하나 다시 디버깅 해봐야겠다.

 

어디가 문제일까?

 

 

 

링크:
https://school.programmers.co.kr/learn/courses/30/lessons/151141

 

습득한 점:

 

테이블 끼리의 조인을 할 때도,

두 테이블에 단순히 같은 값이 있다고 자주쓰던 INNER JOIN으로 그냥 매칭 시킬 것이 아니라

 

묶어야 할 테이블의 데이터 형식에 맞추어 조인 할 수 있도록

CASE 문을 통해, 특정 구간의 숫자 레인지를 값으로 바꾸는 데이터 조작 방식과,

이를 NULL 값이 드러 날 수 있도록 LEFT JOIN 으로 묶는  쿼리 작성 방식을 알게되었음.

 

주어진 테이블의 전체 형태를 보고 데이터 조작과, 테이블 조인을 시도해야 겠다는 생각이 들었음.

테이블을 연계구조를 한눈에 파악하기가 아직은 힘들었는데, 한눈에 파악 안되면 직접 그림을 그려보는 것도 하나의 방법이 될 것 같음.

 

문제:

- 자동차 종류: 트럭 의 대여기록 별 대여금액, 대여기록 ID + 대여금액 리스트 출력
- 컬럼명은 FEE: 대여기록 별 대여 금액
- 대여금액을 기준으로 내림차순 desc, 기록 id 내림차순

 

 

문제파악:

 

날짜의 특정 레인지를 특정 값으로 바꾸는 것이 포인트.

 

- 히스토리 테이블의 기간범위 값들을 특정 4구간으로 변환 >>> why? 기간별 할인율이 다름
- DURATION_TYPE이 원래 존재하는 디스카운트 테이블과 조인 3개의 구간 존재
- none값은 할인을 받지 않는 값으로써 활용해야함. (0~6일 구간)
- 조인했을때, 조인된 테이블에 값이 null값이라면 0으로 치환하기
- 100-ifnull(discount_rate,0)/100 > 할인율이 null값이면 0을 반환, null값이 아니면 할인율 반환
- daily_fee*period*discount_rate FEE 컬럼 형성
- 0으로 치환된 것들은 자동으로 1로 변환 (100-0)/100 =값에 변화가 없음.

# 한눈에 파악 안되면, 테이블 그리기

# 문제 제대로 읽기

 

 

풀이:

WITH def AS (
    SELECT CAR.DAILY_FEE
         , CAR.car_type
         , HISTORY.history_id
         , DATEDIFF(END_DATE,START_DATE) + 1 AS lending_p
         , CASE 
               WHEN DATEDIFF(END_DATE,START_DATE) + 1 >= 90 THEN '90일 이상'
               WHEN DATEDIFF(END_DATE,START_DATE) + 1 >= 30 THEN '30일 이상'
               WHEN DATEDIFF(END_DATE,START_DATE) + 1 >= 7 THEN '7일 이상'
               ELSE 'NONE'
           END AS duration_type
    
      FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY AS HISTORY
      INNER JOIN CAR_RENTAL_COMPANY_CAR AS CAR ON CAR.CAR_ID = HISTORY.CAR_ID AND CAR.CAR_TYPE ='트럭'
    
               )


SELECT def.HISTORY_ID
     , ROUND(def.daily_fee * def.lending_p * 
            (1- (IFNULL(plan.DISCOUNT_RATE,0)/100))) AS FEE
     

FROM def
LEFT JOIN CAR_RENTAL_COMPANY_DISCOUNT_PLAN AS PLAN ON def.DURATION_TYPE = PLAN.DURATION_TYPE
AND PLAN.CAR_TYPE = '트럭'
ORDER BY FEE DESC, def.HISTORY_ID DESC

 

링크: https://school.programmers.co.kr/learn/courses/30/lessons/131534

 

습득한 점:

 

1. 문제를 읽고, 내가 이해한 플로우가 맞는지 예시로 나온 부분도 제대로 파악하기!!!

 

이 문제의 경우 PUCHASED_USERS 를 구하는 과정에서 중복값을 고려하지 않아서 한참을 헤멧다.

읽기만 하고 문제가 원하는 바를 제대로 이해하지 않음.

PUCHASED_USERS 컬럼의 경우 중복을 제거한 숫자를 카운팅하는 점을 주의해야함.

(예시까지 이해하지 않으면, 계속 틀리게 될 것 같다)

 

2. JOIN의 경우 INNER JOIN으로 자동 인식된다는 점

 

3. 테이블 조인할 때, AND조건으로 조인될 조건을 추가할 수 있음.

  INNER JOIN USER_INFO U ON S.USER_ID = U.USER_ID AND YEAR(JOINED) = 2021

ONLINE_SALE 테이블의 JOINED 의 연도가 2021인 경우에만 테이블 조인 할 것.


- Reference

https://extbrain.tistory.com/60

 

***추가 - 날짜데이터에서 일부 데이터만 추출하기

  • YEAR : 연도 추출
  • MONTH : 월 추출
  • DAY : 일 추출 (DAYOFMONTH와 같은 함수)
  • DATE : 년 / 월 / 일 추출  (DATETIME 형식 -> DATE 형식)
  • HOUR : 시 추출
  • MINUTE : 분 추출
  • SECOND : 초 추출

문제

USER_INFO 테이블과 ONLINE_SALE 테이블에서

2021년에 가입한 전체 회원들 중 상품을 구매한 회원수와 상품을 구매한 회원의 비율

             (=2021년에 가입한 회원 중 상품을 구매한 회원수 / 2021년에 가입한 전체 회원 수)을 년, 월 별로 출력하는 SQL문을 작성

 

상품을 구매한 회원의 비율은 소수점 두번째자리에서 반올림

전체 결과는 년을 기준으로 오름차순 정렬

년이 같다면 월을 기준으로 오름차순 정렬

 

 

문제 파악

 

# USER_INFO (회원정보)
# - USER_ID, GENDER, AGE, JOINED

# ONLINE_SALE (온라인 상품 판매정보)
# - ONLINE_SALE_ID, USER_ID, PRODUCT_ID, SALES_AMOUNT, SALES_DATE

# foreign key - USER_ID
# 특이사항 - 판매일이 DATETIME 형식, 성별이 1,2,null 로 구분

형식 :
# YEAR   MONTH   PUCHASED_USERS   PUCHASED_RATIO


풀이

SELECT YEAR, 
       MONTH,
       COUNT(*) AS PUCHASED_USERS, 
				-- COUNT(*) = 상품구매 회원 수                
       ROUND((COUNT(*)/(SELECT COUNT(*) 
                        FROM USER_INFO WHERE YEAR(JOINED) = 2021)),1) AS PUCHASED_RATIO  
     
     
     
FROM (
        SELECT DISTINCT YEAR(S.SALES_DATE) AS YEAR
                      , MONTH(S.SALES_DATE) AS MONTH
                      , U.USER_ID
        FROM ONLINE_SALE S
        INNER JOIN USER_INFO U ON S.USER_ID = U.USER_ID AND YEAR(JOINED) = 2021  -- JOINED 의 연도가 2021인 경우에만 함께 조인
    ) sub

GROUP BY YEAR, MONTH   
ORDER BY YEAR, MONTH

 

 

 

https://school.programmers.co.kr/learn/courses/30/lessons/164673

 

1. 조인조건을 찾아보니 다들 Board_id로 조인했고,

나만 Writer_id 기준으로 묶었더라.

 

2. 쿼리 짜는 것은 쉬웠지만, 데이터 포맷을 바꾸는 것 같은 세부적인 작업에 아직 익숙하지 않아서 연습이 필요하다.

 

3. CREATED_DATE(댓글작성일) 기준으로 오름차순 정렬,

    댓글작성일이 같으면 TITLE(게시글 제목) 기준으로 오름차순 정렬이란 말에 어떻게 풀어야 할 지 살짝 멈칫.

    결과적으로 경우의 수는 같거나 다르냐 2가지 일 뿐인데, 댓글작성일이 같을 경우를 처리하는 쿼리를 어떻게 만들지 생각하고 있었다;;;

4. 문제 제대로 읽자. 쉬운 문제야

 

SELECT TITLE
     , B.BOARD_ID
     , REPLY_ID
     , R.WRITER_ID
     , R.CONTENTS
     , DATE_FORMAT(R.CREATED_DATE, '%Y-%m-%d') AS CREATED_DATE 
FROM USED_GOODS_BOARD AS B
    INNER JOIN USED_GOODS_REPLY AS R ON B.BOARD_ID = R.BOARD_ID

WHERE B.CREATED_DATE BETWEEN '2022-10-01' AND '2022-10-31'
ORDER BY R.CREATED_DATE, TITLE

 

 

문제 설명

다음은 중고거래 게시판 정보를 담은 USED_GOODS_BOARD 테이블과 중고거래 게시판 첨부파일 정보를 담은 USED_GOODS_REPLY 테이블입니다. USED_GOODS_BOARD 테이블은 다음과 같으며 BOARD_ID, WRITER_ID, TITLE, CONTENTS, PRICE, CREATED_DATE, STATUS, VIEWS은 게시글 ID, 작성자 ID, 게시글 제목, 게시글 내용, 가격, 작성일, 거래상태, 조회수를 의미합니다.


Column name Type Nullable
BOARD_ID VARCHAR(5) FALSE
WRITER_ID VARCHAR(50) FALSE
TITLE VARCHAR(100) FALSE
CONTENTS VARCHAR(1000) FALSE
PRICE NUMBER FALSE
CREATED_DATE DATE FALSE
STATUS VARCHAR(10) FALSE
VIEWS NUMBER FALSE

USED_GOODS_REPLY 테이블은 다음과 같으며 REPLY_ID, BOARD_ID, WRITER_ID, CONTENTS, CREATED_DATE는 각각 댓글 ID, 게시글 ID, 작성자 ID, 댓글 내용, 작성일을 의미합니다.


Column name Type Nullable
REPLY_ID VARCHAR(10) FALSE
BOARD_ID VARCHAR(5) FALSE
WRITER_ID VARCHAR(50) FALSE
CONTENTS VARCHAR(1000) TRUE
CREATED_DATE DATE FALSE

문제

USED_GOODS_BOARD와 USED_GOODS_REPLY 테이블에서 2022년 10월에 작성된 게시글 제목, 게시글 ID, 댓글 ID, 댓글 작성자 ID, 댓글 내용, 댓글 작성일을 조회하는 SQL문을 작성해주세요. 결과는 댓글 작성일을 기준으로 오름차순 정렬해주시고, 댓글 작성일이 같다면 게시글 제목을 기준으로 오름차순 정렬해주세요.


예시

USED_GOODS_BOARD 테이블이 다음과 같고

 

BOARD_ID WRITER_ID TITLE CONTENTS PRICE CREATED_DATE STATUS VIEWS
B0001 kwag98 반려견 배변패드 팝니다 정말 저렴히 판매합니다. 전부 미개봉 새상품입니다. 12000 2022-10-01 DONE 250
B0002 lee871201 국내산 볶음참깨 직접 농사지은 참깨입니다. 3000 2022-10-02 DONE 121
B0003 goung12 배드민턴 라켓 사놓고 방치만 해서 팝니다. 9000 2022-10-02 SALE 212
B0004 keel1990 디올 귀걸이 신세계강남점에서 구입. 정품 아닐시 백퍼센트 환불 130000 2022-10-02 SALE 199
B0005 haphli01 스팸클래식 팔아요 유통기한 2025년까지에요 10000 2022-10-02 SALE 121

 

USED_GOODS_REPLY 테이블이 다음과 같을 때

 

REPLY_ID BOARD_ID WRITER_ID CONTENTS CREATED_DATE
R000000001 B0001 s2s2123 구매하겠습니다. 쪽지 드립니다. 2022-10-02
R000000002 B0002 hoho1112 쪽지 주세요. 2022-10-03
R000000003 B0006 hwahwa2 삽니다. 연락주세요. 2022-10-03
R000000004 B0007 hong02 예약중 2022-10-06
R000000005 B0009 hanju23 구매완료 2022-10-07

 

SQL을 실행하면 다음과 같이 출력되어야 합니다.

 

TITLE BOARD_ID REPLY_ID WRITER_ID CONTENTS CREATED_DATE
반려견 배변패드 팝니다 B0001 R000000001 s2s2123 구매하겠습니다. 쪽지 드립니다. 2022-10-02
국내산 볶음참깨 B0002 R000000002 hoho1112 쪽지 주세요. 2022-10-03

 

 

 

-Reference

https://kig6022.tistory.com/7

FORMAT 설명
%M Month 월(Janeary, February ...)
%m Month 월(01, 02, 03 ...)
%W Day of Week 요일(Sunday, Monday ...)
%D Month 월(1st, 2dn, 3rd ...)
%Y Year 연도(1999, 2000, 2020)
%y Year 연도(99, 00, 20)
%X Year 연도(1999, 2000, 2020) %V와 같이쓰임
%x Year 연도(1999, 2000, 2020) %v와 같이쓰임
%a Day of Week요일(Sun, Mon, Tue ...)
%d Day 일(00, 01, 02 ...)
%e Day 일(0, 1, 2 ..)
%c Month(1, 2, 3 ..)
%b Month(Jen Feb ...)
%j n번째 일(100, 365)
%H Hour 시(00, 01, 24) 24시간 형태 
%h Hour 시(01, 02, 12) 12시간 형태
%I(대문자 아이) Hour 시(01, 02 12) 12시간 형태
%l(소문자 엘) Hour 시(1, 2, 12) 12 시간 형태
%i Minute 분(00, 01 59)
%r hh:mm:ss AP | PM
%T hh:mm:ss
%S, %s Second 초
%p AP, PM
%w Day Of Week (0, 1, 2) 0부터 일요일
%U Week 주(시작: 일요일)
%u Week 주(시작 월요일)
%V Week 주(시작: 일요일)
%v Week 주(시작:월요일)

 

+ Recent posts