2015-05-12 78 views
0

我有一個擁有7000多條記錄的數據庫。事實證明,這些記錄中有多個重複項。我發現了幾條關於如何刪除重複項並只保留1條記錄的建議。 但在我的情況下情況有點複雜:如果他們持有與另一個記錄相同的數據,個案不是簡單的重複。相反,有幾個案例完全可以保存相同的數據。只有當它們持有相同的數據並且都在30秒內插入時,它們才被標記爲複製。刪除MySQL數據庫中的重複數

因此我需要重複刪除一個SQL語句(如:所有領域,除iddatetime)如果已經插入40秒內範圍(例如:評估datetime場)。

因爲我只是一個SQL專家,無法在網上找到合適的解決方案,所以我真的希望你們中的一些人能幫助我並指出我朝着正確的方向發展。這將非常感激!

表結構如下:

CREATE TABLE IF NOT EXISTS `wp_ttr_results` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `user_id` int(11) NOT NULL, 
    `schoolyear` varchar(10) CHARACTER SET utf8 DEFAULT NULL, 
    `datetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
    `area` varchar(15) CHARACTER SET utf8 NOT NULL, 
    `content` varchar(10) CHARACTER SET utf8 NOT NULL, 
    `types` varchar(100) CHARACTER SET utf8 NOT NULL, 
    `tasksWrong` varchar(300) DEFAULT NULL, 
    `tasksRight` varchar(300) DEFAULT NULL, 
    `tasksData` longtext CHARACTER SET utf8, 
    `parent_id` varchar(20) DEFAULT NULL, 
    UNIQUE KEY `id` (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=68696 ; 

因此,只要再次澄清,一個重複的情況下,這樣的情況是:

[1]保持相同的數據作爲另一種情況下用於所有領域,除了iddatetime字段

[2]插入在DB,根據datetime字段,另一個記錄40秒內用相同的值

如果滿足兩個條件,則應刪除除1之外的所有情況。

+0

這個ID總是不同的吧?所以重複意味着所有的列,但ID號? – Juru

+0

@Juru ID的確總是不同的(因此auto_increment)。重複保存相同的數據,除了ID和日期時間字段BUT插入其他記錄保存相同數據的40秒內(再次,除了ID和日期時間字段) –

+0

您的問題有一個非常特殊的困難。這是40秒的過濾。如果考慮重複項d1(1),d2(30),d3(41)(數字是創建秒數)。 d2會與d1重複,但d3不會。但是d3對於d2是重複的。那麼d3是否重複?對我而言,只有d2將不得不被刪除,這使得這個問題很難解決。 – Juru

回答

0

威力的工作,但它可能不會非常快...

DELETE FROM dupes 
USING wp_ttr_results AS dupes 
    INNER JOIN wp_ttr_results AS origs 
     ON dupes.field1 = origs.field1 
     AND dupes.field2 = origs.field2 
     AND .... 
     AND AS dupes.id <> origs.id 
     AND dupes.`datetime` BETWEEN orig.`datetime` AND (orig.`datetime` + INTERVAL 40 SECOND) 
; 
+0

我想你會刪除一切。您必須檢查dupes.id是否與origs.id不同,因爲具有相同主鍵的行在定義上相同但不重複。第二個問題是,您需要保留一行重複項,而現在刪除作爲重複項集合一部分的每一行。 – Juru

+0

@Juru哎呀,很好,現在編輯。 – Uueerdo

3

由於@Juru在評論中指出,我們需要相當多的手術切開刀切這一塊。但是可以通過存儲過程以迭代的方式完成此操作。

首先,我們使用一個自聯接,以確定第一個重複的每一個記錄,這本身就不是一個重複:

SELECT DISTINCT 
    MIN(postdups.id AS id) 
FROM wp_ttr_results AS base 
INNER JOIN wp_ttr_results AS postdups 
    ON base.id<postdups.id 
    AND UNIX_TIMESTAMP(postdups.datetime)-UNIX_TIMESTAMP(base.datetime)<40 
    AND base.user_id=postdups.user_id 
    AND base.schoolyear=postdups.schoolyear 
    AND base.area=postdups.area 
    AND base.content=postdups.content 
    AND base.types=postdups.types 
    AND base.tasksWrong=postdups.tasksWrong 
    AND base.tasksRight=postdups.tasksRight 
    AND base.parent_id=postdups.user_id 
LEFT JOIN wp_ttr_results AS predups 
    ON base.id>predups.id 
    AND UNIX_TIMESTAMP(base.datetime)-UNIX_TIMESTAMP(predups.datetime)<40 
    AND base.user_id=predups.user_id 
    AND base.schoolyear=predups.schoolyear 
    AND base.area=predups.area 
    AND base.content=predups.content 
    AND base.types=predups.types 
    AND base.tasksWrong=predups.tasksWrong 
    AND base.tasksRight=predups.tasksRight 
    AND base.parent_id=predups.user_id 
WHERE predups.id IS NULL 
GROUP BY base.id 
; 

此選擇的所有記錄(base.id<postdups.id)最低id,即具有與現有記錄相同的有效負載,並且在40s窗口內(UNIX_TIMESTAMP(dups.datetime)-UNIX_TIMESTAMP(base.datetime)< 40),但是跳過那些基本記錄,這些記錄本身是重複的。在Juru的例子中,:30記錄會被擊中,因爲它是:00記錄的副本,本身不是重複記錄,但:41記錄不會被擊中,因爲它僅與:30重複,本身是重複的:00

我們有

現在我們要刪除這個紀錄 - 因爲MySQL不能從表中正在讀取刪除,我們必須使用一個變量來實現這一目標:

CREATE TEMPORARY TABLE cleanUpDuplicatesTemp SELECT DISTINCT 
    -- as above 
; 
DELETE FROM wp_ttr_results 
WHERE id IN 
    (SELECT id FROM cleanUpDuplicatesTemp) 
; 
DROP TABLE cleanUpDuplicatesTemp 
; 

到現在爲止,我們將刪除每個記錄的第一個副本,在可能發生變化的過程中,會被認爲是重複的...

最後,我們必須循環執行此過程,如果SELECT DISTINCT不返回任何內容,則退出循環。

全部放在一起變成一個存儲proceedure:

DELIMITER ;; 
CREATE PROCEDURE cleanUpDuplicates() 
BEGIN 
    DECLARE numDuplicates INT; 
    iterate: LOOP 
    DROP TABLE IF EXISTS cleanUpDuplicatesTemp; 
    CREATE TEMPORARY TABLE cleanUpDuplicatesTemp 
    SELECT DISTINCT 
     MIN(postdups.id AS id) 
    FROM wp_ttr_results AS base 
    INNER JOIN wp_ttr_results AS postdups 
     ON base.id<postdups.id 
     AND UNIX_TIMESTAMP(postdups.datetime)-UNIX_TIMESTAMP(base.datetime)<40 
     AND base.user_id=postdups.user_id 
     AND base.schoolyear=postdups.schoolyear 
     AND base.area=postdups.area 
     AND base.content=postdups.content 
     AND base.types=postdups.types 
     AND base.tasksWrong=postdups.tasksWrong 
     AND base.tasksRight=postdups.tasksRight 
     AND base.parent_id=postdups.user_id 
    LEFT JOIN wp_ttr_results AS predups 
     ON base.id>predups.id 
     AND UNIX_TIMESTAMP(base.datetime)-UNIX_TIMESTAMP(predups.datetime)<40 
     AND base.user_id=predups.user_id 
     AND base.schoolyear=predups.schoolyear 
     AND base.area=predups.area 
     AND base.content=predups.content 
     AND base.types=predups.types 
     AND base.tasksWrong=predups.tasksWrong 
     AND base.tasksRight=predups.tasksRight 
     AND base.parent_id=predups.user_id 
    WHERE predups.id IS NULL 
    GROUP BY base.id; 
    SELECT COUNT(*) INTO numDuplicates FROM cleanUpDuplicatesTemp; 
    IF numDuplicates<=0 THEN 
     LEAVE iterate; 
    END IF; 
    DELETE FROM wp_ttr_results 
    WHERE id IN 
     (SELECT id FROM cleanUpDuplicatesTemp) 
    END LOOP iterate; 
    DROP TABLE IF EXISTS cleanUpDuplicatesTemp; 
END;; 
DELIMITER ; 

現在簡單CALL cleanUpDuplicates;應該做的伎倆。

+0

仍然有問題。假設您在不考慮40秒的過濾時有重複的記錄d1,d2和d3。 d1在10:00:00創建,d2創建在10:00:30,d3創建在10:00:41。對於d2 d3是重複的,但對於d1而言不是。所以D2是重複的,但D3不是。 – Juru

+0

@Juru不僅「好抓」,而且非常出色 - 感謝你!我會重新考慮。 –

+0

我不認爲這在SQL中是可行的。在程序中要容易得多,每個被識別爲非重複的記錄都應被視爲過濾掉未來40秒內出現的重複記錄的基礎。 – Juru