2017-06-01 71 views
0

我一直在尋找一種方法來使用一些自定義邏輯刪除MySQL數據庫中的一些重複行。使用自定義邏輯刪除重複行

實際DATAS:

id name population 
1 CityA 1000 
2 CityA 50 
3 CityA 0 
4 CityB 0 
5 CityB 0 
6 CityC 10 

期望的結果:

id name population 
1 CityA 1000 
4 CityB 0 
6 CityC 10 

我嘗試這個查詢沒有成功(已刪除所有行了,如果所有人羣都等於0,像在一個城市CityB爲例):

DELETE t 
FROM table AS t, table AS t2 
WHERE t.id != t2.id 
AND t.population <= t2.population 

可以在任何超級英雄解決這個蘇佩問題?

[編輯]工作液http://sqlfiddle.com/#!9/ea3e3/2

+0

如果你有最多的人口數相同的名字,那麼你就需要讓他們全部或只有一個? –

+0

我想在這種情況下只保留一行(不關心所選行) –

回答

2

你可以用與每個城市最高的人口返回該行的ID的子查詢的聯接。

DELETE t1 
FROM YourTable AS t1 
JOIN (SELECT name, MAX(id) AS maxid 
     FROM YourTable AS t2 
     JOIN (SELECT name, MAX(population) AS maxpop 
      FROM YourTable 
      GROUP BY name) AS t3 
     ON t2.name = t3.name AND t2.population = t3.maxpop 
     GROUP BY t2.name) AS t4 
ON t1.name = t4.name AND t1.id != t4.maxid 

我需要一個額外的子查詢嵌套級別,因爲你有多個名字相同的人口。因此,首先需要獲得每個名稱的最大人口數量,然後使用MAX(id)在該組內選擇一個特定ID。

+0

我剛試過你的解決方案,它已經刪除了所有行; p –

+0

@GuillaumeSTLR spencer7593是正確的,我修復了查詢。 – Barmar

+0

@ spencer7593正確。另一種方法是使用'WHERE id NOT IN(子查詢返回所有的maxid)'。 – Barmar

1

看起來像你想在城市name列「匹配」。

首先編寫一條SELECT語句,然後在將其轉換爲DELETE語句之前對其進行測試。

SELECT d.* 
    FROM table d 
    JOIN table k 
    ON k.name  = d.name 
    AND k.population > d.population 
    AND k.id   <> d.id 

我們要保持從k的行,並從d刪除行。

通過用DELETE替換SELECT關鍵字,將其轉換爲DELETE語句。

請注意,如果城市中存在多個具有相同「最高」人口的行,則此查詢不會識別這些行。爲了擺脫具有相同人口價值的「重複」,我們需要一個稍微不同的方法。

我會使用反連接:

SELECT d.* 
    FROM table d 
    LEFT 
    JOIN (SELECT MIN(r.id) AS min_id 
      FROM (SELECT t.name 
         , MAX(t.population) AS max_pop 
        FROM table t 
        GROUP BY t.name 
       ) s 
      JOIN table r 
      ON r.name  = s.name 
      AND r.population = s.max_pop 
      GROUP BY r.name 
     ) q 
    ON q.min_id = d.id 
WHERE q.min_id IS NULL 

內嵌視圖q應該返回id值的列表,從我們要保持的行。任何不在該列表中的id行都是我們想要刪除的行。

如果MySQL在內聯視圖中引用表引用,我們可以將其換行爲另一個內聯視圖作爲解決方法。

SELECT d.* 
    FROM table d 
    LEFT 
    JOIN (SELECT q.min_id 
      FROM (SELECT MIN(r.id) AS min_id 
        FROM (SELECT t.name 
           , MAX(t.population) AS max_pop 
          FROM table t 
          GROUP BY t.name 
         ) s 
        JOIN table r 
         ON r.name  = s.name 
        AND r.population = s.max_pop 
        GROUP BY r.name 
       ) q 
     ) p 
    ON p.min_id = d.id 
WHERE p.min_id IS NULL 

通過用DELETE關鍵字替換最外面的SELECT關鍵字,將其轉換爲DELETE語句。

+0

請參閱我對「不同方法」的回答。 – Barmar

+0

@ spencer7593:非常感謝您的幫助。然而,我專注於Barmar解決方案,它只是工作;-) –

0
CREATE TABLE new_table (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
    name VARCHAR(30), 
    population INT 
); 

INSERT INTO new_table (name, population) 
SELECT old.name, MAX(old.population) 
FROM current_table old 
GROUP BY old.name; 

RENAME TABLE current_table TO archive_table 
, new_table TO current_table; 

然後,一旦你已經檢查數據

DROP TABLE archive_table; 
+0

如果有其他表的外鍵指向這個表,這些ID將會因此而改變。雖然這也可能是'DELETE'方法的問題,因爲當相關行被刪除時它們將變爲無效。 – Barmar

+0

如果Guillame的數據庫中有數百萬行,那麼就地刪除在鎖定方面會出現複雜情況。但我懷疑這不適用。 – symcbean

+0

這是geonames數據庫,所以可能有數百萬行是 –