학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 여기에 질문 내용을 남겨주세요. 컬렉션 프레임 워크 - LinkedList MyLinkedListV1 에서 이해 못하는 소스가 있어서 질문 드려요. 아래 코드에서 x.item을 oldValue에 대입하고 반환하는지 궁금합니다. 그냥 x.item = element만 사용해도 되는거 아닌지? public Object set(int index, Object element) { Node x = getNode(index); Object oldValue = x.item; x.item = element; return oldValue; }
이은경 - 빠짝스터디 1주차 과제 제출합니다. -- ARRAY_STRUCT 연습 문제 -- 연습문제 데이터 생성 CREATE OR REPLACE TABLE advanced.array_exercises as SELECT movie_id, title, actors, genres FROM( SELECT 1 as movie_id, 'Avengers:Endgame' as title, ARRAY<STRUCT<actor STRING, character STRING>>[ STRUCT('Robert Downey Jr.','Tony Stark'), STRUCT('Chris Evans','Steve Rogers') ] as actors, ARRAY<STRING>['Action','Adventure','Drama'] as genres UNION ALL SELECT 2, 'Inception', ARRAY<STRUCT<actor STRING, character STRING>>[ STRUCT('Leonardo Dicaprio','Cobb'), STRUCT('Joseph Gordon-Levitt','Arthur') ], ARRAY<STRING>['Action','Adventure','Sci-Fi'] UNION ALL SELECT 3, 'The Dark Knight', ARRAY<STRUCT<actor STRING, character STRING>>[ STRUCT('Christian Bale','Bruce Wayne'), STRUCT('Heath Ledger','Joker') ], ARRAY<STRING>['Action','Crime','Drama'] ); -- 연습문제 1) array_exercises table에서 title 별로 영화 genres를 UNNEST해서 보여주세요. # UNNEST(ARRAY_COLUMN) AS 새로운 이름 # SELECT 절에서 새로운 이름으로 사용한다. 기존의 ARRAY_COLUMN은 사용하지 않는다. SELECT a.title, genre FROM advanced.array_exercises as a CROSS JOIN UNNEST(genres) as genre; -- 연습문제 2) array_exercises table에서 title 별로 배우(actor)와 배역(character)을 UNNEST해서 보여주세요. 배우와 배역은 별도의 column으로 나와야 합니다. SELECT a.title, person.actor as actor, person.character as character FROM advanced.array_exercises as a CROSS JOIN UNNEST(actors) as person; -- 연습문제 3) array_exercises table에서 title 별로 배우(actor), 배역(character), 장르를 한 row에 표시되도록 출력하세요. # CROSS JOIN 하면 데이터의 중복이 어느 정도 발생, 어쩔 수 없는 이슈이다. SELECT a.title, person.actor as actor, person.character as character, genre FROM advanced.array_exercises as a CROSS JOIN UNNEST(actors) as person CROSS JOIN UNNEST(genres) as genre; -- 3)번 문제 관련 추가 # 3) 결과를 기준으로, 조건문을 사용하고 싶다. # "WHERE"를 바로 사용하면 error 발생 : 실행 순서가 FROM -> JOIN -> SELECT # actors -> UNNEST(actors) : STRUCT<actor, character> # genres -> UNNEST(genres) : STRING SELECT a.title, person.actor as actor, person.character as character, genre FROM advanced.array_exercises as a CROSS JOIN UNNEST(actors) as person CROSS JOIN UNNEST(genres) as genre WHERE person.actor='Chris Evans' -- 그냥 actor 사용하면 error 발생함 and genre='Action'; -- 연습문제 4) 앱 로그 데이터(app_logs)의 배열을 풀어주세요 SELECT a.user_id, a.event_date, a.event_name, a.user_pseudo_id, b.key as key, b.value.string_value as string_value, b.value.int_value as int_value FROM advanced.app_log as a CROSS JOIN UNNEST(event_params) as b; -- 4)번 문제 관련, 추가 사항(일 별, 사용자 수 집계) WITH base as( SELECT a.user_id, a.event_date, a.event_name, a.user_pseudo_id, b.key as key, b.value.string_value as string_value, b.value.int_value as int_value FROM advanced.app_log as a CROSS JOIN UNNEST(event_params) as b ) SELECT event_date,count(user_id) as cnt FROM base GROUP BY event_date ORDER BY event_date; ############################# -- PIVOT 연습문제 -- 연습문제 1) orders 테이블에서 유저(user_id) 별로 주문 금액(amount)의 합게를 PIVOT 해주세요. -- 날짜(order_date)를 행(Row)으로, user_id를 열로 만들어야 합니다. SELECT order_date, MAX(IF(user_id=1,amount,0)) as user_1, MAX(IF(user_id=2,amount,0)) as user_2, MAX(IF(user_id=3,amount,0)) as user_3 FROM advanced.orders GROUP BY order_date ORDER BY order_date; -- 연습문제 2) orders 테이블에서 날짜(order_date) 별로 유저들의 주문 금액(amount)의 합계를 PIVOT 해주세요. -- user_id를 행으로, order_date를 열으로 만들어야 합니다. # column name을 어떻게 지정? -- backtick(`) 사용 SELECT user_id, SUM(IF(order_date='2023-05-01',amount,0)) as `2023-05-01`, SUM(IF(order_date='2023-05-02',amount,0)) as `2023-05-02`, SUM(IF(order_date='2023-05-03',amount,0)) as `2023-05-03`, SUM(IF(order_date='2023-05-04',amount,0)) as `2023-05-04`, SUM(IF(order_date='2023-05-05',amount,0)) as `2023-05-05` FROM advanced.orders GROUP BY user_id ORDER BY user_id; -- 연습문제 3) orders 테이블에서 사용자(user_id) 별, 날짜(order_date) 별로 주문이 있다면 1, 없다면 0으로 PIVOT 해주세요. user_id를 행으로, order_date를 열로 만들고 주문을 많이 해도 1로 처리합니다. # column name을 어떻게 지정? (2번 문제와 연관) -- backtick(`) 사용 SELECT user_id, IF(SUM(IF(order_date='2023-05-01',amount,0))>0,1,0) as `2023-05-01`, IF(SUM(IF(order_date='2023-05-02',amount,0))>0,1,0) as `2023-05-02`, IF(SUM(IF(order_date='2023-05-03',amount,0))>0,1,0) as `2023-05-03`, IF(SUM(IF(order_date='2023-05-04',amount,0))>0,1,0) as `2023-05-04`, IF(SUM(IF(order_date='2023-05-05',amount,0))>0,1,0) as `2023-05-05` FROM advanced.orders GROUP BY user_id ORDER BY user_id; -- 연습문제 3번 다른 풀이 -- 특정 column 대신 "1"을 사용할 수 있다.(유무에 따라서) -- 만약, 횟수를 구해야 할 경우에는 MAX대신 SUM을 사용하면 된다. SELECT user_id, MAX(IF(order_date='2023-05-01',1,0)) as `2023-05-01`, MAX(IF(order_date='2023-05-02',1,0)) as `2023-05-02`, MAX(IF(order_date='2023-05-03',1,0)) as `2023-05-03`, MAX(IF(order_date='2023-05-04',1,0)) as `2023-05-04`, MAX(IF(order_date='2023-05-05',1,0)) as `2023-05-05` FROM advanced.orders GROUP BY user_id ORDER BY user_id; -- 연습문제 4) 앱 로그 데이터 배열 PIVOT 하기 WITH example as ( SELECT a.user_id, a.event_date, a.event_timestamp, a.event_name, a.user_pseudo_id, b.key as key, b.value.string_value as string_value, b.value.int_value as int_value FROM advanced.app_log as a CROSS JOIN UNNEST(event_params) as b WHERE event_date='2022-08-01' ) SELECT user_id, event_date, event_timestamp, event_name, user_pseudo_id, ANY_VALUE(IF(key='firebase_screen',string_value,NULL)) as firebase_screen, ANY_VALUE(IF(key='food_id',int_value,NULL)) as food_id, ANY_VALUE(IF(key='session_id',string_value,NULL)) as session_id FROM example GROUP BY ALL ORDER BY event_date, event_name; -- 퍼널 SQL 연습문제 -- 데이터 PIVOT 한 형태로 변환(시각화 용이) # with 구문으로 만든 데이터들을 저장하는 방법은...? with base_data as( SELECT event_date, event_timestamp, event_name, user_id, user_pseudo_id,platform, MAX(IF(event_params.key="firebase_screen",event_params.value. string_value,NULL)) as firebase_screen, MAX(IF(event_params.key="food_id",event_params.value.int_value,NULL)) as food_id, MAX(IF(event_params.key="session_id",event_params.value.string_value,NULL)) as session_id FROM `advanced.app_log` CROSS JOIN UNNEST(event_params) as event_params WHERE event_date between '2022-08-01' and '2022-08-18' GROUP BY ALL ), filter_event_and_concat_event_and_screen as( -- event_name + screen (필요한 이벤트만 가져올 필요가 있음) SELECT * EXCEPT(event_name, firebase_screen, food_id, event_timestamp), CONCAT(event_name,"-", firebase_screen) as event_name_with_screen, datetime(timestamp_micros(event_timestamp),"Asia/Seoul") as event_datetime FROM base_data WHERE event_name in ("screen_view","click_payment") ), funnel_data as( SELECT event_date, event_name_with_screen, case when event_name_with_screen="screen_view-welcome" then 1 when event_name_with_screen="screen_view-home" then 2 when event_name_with_screen="screen_view-food_category" then 3 when event_name_with_screen="screen_view-restaurant" then 4 when event_name_with_screen="screen_view-cart" then 5 when event_name_with_screen="click_payment-cart" then 6 else NULL end as step_number , count(distinct user_pseudo_id) as cnt FROM filter_event_and_concat_event_and_screen GROUP BY ALL HAVING step_number is not null ORDER BY event_date, step_number ) SELECT event_date, MAX(IF(step_number=1,cnt,NULL)) as `screen_view-welcome`, MAX(IF(step_number=2,cnt,NULL)) as `screen_view-home`, MAX(IF(step_number=3,cnt,NULL)) as `screen_view-food_category`, MAX(IF(step_number=4,cnt,NULL)) as `screen_view-restaurant`, MAX(IF(step_number=5,cnt,NULL)) as `screen_view-cart`, MAX(IF(step_number=6,cnt,NULL)) as `click_payment-cart` FROM funnel_data GROUP BY ALL ORDER BY event_date; 느낀 점 새로운 자료형은 늘 어렵다... 퍼널 분석의 큰 흐름을 배울 수 있어서 좋았습니다! 의사 결정 관련 부분은 정말 많은 도움이 될 것 같습니다. connected sheet 기능 신기하네요..
try catch finally에서 finally에서 발생한 예외로 try에서 발생한 핵심예외가 없어지고 finally에서 발생한 부가예외로 변경되어서 예외가 리턴되는데요, try with resources 와 같이 핵심예외를 반환하면서 부가예외를 반환하는 방법은 따로 없는건가요?
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? ( 예 /아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? ( 예 /아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? ( 예 /아니오) [질문 내용] 자바 중급까지 듣고 스프링 입문 강의를 한번 들어봤는데 자바 중급 강의 까지는 이해도 잘 되고 좋았습니다. 그런데 이번 스프링 입문 강의 들으니 이해가 쉽지가 않네요 스프링 기본 강의로 넘어가면 자바 강의했던거 처럼 차근차근 가르쳐 주시는건가요? 아니면 여기서 이해 안되는 것들 다 알고 넘어가야 하나요?
public void updateUserName(Long userId, String newName) { User user = userRepository.findById(userId) .orElseThrow(() -> new CustomException("User not found with id: " + userId)); user.setName(newName); userRepository.save(user); } 여기서 findById로 인해 영속성 컨텍스트에 user가 올라가있어서 setName만 해줘도 더티체킹으로 인해서 저장되는 걸로 알고있습니다. 그러면 userRepository.save (user);를 따로 해줘야하나요?
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? ( 예 /아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? ( 예 /아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? ( 예 /아니오) [질문 내용] 여기에 질문 내용을 윈도우에서 실행했는데 이런식으로 나옵니다. ㅠㅠ 왜이런건가요?
Optioanl<Member> findById(Long id)와 @Query("select m from Member m where m.id = :id) Optional<Member> findByid(@Param("id") Long id) 가장 위의 코드는 spring data jpa를 이용해서 작성했기 때문에 em.flush()가 호출될 때 바로 일어나지않고 아래의 코드는 jpql로 작성했기 때문에 로직 호출 시에 바로 em.flush()가 발생하는 거 맞나요?
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 말 그대로 입니다. 영한님은 하루에 자바 강의를 찍을 때 평균적으로 하루에 시간을 얼마나 투자하시나요? ('어레이 리스트 에서 링크드리스트로 변환 할때 이런 것은 얼마나 생각을 해야지 나오지?(11:30~40)'라는 생각이 들어서 질문을 합니다.)
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? 예 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예 3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예 [질문 내용] fetchOne()은 결과 값이 없을 경우 null을 반환 하는데 왜 Optional<Member>로 반환 하지 않나요? fetchOne()은 member가 없을 경우 null 반환 합니다. 결과 값을 Optional.ofnullable()로 감싸서 반환 후 서비스 계층에서 null 이면 안될 경우 RutTimeException을 상속한 커스텀 Exception을 발생 시키고 advice에서 처리 하는게 맞나요?
안녕하세요 강의 잘 보고 있습니다. 파사드 패턴에 대해 질문이 있습니다. 얄코님이 올려주신 예시에선 다음과 같이 두 상황이 나온다고 이해했습니다. 다양한 서브 시스템을 파사드 클래스로 조합해 하나의 새로운 로직을 만듦. 여러 서브 시스템을 생성시키고 세부 로직을 파사드 클래스에 위임. 1번의 경우는 일종의 추상화로 이해하였으며, 2번의 경우 서브 시스템에 대한 의존성 주입을 파사드 클래스에 위임 및 세부적인 로직을 캡슐화하는데 의의를 두었다고 이해했습니다. 만약 의존성 주입은 자동으로 이루어지는 3-layer 아키텍쳐에서 파사드 패턴을 도입할 경우 이미 service 레이어에서 내부 로직에 대해 캡슐화가 되었으므로 다양한 서브시스템을 파사드 클래스로 조합하는 것(ex. 1번)에만 집중하면 될까요?
이전에 공부할 때 엔티티매니저에서 트랜잭션을 가져야 로직이 수행된다는 것을 알고 있었습니다. 그런데 이전에 JPA활용2편에 @Transactional을 따로 붙혀주지 않았는데 로직이 실행된걸로 기억하는데 @Service나 @Repository에 의해서 자동으로 트랜잭션이 걸리는 건가요?
안녕하세요. 열혈 수강자 이지수입니다 : ) 문제가 하나씩 풀리니 넘 재밌고 뿌듯합니다 ㅎㅎ 17번 문제에서 풀어준 포켓몬 수를 B 전체 포켓몬 수를 A로 두었는데요 COUNT IF B/A 이런식으로 축약해서 연산할 수는 없나요? 감사합니다. SELECT trainer_id, COUNTIF(status="Released") as B , #풀어준 포켓몬의 수 COUNT(pokemon_id) as A, #전체 포켓몬의 수 COUNTIF(status="Released")/COUNT(pokemon_id) as released_ratio FROM basic.trainer_pokemon GROUP BY trainer_id HAVING released_ratio >=0.2
자바, jvm, jdk 그리고 jre 강의에서 kotlinc 명령어로 .kt 파일을 컴파일하시는걸 봤는데요, 코틀린 컴파일러는 jdk에 포함되어 있는 것이 아니고 따로 설치해야 하는게 맞을까요? jre는 java의 실행환경을 제공하고, jdk는 개발 도구 키트를 제공해서 java를 컴파일하는 javac 등의 명령어? 툴?은 jdk에 포함되어 있다고 강의 교안에 적혀있는데 그럼 코틀린의 컴파일러는 추가적으로 설치가 필요한지, 아니면 뭔가 방법이 있는지 궁금합니다. 우선 컴파일하면 바이트 코드가 되기 때문에 런타임에 필요한 라이브러리만 포함하여 패키징하면 jre에서 실행할 수 있다는 것은 이해했습니다!
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 직접 구현하는 배열 리스트1- 시작의 MyArrayListV1 구현문제를 예외 처리하고 싶은데 아무리 해도 안돼는데 해결책좀 try { list.add("f"); System.out.println(list); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("배열의 범위를 벗어났습니다"); System.out.println("예외 발생 위치: " + e.getMessage()); e.printStackTrace(System.out); } 그리고 아래 MyArrayListV1에 이렇게 작성하면 되긴 돼는데 저는 유지보수를 위해서 try~catch를 하고 싶거든요. 그래서이렇게 질문 남겨요 public void add(Object e) { if (size >= elementData.length) { // 배열이 가득 찼는지 체크 throw new ArrayIndexOutOfBoundsException("용량을 초과했습니다. 현재 크기: " + size + ", 최대 용량: " + elementData.length); } elementData[size] = e; size++; }
public class OrderApiController { @GetMapping("/api/v1/orders") public List<Order> orderV1() { List<Order> all = orderRepository.findAllByString(new OrderSearch()); for (Order order : all) { order.getMember().getName(); order.getDelivery().getAddress(); List<OrderItem> orderItems = order.getOrderItems(); orderItems.forEach(o -> o.getItem().getName()); } return all; } } public class OrderRepository{ public List<Order> findAllByString(OrderSearch orderSearch) { String jpql = "select o from Order o join o.member m"; boolean isFirstCondition = true; //주문 상태 검색 if (orderSearch.getOrderStatus() != null) { if (isFirstCondition) { jpql += " where"; isFirstCondition = false; } else { jpql += " and"; } jpql += " o.status = :status"; } ... 트랜잭션을 따로 붙히지 않았는데 상관없는건가요? List<Order> all = orderRepository.findAllByString(new OrderSearch()); 이 때 연관관계매핑되어 있는 객체들은 지연로딩에 의해 프록시로 존재하고 for (Order order : all) { order.getMember().getName(); order.getDelivery().getAddress(); List<OrderItem> orderItems = order.getOrderItems(); orderItems.forEach(o -> o.getItem().getName()); } 이 때 강제호출 되어서 쿼리가 나가는 게 맞나요? orderRepository.findAllByString에서 repository에서 작업이 마쳐서 트랜잭션이 커밋되어서 영속성 컨텍스트에는 아무값이 없을 거 같은데 컨트롤러에서 리턴하기 전까지 하나의 트랜잭션 안에서 작동이 이뤄지는 건가요? 트랜잭션이 언제 시작하고 언제 종료되는 지 궁금합니다
목차 과제별 작성 쿼리 쿼리 작성 후 느낀점 과제별 작성 쿼리 (1) ARRAY·STRUCT ## 연습문제 1번 -- select -- title, -- genre -- from advanced.array_exercises -- cross join unnest(genres) as genre ## 연습문제 2번 -- select -- title, -- actor.actor, -- actor.character, -- from advanced.array_exercises -- cross join unnest(actors) as actor ## 연습문제 3번 -- select -- title, -- actor.actor, -- actor.character, -- genre -- from advanced.array_exercises -- cross join unnest (actors) as actor -- cross join unnest (genres) as genre ## 연습문제 4번 -- select -- user_id, -- event_date, -- event_name, -- user_pseudo_id, -- event_param.key, -- event_param.value, -- event_param.value.int_value -- from advanced.app_logs -- cross join unnest(event_params) as event_param -- where -- event_date = '2022-08-01' PIVOT ## 연습문제 1번 -- select -- order_date, -- sum(if(user_id=1, amount, 0)) as user_1, -- sum(if(user_id=1, amount, 0)) as user_2, -- sum(if(user_id=1, amount, 0)) as user_3, -- from advanced.orders -- group by -- order_date -- order by -- order_date asc ## 연습문제 2번 -- select -- user_id, -- sum(if(order_date='2023-05-01', amount, 0)) as 2023-05-01 , -- sum(if(order_date='2023-05-02', amount, 0)) as 2023-05-02 , -- sum(if(order_date='2023-05-03', amount, 0)) as 2023-05-03 , -- sum(if(order_date='2023-05-04', amount, 0)) as 2023-05-04 , -- sum(if(order_date='2023-05-05', amount, 0)) as 2023-05-05 -- from advanced.orders -- group by -- user_id ## 연습문제 3번 -- select -- user_id, -- max(if(order_date = '2023-05-01', 1, 0)) as 2023-05-01 , -- max(if(order_date = '2023-05-02', 1, 0)) as 2023-05-02 , -- max(if(order_date = '2023-05-03', 1, 0)) as 2023-05-03 , -- max(if(order_date = '2023-05-04', 1, 0)) as 2023-05-04 , -- max(if(order_date = '2023-05-05', 1, 0)) as 2023-05-05 -- from advanced.orders -- group by -- user_id ## 앱 로그 데이터 배열 PIVOT -- with base as ( -- select -- user_id, -- event_date, -- event_name, -- user_pseudo_id, -- event_param.key as key, -- event_param.value.string_value as string_value, -- event_param.value.int_value as int_value -- from advanced.app_logs -- cross join unnest(event_params) as event_param -- where -- event_date = '2022-08-01' and -- event_name = 'click_cart' -- ) -- select -- user_id, -- event_date, -- event_name, -- user_pseudo_id, -- max(if(key = 'firebase_screen', string_value, null)) as firebase_screen, -- max(if(key = 'food_id', int_value, null)) as food_id, -- max(if(key = 'session_id', string_value, null)) as session_id, -- from base -- group by all 퍼널 ## step1 : 퍼널정의, 유저 집계 방식 정하기 ## step2 : 데이터 확인 -- select -- *, -- event_param.key, -- event_param.value.string_value, -- event_param.value.int_value, -- from advanced.app_logs -- cross join unnest(event_params) as event_param -- where -- event_date between '2022-08-01' and '2022-08-18' -- limit 100 ## step3 : pivot 해서 concat 하기 좋은 형태로 만들기 with base as ( select *, max(if(event_param.key = 'firebase_screen', event_param.value.string_value, null)) as firebase_screen, max(if(event_param.key = 'food_id', event_param.value.int_value, null)) as food_id, max(if(event_param.key = 'session_id', event_param.value.string_value, null)) as session_id from advanced.app_logs cross join unnest(event_params) as event_param where event_date between '2022-08-01' and '2022-08-18' group by all ) ## step4 : concat해서 퍼널 컬럼 정의하기 , base2 as ( select *, concat(event_name, '-', firebase_screen) as event_name_with_screen from base ) ## step5 : 퍼널별 유저수 쿼리 작성 I step_number, cnt 컬럼 생성 -- select -- event_name_with_screen, -- case -- when(event_name_with_screen = 'screen_view-welcome') then 1 -- when(event_name_with_screen = 'screen_view-home') then 2 -- when(event_name_with_screen = 'screen_view-food_category') then 3 -- when(event_name_with_screen = 'screen_view-restaurant') then 4 -- when(event_name_with_screen = 'screen_view-cart') then 5 -- when(event_name_with_screen = 'click_payment-cart') then 6 -- else null -- end as step_number, -- count(distinct user_pseudo_id) as cnt -- from base2 -- group by -- 1 -- having -- step_number is not null ## step6 : 일자별 퍼널 쿼리 작성 select event_date, event_name_with_screen, case when(event_name_with_screen = 'screen_view-welcome') then 1 when(event_name_with_screen = 'screen_view-home') then 2 when(event_name_with_screen = 'screen_view-food_category') then 3 when(event_name_with_screen = 'screen_view-restaurant') then 4 when(event_name_with_screen = 'screen_view-cart') then 5 when(event_name_with_screen = 'click_payment-cart') then 6 else null end as step_number, count(distinct user_pseudo_id) as cnt from base2 group by all having step_number is not null order by event_date 쿼리 작성 후 느낀점 (1) ARRAY·STRUCT I 쿼리를 풀고난 이후 느낀점 1. '~별'이라고 해서 무조건 GROUP BY는 아니다 ARRAY 또는 STRUCT 같은 구조체 타입 컬럼을 만나기 전에는 '~별'이라는 텍스트가 있을 때 GROUP BY를 떠올렸습니다. 그러나 구조체 타입 컬럼을 다루고난 이후, 생각이 달라졌습니다. 비구조체 컬럼을 구조체 컬럼과 함께 출력할 때는 '~별'을 GROUP BY로 출력할 수 없다는 걸 알았습니다. 2. CROSS JOIN을 2개 이상 사용할 때는 CROSS JOIN을 2개 이상 입력할 때는 쉼표를 사용하지 않는다는 걸 알았습니다. 다른 구간에서 요소를 2개 이상 입력할 때는 쉼표를 사용해서 요소를 분리 했었는데, CROSS JOIN은 아니였습니다. CROSS JOIN을 2개 이상 사용할 때 쉼표를 쓰면 다음과 같은 오류가 발생하는 걸 알았습니다 : --> Syntax error: Unexpected keyword CROSS at [24:1] (2) PIVOT I 쿼리를 풀고난 이후 느낀점 1. 날짜 데이터와 다르게 일반 숫자데이터는 따옴표를 쓰지 않는다 EX) if(user_id=1, amount, 0) --> O if(user_id='1', amount, 0) --> X + if(user_id='2024-10-22', amount, 0) --> O 2. 컬럼명에 대시(또는 한글)를 사용할 때는 따옴표가 아닌 백틱으로 컬럼명을 감싼다 대시를 컬럼명으로 할 때는 백틱을 사용. as 2023-05-01 --> O as '2023-05-01' --> X + as 빠짝스터디 --> O as '빠짝스터디' --> X 3. 일반 테이블을 피봇테이블로 만들 때 고려해 봐야 할 2가지 1. 행에 2개 이상 중복이 있는지 확인한다. 2. 2개 이상 중복이 있지만, 피봇테이블로 변경 했을 때 효율적인지 생각해 본다. 위 2개 조건을 모두 충족하는 컬럼을 피봇테이블 만들 때 기준 컬럼으로 삼음. EX) *'앱 로그 데이터 배열 PIVOT 하기' 테이블 참고 student 열 중복 있음 (A, A, A, B, B, B) 기준 열로 삼으면 → 각 학생의 모든 과목 점수를 한눈에 볼 수 있어서 효율적! subject 열 중복 있음 (수학, 영어, 과학이 2번씩) 기준 열로 삼으면 → 과목별로 학생들의 점수를 볼 수는 있지만, 한 학생의 전체 성적을 보기는 어려움 score 열 중복이 없음. 기준 열로 삼으면 → 같은 점수끼리 모이겠지만, 효율이 떨어짐. (3) 퍼널 I 쿼리를 풀고난 이후 느낀점 1. concat 할 때 따옴표 사용 주의 처음 concat 할 때 다음과 같이 입력했습니다. concat(event_name, '-', 'firebase_screen') 'firebase_screen'라고 작성했었는데, 이렇게 하니깐 firebase_screen 내에 있는 데이터랑 concat 된 게 아니라 'firebase_screen'라는 문자 자체랑 concat이 되어서 혼란스러웠습니다. concat 내에서는 따옴표를 쓰지 않고, 컬럼끼리 연결해야겠다는 걸 알게 됐습니다.