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를 정해주는 것이 익숙하지 않은 것 같다.

좋은 웹페이지 즐겨찾기