显示每个客户每月平均花费金额的数据透视查询

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

我试图通过针对 Sakila Sample Database 显示每个客户每月花费的平均金额编写一个来更好地编写数据透视查询。我已经掌握了基本查询,但我不确定如何将其转换为数据透视查询。

这是该查询的 SQL:

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

这是上面的查询结果在我的 Navicat 数据库开发和管理客户端中:

任何人都知道如何将这些结果转换为数据透视查询,以便从左到右的列为 customer_id、last_name、first_name、一月、二月、... Dec,avg_rental_amt,在 MySQL 或 SQL 服务器中?如果可能的话,我希望结果看起来像这样:

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...

谢谢!

罗布

您可以进行条件聚合:

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

此语法应该适用于 SQL 服务器和 MySQL。

请注意,这会计算同一客户 在不同年份 的平均租金 - 与您的原始查询中一样。这可能是,也可能不是您想要的。如果没有,您需要根据您的具体需要调整条件表达式。

您可以通过应用条件聚合来动态管理返回结果

SQL 服务器 (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;

我的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;