【问题标题】:How to update reference table with duplicate id?如何更新具有重复 ID 的引用表?
【发布时间】:2019-09-06 16:56:32
【问题描述】:

我正在使用 postgresql。 要删除表中的重复项,我使用以下查询:

DELETE FROM dups a USING (
      SELECT MIN(ctid) as ctid, key
        FROM dups 
        GROUP BY key HAVING COUNT(*) > 1
      ) b
      WHERE a.key = b.key 
      AND a.ctid <> b.ctid

参考:https://stackoverflow.com/a/12963112/4940278

但是,有一个表说ref_table,其中也引用了dups.id。在删除重复项之前,我需要更新另一个表。 用副本的 id 更新引用表的查询是什么,这样就不会丢失数据?

例如:

表 1,比如dups

id key
1  Luna
2  Hermione
3  Luna

表 2,比如ref_table

id dups_id data
1   2     Auror
2   1     Researcher

现在删除重复项的查询将删除 dups 表中 id 为 1 的记录,因为它是重复项。 但是,ref_table 中引用了该记录,因此我需要使用将要保留的记录来更新它。

i.e) Table1 应该变成:

id key
2  Hermione
3  Luna

表2应该变成:

id dups_id data
1   2     Auror
2   3     Researcher

【问题讨论】:

    标签: sql postgresql postgresql-9.4


    【解决方案1】:

    使用 CTE 识别 dups 中维护的行,然后更新 ref 行以便 FK 仅指向它们,最后删除不再需要的行。

    with keeper as                                     -- identify dups rows to be kept
         ( select id, key
                , max(id) over(partition by key) mid 
             from dups)
    
       , u as                                          -- update ref so dup_id references only those being kept
         ( update ref r 
            set dup_id =  (select k.mid 
                             from keeper k
                             join dups   d 
                               on (k.id=d.id)
                            where r.dup_id != k.mid
                              and r.dup_id = k.id
                          )
         )   
    
    delete                                             -- final target remove dups rows no lnger needed
      from dups d                                            
     where d.id not in (select mid from keeper);    
    

    【讨论】:

    • 这里的join条件不应该是k.keyid=d.key吗?
    • @Surya,没有。首先 k.keyid 不存在,但我认为这是一个错字,你的意思是 k.key=d.key。仍然没有;这样做会导致“错误:子查询返回多行”。 Keeper CTE 包含 2 个 id - 需要更新的值,以及要更新到并最终保留的值 - 在删除期间。实际上,keeper CTE 中根本不需要列 Key - 似乎它是测试的残余物。免责声明:对于完整的披露者,我将两个 id 列定义为 PK 并将 ref.dups_id 定义为 FK。还为 Luna 添加了第三行和另一组用于测试的副本。
    • 感谢您的解释。不过我还是不明白。我还不知道partition by 做了什么。但我会阅读文档并了解这个查询是如何工作的。您的查询有效。但不幸的是,我的生产数据很大,有152670 记录。该表也没有索引。所以查询永远运行。所以我决定使用 ruby​​ 脚本更新和删除记录。
    • @Surya,Partition 子句适用于窗口函数,并为窗口函数提供与 Group By 为整个查询提供的相同功能。顺便说一句,152000+ 行的表并不大,它实际上接近于小。您最好创建一个 PK(这将创建一个索引)。祝你好运。
    • 如果 PK 你的意思是 PrimaryKey 我确实有 PrimaryKey id 是主键。但我认为如果我在key 上建立索引会更快,也许我应该在key 上建立索引并再次尝试此查询。无论如何,谢谢。
    【解决方案2】:

    您可以使用 CTE:

    with d as (
          DELETE FROM dups a USING
                 (SELECT MIN(ctid) as ctid, key
                  FROM dups 
                  GROUP BY key HAVING COUNT(*) > 1
                 ) b
          WHERE a.key = b.key AND a.ctid <> b.ctid
          RETURNING *
         )
    update othertable
       set . . .
       from d
       where . . .;
    

    【讨论】:

    • ` DELETE FROM dups a USING (SELECT MIN(ctid) as ctid, key FROM dups GROUP BY key HAVING COUNT(*) > 1 ) b WHERE a.key = b.key AND a.ctid b.ctid RETURNING *` 这给了我删除的记录,但我需要访问保留的记录才能更新另一个表。我该怎么做?请更清楚
    • @Surya 。 . .我不明白。如果需要访问保留的记录,查询delete后面的表即可。
    • 已用示例更新问题以获得更清晰的信息,请查看@Gordon
    猜你喜欢
    • 1970-01-01
    • 2021-03-28
    • 1970-01-01
    • 1970-01-01
    • 2021-07-30
    • 1970-01-01
    • 2018-11-30
    • 2021-06-11
    • 2021-04-13
    相关资源
    最近更新 更多