Oracle SQL - 对 ID 求和分区但排除重复

Posted

技术标签:

【中文标题】Oracle SQL - 对 ID 求和分区但排除重复【英文标题】:Oracle SQL - Sum Partition over ID but exclude repeated 【发布时间】:2018-01-18 07:35:51 【问题描述】:

我的桌子:

HEAD_ID  LINE ID  ADJUST_ID  LIST_PRICE  DISCOUNT
01       01       01         200         15
01       01       02         200         0
01       01       03         200         10
01       02       01         300         16
01       02       02         300         0
02       01       01         300         15
02       01       02         300         0
02       01       03         300         10
02       02       01         100         16
02       02       02         100         0

我需要按 HEAD_ID 和 LINE_ID 对 LIST_PRICE 组进行总和,但不包括 ADJUST_ID。 TOTAL_LIST_PRICE 应该是 head id 中所有 line ID 的总和,但不重复。例如:

For the TOTAL_LIST_PRICE for HEAD_ID 01 is (HEAD_ID 01 LINE_ID 01) + (HEAD_ID 01 LINE_ID 02) = 200 + 300 = 500
For the TOTAL_LIST_PRICE for HEAD_ID 02 is (HEAD_ID 02 LINE_ID 01) + (HEAD_ID 02 LINE_ID 02) = 300 + 105 = 405

预期的结果是这样的:

HEAD_ID  LINE ID  ADJUST_ID  LIST_PRICE  DISCOUNT  TOTAL_LIST_PRICE
01       01       01         200         15        500
01       01       02         200         0         500
01       01       03         200         10        500
01       02       01         300         16        500
01       02       02         300         0         500
02       01       01         300         15        405 
02       01       02         300         0         405
02       01       03         300         10        405
02       02       01         105         16        405
02       02       02         105         0         405

我的查询是这样的 -

SELECT head_id, line_id, adjust_id, list_price, discount,
   SUM(LIST_PRICE) OVER (PARTITION BY head_id, line_id) TOTAL_LIST_PRICE 
FROM TABLE;

输出 -

HEAD_ID  LINE ID  ADJUST_ID  LIST_PRICE  DISCOUNT  TOTAL_LIST_PRICE
01       01       01         200         15        600
01       01       02         200         0         600
01       01       03         200         10        600
01       02       01         300         16        600
01       02       02         300         0         600
02       01       01         300         15        900 
02       01       02         300         0         900
02       01       03         300         10        900
02       02       01         105         16        210
02       02       02         105         0         210

我在 SUM OVER PARTITION 中遗漏了什么?还是我为此使用了错误的方法?

谢谢

【问题讨论】:

你预期的结果是错误的。 200+200+200600 不是 500 如果我按 line_idhead_id 分组。并且查询是正确的。有没有你想返回的具体结果 嗨,XING,我预计 500 作为预期结果。我需要的是从 HEAD_ID 01 获取 LINE_ID 01 和 02 的总和。所以 200 (HEAD_ID 01, LINE_ID 01) + 300 (HEAD_ID 01, LINE_ID 02)。 HEAD_ID 02 也是如此。TOTAL_LIST_PRICE 将为 405,因为 300 (HEAD_ID 02, LINE_ID 01) + 105 (HEAD_ID 02, LINE_ID 02)。 【参考方案1】:

您可以使用ROW_NUMBER 分析函数和PARTITION BY Head_id, Line_id, List_Price,它将为该分区的每一行编号,然后在ROW_NUMBER1 时进行过滤,将为每个Head_id, Line_id 提供唯一的List_Price,这只是需要为每个Head_id 提供SUMmed 才能提供所需的输出:

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE table_name ( HEAD_ID, LINE_ID, ADJUST_ID, LIST_PRICE, DISCOUNT ) AS
SELECT '01', '01', '01', 200, 15 FROM DUAL UNION ALL
SELECT '01', '01', '02', 200,  0 FROM DUAL UNION ALL
SELECT '01', '01', '03', 200, 10 FROM DUAL UNION ALL
SELECT '01', '02', '01', 300, 16 FROM DUAL UNION ALL
SELECT '01', '02', '02', 300,  0 FROM DUAL UNION ALL
SELECT '02', '01', '01', 300, 15 FROM DUAL UNION ALL
SELECT '02', '01', '02', 300,  0 FROM DUAL UNION ALL
SELECT '02', '01', '03', 300, 10 FROM DUAL UNION ALL
SELECT '02', '02', '01', 100, 16 FROM DUAL UNION ALL
SELECT '02', '02', '02', 100,  0 FROM DUAL;

查询 1

SELECT HEAD_ID,
       LINE_ID,
       ADJUST_ID,
       LIST_PRICE,
       DISCOUNT,
       SUM( CASE rn WHEN 1 THEN LIST_PRICE END ) OVER ( PARTITION BY Head_id )
         AS total_list_price
FROM   (
  SELECT t.*,
         ROW_NUMBER() OVER ( PARTITION BY Head_id, Line_id, List_Price
                             ORDER BY ROWNUM ) AS rn
  FROM   table_name t
)

Results

| HEAD_ID | LINE_ID | ADJUST_ID | LIST_PRICE | DISCOUNT | TOTAL_LIST_PRICE |
|---------|---------|-----------|------------|----------|------------------|
|      01 |      01 |        01 |        200 |       15 |              500 |
|      01 |      01 |        02 |        200 |        0 |              500 |
|      01 |      01 |        03 |        200 |       10 |              500 |
|      01 |      02 |        01 |        300 |       16 |              500 |
|      01 |      02 |        02 |        300 |        0 |              500 |
|      02 |      01 |        01 |        300 |       15 |              400 |
|      02 |      01 |        02 |        300 |        0 |              400 |
|      02 |      01 |        03 |        300 |       10 |              400 |
|      02 |      02 |        01 |        100 |       16 |              400 |
|      02 |      02 |        02 |        100 |        0 |              400 |

【讨论】:

【参考方案2】:
select head_id, 
       line_id, 
       adjust_id, 
       list_price, 
       discount,
       sum(case when fl = 1 then list_price end) over(partition by head_id) total_list_price
from (select head_id, line_id, adjust_id, list_price, discount,
             row_number()over(partition by head_id, line_id order by head_id) fl
        from tab) q;

【讨论】:

我在 ROW_NUMBER 上收到“窗口规范中缺少 ORDER BY 表达式”错误 刚刚为窗口功能添加了 order by。我在 postgresql 中创建了它,它不是强制性的。 如果一个HEAD_ID, LINE_ID分区有多个LIST_PRICEs,那么总数将只包括一个;尽管从@scholarwithfire 的问题中不清楚这是否会发生。

以上是关于Oracle SQL - 对 ID 求和分区但排除重复的主要内容,如果未能解决你的问题,请参考以下文章

oracle SQL left join()或full out join()根据键排除记录

在case sql语句中对范围间隔求和

如何根据 SQL 中的分区对行求和?

SQL |如何对 3 个项目的分区组求和?

在Oracle SQL中,只对特定条件下的字段进行求和。

Oracle SQL:根据每个分区中的第一行进一步对 PARTITION BY 组进行排序