I'm trying to get better at writing pivot queries by writing one against the Sakila Sample Database that shows the average amount spent per customer for each month. I've got the basic query down, but I'm not sure how to turn it into a pivot query.
Here's the SQL for that query:
SELECT
payment.amount AS amount,
customer.customer_id AS customer_id,
customer.last_name AS last_name,
customer.first_name AS first_name,
FORMAT ( rental.rental_date, 'MMM' ) _Month,
avg(amount) over ( PARTITION BY customer.customer_id ) avg_rental_amt
FROM
customer INNER JOIN
rental
ON customer.customer_id = rental.customer_id INNER JOIN
payment
ON payment.rental_id = rental.rental_id AND
payment.customer_id = customer.customer_id
And here is the above query with results in my Navicat database development and admin client:
Anyone know how to convert these results to a pivot query so that the columns from left to right are customer_id, last_name, first_name, Jan, Feb, ...Dec, avg_rental_amt, in either MySQL or SQL Server? I'd like the results to look something like this if possible:
customer_id last_name first_name Jan Feb...Dec avg_rental_amt
505 ABNEY RAFAEL 4.9 2.9 3.4 4.65
504 ADAM NATHANIEL 5.3 4.4 5.2 4.77
36 ADAMS KATHLEEN .9 .9 3.1 3.43
etc...
Thanks!
Rob
You can do conditional aggregation:
SELECT c.customer_id, c.last_name, c.first_name,
AVG(CASE WHEN MONTH(r.rental_date) = 1 THEN p.amount END) as avg_rental_january,
AVG(CASE WHEN MONTH(r.rental_date) = 2 THEN p.amount END) as avg_rental_feb,
...
AVG(CASE WHEN MONTH(r.rental_date) = 12 THEN p.amount END) as avg_rental_december,
AVG(p.amount) avg_rental
FROM customer c
INNER JOIN rental r ON c.customer_id = r.customer_id
INNER JOIN payment p ON p.rental_id = r.rental_id AND p.customer_id = c.customer_id
GROUP BY c.customer_id, c.last_name, c.first_name
This syntax should work in both SQL Server and MySQL.
Note that this averages together rentals of the same customer over different years - as in your original query. This may, or may not be what you want. If not, you would need to adapt the conditional expressions for your exact need.
You can dynamically manage returning results by applying conditional aggregation
in SQL Server ( Demo ) :
DECLARE @cols AS NVARCHAR(MAX), @query AS NVARCHAR(MAX);
WITH t AS
(
SELECT 1 AS n UNION ALL
SELECT n + 1 FROM t WHERE n + 1 <= 12
)
SELECT @cols = STRING_AGG(CONCAT('AVG(CASE WHEN MONTH(r.rental_date)=',n,
' THEN r.amount END) "',
FORMAT ( DATEADD( month , n , -1 ), 'MMM' ),
'"'), ',')
FROM t;
SET @query =
CONCAT(
'SELECT c.customer_id AS customer_id,' , @cols ,
' , AVG(r.amount) AS avg_rental_amt ',
' FROM customer c
JOIN rental r
ON c.customer_id = r.customer_id
JOIN payment p
ON p.rental_id = r.rental_id
AND p.customer_id = c.customer_id
GROUP BY c.customer_id, c.last_name, c.first_name');
EXEC sp_executesql @query;
in My SQL ( Demo ):
SET @query = NULL;
SET @cols = NULL;
SELECT GROUP_CONCAT(
CONCAT(
'AVG(CASE WHEN MONTH(r.rental_date)=', n,
' THEN r.amount END) AS "',
DATE_FORMAT(CONCAT('0000-',LPAD(n,2,0),'-00'), '%b'),
'"'
)
)
INTO @cols
FROM
(
SELECT @n := @n + 1 AS n
FROM information_schema.tables
JOIN (SELECT @n := 0) AS iter ) nn
WHERE n <= 12;
SET @query =
CONCAT(
'SELECT c.customer_id AS customer_id,' , @cols ,
' , AVG(r.amount) AS avg_rental_amt ',
' FROM customer c
JOIN rental r
ON c.customer_id = r.customer_id
JOIN payment p
ON p.rental_id = r.rental_id
AND p.customer_id = c.customer_id
GROUP BY c.customer_id, c.last_name, c.first_name');
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.