2012-07-15 130 views
5

此處想要刪除具有重複列值(Product)的行,然後將其用作主鍵刪除沒有主鍵的重複項

該列的類型爲nvarchar,我們不希望對一個產品有2行。 數據庫是一個大型的數據庫,大約有數千行我們需要刪除。

在查詢所有重複項的過程中,我們希望保留第一項並刪除第二項作爲重複項。

目前還沒有主鍵,我們希望在刪除重複項之後進行。 然後Product columm可能是我們的主要關鍵。

數據庫是SQL Server CE。

我試了幾種方法,而且大多得到類似的錯誤:

有解析查詢時出現錯誤。 [令牌行號= 2,令牌行偏移= 1,令牌在錯誤= FROM]

其中我嘗試的方法:

DELETE FROM TblProducts 
FROM TblProducts w 
    INNER JOIN (
      SELECT Product 
      FROM TblProducts 
      GROUP BY Product 
      HAVING COUNT(*) > 1 
      )Dup ON w.Product = Dup.Product 

的首選方法試圖學習和調整我的代碼用類似的東西 (它尚未糾正):

SELECT Product, COUNT(*) TotalCount 
FROM TblProducts 
GROUP BY Product 
HAVING COUNT(*) > 1 
ORDER BY COUNT(*) DESC 

-- 
;WITH cte -- These 3 lines are the lines I have more doubt on them 
    AS (SELECT ROW_NUMBER() OVER (PARTITION BY Product 
             ORDER BY (SELECT 0)) RN 
     FROM Word) 
DELETE FROM cte 
WHERE RN > 1 
+0

數據庫有多大。我們在這裏談論數百萬行嗎?十億? – 2012-07-15 11:44:10

+0

大約有200,000條記錄,其中有3000個重複,但並不多:D – Sypress 2012-07-15 11:44:54

+0

當您有兩條記錄的產品數據相同但其他列中的數據不同時,您如何知道哪一條是正確的? – 2012-07-15 11:45:37

回答

4

如果您有兩個具有相同產品列的不同記錄,那麼您可以使用某些標準選擇不想要的記錄,例如,

CREATE TABLE victims AS 
    SELECT MAX(entryDate) AS date, Product, COUNT(*) AS dups FROM ProductsTable WHERE ... 
    GROUP BY Product HAVING dups > 1; 

然後,您可以在ProductTable和受害者之間執行DELETE JOIN。

或者您也可以選擇僅產品,然後對其他JOIN條件執行DELETE,例如具有無效的CustomerId或EntryDate NULL或其他任何其他條件。如果你的知道有一個且只有一個有效的產品副本,並且所有其他產品都可以通過無效數據識別,那麼這將起作用。

假設你改爲擁有IDENTICAL記錄(或者你有兩個完全相同或不相同的產品,或者對於某些產品你可能有幾個產品,你不知道是哪一個產品)。您運行完全相同的查詢。然後,在ProductsTable和SELECT DISTINCT上運行SELECT查詢,所有匹配要被重複刪除的產品代碼的產品,按產品分組,以及爲所有字段選擇合適的聚合函數(如果相同,則聚合應該執行;否則,我通常嘗試使用MAX或MIN)。這將爲每個產品「精確保存」一行。

此時您運行DELETE JOIN並殺死所有重複的產品。然後,只需將保存的和重複的子集重新導入主表。

當然,在DELETE JOIN和INSERT SELECT之間,您將使DB處於不穩定狀態,並且至少有一個重複的所有產品都會消失。

另一種方式應在MySQL的工作:

-- Create an empty table 
CREATE TABLE deduped AS SELECT * FROM ProductsTable WHERE false; 

CREATE UNIQUE INDEX deduped_ndx ON deduped(Product); 

-- DROP duplicate rows, Joe the Butcher's way 
INSERT IGNORE INTO deduped SELECT * FROM ProductsTable; 

ALTER TABLE ProductsTable RENAME TO ProductsBackup; 

ALTER TABLE deduped RENAME TO ProductsTable; 
-- TODO: Copy all indexes from ProductsTable on deduped. 

注意:以上的方式不起作用如果要區分「好記」和「無效的重複」。它只適用於你有冗餘DUPLICATE記錄,或者如果你不在乎你保留的行和你丟棄!

編輯: 你說「重複」有無效的字段。在這種情況下,你可以修改上面分揀招:

SELECT * FROM ProductsTable ORDER BY Product, FieldWhichShouldNotBeNULL IS NULL; 

那麼,如果你只有一排產品,一切都很好,它會被選中。如果你有更多的,那麼(FieldWhichShouldNeverBeNull IS NULL)是FALSE的那個(也就是FieldWhichShouldNeverBeNull實際上不應該爲null的那個實際上不應該是null)將被首先選擇並被插入。所有其他人都會因IGNORE條款而無聲地反彈產品的獨特性。不是一個非常好的方式來做到這一點(並檢查我沒有在我的條款中混淆真假),但它應該工作。

編輯
其實更多的新的答案

的這是一個簡單的表來說明問題

CREATE TABLE ProductTable (Product varchar(10), Description varchar(10)); 
INSERT INTO ProductTable VALUES ('CBPD10', 'C-Beam Prj'); 
INSERT INTO ProductTable VALUES ('CBPD11', 'C Proj Mk2'); 
INSERT INTO ProductTable VALUES ('CBPD12', 'C Proj Mk3'); 

沒有指數着呢,沒有主鍵。我們仍然可以將產品聲明爲主鍵。

但是發生了一些不好的事情。兩個新記錄進入,並且都有NULL描述。

然而,第二個是有效的產品,因爲在此之前我們對CBPD14一無所知,因此我們不希望完全失去此記錄。我們雖然想擺脫虛假的CBPD10。

INSERT INTO ProductTable VALUES ('CBPD10', NULL); 
INSERT INTO ProductTable VALUES ('CBPD14', NULL); 

粗魯DELETE從ProductTable WHERE描述IS NULL是不可能的,它會殺死CBPD14這是不重複的。

所以我們這樣做。首先得到重複列表:

SELECT Product, COUNT(*) AS Dups FROM ProductTable GROUP BY Product HAVING Dups > 1; 

我們假設:「每組壞記錄至少有一個好記錄」。

我們通過設定相反的方向並查詢它來檢查這個假設。如果所有都是協同的,我們期望這個查詢不返回任何內容。

SELECT Dups.Product FROM ProductTable 
RIGHT JOIN (SELECT Product, COUNT(*) AS Dups FROM ProductTable GROUP BY Product HAVING Dups > 1) AS Dups 
ON (ProductTable.Product = Dups.Product 
     AND ProductTable.Description IS NOT NULL) 
WHERE ProductTable.Description IS NULL; 

爲了進一步驗證,我插入了代表這種失敗模式的兩條記錄;現在我確實希望上面的查詢返回新的代碼。

INSERT INTO ProductTable VALUES ("AC5", NULL), ("AC5", NULL); 

現在的 「檢查」 查詢確實返回,

AC5 

所以,複本的產生看起來不錯。

我現在着手刪除所有不是有效的重複記錄。如果有重複的有效記錄,除非發現一些條件,否則它們將保持複製狀態,在它們之間區分一個「良好」記錄並聲明所有其他記錄「無效」(可能重複該過程的字段與描述不同)。

但是,有一個問題。 目前,您無法從表中刪除並從子查詢http://dev.mysql.com/doc/refman/5.0/en/delete.html)中的同一表中進行選擇。因此,需要一點點的解決方法:

CREATE TEMPORARY TABLE Dups AS 
    SELECT Product, COUNT(*) AS Duplicates 
     FROM ProductTable GROUP BY Product HAVING Duplicates > 1; 

DELETE ProductTable FROM ProductTable JOIN Dups USING (Product) 
    WHERE Description IS NULL; 

現在這將刪除所有無效的記錄,只要他們出現在複本表。

因此,我們的CBPD14記錄將保持不變,因爲它不會出現在那裏。 CBPD10的「良好」記錄將保持不變,因爲它的描述不是NULL。所有其他 - 噗。

再次讓我的狀態,如果一個記錄有沒有有效記錄,但重複,然後所有副本該記錄的會被殺死 - 不會有幸存者

爲了避免這種情況,可以先將表示這種失敗模式的行放入另一個臨時表中,然後在刪除後將它們重新插入到主表中(在上面的查詢中,檢查「哪個不應該返回」)使用交易可能是有序的)。

+0

將嘗試並儘快給出一個反饋,謝謝 – Sypress 2012-07-15 12:00:46

+0

朋友,我試圖根據你的方法,如果可能的話,請提供樣本3-5行代碼基於你思考和總結。將不勝感激。 – Sypress 2012-07-15 14:55:57

+1

可以。我會包括一個小例子,以確定我理解你的問題。刪除大量的數據總是讓我感到緊張:-) – LSerni 2012-07-15 15:38:02

-2

試試這個:

DELETE FROM TblProducts  
WHERE Product IN 
     (
    SELECT Product 
    FROM TblProducts 
    GROUP BY Product 
    HAVING COUNT(*) > 1) 

這有缺陷,它會刪除所有記錄與一個重複的產品。你可能想要做的只是刪除給定產品的每一組記錄中的一個。將所有重複項首先複製到單獨的表中,然後以某種方式從該表中刪除重複項,然後應用上述內容,然後將剩餘的產品複製回原始表可能是值得的。

+0

將嘗試請儘快給我一個反饋,謝謝 – Sypress 2012-07-15 12:00:40

+0

這個執行速度真的很慢!它差不多半個小時了...... – Sypress 2012-07-15 12:34:21

+2

Walter,如果你知道它會把表中的每一個產品都打開,你甚至會發布代碼有重複(包括該操作需要保留的重複)?希望Sypress或者在執行OR之前閱讀代碼下面的段落,或者有最近的完整備份... – brian 2012-07-15 19:39:39

1

通過編寫舊錶並重命名來創建新表。還要將舊錶中的所有對象(索引等)編寫爲新的。將保持器插入新表中。如果您的數據庫處於批量記錄或簡單恢復模式,則該操作將被最小化記錄。放下舊桌子,然後將新桌子重新命名爲舊名稱。

這比刪除的好處是插入可以被最小化記錄。刪除做雙重工作,因爲不僅數據被刪除,而且刪除必須寫入事務日誌。對於大表,插入最少的插入將比刪除快得多。

1

如果它不是那麼大,你有一些宕機時間,並且你有Sql服務器管理工​​作室,你可以使用GUI在桌子上放一個標識字段。現在你有像CTE這樣的情況,除了行本身是真正獨特的。所以現在你可以做到以下幾點

SELECT MIN(table_a.MyTempIDField) 
FROM 
table_a lhs 
join table_1 rhs 
on lhs.field1 = rhs.field1 
and lhs.field2 = rhs.field2 [etc] 
WHERE 
table_a.MyTempIDField <> table_b.MyTempIDField 
GROUP BY 
lhs.field1, rhs.field2 etc 

這給你所有'好'重複。現在你可以用DELETE FROM查詢來包裝這個查詢。

DELETE FROM lhs 
FROM table_a lhs 
join table_b rhs 
on lhs.field1 = rhs.field1 
and lhs.field2 = rhs.field2 [etc] 
WHERE 
lhs.MyTempIDField <> rhs.MyTempIDField 
and lhs.MyTempIDField not in (

SELECT MIN(lhs.MyTempIDField) 
FROM 
table_a lhs 
join table_a rhs 
on lhs.field1 = rhs.field1 
and lhs.field2 = rhs.field2 [etc] 
WHERE 
lhs.MyTempIDField <> rhs.MyTempIDField 
GROUP BY 
    lhs.field1, lhs.field2 etc 
) 
+0

嗨,謝謝你會試試這個,你認爲這是Compact版嗎? – Sypress 2012-07-15 19:36:19

+0

在語言方面應該不重要,如果需要,可以通過腳本非常輕鬆地添加標識行。 – 2012-07-15 23:24:35