简体   繁体   中英

Pivot query that shows the average amount spent per customer for each month

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM