1. 程式人生 > >PostgreSQL 中如何delete重複資料

PostgreSQL 中如何delete重複資料

問題提出

時常有這樣的case: DB例項執行一段時間後,發現需要給1個table中的某(些)欄位加unique 約束,

但建立unique constraints(或 index)時, 報出 DETAIL: Key (col)=(value) is duplicated !

此時就需要先按照一定邏輯將重複資料僅保留1條, 將冗餘的delete掉

分析問題

delete資料, 重點自然在於定位所有待delete的row, 或需要保留的row.

解決問題

以假設業務要求要保留如下test表中每組info重複值中id最小的row為例

nfo

方法1 正向思維, 使用 array

使用高階資料型別array及其強大的function, 一次定位需要delete的row

array

方法2 正向思維, 使用 window function

思路同 方法1, 讓我們體驗一下 window function

window function

方法3 逆向思維, 使用 not in

排除法, 逆向定位

not in

方法4 逆向思維, 使用 not exists

思路同 方法3

 not exists

方法5 正逆結合, 使用 in, not in

先定位存在重複值的組大集合,再排除小集合

方法6 正逆結合, 使用 exists, not exists

思路同 方法5

 exists

方法7 直接製作單條SQL

將所有存在重複值的組找到, 然後逐一定位需要保留每組中的最小id, 其餘delete

SQL

方法8 複製資料到新表

如果應用可以接受短暫停止寫入, 可以將所需唯一資料複製到新表

資料

放在事務裡是為了保證所做操作原子性, 避免出現瞬間無表可用的視窗期

注: 為了便於與其他方式對比, 方法8會按照保留id的方式測試, 如果不保留id, group by 比 distinct 執行速度略快.

測試資料

由於query在 table 資料分佈不同的情況下執行效率存在差異, 所以我們構造3組測試資料進行對比

生成資料

資料

資料分佈

資料分佈

各種方法對比

分析上表可知,

使用正向思維(方法1,2), 平均執行時間會隨著冗餘資料的增加而增加, 在冗餘資料較少時, 推薦方法2;

使用逆向思維(方法3,4), 平均執行時間會隨著冗餘資料的增加而減少, 在冗餘資料較多時, 推薦方法4;

正逆結合的思維(方法5,6)平均執行時間並不佔優勢, 原因是需要2次subquery來最終定位資料;

方法7 方法7 執行總時間最長(隨著單條SQL的總條數的增加而增加),

但實際上對DB例項的衝擊最小, 把1個長時間的對大量row 的lock, 離散化為僅對單個row或幾個row的極短時間的lock,

在壓力較大的生產環境中, 推薦此方法;

方法8 步驟稍繁, 在實際生產環境中由於table的欄位可能較多,且整個table的(包括所有index)都會重建, 所以速度並不佔優, 但卻順便把table徹底維護了一下 , 對於udpate, delete非常頻繁的table, total size(包括所有index)會大為縮小(由於MVCC), 綜合性能會明顯提升.

總結

看一下DELETE 的語法

DELETE

所以其實還有其他一些具體方法, 比如 使用 WITH Queries構造臨時表, 使用 USING using_list 替代子查詢, 使用儲存過程將方法1封裝起來(不推薦,因為這樣整個delete過程為一個大事務)等等;

但整體思路無外乎上面的套路, 條條大路通羅馬,結合table中資料分佈情況(具體問題具體分析),選擇效率較高, 且是您最鍾情的那個style就可以了.

DBA 日常操作選取原則

壓力大的線上生產DB例項(尤其是交易系統), 首選對生產衝擊最小的,

壓力不大的生產DB例項或DB beta/dev 例項首選一條SQL且執行時間快的方法。

重複值delete之後,就可以建立唯一索引了,方法如下:

DBA

文章來自微信公眾號: Qunar技術沙龍