2012-03-20 182 views
3

我有一個帶有唯一索引的條形碼列表。數據已在每個條形碼的末尾添加了額外的字符(-xx)以防止出現重複,但是一旦刪除後綴,將會出現大量重複項。下面是數據的一個示例:Mysql - 刪除重複記錄

itemnumber barcode 

17912  2-14 
18082  2-1 
21870  2-10 
29219  2-8 

然後我創建了兩個臨時表,瑪蒂和曼尼,既與itemnumber和剝離下來條形碼。所以,這兩個表將包含

itemnumber barcode 

17912  2 
18082  2 
21870  2 
29219  2 

而且我試圖刪除所有,但在馬蒂表條形碼中的第一項「2」(和所有其他條形碼)。我希望然後用正確的第一項更新原始表,用戶可以在應用程序中及時修復重複項。

所以,這是我的查詢,刪除所有,但在馬蒂表中的每個條形碼

DELETE FROM marty 
    WHERE itemnumber NOT IN 
    (SELECT MIN(itemnumber) FROM manny GROUP BY barcode) 

中的第一項有13萬行的馬蒂和曼尼。該查詢花了24小時,然後沒有正確完成。與服務器的連接崩潰,並且查詢沒有執行所有更新。

有沒有更好的方式來處理這一點,就不是我們的子查詢,我認爲這是造成延誤?這個團隊可能會放慢速度,因爲有這麼多的記錄。

感謝

+0

是'itemnumber'獨特之處?如果是的話,我認爲你的方法最終會奏效,儘管它效率低下,因爲它搜索整個「曼尼」表中的每一行「烈士」。 – gcbenison 2012-03-20 12:44:39

回答

1

MySQL的使用IN非常大集時是出了名的慢。一個腳本替代:

使用腳本來構建一個長itemnumber = X OR itemnumber = y OR itemnumber = z條(塊大小〜1000)和INSERT匹配的行(即不會在你以前的查詢已經DELETE d的那些)到新表,TRUNCATE現有並將新表格的內容加載回舊版本INSERT INTO marty SELECT * FROM marty_tmp

您可能希望鎖定表或在交易的最後TRUNCATEINSERT運行。

編輯:

  • 查詢SELECT MIN(itemnumber) FROM manny GROUP BY barcode從腳本,並將結果存儲在desiredItemNumbers陣列的1000個desiredItemNumbers
  • 採取分批構建此查詢:INSERT INTO manny_tmp SELECT * FROM manny WHERE itemnumber = desiredItemNumbers[0] OR itemnumber = desiredItemNumbers[1] ...。重新運行此查詢,直至用盡desiredItemNumbers數組爲止(n.b.最後一個查詢的可能少於1000個desiredItemNumbers)。
  • 您現在有一個表格,其結果是您將剩下的結果爲DELETEd,因此請交換martymarty_tmp表格的內容。
  • TRUNCATE marty
  • INSERT INTO marty SELECT * FROM marty_tmp
+0

嗨,安迪,我不太相信「構建一個很長的........」我仍然需要確定重複條形碼列表的第一個itemnumber。那是我遇到問題的地方,我知道實現這一目標的唯一方法是使用select in和group by,這太慢了。 MartinMac – 2012-03-20 12:23:04

+0

@Martin更新了答案 – Andy 2012-03-20 13:58:49

0

如果要創建臨時表反正,怎麼樣用 「INSERT INTO」 建立你的表或「CREATE TABLE .. AS ...「基於:

SELECT MIN(itemnumber) AS itemnumber, barcode 
    FROM marty 
    GROUP BY barcode 
+0

Glenn,太棒了。不錯,簡單,速度非常快,而且運行良好。我只需要使用substr(條形碼,1,長度(條形碼))去除已使用的後綴。太感謝了。 – 2012-03-20 15:44:12

1

這裏是一個兩階段的辦法,避免了使用NOT IN它也不會使用臨時表。‘曼尼’首先,加盟。‘馬蒂’給自己挑選出的行itemnumber!=分鐘(itemnumber)。使用UPDATE設置barcode爲這些行NULL。第二遍用DELETE然後刪除在第一階段中被標記的所有行。

對於這個例子,我分裂的barcode柱「烈士」分成兩列;可以用原始格式的表進行一些修改(需要動態分割列值)。

select * from marty; 
+------------+---------+---------+ 
| itemnumber | barcode | subcode | 
+------------+---------+---------+ 
|  17912 |  2 |  14 | 
|  18082 |  2 |  1 | 
|  21870 |  2 |  10 | 
|  29219 |  2 |  8 | 
|  30133 |  3 |  5 | 
|  30134 |  3 |  7 | 
|  30139 |  3 |  9 | 
|  30142 |  3 |  12 | 
+------------+---------+---------+ 
8 rows in set (0.00 sec) 

UPDATE 
    (marty m1 
    JOIN 
    (SELECT barcode, 
      MIN(itemnumber) AS itemnumber 
     FROM marty 
     GROUP BY barcode) m2 
    USING(barcode)) 
SET m1.barcode = NULL WHERE m1.itemnumber != m2.itemnumber; 

mysql> select * from marty; 
+------------+---------+---------+ 
| itemnumber | barcode | subcode | 
+------------+---------+---------+ 
|  17912 |  2 |  14 | 
|  18082 | NULL |  1 | 
|  21870 | NULL |  10 | 
|  29219 | NULL |  8 | 
|  30133 |  3 |  5 | 
|  30134 | NULL |  7 | 
|  30139 | NULL |  9 | 
|  30142 | NULL |  12 | 
+------------+---------+---------+ 
8 rows in set (0.00 sec) 

DELETE FROM marty WHERE barcode IS NULL; 
2

一個多種變異:這個變體工作沒有任何臨時表刪除重複:

Delete m1 
From Marty m1 
join Marty m2 
    on m1.barcode = m2.barcode 
    and m1.itemnumber > m2.itemnumber