JOIN, UNION, WITH, Subquery, 조건절
전환율 - 구매 수/클릭 수
두 개 이상의 테이블을 특정 key 기준으로 결합하는 것
테이블을 처음부터 합쳐놓으면 되는 것 아닌지?
INNER JOIN → 교집합
SELECT products.*, manager.name as manager_name
FROM products INNER JOIN managers
on products.category = managers.managing;
컬럼 값이 양쪽 테이블에서 일치하는 경우만 가져옴
데이터 정확성이 높음
대상 행 수를 줄이기 때문에 일반적으로 다른 JOIN들에 비해 빠름
SELECT clicks.*, 1 as ordered
FROM clicks INNER JOIN orders
ON clicks.user_name = orders.user_name
AND clicks.product_id = orders.product_id
AND clicks.date = orders.date;
ON에서도 AND를 이용해 조건을 여러개 달아줄 수 있음
SELECT clicks.*, 1 as ordered
FROM clicks INNER JOIN orders
ON clicks.user_name = orders.user_name
AND clicks.product_id = orders.product_id
AND clicks.date = orders.date
INNER JOIN products on clicks.product_id = products.product_id;
LEFT JOIN
왼쪽 테이블의 모든 행과 오른쪽 테이블에서 일치하는 행을 가져옴
만약 왼쪽 테이블 데이터와 일치하는 데이터가 오른쪽 테이블에 없을 땐 null로 채움
좌측 테이블의 데이터와 일치하는 데이터가 우측에 여러개일 땐 결과 테이블이 좌측 테이블 행 수보다 늘어날 수 있다.
JOIN 결과는 테이블이므로 where절로 필터링할 수 있음
- 쿼리의 결과물은 임시 테이블로 저장이 되었다가 세션이 끝나면 삭제되는 방식으로 작동
- JOIN의 결과에 필터링이 적용됨
- 쿼리를 효율적으로 작성하기 위해선 필터링 후 join을 거는 게 좋다.
SELECT clicks.*, odr_index, name, category
FROM clicks LEFT JOIN orders
ON clicks.user_name = orders.user_name
AND clicks.product_id = order.product_id
AND clicks.date = order.date
INNER JOIN products ON clicks.product_id = products.product_id
WHERE products.category = '디지털'
AND odr_index is NULL;
만약 교집합 부분은 제외하고싶다면, where 절에 right.key is null
이라는 조건을 주면 된다.
RIGHT JOIN
FULL OUTER JOIN (mysql은 지원하지 않음)
CROSS JOIN
두 테이블간 가능한 모든 조합
3행 테이블 CROSS JOIN 3행 테이블 → 9행 테이블
cross join 결과의 사이즈가 매우 커지기 때문에 사용에 유의해야함
클릭과 구매 날짜간 차이, 연관 클릭 추천(상품간 유사도)
SELECT products.*,
products_B.product_id as B_product_id,
products_B.category as B_category,
products_B.name as B_name,
products_B.price as B_price
FROM products CROSS JOIN products_B
유사도 계산?
Alias
SELF JOIN
JOIN을 자기 자신과!
alias를 무조건 사용해야함
직원 테이블에서 대체자를 가져올 때!
WITH odr_cnt as (
SELECT c.customer_id, count(distinct order_id) as odr_cnt, sum(amount) as total_purchase
FROM Customers c INNER JOIN Orders o on c.customer_id = o.customer_id
GROUP BY 1
ORDER BY 2 DESC
),
ship_cnt as (
SELECT
FROM Customers c INNER JOIN Shippings s on c.customer_id = s.customer
WHERE status = 'Pending'
GROUP BY 1
ORDER BY 2 DESC
)
SELECT oc.customer_id, odr_cnt, total_purchase, COALESCE(ship_cnt, 0) as shipping_cnt
FROM odr_cnt oc LEFT JOIN ship_cnt sc on oc.customer_id = sc.customer_id
다른 쿼리 내부에 포함된 쿼리로 주로 더 큰 쿼리의 일부로 사용됨
잘못 사용할 경우 많은 리소스를 필요로한다.
SELECT 구문에서
FROM 절에
WEHRE 절에
EXISTS와 함께
동일한 결과물이더라고 쿼리는 매우 다양할 수 있다!
상황에 맞게 가장 효율적인 쿼리를 찾는게 좋다.
string은 시간 관련 연산이 어려움
DATE는 시간을 저장하지 않아 저장 공간을 덜 차지함
DATETIME는 날짜와 시간을 모두 저장. 시간 간 연산 가능. DATE에 비해 더 많은 정보를 저장하므로 저장공간을 더 차지
TIMESTAMP은 DATETIME과 유사하며, 뒤쪽에 타임존이 붙을 수 있음. DATETIME보다 제한적(기간이). 시간간 연산이 가능
NOW()
SYSDATE()
YEAR(), MONTH(), DAY(), HOUR(), MINUTE(), SECOND()
WEEKDAY() → 숫자, MONTHNAME(), DAYNAME()→문자
DATEDIFF(NOW(), STR_TO_DATE(date, ‘%Y%m%d))
DATE_FORMAT(date, '%Y-%m-%d %T')
클릭 후 다음 클릭까지 몇초가 걸리는지, 구매까지는 몇초가 걸리는지
고객들이 이탈하는 구간, 페이지 전환이 오래 걸리는 경우 등을 분석하기 위해 이를 사용한다.
UNION에선 원래 컬럼의 타입이 동일해야 결합되지만 편의를 위해 타입 변환을 해준다.
하지만 데이터의 타입을 제대로 알지 못하고 결합하는 건 좋지 않음.
타입 변환 후 UNION 할 것!
IF
SELECT IF(price >= 10000, '고가','저가') AS '가격 구분'
IFNULL()
SELECT IFNULL(date, 20231104)
SELECT user_name, price_class, count(1) as cnt
FROM clicks c
INNER JOIN (SELECT product_id, IF(price>5000, '고가','저가') as price_class
FROM products) p
ON c.product_id = p.product_id
GROUP BY 1,2
ORDER BY 1,2
고가의 상품 클릭 수가 많은 고객에게는 할인률이 높은 쿠폰을, 저가 상품에 주로 관심이 있는 고객에게는 최소 사용 가능 금액이 낮은 쿠폰을 지급해 CRM 마케팅을 해볼 수 있음
CASE WHEN
SELECT price,
CASE WHEN PRICE>10000 THEN '고가'
WHEN (price <= 10000 AND price>4000) THEN '중가'
ELSE '저가'
END AS price_class
FROM products_B
CASE WHEN 조건 then 값
~
ELSE 값
END as 컬럼명
SELECT date,
CASE date
WHEN '20231014' THEN '첫째날'
WHEN '20231015' THEN '둘째날'
ELSE '기타'
END AS date_group
FROM clicks
where 절에 case를 이용하면 조건을 바꾸기 쉬움
SELECT *
FROM products
ORDER BY (
CASE
WHEN category = '디지털' THEN 1
WHEN category = '주방용품' THEN 2
WHEN category = '%보호대%' THEN 3
ELSE 4
END
)
SELECT age,
RANK() OVER (ORDER BY age) AS asc_rank, --나이 순서로 랭크를 매김
DENSE_RANK() OVER (ORDER BY age) AS dense_rank --빽빽하게 랭크를 매김
PERCENT_RANK() OVER (ORDER BY age) AS percent_rank --퍼센트로 랭크를 매김 (나보다 작은 갯수/전체 갯수)
PERCENT_RANK() OVER (PARTITION BY date ORFER BY price) --날짜가 같은 데이터끼리 가격별로 랭크를 매김
SELECT clk_index, user_name, date,
LEAD(clk_index,1) OVER (partition by user_name, date order by clk_index) next_click
FROM clicks
ORDER BY 3,1
파티션 내에서 N번째 다음으로 오는 값을 찾는 함수 LEAD에 두번째 인자 1은 바로 다음 값을 의미함. 즉 여기선 유저 이름과 날짜로 파티셔닝(?) 하고 데이터의 clk_index의 다음 값을 반환… 어렵다.