# PostgreSQL database hands-on session 2 (solution) ## Initialize dvdrental demo database 1. See previous demo in [Session 1](https://hackmd.io/CHBzTAb1QxaYrg2WctNasA?view). 2. Main tutorial page for [PostgreSQL](https://www.postgresqltutorial.com/postgresql-getting-started/). ### Exercises and solutions 1. **Grouping Data** [[Tutorial link]](https://www.postgresqltutorial.com/postgresql-group-by/) a. Find the top (or first) 10 customers’ IDs who spend more money than the average spendings of all customers. Hint: "GROUP BY" and "AVG". ```SQL SELECT customer_id, AVG(amount) FROM payment GROUP BY customer_id Having AVG(amount)>4.2006056453822965 -- (SELECT AVG(amount) FROM payment) ORDER BY AVG(amount) LIMIT 10; -- Top 10 by using DESC ``` b. Challenge: Find customers’ full names who spend more money than the average spendings of all customers. (Hint: "GROUP BY" and "JOIN") ```SQL SELECT c.first_name || ' ' || c.last_name AS full_name, AVG (p.amount) AS amount FROM payment AS p INNER JOIN customer AS c on p.customer_id = c.customer_id GROUP BY full_name Having AVG(p.amount)>4.2006056453822965 ORDER BY AVG (p.amount); ``` 2. **Set Operations** [[Tutorial link]](https://www.postgresqltutorial.com/postgresql-union/) a. Find customers’ IDs whose total amount (SUM(amount) for each customer_id in payment table) have been spent more than 200 or less than 40. (Hint: "UNION") ```SQL SELECT customer_id, SUM (amount) FROM payment GROUP BY customer_id HAVING SUM (amount) < 40 UNION ALL SELECT customer_id, SUM (amount) FROM payment GROUP BY customer_id HAVING SUM (amount) > 200 ; ``` b. Challenges: Find customers’ IDs who have spent more than 90 total amount (SUM(amount) for each customer_id in payment table) but less than 20-time payments (COUNT(amount) for each customer_id in payment table). (Hint: "INTERSECTION") ```SQL SELECT customer_id FROM payment GROUP BY customer_id HAVING COUNT (amount) < 20 INTERSECT SELECT customer_id FROM payment GROUP BY customer_id HAVING SUM (amount) > 90 ; ``` 3. **Grouping sets, Cube, and Rollup** [[Tutorial link]](https://www.postgresqltutorial.com/postgresql-grouping-sets/) a. Find customers’ last names who have more than 20-time payments. (Hint: use subquery and “IN”) ```SQL SELECT last_name FROM customer WHERE customer_id IN ( SELECT customer_id FROM payment GROUP BY customer_id HAVING COUNT (amount) > 20) limit 10; ``` b. Challenge: Find (optional: top/first 10) customers’ IDs whose total payment amounts are more than each customer’s average total amount. (Hint: use subquery and “AVG”) ```SQL SELECT customer_id FROM payment GROUP BY customer_id HAVING SUM(amount) > (SELECT AVG(total_amount) FROM (SELECT SUM(amount) total_amount FROM payment group by customer_id) AS just_a_name) LIMIT 10; ``` 4. **Subquery** [[Tutorial link]](https://www.postgresqltutorial.com/postgresql-subquery/) a. Challenge: The following finds the films whose lengths are greater than or equal to the maximum length of any film category: ```SQL SELECT title, length FROM film WHERE length >= ANY( SELECT MAX(length) FROM film INNER JOIN film_category USING(film_id) GROUP BY category_id) LIMIT 10; ``` Rewrite the above query without “ANY” keyword to implement the exact same function. (Hint: use “MIN” instead of “MAX”.) ```SQL SELECT title, length FROM film WHERE length >= (SELECT MIN(max_length) FROM ( SELECT MAX( length ) AS max_length FROM film INNER JOIN film_category USING(film_id) GROUP BY category_id ) AS foo) LIMIT 10; ``` 5. **Common Table Expressions** [[Tutorial link]](https://www.postgresqltutorial.com/postgresql-cte/) a. Challenge: Define a CTE that returns customers’ IDs and amounts ranking by the amount of each payment, then find the customers’ IDs and amounts whose ranking are one. (HINT: "WITH", "RANK() OVER") ```SQL WITH cte_payment AS ( SELECT customer_id, amount, RANK() OVER (PARTITION BY customer_id ORDER BY amount DESC) amount_rank FROM payment) SELECT * FROM cte_payment WHERE amount_rank = 1; ```