2013-05-08 88 views
22

我正在清理沒有主鍵的數據庫表(我知道,我知道,他們在想什麼?)。我無法添加主鍵,因爲列中有重複項,會成爲主鍵。重複值來自兩行在所有方面相同的行之一。我不能通過GUI刪除行(在本例中爲MySQL Workbench,但我正在尋找一種數據庫不可知的方法),因爲它拒絕在沒有主鍵(或至少一個UQ NN列)的表上執行任務,並且我無法添加主鍵,因爲列中有重複項,會成爲主鍵。重複值來自一個...如何刪除兩個完全相同的行之一?

如何刪除雙胞胎之一?

+0

這樣的重複有多少呢? – Alnitak 2013-05-08 12:12:09

+0

@Alnitak在這個問題的原始上下文中,只有一兩個 - 我剛剛發現了另一個帶* loaded * duplciates的表,其中像http://stackoverflow.com/a/3777663/236081這樣的策略可能會更多適當的 – d3vid 2013-05-08 13:01:05

回答

19

一個選項來解決你的問題是創建一個新表具有相同的架構,然後執行:

INSERT INTO new_table (SELECT DISTINCT * FROM old_table) 

,然後只需重命名錶。

您當然需要大約相同數量的空間,因爲您的磁盤需要備份磁盤才能完成此操作!

效率不高,但它非常簡單。

+0

我仍然認爲我的同事的解決方案(在我的答案)是整潔的,但你的確是超級簡單,你解釋的注意事項好, – d3vid 2013-05-08 14:05:19

+0

嗯,這實際上沒有工作:(爲某些原因new_table包含了old_table中的所有內容 - 任何想法? – d3vid 2013-05-10 12:42:05

+0

我剛剛在一張小表上嘗試過它,它的工作原理與預期一致 – Alnitak 2013-05-10 13:15:00

1

在我的情況下,我可能得到GUI給我一串問題行的值(或者,我可以這樣做手工)。在同事的建議下,在其債務我仍然,我用它來創建一個INSERT語句:

INSERT 
'ID1219243408800307444663', '2004-01-20 10:20:55', 'INFORMATION', 'admin' (...) 
INTO some_table; 

我測試的INSERT語句,所以,我現在生下三胞胎。最後,我跑了一個簡單的DELETE鍵刪除所有的人......

DELETE FROM some_table WHERE logid = 'ID1219243408800307444663'; 

隨後的插入的一個更多的時間,讓我用一個單一的行,主鍵的明亮的可能性。

19

注意,MySQL有自己的擴展DELETE,這是DELETE ... LIMIT,這在平時的工作方式你會從LIMIT期望:http://dev.mysql.com/doc/refman/5.0/en/delete.html

的MySQL特定LIMIT ROW_COUNT選項刪除告訴服務器 控制返回到 客戶端之前要刪除的最大行數。這可以用來確保給定的DELETE語句 不會花費太多時間。您可以簡單地重複DELETE 語句,直到受影響的行數小於LIMIT 值。

因此,你可以使用DELETE FROM some_table WHERE x="y" AND foo="bar" LIMIT 1;請注意,有沒有一個簡單的方法,說「刪除一切,除了一個」 - 只是不停地檢查你是否還有一行重複。

+0

這是很好的知道;我試圖以數據庫不可知的方式提出我的問題,因爲,誰知道,下次我可能會使用PostgreSQL,你知道這是一個常見的擴展嗎? – d3vid 2013-05-08 12:42:09

+0

僅限於MySQL,對不起。(我還沒有看到這個在其他地方執行) – Piskvor 2013-05-08 12:46:31

1

的情況下,你可以像

ALTER TABLE yourtable ADD IDCOLUMN bigint NOT NULL IDENTITY (1, 1) 

這麼做添加一列。

然後按您的問題列對計數行進行計數,其中count> 1,這將識別您的雙胞胎(或三胞胎或其他)。

然後選擇您的問題列,其內容等於上面標識的內容並檢查IDCOLUMN中的ID。

從您的表中刪除IDCOLUMN等於這些ID之一。

35
SET ROWCOUNT 1 
DELETE FROM [table] WHERE .... 
SET ROWCOUNT 0 

這隻會刪除兩個相同的行

+2

迄今爲止最簡單的解決方案..不需要重複的表或改變當前表像所有其他建議。 – woony 2015-06-25 12:15:53

+3

更簡單會是'DELETE TOP 1 FROM ...'。如果使用SQL Server,以後還會更好:[備註 - 重要 - 使用SET ROWCOUNT不會影響DELETE,INSERT和UPD在未來版本的SQL Server中的ATE語句](https://technet.microsoft.com/en-us/library/ms188774.aspx) – ToolmakerSteve 2015-07-11 23:37:50

0

我添加了一個GUID列表中的一個,並設置它生成每行一個新的ID。然後我可以使用GUI刪除行。

2

刪除top(1)適用於Microsoft SQL Server(T-SQL)。

2

這可以通過使用CTE和ROW_NUMBER()功能,以下來完成:

/* Sample Data */ 
    CREATE TABLE #dupes (ID INT, DWCreated DATETIME2(3)) 

    INSERT INTO #dupes (ID, DWCreated) SELECT 1, '2015-08-03 01:02:03.456' 
    INSERT INTO #dupes (ID, DWCreated) SELECT 2, '2014-08-03 01:02:03.456' 
    INSERT INTO #dupes (ID, DWCreated) SELECT 1, '2013-08-03 01:02:03.456' 

/* Check sample data - returns three rows, with two rows for ID#1 */ 
    SELECT * FROM #dupes 

/* CTE to give each row that shares an ID a unique number */ 
    ;WITH toDelete AS 
     (
     SELECT ID, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY DWCreated) AS RN 
     FROM #dupes 
    ) 

    /* Delete any row that is not the first instance of an ID */ 
    DELETE FROM toDelete WHERE RN > 1 

/* Check the results: ID is now unique */ 
    SELECT * FROM #dupes 

/* Clean up */ 
    DROP TABLE #dupes 

有一列ORDER BY是很方便的,但沒有必要,除非你有一個偏好該行刪除。這也將處理重複記錄的所有實例,而不是一次強制刪除一行。

+0

這裏最好的解決方案恕我直言。 :) – 2018-02-05 11:31:54

6

對於PostgreSQL,你可以這樣做:

DELETE FROM tablename 
WHERE id IN (SELECT id 
      FROM (SELECT id, ROW_NUMBER() 
       OVER (partition BY column1, column2, column3 ORDER BY id) AS rnum 
       FROM tablename) t 
      WHERE t.rnum > 1); 

列1,列2,欄3將列集具有重複的值。

參考here

+1

雖然這可能在理論上回答這個問題,[這將是更可取的](/ meta.stackoverflow.com/q/8259)在這裏包括答案的基本部分,並提供鏈接供參考。 – 2016-11-07 13:53:34

+0

感謝您的提示,編輯我的答案:) – 2016-11-07 13:56:22

+0

這是否仍然需要'id'是唯一的行之間? – Narfanator 2017-05-18 19:29:54

3

嘗試限制1?這隻會刪除符合您DELETE查詢

DELETE FROM `table_name` WHERE `column_name`='value' LIMIT 1; 
0

,該行1在PostgreSQL的有一個叫ctid隱含列。請參閱wiki。所以,你可以自由地使用以下命令:

WITH cte1 as(
    SELECT unique_column, max(ctid) as max_ctid 
    FROM table_1 
    GROUP BY unique_column 
    HAVING count(*) > 1 
), cte2 as(
    SELECT t.ctid as target_ctid 
    FROM table_1 t 
    JOIN cte1 USING(unique_column) 
    WHERE t.ctid != max_ctid 
) 
DELETE FROM table_1 
WHERE ctid IN(SELECT target_ctid FROM cte2) 

我不知道它是如何安全地使用這個時候有併發更新的可能性。所以人們可能會發現在實際進行清理之前製作一個LOCK TABLE table_1 IN ACCESS EXCLUSIVE MODE;是明智的。

1

您可以使用最大值,這與我的情況相關。

DELETE FROM [table] where id in 
(select max(id) from [table] group by id, col2, col3 having count(id) > 1) 

務必首先測試您的結果,並在您的「擁有」條款中有一個限制條件。有了這麼大的刪除查詢,您可能需要先更新數據庫。

1

這適用於PostgreSQL的

DELETE FROM tablename WHERE id = 123 AND ctid IN (SELECT ctid FROM tablename WHERE id = 123 LIMIT 1)