【问题标题】:Filter duplicate rows in Postgres based on conditions between those rows根据这些行之间的条件过滤 Postgres 中的重复行
【发布时间】:2019-07-29 16:04:21
【问题描述】:

给定一张桌子

CREATE TABLE data(
 irs_number VARCHAR (50),
 mop_up INTEGER,
 ou VARCHAR (50)
);

我将如何返回所有匹配的记录...

  • 在另一行中至少有一个与 irs_number 相同的值,并且
  • 必须将具有相同irs_numbermop_up 中的至少一个设置为1 AND
  • ou 的值不相同,即只返回那些不匹配到具有相同 irs_number 的行的值。

... 这样所有irs_numbers 都将被返回(不仅仅是一个条件为真的——见下面的例子)。

我试过了,但查询无法在合理的时间内完成:

SELECT irs_number, mop_up, ou
FROM data outer_data
WHERE (SELECT count(*)
FROM data inner_data
WHERE inner_data.irs_number = outer_data.irs_number
AND inner_data.mop_up = 1 OR outer_data.mop_up = 1
AND inner_data.ou <> outer_data.ou
);

以及此处描述的重复计数的变化:How to find duplicate records in PostgreSQL - 他们将始终只返回重复项,而不是应用的正确过滤器。


编辑

示例数据:

INSERT INTO data VALUES 
('0001', 1, 'abc'),
('0001', 0, 'abc'),
('0001', 0, 'cde'),
('0001', 0, 'abc'),
('0002', 1, 'abc'),
('0002', 0, 'abc'),
('0003', 0, 'abc'),
('0003', 0, 'xyz')
;

SQLFiddle:http://sqlfiddle.com/#!17/be28f

理想情况下,查询应该返回:

irs_number  mop_up  ou
-----------------------
0001        1       abc
0001        0       abc
0001        0       cde
0001        0       abc

(顺序不重要) 这意味着它应该返回与irs_number 匹配的所有行与上述条件。

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    你应该可以通过一个简单的exists 子句来做到这一点:

    SELECT irs_number, mop_up, ou
    FROM data d
    WHERE EXISTS (SELECT 1
                  FROM data d2
                  WHERE d2.irs_number = d.irs_number AND
                        d2.mop_up = 1 AND
                        d2.ou <> d.ou
                 );
    

    编辑:

    以上误解了这个问题。它假定mop_up = 1 需要位于不同 ou 上。当我阅读这个问题时,这是模棱两可的,但似乎不是你想要的。所以,两个exists 地址:

    SELECT irs_number, mop_up, ou
    FROM data d
    WHERE EXISTS (SELECT 1
                  FROM data d2
                  WHERE d2.irs_number = d.irs_number AND
                        d2.mop_up = 1
                 ) AND
         EXISTS (SELECT 1
                  FROM data d2
                  WHERE d2.irs_number = d.irs_number AND
                        d2.ou <> d.ou
                 );
    

    Here 是一个 dbfiddle。

    这两种解决方案都可以利用(irs_number, mop_up, ou) 上的索引。

    【讨论】:

    • 这与题的要求无关。
    • 这将只返回那些将mop_up 设置为1 但不是所有在至少mop_up 设置为1 的地方
    • @dh762 。 . .一点也不。这将返回所有与mop_up = 1 对应的行的irs_numbers - 这就是您所要求的。我不知道您为什么将相关子查询中的 where 子句与外部查询返回的内容混淆。
    • 正确,但这只能用作子查询,因为最终查询应返回带有子查询 irs_number 的所有记录 - 请参阅示例 - 您的查询只返回 1 行带有 0001 而不是全部4 与0001。从预编辑问题中可能不明显(将更新)
    • @dh762 。 . .我知道了。我对第二个和第三个要点的解释与您的意图不同。我已经调整了答案。
    【解决方案2】:

    我认为这个加入会做:

    SELECT * FROM data 
    WHERE irs_number in (
      SELECT irs_number
      FROM data d
      WHERE EXISTS (SELECT 1
        FROM data 
        WHERE irs_number = d.irs_number
        AND (mop_up = 1 OR d.mop_up = 1)
        AND ou <> d.ou
      )
    )
    

    demo

    【讨论】:

    • 它也会返回只有一个 irs_number@forpas 的行
    • 这会连接具有相同irs_number 和不同ou 的行,这意味着具有相同irs_number 的不同行。您可以发布示例数据和预期结果以便清楚吗?
    • 对评论的更正:只有在同一个ou 中有重复的irs_numbers 时它才会返回一行(至少有一个mop_up = true)。很高兴添加示例数据
    • 为什么要返回0001 0 abc?两行中的任何一行都不存在具有相同 irs_number 和不同 ou 且 mop_up = 1 的行。
    猜你喜欢
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 2019-05-20
    • 2022-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-27
    • 2018-11-25
    相关资源
    最近更新 更多