【问题标题】:Max date over each item每个项目的最大日期
【发布时间】:2019-12-26 15:05:50
【问题描述】:

我有一张简化后的表格,如下所示:

WITH TBL (ITEM, COST, DAY) AS (
  SELECT 'A', 6, TO_DATE('2019-08-13', 'YYYY-MM-DD') FROM DUAL
  UNION ALL
  SELECT 'B', 4, TO_DATE('2019-08-21', 'YYYY-MM-DD') FROM DUAL
  UNION ALL
  SELECT 'B', 4, TO_DATE('2019-08-18', 'YYYY-MM-DD') FROM DUAL
  UNION ALL
  SELECT 'A', 2, TO_DATE('2019-08-21', 'YYYY-MM-DD') FROM DUAL
  UNION ALL
  SELECT 'B', 5, TO_DATE('2019-08-16', 'YYYY-MM-DD') FROM DUAL
  UNION ALL
  SELECT 'C', 2, TO_DATE('2019-08-15', 'YYYY-MM-DD') FROM DUAL
) SELECT ITEM, COST, DAY FROM TBL;


ITEM | COST | DAY
-----+------+--------------------
A    | 6    | 2019-08-13 00:00:00
B    | 4    | 2019-08-21 00:00:00
B    | 4    | 2019-08-18 00:00:00
A    | 2    | 2019-08-21 00:00:00
B    | 5    | 2019-08-16 00:00:00
C    | 2    | 2019-08-15 00:00:00

我要查询每件商品的最新费用。

ITEM | COST | DAY
-----+------+--------------------
B    | 4    | 2019-08-21 00:00:00
A    | 2    | 2019-08-21 00:00:00
C    | 2    | 2019-08-15 00:00:00

我这样做的方法是让 CTE 获取每个项目的最新日期,然后 加入费用。

WITH CTE (ITEM, DAY) AS (
  SELECT ITEM, MAX(DAY)
  FROM TBL
  GROUP BY ITEM
)
SELECT CTE.ITEM, TBL.COST, CTE.DAY
FROM CTE
JOIN TBL ON TBL.ITEM = CTE.ITEM AND TBL.DAY = CTE.DAY;

我的问题是,是否有一种更简单的方法可以在没有 CTE 的情况下在一个查询中完成。

我尝试将 MAX 与 GROUP BY 结合使用,但没有找到任何方法使其发挥作用。

SELECT ITEM, COST, MAX(DAY)
FROM TBL
GROUP BY ITEM, COST;

当我真正需要的是将其与 MAX 天相应地分组时,它只会将具有相同项目和成本的记录分组。

ITEM | COST | DAY
-----+------+--------------------
A    | 6    | 2019-08-13 00:00:00
B    | 4    | 2019-08-21 00:00:00
A    | 2    | 2019-08-21 00:00:00
B    | 5    | 2019-08-16 00:00:00
C    | 2    | 2019-08-15 00:00:00

【问题讨论】:

    标签: sql oracle oracle11g group-by maxdate


    【解决方案1】:

    不存在:

    SELECT t.ITEM, t.COST, t.DAY
    FROM TBL t
    WHERE NOT EXISTS (
      SELECT 1 FROM TBL
      WHERE ITEM = t.ITEM AND DAY > t.DAY
    )
    

    请参阅demo
    结果:

    > ITEM | COST | DAY      
    > :--- | ---: | :--------
    > A    |    2 | 21-AUG-19
    > B    |    4 | 21-AUG-19
    > C    |    2 | 15-AUG-19
    

    【讨论】:

    • 虽然我很困惑,但因为我从未使用过NOT EXISTS,这很有效。你能解释一下逻辑吗?
    • 它只返回不存在具有更大日期的同一项目的另一行的行。这意味着结果仅包含每个项目的最新日期的行。
    【解决方案2】:

    您可以使用row_number 解析函数,它将根据partition byorder by 子句对每一行进行编号:

    试试这个:

    SELECT ITEM, COST, DAY FROM
    (SELECT t.ITEM, t.COST, t.DAY,
    ROW_NUMBER() over (partition by t.ITEM ORDER BY T.DAY DESC) AS RN
    FROM TBL t)
    WHERE RN = 1;
    

    Demo

    干杯!!

    【讨论】:

    • 这似乎是正确的,即使我无法让它工作。它适用于 Oracle 11g 吗?在询问之前,我确实尝试使用 OVER 和 PARTITION BY 但失败了,而且代码中的分区也输入错误。
    • 抱歉错字。你确定它不工作吗?见demo
    • 没关系,它确实有效,只是在ROW_NUMBER 之后缺少()
    • 酷。它发生了,但很高兴知道它有效:)
    【解决方案3】:

    最简单的是带有第一个/最后一个选项的最小/最大:

    select item, max(day), max(cost) keep (dense_rank last order by day) from tbl group by item;
    

    demo

    Link 文档和示例。

    【讨论】:

    • 它有效。 max(cost) keep (dense_rank last order by day) 到底是做什么的?
    • 它像 max 一样工作,但标准不是 cost,而是当天。所以它找到了最后一天的成本价值。如果有两个这样的值 max() 需要更大的值。如果你想降低使用 min()。
    • 哦,我明白了。我在同一日期尝试了一个具有 2 个不同成本的项目,可以看到最大/最小选择一个成本,而 forpas's answer 选择两者。不太确定接受哪个答案,因为我认为不会发生这种情况,但如果发生这种情况,我可能只希望返回 1 条记录,所以......
    • 虽然在尝试查看另一个答案是否也可以返回一个成本后,我发现它可以通过将最大/最小值添加到成本和分组到项目和日期来实现。所以这两个答案都是正确的并且非常好,只能接受一个不幸但都赞成。
    猜你喜欢
    • 2021-12-19
    • 1970-01-01
    • 2020-10-30
    • 2017-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    相关资源
    最近更新 更多