【问题标题】:MAX() OVER PARTITION BY in Oracle SQLOracle SQL 中的 MAX() OVER PARTITION BY
【发布时间】:2018-09-01 08:00:29
【问题描述】:

我正在尝试使用 MAX() OVER PARTITION BY 函数来评估我公司购买的特定部件的最新收据。以下是去年几个部分的信息示例表:

| VEND_NUM | VEND_NAME    | RECEIPT_NUM | RECEIPT_ITEM | RECEIPT_DATE |
|----------|--------------|-------------|----------|--------------|
| 100      | SmallTech    | 2001        | 5844HAJ  | 11/22/2017   |
| 100      | SmallTech    | 3188        | 5521LRO  | 12/31/2017   |
| 200      | RealSolution | 5109        | 8715JUI  | 05/01/2017   |
| 100      | SmallTech    | 3232        | 8715JUI  | 11/01/2017   |
| 200      | RealSolution | 2101        | 4715TEN  | 01/01/2017   |

如您所见,第三行和第四行显示了相同部件号的两个不同供应商。

这是我当前的查询:

WITH

-- various other subqueries above...

    AllData AS
    (
        SELECT VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM, RECEIPT_DATE
        FROM tblVend
            INNER JOIN tblReceipt ON VEND_NUM = RECEIPT_VEND_NUM
        WHERE
            VEND_NUM = '100' OR VEND_NUM = '200' AND RECEIPT_DATE >= '01-Jan-2017'
    ),

    SELECT MAX(RECEIPT_DATE) OVER PARTITION BY(RECEIPT_ITEM) AS "Recent Date", RECEIPT_ITEM
    FROM AllData

我的返回集如下所示:

| Recent Date | RECEIPT_ITEM |
|-------------|--------------|
| 11/22/2017  | 5844HAJ      |
| 12/31/2017  | 5521LRO      |
| 11/01/2017  | 8715JUI      |
| 11/01/2017  | 8715JUI      |
| 01/01/2017  | 4715TEN      |

但是,它应该是这样的:

| Recent Date | RECEIPT_ITEM |
|-------------|--------------|
| 11/22/2017  | 5844HAJ      |
| 12/31/2017  | 5521LRO      |
| 11/01/2017  | 8715JUI      |
| 01/01/2017  | 4715TEN      |

任何人都可以就我做错了什么提供建议吗?看起来它只是在替换最近的日期,而不是只给我想要的最近的行。

最终,我希望我的桌子看起来像这样。但是,我不知道如何正确使用 MAX() 或 MAX() OVER PARTITION BY() 函数来实现这一点:

| VEND_NUM | VEND_NAME    | RECEIPT_NUM | RECEIPT_ITEM | RECEIPT_DATE |
|----------|--------------|-------------|----------|--------------|
| 100      | SmallTech    | 2001        | 5844HAJ  | 11/22/2017   |
| 100      | SmallTech    | 3188        | 5521LRO  | 12/31/2017   |
| 100      | SmallTech    | 3232        | 8715JUI  | 11/01/2017   |
| 200      | RealSolution | 2101        | 4715TEN  | 01/01/2017   |

【问题讨论】:

  • 可能使用partition by trim(receipt_item) .. 该列中可能有一个额外的空格。
  • 嗨@VamsiPrabhala,非常感谢您的建议。不幸的是,同样的事情......我不知道为什么我的 MAX() 不起作用。
  • 听起来您想过滤除每个部分的最新收据之外的所有内容?如果是这样,max() 不一定是这样做的方法。
  • @WilliamRobertson 纠正威廉,抱歉措辞不佳。我一直在研究如何解决这个问题,我错误地认为 max 是我最好的方法。

标签: sql oracle window-functions


【解决方案1】:

使用窗口函数ROW_NUMBER() OVER (PARTITION BY receipt_item ORDER BY receipt_date DESC) 为每一行分配一个序列号。 receipt_item 的最新 receipt_date 行将编号为 1。

WITH
-- various other subqueries above...

    AllData AS
    (
        SELECT VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM, RECEIPT_DATE,
        ROW_NUMBER() OVER (PARTITION BY RECEIPT_ITEM ORDER BY RECEIPT_DATE DESC ) AS RN
        FROM tblVend
            INNER JOIN tblReceipt ON VEND_NUM = RECEIPT_VEND_NUM
        WHERE
            VEND_NUM IN ( '100','200')  AND RECEIPT_DATE >= '01-Jan-2017'
    )
   SELECT VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM, RECEIPT_DATE
   FROM AllData WHERE RN = 1

【讨论】:

  • 非常感谢您的解释和代码。这确实有效。但是,我相信我在我的问题上卖空了自己,我接受了错误。我还想最终抓住“RECEIPT_ITEM”出现的“RECEIPT_NUM”。可以加吗?
  • 我已经编辑了我的问题以反映我的最终目标
  • 嗨,@kc2018。感谢您的编辑。我不断收到“在窗口函数中找不到 ORDER_BY”错误,我现在正在尝试调试。它与“t”子查询有关
  • 一定要用cte吗?
  • 坦率地说,很抱歉,我不知道 cte 是什么。但我可以告诉你的是,我无权在数据库端编辑任何管理内容。我必须能够从数据库中查询和读取。
【解决方案2】:

我在这里看到了几个问题。一,使用聚合函数MAX() 作为分析函数(Oracle 将其称为窗口函数)的语法如下所示:

MAX(receipt_date) OVER ( PARTITION BY receipt_item )

(注意括号的位置)。其次,从您想要的结果集中,您实际上并不想要一个窗口函数,而是想要聚合。窗口(或分析)函数将始终为其分区中的每一行返回一行;这就是它的工作方式。所以我想你想要的是这样的:

WITH
-- various other subqueries above...
AllData AS
(
    SELECT VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM, RECEIPT_DATE
      FROM tblVend
     INNER JOIN tblReceipt ON VEND_NUM = RECEIPT_VEND_NUM
     WHERE ( VEND_NUM = '100' OR VEND_NUM = '200' ) AND RECEIPT_DATE >= DATE'2017-01-01'
)
SELECT VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM, MAX(RECEIPT_DATE)
  FROM AllData
 GROUP BY VEND_NUM, VEND_NAME, RECEIPT_NUM, RECEIPT_ITEM;

现在我对上述内容进行了一些小改动,例如在OR 条件周围加上括号(使用IN ('100','200') 可能会更好),因为AND 优先于OR(所以你的查询会得到结果VEND_NUM = '100' OR ( VEND_NUM = '200' RECEIPT_DATE >= DATE'2017-01-01' ) ...但也许这就是你想要的?)。

【讨论】:

  • 嗨,@DavidFaber。非常感谢您的回答、输入和解释。显然我还是个菜鸟,但我正在努力学习。您的解决方案类似于 kc 最初在下面发布的解决方案。但是,如果最终我想在问题末尾列出决赛桌,我该如何编辑您的示例?
  • 我对我的答案进行了更新,但我必须注意我对您的数据没有很好的了解。
  • 嘿,@DavidFaber。感谢您的意见。不幸的是,这并没有产生我正在寻找的结果。没想到这个问题这么难。
【解决方案3】:

只是路过,但我认为您必须将日期格式化为 'YYYY-MM-DD' 格式,这样它就不会考虑“时间”。

【讨论】:

    【解决方案4】:

    这回答了问题的原始版本。

    您的where 子句应该如下所示:

     WHERE VEND_NUM IN ('100', '200') AND RECEIPT_DATE >= DATE '2017-01-01'
    

    很可能你想要的只是:

    SELECT DISTINCT RECEIPT_DATE, RECEIPT_ITEM
    FROM tblVend INNER JOIN
         tblReceipt
         ON VEND_NUM = RECEIPT_VEND_NUM
    WHERE VEND_NUM IN ('100', '200') AND RECEIPT_DATE >= DATE '2017-01-01';
    

    至少,这会返回您想要返回的内容。

    【讨论】:

    • 非常感谢您的解释和代码。这在上述情况下确实有效。但是,我相信我在我的问题上卖空了自己,我接受了错误。我还想最终抓住“RECEIPT_ITEM”出现的“RECEIPT_NUM”。可以添加吗?这样,我可以在以后的子查询中使用“RECEIPT_NUM”来添加更多数据。
    • 我已经编辑了我的问题以反映预期的最终目标。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-24
    • 2021-12-15
    • 2019-03-21
    • 1970-01-01
    • 2011-03-07
    • 1970-01-01
    相关资源
    最近更新 更多