본문 바로가기
SQL

[SQL] SUBQUERIES and CTEs

by busybee-busylife 2025. 2. 10.
728x90

Independent Subquery

- 실행할 때마다 같은 값을 출력 

- 그 값을 기억해뒀다가 반복해서 출력함(계산은 1번만 실행) 

cf) Correlated Subquery: 실행 위치에 따라 출력값이 달라짐 

ex. 영화 전체의 평균: Independent Subquery 

      그 해(year)의 평균: Correlated Subquery 

-- 전체 영화들 중 평점이나 수익이 평균보다 높은 영화의 리스트 구하기 


SELECT title FROM movies
  WHERE rating > AVG(rating);
--- !!!에러에러!!!
  

SELECT title, rating, (SELECT AVG(rating) FROM movies) AS avg_rating FROM movies
  WHERE rating > (SELECT AVG(rating) FROM movies);
-- 성능 문제: 전체 DB를 2번 검색한다 
-- AVG(ratinig)을 구하기 위해 서브쿼리를 반복하고 있다 
-- (SELECT AVG(rating) FROM movies): 😀Independent Subqueries😀 
-- AVG(rating)의 값은 상수(독립적)

 

 

CTE: Common Table Expression 

Independent Subquery를 재사용 할 수 있도록 해준다 

ex. 위의 사례에서 AVG(rating)을 재사용하고 + 함께 반환하고 싶음 

WITH avg_rating_cte AS (SELECT AVG(rating) FROM movies) 

SELECT title, rating, (SELECT * FROM avg_rating_cte) AS avg_rating FROM movies
  WHERE rating > (SELECT * FROM avg_rating_cte);
-- CTE 선언 후 -> SELECT 구문
WITH movie_avg_per_year AS (SELECT AVG(inner_movies.rating) FROM movies AS inner_movies 
                            	WHERE inner_movies.release_date = main_movies.release_date) 
SELECT main_movies.title,
			 main_movies.director,
       main_movies.rating,
       main_movies.release_date,
       (SELECT * FROM movie_avg_per_year) AS year_average
       FROM movies AS main_movies 
       WHERE main_movies.release_date > 2020 AND main_movies.rating > (	SELECT AVG(inner_movies.rating) FROM movies AS inner_movies
       WHERE inner_movies.release_date = main_movies.release_date);

SQLite의 CTE: 아래에서 생성된 요소들을 참조할 수 있다

 

-- 전체 감독의 평균 revenue 보다 높은 revenue를 버는 감독 구하기 


SELECT director, SUM(revenue) AS career_revenue FROM movies 
  WHERE director IS NOT NULL AND revenue IS NOT NULL 
  GROUP BY director;
-- 우선 감독별 career revenue를 구하고(감독별 총 revenue의 합) 


WITH directors_revenues AS (SELECT director, SUM(revenue) AS career_revenue FROM movies 
  WHERE director IS NOT NULL AND revenue IS NOT NULL 
  GROUP BY director) 
SELECT director, SUM(revenue) AS total_revenue FROM movies
WHERE director IS NOT NULL AND revenue IS NOT NULL 
GROUP BY director 
HAVING total_revenue > (SELECT AVG(career_revenue) FROM directors_revenues);  
-- 위에서 만든 감독별 career revenue를 CTE로 넣어주고 
-- CTE에서 만든 career_revenue의 전체 평균을 구해서 total_revenue와 비교
CREATE INDEX inx_director ON movies (director);


WITH director_stats AS (SELECT director, AVG(rating) AS avg_rating, COUNT(*) AS total_movies,
MAX(rating) AS best_rating, MIN(rating) AS worst_rating, MAX(budget) AS highest_budget, MIN(budget) AS lowest_budget
FROM movies WHERE director IS NOT NULL AND budget IS NOT NULL AND rating IS NOT NULL GROUP BY director)
SELECT director, avg_rating, total_movies, best_rating, worst_rating, highest_budget, lowest_budget,
 (SELECT title FROM movies WHERE rating IS NOT NULL AND budget IS NOT NULL AND director = ds.director
ORDER BY rating DESC LIMIT 1) AS best_rated_movie,
 (SELECT title FROM movies WHERE rating IS NOT NULL AND budget IS NOT NULL AND director = ds.director 
ORDER BY rating ASC LIMIT 1 ) AS worst_rated_movie,
 (SELECT title FROM movies WHERE rating IS NOT NULL AND budget IS NOT NULL AND director = ds.director
ORDER BY budget DESC LIMIT 1 ) AS most_expensive_movie,
 (SELECT title FROM movies WHERE rating IS NOT NULL AND budget IS NOT NULL AND director = ds.director
ORDER BY budget ASC LIMIT 1 ) AS least_expensive_movie
FROM director_stats AS ds;

INDEX 없이 아래 코드를 실행하면 엄청난 시간이 걸린다.. 
INDEX에 대해서는 다음 글에서 

728x90

'SQL' 카테고리의 다른 글

[SQL] MySQL  (0) 2025.02.10
[SQLite] INDEX  (1) 2025.02.10
[SQL] DATA MANIPULATION LANGUAGE  (1) 2025.02.06
[SQL] DATA DEFINITION LANGUAGE  (0) 2025.02.04
[SQL] SQLITE  (2) 2025.02.03