SQL 14일차
LAG, LEAD 함수
특정 집합 내에서 결과 건수의 변화 없이 해당 집합안에서 특정 컬럼의 인전 행의 값 혹은 다음 행의 값을 구한다,
LAG 함수 실습 - 이전 행의 값을 찾는다.
SELECT p.proudct_name, pg.group_name, p.price
, LAG (p.price, 1) OVER (
PARTITION BY pg.group_name ORDER BY p.price)
AS PREV_PRICE
, p.price - LAG(p.price, 1) OVER (
PARTITION BY pg.group_name ORDER BY p.price)
AS CUR_PERV_DIFF
FROM product p
INNER JOIN product_group pg ON (p.group_id = pg.group_id)
LAG(p.price, 1)에서 1은 1칸 위의 값을 의미한다.
그룹별로 순서대로 두었을때 자신의 위의 한칸 앞에 있는 값을 구한다.
이를 이용해 이전 행과의 차를 구할 수 있다.
그럼 2행 위의 값을 출력할 수도 있을까?
SELECT p.proudct_name, pg.group_name, p.price
, LAG (p.price, 2) OVER (
PARTITION BY pg.group_name ORDER BY p.price)
AS PREV_PRICE
, p.price - LAG(p.price, 2) OVER (
PARTITION BY pg.group_name ORDER BY p.price)
AS CUR_PERV_DIFF
FROM product p
INNER JOIN product_group pg ON (p.group_id = pg.group_id)
다음과 같이 2개의 위에 것을 찾아서 출력하는 것이 가능하다.
LAG와 반대로 다음행의 값을 찾는것이 LEAD이다.
LEAD함수실습 - 다음행의 값을 찾는다.
SELECT p.proudct_name, pg.group_name, p.price
, LEAD (p.price, 1) OVER (
PARTITION BY pg.group_name ORDER BY p.price)
AS NEXT_PRICE
, p.price - LEAD(p.price, 1) OVER (
PARTITION BY pg.group_name ORDER BY p.price)
AS CUR_NEXT_DIFF
FROM product p
INNER JOIN product_group pg ON (p.group_id = pg.group_id)
다음 값을 찾아서 추출할 수 있다.
이것 또한 범위를 정할 수 있다.
2번째 다음 값이랑 비교해보자
SELECT p.proudct_name, pg.group_name, p.price
, LEAD (p.price, 2) OVER (
PARTITION BY pg.group_name ORDER BY p.price)
AS NEXT_PRICE
, p.price - LEAD(p.price, 2) OVER (
PARTITION BY pg.group_name ORDER BY p.price)
AS CUR_NEXT_DIFF
FROM product p
INNER JOIN product_group pg ON (p.group_id = pg.group_id)
실습 문제 - 1
RENTAL 테이블을 이용하여 연, 연월, 연월일, 전체 각각의 기준으로 RENTAL_ID기준 렌탈이 일어난 횟수를 출력하라. (전체 데이터 기준으로 모든 행을 출력)
SELECT EXTRACT (YEAR FROM r.rental_date) AS Y
, EXTRACT (MONTH FROM r.rental_date) AS M
, EXTRACT (DAY FROM r.rental_date) AS D
, count(*)
FROM rental r
GROUP BY ROLLUP (Y, M, D)
ORDER BY Y, M, D
다음과 같이 모든 연, 연월, 연월일, 전체를 출력하였다.
수업에서는 TO_CHAR를 이용하여 문제를 해결하였다.
실습 문제 - 2
RENTAL과 CUSTOMER 테이블을 이용하여 현재까지 가장 많이 RENTAL을 한 고객의 고객ID, 렌탈 순위, 누적렌탈횟수, 이름을 출력하라.
혼자서 직접 해보려고 하였으나 계속 실패하였다. 시간이 될때마다 이 문제를 다시 풀어보는 것도 좋을 것 같다.
SELECT r.customer_id
, ROW_NUMBER() OVER (ORDER BY COUNT(r.rental_id) DESC) AS rental_rank
, count(*) AS rental_count
, max(c.first_name) AS first_name
, max(c.last_name) AS last_name
FROM rental r
INNER JOIN customer c
ON r.customer_id = c.customer_id
GROUP BY r.customer_id
ORDER BY rental_rank
LIMIT 1;
max(c.first_name)을 하는 이유는 그룹 바이로 묶었기 떄문에 이 값이 특정되지 않기때문이다. 그렇지만 customer_id에 이름이 종속되기 때문에 값이 하나인 것을 알고 있다. 그렇기 때문에 여러 값중에 하나 특정한 값을 정해줘야 해결이 된다.
MAX()를 이용하여 가장 큰 값을 호출하지만 다 같은 값이기에 한 개의 값을 선택하는 것이다.
생각대로 SQL - 8
문제1번) dvd 대여를 제일 많이한 고객 이름은? (analytic funtion 활용)
실습 문제-2와 유사한 문제이다.
분석 함수 미사용
SELECT MAX(c.first_name), MAX(c.last_name), r.customer_id, count(*)
FROM rental r
INNER JOIN customer c ON r.customer_id = c.customer_id
GROUP BY r.customer_id
ORDER BY count(*) DESC
LIMIT 1;
분석 함수 사용
SELECT MAX(c.first_name)
, MAX(c.last_name)
, r.customer_id
, ROW_NUMBER () OVER (ORDER BY count(*)DESC) AS RENTAL_RANK
FROM rental r
INNER JOIN customer c ON r.customer_id = c.customer_id
GROUP BY r.customer_id
ORDER BY RENTAL_RANK
LIMIT 1;
문제2번) 매출을 가장 많이 올린 dvd 고객 이름은? (analytic funtion 활용)
분석 함수 미사용
SELECT c.first_name, c.last_name, sum(p.amount)
FROM customer c
INNER JOIN rental r ON c.customer_id = r.customer_id
INNER JOIN payment p ON r.rental_id = p.rental_id
GROUP BY c.customer_id
ORDER BY sum(p.amount) DESC
LIMIT 1;
분석 함수 사용
SELECT c.first_name
, c.last_name
, sum(p.amount)
, ROW_NUMBER () OVER (ORDER BY sum(p.amount)DESC) AS rank
FROM customer c
INNER JOIN rental r ON c.customer_id = r.customer_id
INNER JOIN payment p ON r.rental_id = p.rental_id
GROUP BY c.customer_id
LIMIT 1;
문제3번) dvd 대여가 가장 적은 도시는? (anlytic funtion)
SELECT c2.city, ROW_NUMBER() OVER (ORDER BY count(*)), count(*)
FROM rental r
INNER JOIN customer c ON r.customer_id = c.customer_id
INNER JOIN address a ON c.address_id = a.address_id
INNER JOIN city c2 ON a.city_id = c2.city_id
GROUP BY c2.city
LIMIT 1;
문제4번) 매출이 가장 안나오는 도시는? (anlytic funtion)
SELECT c.city, ROW_NUMBER() OVER (ORDER BY SUM(p.amount)), sum(p.amount)
FROM city c
INNER JOIN address a ON c.city_id = a.city_id
INNER JOIN customer c2 ON a.address_id = c2.address_id
INNER JOIN rental r ON c2.customer_id = r.customer_id
INNER JOIN payment p ON r.rental_id = p.rental_id
GROUP BY c.city
LIMIT 1;
문제5번) 월별 매출액을 구하고 이전 월보다 매출액이 줄어든 월을 구하세요. (일자는 payment_date 기준)
SELECT m, cur, prev
FROM (
SELECT EXTRACT (MONTH FROM p.payment_date) AS m
, sum(p.amount) AS cur
, LAG(sum(p.amount), 1) OVER ( ORDER BY sum(p.amount)) AS prev
FROM payment p
GROUP BY EXTRACT (MONTH FROM p.payment_date)
) A
WHERE A.cur < A.prev
문제6번) 도시별 dvd 대여 매출 순위를 구하세요.
SELECT c.city, ROW_NUMBER() OVER (ORDER BY SUM(p.amount) desc), sum(p.amount)
FROM city c
INNER JOIN address a ON c.city_id = a.city_id
INNER JOIN customer c2 ON a.address_id = c2.address_id
INNER JOIN rental r ON c2.customer_id = r.customer_id
INNER JOIN payment p ON r.rental_id = p.rental_id
GROUP BY c.city ;
문제7번) 대여점별 매출 순위를 구하세요.
SELECT s.store_id , ROW_NUMBER() OVER (ORDER BY sum(p.amount) DESC) , sum(p.amount)
FROM staff s
INNER JOIN rental r ON s.staff_id = r.staff_id
INNER JOIN payment p ON r.rental_id = p.rental_id
GROUP BY s.store_id
문제8번) 나라별로 가장 대여를 많이한 고객 TOP 5를 구하세요.
각 나라별 순위를 매기는 것은 가능을 하였으나 TOP 5만을 구하는 것은 하지 못하였다.
SELECT max(c3.country) AS country
, ROW_NUMBER() over(PARTITION BY max(c3.country) ORDER BY sum(p.amount) DESC) AS RPC
, c.first_name, c.last_name
, sum(p.amount)
FROM customer c
INNER JOIN address a ON c.address_id = a.address_id
INNER JOIN city c2 ON a.city_id = c2.city_id
INNER JOIN country c3 ON c2.country_id = c3.country_id
INNER JOIN rental r ON c.customer_id = r.customer_id
INNER JOIN payment p ON r.rental_id = p.rental_id
GROUP BY c.customer_id
풀이에서는 서브쿼리를 이용하여 문제를 해결하였다.
다음은 풀이를 보고 다시 적은 코드이다,
SELECT country, RPC, first_name, last_name
FROM (
SELECT max(c3.country) AS country
, ROW_NUMBER() over(PARTITION BY max(c3.country) ORDER BY sum(p.amount) DESC) AS RPC
, c.first_name, c.last_name
, sum(p.amount)
FROM customer c
INNER JOIN address a ON c.address_id = a.address_id
INNER JOIN city c2 ON a.city_id = c2.city_id
INNER JOIN country c3 ON c2.country_id = c3.country_id
INNER JOIN rental r ON c.customer_id = r.customer_id
INNER JOIN payment p ON r.rental_id = p.rental_id
GROUP BY c.customer_id
) db
WHERE RPC <= 5;
문제9번) 영화 카테고리 (Category) 별로 대여가 가장 많이 된 영화 TOP 5를 구하세요
SELECT category_name, RPC, title, cn
FROM (
SELECT max(c."name") AS category_name
, ROW_NUMBER() OVER(PARTITION BY max(c."name") ORDER BY count(*) DESC) AS RPC
, f.title
, count(*) AS cn
FROM film f
INNER JOIN inventory i ON f.film_id = i.film_id
INNER JOIN rental r ON i.inventory_id = r.inventory_id
INNER JOIN film_category fc ON f.film_id = fc.film_id
INNER JOIN category c ON fc.category_id = c.category_id
GROUP BY f.film_id
) db
WHERE RPC <= 5;
문제10번) 매출이 가장 많은 영화 카테고리와 매출이 가장 작은 영화 카테고리를 구하세요. (first_value, last_value)
SELECT DISTINCT FIRST_VALUE (c."name") over(ORDER BY count(*) DESC)
, LAST_VALUE (c."name") OVER (ORDER BY count(*) DESC
RANGE BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING)
FROM film f
INNER JOIN inventory i ON f.film_id = i.film_id
INNER JOIN rental r ON i.inventory_id = r.inventory_id
INNER JOIN film_category fc ON f.film_id = fc.film_id
INNER JOIN category c ON fc.category_id = c.category_id
GROUP BY c."name"
근데 기본적으로 DESC와 ASC를 이용해 first_value를 호출할 것 같다. RANGE를 정해주는 것이 익숙하지 않은 것 같다.
Author And Source
이 문제에 관하여(SQL 14일차), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@fe26ming/SQL-14일차저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)