2012-04-03 42 views
0

我們試圖以刪除重複項而不是拋出錯誤的方式向我們的postgres表中添加一個唯一約束。唯一約束跨越兩列,並且沒有主鍵。例如:在違反行的多列上添加唯一約束

i_id | term | date_created 
1 | 'mako' | 123456789 
1 | 'mako' | 123451234 
1 | 'tele' | 213456852 
2 | 'rake' | 598521542 

因此,在本例中,我們需要刪除第二行,然後才能安全地添加唯一約束。通常情況下,我們將執行刪除命令,其中引入了不同的選擇,但我們沒有任何區分鍵的行。具體來說,唯一的關鍵將在列[i_id,term]之上。

(WTF沒有我們從一開始就具有唯一約束?去圖)

我想一個delete語句將是最好的,但我不能簡單地寫

delete from table where row_id not in (select row_id ... distinct something ...) 

,因爲該行沒有主鍵。如果可能,我寧願避免臨時表。有什麼建議麼?

編輯:對不起。我們使用的是postgres 8.4。

編輯2:我們正在使用的解決方案是:

delete from table where ctid not in (
    select 
    distinct on (i_id, term) 
    ctid 
    from table 
    order by i_id, term 
); 

謝謝你們!

+0

什麼是您的PostgreSQL版本? (這幾乎總是有用的添加。) – 2012-04-03 18:30:41

回答

1
DELETE FROM the_table 
WHERE ctid NOT IN (SELECT min(b.ctid) 
        FROM the_table b 
        GROUP BY b.i_id, b.term) 
+0

ctid很棒。謝謝! – whiterook6 2012-04-03 19:33:25

+0

這非常優雅。但是'min(ctid)'怎麼保證按照'date_created'匹配最老的(或最新的)行? – 2012-04-03 19:39:59

+0

它沒有。我在 – 2012-04-03 19:46:48

0

看起來您的dups在date_created列中的確有區別,因此您可以使用windowing函數捕獲重複集的第一行以外的所有值,然後使用它刪除多餘的行。

delete from foo 
using 
(select i_id, term, date_created 
from (
    select foo.* 
    , row_number() over (partition by i_id, term order by date_created asc) the_rank 
    from foo 
) ranked 
where ranked.the_rank <> 1 
) extras 
where foo.i_id = extras.i_id and foo.term = extras.term and foo.date_created = extras.date_created; 

這與Erwin的答案基本相似。它在選擇最舊的條目而不是最新條目方面有所不同,可以通過在排名的子查詢中更改ASC到ASC的順序來更改。

+0

這基本上是我的答案,只是用子查詢而不是CTE和WHERE子句的更詳細的語法...只有你採取最舊的行,而不是最新的。 – 2012-04-03 18:52:22

+0

@ErwinBrandstetter的確是這樣,我們必須在答案發布中跨越 - 我注意到SO有一些實質性的延遲。什麼是「重複答案」的禮儀? – dbenhur 2012-04-03 22:20:46

+0

我想達成的共識是刪除以後重複的答案。但你的回答並不完全是愚蠢的,我會離開它。此外,CTE需要Postgres 8.4或更高版本,而子查詢則使用舊版本。 (好吧,窗口函數需要8.4+,無論如何。) – 2012-04-03 22:56:19