【问题标题】:HAVING - GROUP BY to get the latest record [duplicate]HAVING - GROUP BY 获取最新记录[重复]
【发布时间】:2013-06-27 03:45:33
【问题描述】:

我有一个这样的名为 RESULTS 的表:

RESULTS_IDN    NAME    SUBJECT    YEAR    QUALIFIED
1              MARK    ENGLISH    1989    N
3              MARK    ENGLISH    1991    N
5              MARK    ENGLISH    1993    Y
7              MARK    ENGLISH    1995    N
2              MARK    MATH       1990    N
5              MARK    MATH       1993    N
6              MARK    MATH       1995    Y
4              MARK    SCIENCE    1991    N
9              MARK    SCIENCE    1997    Y

我需要知道他所写的最新考试的 SUBJECT 的候选人的资格状态,我如何为此编写查询(ORACLE/MSSQL)?

例如输入

NAME,SUBJECT  OUTPUT NAME IDN SUBJECT YEAR Q
MARK,ENGLISH  OUTPUT MARK 7   ENGLISH 1995 N 
MARK SCIENCE  OUTPUT MARK 9   SCIENCE 1997 Y
MARK MATH     OUTPUT MARK 6   MATH    1995 Y

我知道解决这个问题的一种方法。

(SELECT NAME SUBJECT YEAR MAX(YEAR) YEAR
FROM RESULTS WHERE NAME = 'MARK' AND SUBJECT ='MATH'
GROUP BY NAME SUBJECT YEAR) LATEST 

在 IDN 上将上表加入到同一个表中,我可以得到结果。但这是双重工作。无论如何,我可以使用 HAVING CLAUSE 或其他方式获得 MAX(YEAR) 并获得相应的年份吗?我需要对 GROUP BY 数据进行 2 次操作,一个是最新的,一个是对应的 Qualified 状态。

PS : 当然,数据库中有 100 个这样的候选人的记录。

更新:根据答案 2,这个问题也被归类为 greatest-n-per-group 问题。有趣的是知道它是 DB 中的一个分类问题。

【问题讨论】:

    标签: sql sql-server oracle group-by greatest-n-per-group


    【解决方案1】:

    这是 的老生常谈的问题:

    SELECT t1.*
    FROM RESULTS AS t1
    LEFT JOIN RESULTS AS t2
      ON t1.NAME = t2.NAME AND t1.SUBJECT = t2.SUBJECT AND t1.YEAR < t2.YEAR
    WHERE t2.NAME IS NULL
    

    【讨论】:

    • 感谢您指出问题,它甚至还有名字!正如 Ben 所说,我也担心 2 次索引扫描,这对于这个问题来说并不理想。
    • 嗯,你们都说对了,还有其他非常有趣的解决方案(比如@Ben 的解决方案)可能会更快..
    • ...但是当心@Nishant,这些不是便携式的!
    • 所有主要的 RDBMS bar MySQL 都提供窗口化功能,因此说它“不可移植”有点误导。它可以在 DB2、Oracle、Postgres、SQL Server、Sybase、Informix、Teradata 和(我相信)Firebird 3 上运行。它们也是解决这个问题的标准方法,再次在每个 RDBMS 中保存 MySQL,因为它们被证明是获取数据的最快方式。唯一可能的例外是 Oracle,它对 SQL 标准的非标准 keep dense_rank 扩展比窗口/分析函数更快、更有效。
    • @Nishant 没问题,Bens 的答案是最好的 - 我只是想确保您知道这不是便携式解决方案。
    【解决方案2】:

    在 Oracle 和 SQL Server 中,您可以使用分析/窗口函数 RANK() 或 ROW_NUMBER() 来实现:

    select *
      from ( select a.*
                  , rank() over ( partition by name, subject order by year desc ) rnk
               from ... a
                    )
     where rnk = 1
    

    RANK() 将为每个 namesubject 中最新的每一行返回 1,ROW_NUMBER() 将返回一个 随机 行。

    仅在 Oracle 中,您就可以使用 KEEP 为您提供相同的结果:

    select name, subject, max(year) as year
         , max(qualified) keep (dense_rank first order by year desc) as qualified
      from ...
     group by name, subject
    

    【讨论】:

    • 谢谢!我跳过了关于分析/窗口函数的阅读......有人让我阅读。
    • 我的微妙问题是,我们是否可以使用聚合函数,然后根据 group_by 中排除的“其他”行检索参数。
    • 答案中的 Oracle 语法为您提供了您对@Nishant 的要求。但是你不能用 SQL Server 做到这一点。
    • 我要问的内容是否有任何技术术语,它基本上是 Group By 中另一种条件的条件。只是好奇 。刚刚检查了“超过”一个 Group 的东西,但是我们想收集一些匹配的东西,它不能被归类为 Predicate , And Or 等等 - 因为 Have 工作在一个 Group 上。 Ben 如果我需要 Oracle MSSQL 兼容代码,我们需要使用我的解决方案吗?
    • 我认为它仍然是一个聚合函数@Nishant,尽管有一个额外的 ORDER BY。您不必使用您的解决方案,因为您提到这将意味着您的表/索引的两次扫描。您可以使用我最初建议的分析函数,它们与 SQL Server 和 Oracle 兼容(并且只涉及一次扫描)。
    猜你喜欢
    • 2016-05-15
    • 1970-01-01
    • 2020-12-19
    • 2021-07-20
    • 2020-08-11
    • 2021-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多