【问题标题】:Window function inside a pivot, is it possible?枢轴内的窗口功能,可能吗?
【发布时间】:2011-07-12 21:26:57
【问题描述】:

我有这张桌子

我需要创建一个支点,将情绪显示为列,平均情绪级别按用户 ID、用户日期、情绪分组。例如,对于 user_id = 1、user_date = 2011-07-13 和emotion = 'Anger',平均emotion_level 应为4.0。

我创建了一个枢轴:

select USER_ID, user_date,  
AVG(case emotion when 'Anger' then convert(float, emotion_level) else 0 end)  as Anger,
AVG(case emotion when 'Sadness' then convert(float, emotion_level) else 0 end) as Sadness,
AVG(case emotion when 'Interest' then convert(float, emotion_level) else 0 end) as Interest
from emotions group by USER_ID, user_date;

半有效,但计算所有情绪的平均情绪水平,但不计算按用户、日期和情绪分组的情绪。

我对第一个用户的结果 + 情绪 = 'Anger' = 2,但应该是 4。

我想,我应该使用窗口函数(over(按 user_id、user_date、emotion 分区)),但无法运行语法。

有可能吗?

我在 prod 中使用的是 PostgreSQL 9,但上面的示例是用 SQL Server 编写的。

【问题讨论】:

    标签: sql postgresql pivot window-functions


    【解决方案1】:
    WITH    q (id, user_id, user_date, emotion, emotion_level) AS
            (
            VALUES
            (1, 1, '2011-07-13'::DATE, 'Anger', 3),
            (2, 1, '2011-07-13'::DATE, 'Anger', 5),
            (3, 1, '2011-07-13'::DATE, 'Sadness', 2),
            (4, 1, '2011-07-13'::DATE, 'Interest', 2),
            (5, 2, '2011-07-13'::DATE, 'Anger', 1),
            (6, 2, '2011-07-13'::DATE, 'Sadness', 4),
            (7, 2, '2011-07-13'::DATE, 'Sadness', 5),
            (8, 2, '2011-07-13'::DATE, 'Interest', 3),
            (9, 3, '2011-07-13'::DATE, 'Anger', 1),
            (10, 3, '2011-07-13'::DATE, 'Sadness', 3),
            (11, 3, '2011-07-13'::DATE, 'Interest', 4),
            (12, 3, '2011-07-13'::DATE, 'Interest', 5)
            ) 
    SELECT  user_id, user_date,
            AVG(CASE emotion WHEN 'Anger' THEN emotion_level END)::numeric(3, 2) AS Anger,
            AVG(CASE emotion WHEN 'Sadness' THEN emotion_level END)::numeric(3, 2) AS Sadness,
            AVG(CASE emotion WHEN 'Interest' THEN emotion_level END)::numeric(3, 2) AS Interest
    FROM    q
    GROUP BY
            user_id, user_date
    ORDER BY
            user_id, user_date
    

    问题是你最初使用的表达方式:

    AVG(case emotion when 'Interest' then convert(float, emotion_level) else 0 end)
    

    对给定用户在给定日期的所有记录进行平均,并将非Interest 条目视为0,而应将它们视为NULL,因此它们不会影响Interest 平均值。

    【讨论】:

      【解决方案2】:

      我首先在表中将'emotion_level'的数据类型更新为'float',然后使用pivot执行了操作。如果数据类型未更新,则所需值为 int 数据类型。

      select user_id, user_date, Anger, Sadness, Interest 
      from (select user_id,user_date,emotion, emotion_level 
      from emotions) as emo
      pivot(avg(emotion_level) 
      for emotion in (Anger, Sadness, Interest)) as P;
      

      上述查询的结果在下面的链接中

      【讨论】:

        【解决方案3】:

        我会定义一个 CTE,然后对其定义 join - 您的主要问题可能是您不是 grouping emotion_level。试试这个:

        WITH average_emotion (user_id, date, emotion, average_level) as 
                             (SELECT user_id, user_date, emotion, AVG(convert(float, emotion_level))
                              FROM emotions
                              GROUP BY user_id, user_date, emotion)
        SELECT a.user_id, a.user_date, COALESCE(b.average_level, 0) as Anger
        FROM emotions as a
        LEFT JOIN average_emotion as b
        ON b.user_id = a.user_id
        AND b.date = a.user_date
        AND b.emotion = 'Anger'
        GROUP BY a.user_id, a.user_date
        

        然后,只需在每次需要新列时添加额外的left joins。

        有很多方法可以重构它,主要与数据模型的其余部分和所需的输出有关。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-01-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-22
          • 1970-01-01
          相关资源
          最近更新 更多