【问题标题】:Using FIRST_VALUE without including inner columns in group by使用 FIRST_VALUE 而不包括 group by 中的内部列
【发布时间】:2019-02-25 21:18:22
【问题描述】:

我使用的表格如下所示:

userID, eventDate, eventName
1  2019-01-01  buySoup
2  2019-01-01  buyEggs
2  2019-01-03  buyMilk
2  2019-01-04  buyMilk
3  2019-01-02  buyBread
3  2019-01-03  buyBread

我目前的查询是:

SELECT
    userID,
    FIRST_VALUE(eventName) OVER (
        PARTITION BY userID ORDER BY eventDate ASC
    ) AS firstBought 
FROM table 
GROUP BY userID

我觉得这应该返回:

userID, firstBought
1  buySoup
2  buyEggs
3  buyBread

相反,它给出了错误:

'错误:列“table.eventName”必须出现在 GROUP BY 子句中或用于聚合函数中'

有没有办法在不将其包含在按函数分组或创建子查询的情况下获取此值?我正在使用 PostgreSQL。

如果我确实将它包含在 group by 子句中,它会返回

userID, firstBought
1  buySoup
2  buyEggs
2  buyEggs
2  buyEggs
3  buyBread
3  buyBread

我知道我可以将其设为子查询,然后按用户 ID、firstBought 分组,但我不想创建另一个子查询。

【问题讨论】:

    标签: sql postgresql vertica


    【解决方案1】:

    select distinct代替group by

    select distinct userID,
           FIRST_VALUE(eventName) over (partition by userID order by eventDate ASC) as firstBought 
    from table ;
    

    或者,您可以使用数组:

    select userId,
           (array_agg(eventName order by eventDate))[1] as firstBought
    from table
    group by userId;
    

    Postgres 没有“第一个”聚合函数,但这很好用。

    【讨论】:

    • 虽然这很有用,并且完全按预期工作,但我的表实际上还包含更多列,并且我的查询包括 max() 和这些列上的其他聚合函数。这意味着我最后确实需要 group by。还有其他解决办法吗?
    • @Cyborgcanoe 。 . .这个答案提供了两种解决方案。第二个使用group by
    • 谢谢,第二个答案很好。不幸的是,我实际上使用的是 Vertica Analytic Database v9.1.1-5,它是 postgres 的一个分支,它不包括 array_agg。无论如何,谢谢!
    • @Cyborgcanoe 。 . .你应该正确地标记你的问题。
    【解决方案2】:

    我猜 PostgreSQL 的 DISTINCT ON 可以解决问题:

    SELECT DISTINCT ON (userid)
           userid, eventdate, eventname
    FROM "table"
    ORDER BY (eventdate);
    

    这将为您提供每个 userid 的行,最少 eventdate

    【讨论】:

      【解决方案3】:

      我同意 A. Saunders 的观​​点。

      你需要一个外部查询。

      除了 SELECT DISTINCT 实际上归结为 SELECT 列表的所有列的 GROUP BY 之外,您不能将 OLAP 和 GROUP BY 函数混合到同一个 SELECT 中。

      所以,如果你有 MAX(),你必须:

      WITH -- your input data ...
      input(userID,eventDate,eventName) AS (
                SELECT 1,DATE '2019-01-01','buySoup'
      UNION ALL SELECT 2,DATE '2019-01-01','buyEggs'
      UNION ALL SELECT 2,DATE '2019-01-03','buyMilk'
      UNION ALL SELECT 2,DATE '2019-01-04','buyMilk'
      UNION ALL SELECT 3,DATE '2019-01-02','buyBread'
      UNION ALL SELECT 3,DATE '2019-01-03','buyBread'
      )
      ,
      getfirstbought AS (
        SELECT 
          userid
        , eventdate
        , FIRST_VALUE(eventname) OVER (
            PARTITION BY userid ORDER BY eventdate
         ) AS firstbought
        FROM input
      )
      SELECT
        userid
      , firstbought
      , MAX(eventdate) AS maxdt
      FROM getfirstbought
      GROUP BY 1,2;
      -- out  userid | firstbought |   maxdt    
      -- out --------+-------------+------------
      -- out       2 | buyEggs     | 2019-01-04
      -- out       3 | buyBread    | 2019-01-03
      -- out       1 | buySoup     | 2019-01-01
      -- out (3 rows)
      -- out 
      -- out Time: First fetch (3 rows): 22.157 ms. All rows formatted: 22.208 ms
      

      【讨论】:

        【解决方案4】:

        FIRST_VALUE 不是聚合函数。它是一个分析窗函数。因此,您的基本查询不需要GROUP BY 子句。应该改写为:

        SELECT 
                userID,
                FIRST_VALUE(eventName) over (PARTITION BY userID ORDER BY eventDate ASC) AS firstBought
        FROM table;
        

        从您的上述 cmets 之一看来,您正在使用其他函数,包括像 MAX 这样的聚合函数。要完成您要执行的操作,您需要将上述查询用作子查询。这将允许您使用聚合函数并从基本查询中获取唯一值。查询可能看起来像这样(我添加了一个价格列作为示例)。

        SELECT userID, firstBought, MAX(price)
        FROM (
                SELECT userID, price, FIRST_VALUE(eventName) over (partition by userID order by eventDate ASC) as firstBought 
                from test
        ) x
        GROUP BY userId, firstBought;
        

        这应该可以解决问题!您可以在外部查询中使用其他聚合函数,在子查询中使用其他窗口函数。

        【讨论】:

          猜你喜欢
          • 2015-03-26
          • 2010-12-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多