通过比较2个字段,SQL在同一个表中选择连接

时间:2016-07-21 11:40:21

标签: sql join inner-join self-join

我的下表以这些记录为例:

+------+---------+---------+
| key  | amount1 | amount2 |
|------|---------|---------|
| A    | 100     | 0       |
| B    | 0       | 100     |
| C    | 100     | 0       |
| D    | 66      | 34      |
| E    | 99      | 12      |
| F    | 100     | 12      |
| G    | 12      | 99      |
+------+---------+---------+

我想删除所有记录,其中一行的字段amount1 =另一行的amount2,但只有成对:如果我找到2条记录,其中amount1 = amount2,那么我可以删除它们,如果我找到了第三条记录我必须保留它。

示例:上面的第一条记录,其中key = A,其中amount1 = 100,amount2 = 0,这里的对将是密钥B,其中amount1 = 0,amount2 = 100. row with key = C必须保留。

+------+---------+---------+
| key  | amount1 | amount2 |
+------+---------+---------+
| A    | 100     | 0       | 
| B    | 0       | 100     |  Pair found with key = A : Delete key = A and key = B
| C    | 100     | 0       |  No pair found as the 2 first records compose a pair
| D    | 66      | 34      |  No pair found
| E    | 99      | 12      |  No pair found
| F    | 100     | 12      |  No pair found
| G    | 12      | 99      |  Pair found with key = E : Delete records with key = E and key = G
+------+---------+---------+

预期结果如下:

+------+---------+---------+
| key  | amount1 | amount2 |
+------+---------+---------+
| C    | 100     | 0       |
| D    | 66      | 34      |
| F    | 100     | 12      |
+------+---------+---------+

所以要么我想确定所有对,然后删除它们,要么直接只显示没有对的行。

有任何线索吗?

先谢谢你的帮助,

2 个答案:

答案 0 :(得分:0)

一种可行的方法,而不是特别好。

DELETE a
FROM some_table a
INNER JOIN
(
    SELECT a.amount1  AS am1, 
            a.amount2  AS am2,
            b.amount1  AS bm1,
            b.amount2  AS bm2,
            MIN(a.`key`) AS del_key
    FROM some_table a
    INNER JOIN some_table b
    ON a.amount1 = b.amount2
    AND a.amount2 = b.amount1
    GROUP BY a.amount1, 
            a.amount2,
            b.amount1,
            b.amount2
    UNION
    SELECT a.amount1  AS am1, 
            a.amount2  AS am2,
            b.amount1  AS bm1,
            b.amount2  AS bm2,
            MIN(b.`key`) del_key
    FROM some_table a
    INNER JOIN some_table b
    ON a.amount1 = b.amount2
    AND a.amount2 = b.amount1
    GROUP BY a.amount1, 
            a.amount2,
            b.amount1,
            b.amount2
) b
ON a.key = b.del_key

这将获得每个键的匹配数量和最小值。然后将此作为子查询作为DELETE语句的一部分与原始表连接。

答案 1 :(得分:0)

像这样的东西。请注意删除amount1 = amount2的行对所需的额外工作量。我在测试数据中添加了一些额外的行来测试查询的正常工作。

我说明了(使用MINUS操作符)显示"剩余"的方法。在"删除"之后的查询中的行(减号)成对的行。类似的东西可以用于DELETE语句 - 尽管更好的做法是使用额外的列flag来标记成对的行,而不是删除它们(在现实生活中,你不想简单地想要删除历史信息)。

ADDED:此解决方案适用于Oracle;我不知道其他数据库产品可能需要哪些更改(如果有的话)。另外:请注意,在此解决方案中,如果您有两行金额(12,99)和五行金额(99,12),那么在"删除"之后会留下什么?是(99,12)三行。在我看来,这是对这种情况下要求的合理解释。

with
     input_data ( key, amount1, amount2 ) as (
       select     'A',     100,       0   from dual union all
       select     'B',       0,     100   from dual union all
       select     'C',     100,       0   from dual union all
       select     'D',      66,      34   from dual union all
       select     'E',      99,      12   from dual union all
       select     'F',     100,      12   from dual union all
       select     'G',      12,      99   from dual union all
       select     'H',     200,     200   from dual union all
       select     'I',     200,     200   from dual union all
       select     'J',     200,     200   from dual union all
       select     'K',     300,     300   from dual
     ),
     prep ( key, amount1, amount2, rn ) as (
       select key, amount1, amount2, 
              row_number() over (partition by amount1, amount2 order by key)
       from   input_data
     )
select   key,   amount1,   amount2
from   input_data
minus
select a.key, a.amount1, a.amount2
from   prep a inner join prep b
              on
                 (
                       a.amount1    = b.amount2
                   and a.amount2    = b.amount1
                   and a.amount1   != a.amount2
                   and a.rn         = b.rn
                 )
                 or
                 (
                       a.amount1    = b.amount2
                   and a.amount2    = b.amount1
                   and a.amount1    = b.amount1
                   and 
                       (
                             mod(a.rn, 2) = 1
                         and b.rn         = a.rn + 1 
                       )
                       or
                       (
                             mod(a.rn, 2) = 0
                         and b.rn         = a.rn - 1                       
                       )
                 )
;

KEY    AMOUNT1    AMOUNT2
--- ---------- ----------
D           66         34
F          100         12
J          200        200
K          300        300