-- 코드를 입력하세요
select a.YEAR
, a.MONTH * 1 as MONTH
, count(a.USER_ID) as PUCHASED_USERS
, round(count(a.USER_ID)/(select count(u.USER_ID) from USER_INFO u where date_format(u.JOINED, '%Y') = '2021'), 1) as PUCHASED_RATIO
from (
SELECT distinct b.USER_ID
, date_format(b.SALES_DATE, '%Y') YEAR
, date_format(b.SALES_DATE, '%m') MONTH
, date_format(b.SALES_DATE, '%Y-%m') YM
from ONLINE_SALE b
inner join USER_INFO u
on b.USER_ID = u.USER_ID
and date_format(u.JOINED, '%Y') = '2021'
) a
group by a.YM
order by YEAR, MONTH
# select a.YEAR
# , a.MONTH * 1 as MONTH
# , count(distinct a.USER_ID) as PUCHASED_USERS
# , round(count(distinct a.USER_ID)/count(distinct b.USER_ID), 1) as PUCHASED_RATIO
# from (
# SELECT distinct b.USER_ID
# , date_format(b.SALES_DATE, '%Y') YEAR
# , date_format(b.SALES_DATE, '%m') MONTH
# , date_format(b.SALES_DATE, '%Y-%m') YM
# from ONLINE_SALE b
# ) a
# inner join USER_INFO b
# on date_format(b.JOINED, '%Y-%m') <= a.YM
# and b.GENDER is not null
# group by a.YM
# order by YEAR, MONTH
문제 제대로 안읽음;;
이게 레벨 5던데, 문제 자체가 그냥 헷갈림...내가 대충 읽은건가;;
첨에 아래 쿼리 만들었는데,,
아래 쿼리는 (매월 구매회원 / 매월 회원수)을 구하는거임...아 이정도 되니까 레벨5 구나 싶었음.
근데 아무리 돌려도 정답이 아니라길래;;
뭐 2021년 회원 중...이라는;; 이걸 읽지도 않고, 반올림 하는것도 안읽고;;
위 쿼리에선 스칼라서브쿼리를 사용하였다, 2021년 이라는 데이터에 대해서 반복적으로 가져올테니, 매 행마다 실행하는게 아니라,
한번 실행하고 메모리에 올려두고 재사용할테니 요게 더 빠를 거라 판단,
아래 쿼리의 경우 매년 매월 이라는 그나마 좀 다양한 경우의 수가 있기에, 범위가 커질수록 스칼라서브쿼리가 불리할거라 판단했고,
조인을 통해 가져오는 영역은 count(distinct b.USER_ID) 뿐일거라, 범위의 데이터를 통으로 가져오는게 아니라, 저걸 위한 데이터를 알아서 옵티마이저가 잘 연산해 주겠지 하는 막연한 믿음으로 작성한 쿼리,
-- 코드를 입력하세요
SELECT date_format(b.SALES_DATE, '%Y') as YEAR
# , cast(date_format(b.SALES_DATE, '%m') as unsigned) as MONTH
, date_format(b.SALES_DATE, '%m')*1 as MONTH
, a.GENDER
, count(distinct a.USER_ID)as USERS
from USER_INFO a
inner join ONLINE_SALE b
on a.USER_ID = b.USER_ID
where a.GENDER is not null
group by date_format(b.SALES_DATE, '%Y%m'), a.GENDER
order by YEAR, MONTH, GENDER, USERS
select r.CATEGORY
, r.PRICE as MAX_PRICE
, r.PRODUCT_NAME
from (
select @rank := if(@cate = t.CATEGORY, @rank + 1, 1) as num
, @cate := t.CATEGORY
, t.CATEGORY
, t.PRICE
, t.PRODUCT_NAME
from (
SELECT a.PRODUCT_ID
, a.PRODUCT_NAME
, a.PRODUCT_CD
, a.CATEGORY
, a.PRICE
from FOOD_PRODUCT a
where a.CATEGORY in ('과자', '국', '김치', '식용유')
order by a.CATEGORY, a.PRICE desc
) t
) r
where r.num = 1
order by r.PRICE desc
select t.CATEGORY
, t.PRICE as MAX_PRICE
, t.PRODUCT_NAME
from (
SELECT a.PRODUCT_ID
, a.PRODUCT_NAME
, a.PRODUCT_CD
, a.CATEGORY
, a.PRICE
, rank() over(partition by a.CATEGORY order by max(a.PRICE) desc) as r
from FOOD_PRODUCT a
where a.CATEGORY in ('과자', '국', '김치', '식용유')
group by a.PRODUCT_ID
) t
where t.r = 1
order by t.PRICE desc
두가지 버전,
MYSQL 8부터 오라클 처럼 partition by를 지원하는 윈도우 함수가 가능하다. 핵 편함.
하지만, 5.7까지는 안되니깐, 여기선 변수를 사용한 처리가 가능하다.(자료구조상 사이즈 1개 짜리 큐를 이용하는 느낌?)
SELECT left(SALES_DATE, 10) as SALES_DATE
, PRODUCT_ID
, USER_ID
, SALES_AMOUNT
from ONLINE_SALE A
where left(SALES_DATE, 7) = '2022-03'
UNION ALL
SELECT left(SALES_DATE, 10) as SALES_DATE
, PRODUCT_ID
, null
, SALES_AMOUNT
from OFFLINE_SALE A
where left(SALES_DATE, 7) = '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 ONLINE_SALE A
where SALES_DATE between date('2022-03-01') and date('2022-03-31')
UNION ALL
SELECT date_format(SALES_DATE, '%Y-%m-%d') as SALES_DATE
, PRODUCT_ID
, null
, SALES_AMOUNT
from OFFLINE_SALE A
where SALES_DATE between date('2022-03-01') and date('2022-03-31')
order by SALES_DATE, PRODUCT_ID, USER_ID
아님 뭐 메서드 내 필드를 이용해서 이렇게 바꿀까? 이것도 뭐 크게 다른건 없다고 느끼는데, 그렇다고 싱글톤 기반의 빈에서 클래스 필드를 맘대로 교체할 수도 없는 노릇이고,
좀 더 고차원 레이어에 영향이 안가는 방법의 코드를 작성 할 수는 없을까?
있다, Enum과 Provider를 사용하면 된다. Spring Container는 등록된 모든 Bean에 대한 제어가 가능하다. 일단 등록되어 있으니 가져오는 것도 가능하다. 구조적으로는 위에 if문을 사용하는 것과 차이가 없지만,
적어도 인터페이스를 호출 하는 부분을 타입이나, 유형 때문에 바꿀 일은 없을 거다.
예시 코드 들어간다.
참고로 제 코드가 좋은 코드는 아닙니다용..제발...
public interface PayService {
void pay();
}
private class PayKakaoService implements PayService {
@Override
public void pay() {
}
}
private class PaySamsungService implements PayService {
@Override
public void pay() {
}
}
private class PayNaverService implements PayService {
@Override
public void pay() {
}
}
// private PayService payKakaoService;
// private PayService paySamsungService;
// private PayService payNaverService;
@Getter
@RequiredArgsConstructor
public enum PayType {
Kakao(PayKakaoService.class), Samsung(PaySamsungService.class), Naver(
PayNaverService.class);
private final Class<? extends PayService> clazz;
}
public class PayServiceProvider {
private static ApplicationContext applicationContext;
private static Map<PayType, Class<? extends PayService>> payServiceMap = new HashMap<>();
static {
for (PayType payType : PayType.values()) {
payServiceMap.put(payType, payType.getClazz());
}
}
public static PayService service(PayType payType) throws Exception {
Class<? extends PayService> payServiceClass = payServiceMap.get(payType);
if (payServiceClass == null) {
throw new Exception();
}
return applicationContext.getBean(payServiceClass);
}
}
@PostMapping("/payment")
public ResponseEntity<?> pay(PayType payType) throws Exception {
// PayService ps = null;
//
// if (payType == PayType.Kakao) {
// ps = payKakaoService;
// }
//
// if (payType == PayType.Samsung) {
// ps = paySamsungService;
// }
//
// if (payType == PayType.Naver) {
// ps = payNaverService;
// }
//
// ps.pay();
PayServiceProvider.service(payType).pay();
return ResponseEntity.ok().build();
}
// 실행
void callPayTest() throws Exception {
this.pay(PayType.Samsung);
}
일단, 해당 코드는 provider에서 사용하는 applicationContext에 대한 주입이 없긴하다. 요건 이제 @PostConstruct를 사용해서 주입하면 되긴합니다요. 물론 주입하는 함수도 provider에 static으로 만들어 줘야 합니다. (대충 읽고 코드만 복붙하면 안되게 하려는 함정...)