【问题标题】:MAX() and MAX() OVER PARTITION BY produces error 3504 in Teradata QueryMAX() 和 MAX() OVER PARTITION BY 在 Teradata 查询中产生错误 3504
【发布时间】:2011-03-07 00:13:43
【问题描述】:

我正在尝试生成一个结果表,其中包含每个课程代码的最后完成课程日期,以及每个员工最后完成的课程代码。以下是我的查询:

SELECT employee_number,
       MAX(course_completion_date) 
           OVER (PARTITION BY course_code) AS max_course_date,
       MAX(course_completion_date) AS max_date
FROM employee_course_completion
WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
GROUP BY employee_number

此查询产生以下错误:

3504 : Selected non-aggregate values must be part of the associated group

如果我删除 MAX() OVER (PARTITION BY...) 行,查询执行得很好,所以我已将问题隔离到该行,但在搜索这些论坛和互联网后,我看不到我做错了什么。有人可以帮忙吗?

【问题讨论】:

  • 因为您使用 OVER 参考 MAX,SQL 将这些视为分析函数 - 而不是聚合。

标签: sql aggregate-functions teradata database-partitioning


【解决方案1】:

正如小马在评论中所说,您不能将 OLAP 函数与聚合函数混合使用。

也许更容易获取每位员工的最后完成日期,并将其加入包含三个目标课程中每门课程的最后完成日期的数据集。

这是一个未经检验的想法,希望能让你走上正确的道路:

  SELECT employee_number,
         course_code,
         MAX(course_completion_date) AS max_date,
         lcc.LAST_COURSE_COMPLETED
    FROM employee_course_completion ecc
         LEFT JOIN (
             SELECT employee_number,
                    MAX(course_completion_date) AS LAST_COURSE_COMPLETED
               FROM employee_course_completion
              WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
         ) lcc
         ON lcc.employee_number = ecc.employee_number
   WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
GROUP BY employee_number, course_code, lcc.LAST_COURSE_COMPLETED

【讨论】:

    【解决方案2】:

    逻辑上 OLAP 函数是在 GROUP BY/HAVING 之后计算的,因此您只能访问 GROUP BY 中的列或具有聚合函数的列。以下看起来很奇怪,但是是标准 SQL:

    SELECT employee_number,
           MAX(MAX(course_completion_date)) 
               OVER (PARTITION BY course_code) AS max_course_date,
           MAX(course_completion_date) AS max_date
    FROM employee_course_completion
    WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
    GROUP BY employee_number, course_code
    

    由于 Teradata 允许重复使用别名,这也有效:

    SELECT employee_number,
           MAX(max_date) 
               OVER (PARTITION BY course_code) AS max_course_date,
           MAX(course_completion_date) AS max_date
    FROM employee_course_completion
    WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
    GROUP BY employee_number, course_code
    

    【讨论】:

    • 您已声明 olap 函数是在 group by/having 之后计算的,但在您上面的代码中,您使用的是 course_code partition 子句,它不是 group by 子句的一部分。上面的代码在 oracle 中不起作用。错误是“ORA-00979:不是 GROUP BY 表达式”
    • @frank:正确,course_code必须添加到GROUP BY
    • 感谢您的确认。分析函数的新手不确定我是否正确。
    【解决方案3】:

    我知道这是一个非常古老的问题,但有人问过我类似的问题。

    我没有 TeraData,但您不能执行以下操作吗?

    SELECT employee_number,
           course_code,
           MAX(course_completion_date)                                     AS max_course_date,
           MAX(course_completion_date) OVER (PARTITION BY employee_number) AS max_date
    FROM employee_course_completion
    WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
    GROUP BY employee_number, course_code
    

    GROUP BY 现在可确保每位员工每门课程一排。这意味着您只需要直接的MAX() 即可获得max_course_date

    在您的 GROUP BY 只为每位员工提供一行,而 MAX() OVER() 试图为这一行提供多个结果之前(每门课程一个)

    相反,您现在需要OVER() 子句来获取整个员工的MAX()。现在这是合法的,因为每一行都只得到一个答案(因为它是从超集而不是子集派生的)。同样,出于同样的原因,OVER() 子句现在引用了一个有效的标量值,如GROUP BY 子句所定义的那样; employee_number.


    也许一种简短的说法是,带有OVER() 子句的aggregate 必须是GROUP BY 的超集,而不是子集。

    在代表所需行的级别使用GROUP BY 创建查询,然后如果要在更高级别聚合,请指定OVER() 子句。

    【讨论】:

    • 这也会产生同样的错误信息。从逻辑上讲,OLAP 函数是在 GROUP bY/HAVING 之后计算的,因此您只能访问 GROUP BY 中的列或具有聚合函数的列。以下看起来很奇怪,但是是标准 SQL:MAX(MAX(course_completion_date)) OVER (PARTITION BY employee_number) 由于 Teradata 允许重新使用别名,这也可以:MAX(max_course_date) OVER (按员工编号分区)
    【解决方案4】:

    我认为这会起作用,即使这是很久以前的事了。

    SELECT employee_number, Row_Number()  
       OVER (PARTITION BY course_code ORDER BY course_completion_date DESC ) as rownum
    FROM employee_course_completion
    WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
       AND rownum = 1
    

    如果您想在日期相同的情况下获取最后一个 Id,那么您可以使用它,假设您的主键是 Id。

    SELECT employee_number, Row_Number()  
       OVER (PARTITION BY course_code ORDER BY course_completion_date DESC, Id Desc) as rownum    FROM employee_course_completion
    WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
       AND rownum = 1
    

    【讨论】:

      【解决方案5】:
      SELECT employee_number, course_code, MAX(course_completion_date) AS max_date
      FROM employee_course_completion
      WHERE course_code IN ('M910303', 'M91301R', 'M91301P')
      GROUP BY employee_number, course_code
      

      【讨论】:

      • 欢迎来到 SO!感谢您发布答案,但需要解释为什么它可以解决问题。
      猜你喜欢
      • 2018-09-01
      • 2020-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-06
      • 1970-01-01
      • 2019-10-20
      相关资源
      最近更新 更多