【问题标题】:Removing duplicates with aggregated values that cancel out删除具有取消的聚合值的重复项
【发布时间】:2021-06-11 20:03:15
【问题描述】:

我正在尝试删除在序列号中找到的重复项,但前提是在汇总金额账单时取消了重复序列号的金额账单。例如,有五个序列号为“abc-321”,但有五个不同的会费金额($500、$250、$-250、$30、$-30)。四个重复项将是 $250、$-250、$30、$-30,因为将它们加在一起会取消它们或为 0。
我只能想出一个查询来确定重复的序列号是什么,但不知道如何聚合它们以取消它们。

SELECT a.Serial, a.BillAmt, a.Date, a.Code 
FROM TableA a 
WHERE (a.Serial) in 
               (SELECT Serial 
                FROM TableA a 
                GROUP BY Serial 
                HAVING COUNT(Serial)>1) 
GROUP BY a.Serial, a.BillAmt, a.Date, a.Code 
ORDER BY a.Serial ASC;

样本输出:

+--------+---------+----------+------+
|Serial  | BillAmt | Date     | Code |
+--------+---------+----------+------+
|abc-112 | $240    | 20200720 | MPO  |
|abc-112 | -$400   | 20200527 | CPP  |
|acc-130 | $300    | 20200515 | CPP  |
|acc-130 | $300    | 20200420 | DUB  |
|acc-130 | -$300   | 20200515 | CPP  |
|bcc-111 | $500    | 20200701 | MPO  |
|bcc-111 | -$500   | 20200701 | MPO  |
|caa-321 | $700    | 20200805 | DUB  |
|caa-321 | $700    | 20200805 | MPO  |
+--------+---------+----------+------+

想要的结果:

+--------+---------+----------+------+
|Serial  | BillAmt | Date     | Code |
+--------+---------+----------+------+
|abc-112 | $240    | 20200720 | MPO  |
|abc-112 | -$400   | 20200527 | CPP  |
|acc-130 | $300    | 20200420 | DUB  |
|caa-321 | $700    | 20200805 | DUB  |
|caa-321 | $700    | 20200805 | MPO  |
+--------+---------+----------+------+

【问题讨论】:

  • 请提供样本数据和期望的结果。如果有超过两行具有相同的绝对值怎么办?
  • 请只标记您真正使用的RDBMS。
  • 提示:不要重复使用同一个表别名。可能会很混乱。
  • 如果有 500 美元、250 美元、-250 美元、250 美元、30 美元、-30 美元、-30 美元怎么办
  • @jarlh - 注意不要使用相同的别名表。谢谢。

标签: duplicates ssms


【解决方案1】:

您正在尝试将数据与自身进行比较。您可以使用从 TableA 到 TableA 的 JOIN 语句来做到这一点。

以下是对我有用的方法,其中包含一些测试数据。我通过执行部分 OUTER JOIN(特别是 LEFT JOIN)来做到这一点,其中一个 JOIN 条件是 BillAmt 的取消。然后在 WHERE 中查找 JOIN 失败的位置。这将是右侧为 NULL 的位置。

CREATE TABLE TableA (
Serial VARCHAR(10),
BillAmt DOUBLE,
`Date` DATE,
Code INT
);

INSERT INTO TableA (Serial, BillAmt, Date, Code) VALUES
('abc-321',500,'2021-06-01',1),
('abc-321',250,'2021-06-02',1),
('abc-321',-250,'2021-06-03',1),
('abc-321',50,'2021-06-04',1),
('abc-321',-50,'2021-06-05',1),
('abc-321',123,'2021-06-06',1);

SELECT a.Serial, a.BillAmt, a.Date, a.Code 
FROM TableA AS a
LEFT JOIN TableA AS b
  ON a.Serial = b.Serial
 AND a.BillAmt = (b.BillAmt * -1)
WHERE b.Serial IS NULL;

它给出了这个输出:

+---------+---------+------------+------+
| Serial  | BillAmt | Date       | Code |
+---------+---------+------------+------+
| abc-321 |     500 | 2021-06-01 |    1 |
| abc-321 |     123 | 2021-06-06 |    1 |
+---------+---------+------------+------+
2 rows in set (0.00 sec)

【讨论】:

  • $250, $-250, $-250 ?
  • @Serg - 呃。是的,可能是个问题。 TBH,他们可能需要 A 和 B 之间更强的匹配,而不仅仅是价格匹配。应该有交易 ID 或费用 ID 什么的。
  • 是的。我很抱歉。我没有包括所有标准,只是账单金额必须取消。在该示例 Serg 中,代码和日期必须匹配。因此,根据您使用 $-250 的示例,日期和代码很可能与两者不同。
【解决方案2】:

此解决方案将为您提供没有自连接的结果,并且只通过表。

  • 我们根据BillAmt 的绝对值计算每个Serial 的正数总数(忽略负号)
  • 底片数量相同
  • 我们根据符号计算同一分组中的行号
  • 我们只过滤BillAmt 为正数且当前行号或为负数且当前行数
SELECT a.Serial, a.BillAmt, a.Date, a.Code 
FROM (
    SELECT a.*,
        positives = COUNT(CASE WHEN a.BillAmt > 0 THEN 1 END) OVER (PARTITION BY a.Serial, ABS(a.BillAmt)),
        negatives = COUNT(CASE WHEN a.BillAmt < 0 THEN 1 END) OVER (PARTITION BY a.Serial, ABS(a.BillAmt)),
        rn = ROW_NUMBER() OVER (PARTITION BY a.Serial, ABS(a.BillAmt), SIGN(a.BillAmt) ORDER BY a.Date)
    FROM TableA a
) a
WHERE a.BillAmt > 0 AND rn <= positives - negatives
   OR a.BillAmt < 0 AND rn <= negatives - positives
ORDER BY a.Serial ASC;

db<>fiddle

【讨论】:

  • 是的,这似乎有效。谢谢你。试图理解这个查询,为什么要在 PARTITION 参数中使用 SIGN 函数?
  • 因为您想对每个标志的行数进行编号,以便您可以将其限制为WHERE 中每个标志的正确行数。如果您遵循逻辑,您将看到它是如何工作的
猜你喜欢
  • 1970-01-01
  • 2015-07-26
  • 2016-08-23
  • 2017-07-24
  • 1970-01-01
  • 2019-08-08
  • 1970-01-01
  • 1970-01-01
  • 2015-09-26
相关资源
最近更新 更多