如何實現 MySQL 刪除重複記錄而且只保留一條


點擊上方 java項目開發選擇 設爲星標前端

優質項目,及時送達vue

-----java

做者:千gmysql

blog.csdn.net/n950814abc/article/details/82284838react

最近在作題庫系統,因爲在題庫中添加了重複的試題,因此須要查詢出重複的試題,而且刪除掉重複的試題只保留其中1條,以保證考試的時候抽不到重複的題。web

首先寫了一個小的例子:面試

1、單個字段的操做

這是數據庫中的表:spring

分組介紹:

Select 重複字段 From 表 Group By 重複字段 Having Count(*)>1

查看是否有重複的數據:sql

  • GROUP BY <列名序列>數據庫

  • HAVING <組條件表達式>

查詢出:根據dname分組,同時知足having字句中組條件表達式(重複次數大於1)的那些組

count(*)與count(1) 其實沒有什麼差異,用哪一個均可以

count(*)與count(列名)的區別:

count(*)將返回表格中全部存在的行的總數包括值爲null的行,然而count(列名)將返回表格中除去null之外的全部行的總數(有默認值的列也會被計入)

1. 查詢所有重複的數據:

Select * From 表 Where 重複字段 In (Select 重複字段 From 表 Group By 重複字段 Having Count(*)>1)

2. 刪除所有重複試題:

將上面的查詢select改成delete(這樣會出錯的)

DELETE
FROM
 dept
WHERE
 dname IN (
  SELECT
   dname
  FROM
   dept
  GROUP BY
   dname
  HAVING
   count(1) > 1
 )

會出現以下錯誤:[Err] 1093 - You can't specify target table 'dept' for update in FROM clause

緣由是:更新這個表的同時又查詢了這個表,查詢這個表的同時又去更新了這個表,能夠理解爲死鎖。mysql不支持這種更新查詢同一張表的操做

解決辦法:把要更新的幾列數據查詢出來作爲一個第三方表,而後篩選更新。

3. 查詢表中多餘重複試題(根據depno來判斷,除了rowid最小的一個)

a. 第一種方法:

SELECT
 *
FROM
 dept
WHERE
 dname IN (
  SELECT
   dname
  FROM
   dept
  GROUP BY
   dname
  HAVING
   COUNT(1) > 1
 )
AND deptno NOT IN (
 SELECT
  MIN(deptno)
 FROM
  dept
 GROUP BY
  dname
 HAVING
  COUNT(1) > 1
)

上面這種寫法正確,可是查詢的速度太慢,能夠試一下下面這種方法:

b. 第二種方法:

☆根據dname分組,查找出deptno最小的。而後再查找deptno不包含剛纔查出來的。這樣就查詢出了全部的重複數據(除了deptno最小的那行)。搜索Java知音公衆號,回覆「後端面試」,送你一份Java面試題寶典.pdf

SELECT *
FROM
 dept
WHERE
 deptno NOT IN (
  SELECT
   dt.minno
  FROM
   (
    SELECT
     MIN(deptno) AS minno
    FROM
     dept
    GROUP BY
     dname
   ) dt
 )

c. 補充第三種方法:

SELECT
 * 
FROM
 table_name AS ta 
WHERE
 ta.惟一鍵 <> ( SELECT max( tb.惟一鍵 ) FROM table_name AS tb WHERE ta.判斷重複的列 = tb.判斷重複的列 );

4. 刪除表中多餘重複試題而且只留1條:

a. 第一種方法:

DELETE
FROM
 dept
WHERE
 dname IN (
  SELECT
   t.dname
  FROM
   (
    SELECT
     dname
    FROM
     dept
    GROUP BY
     dname
    HAVING
     count(1) > 1
   ) t
 )
AND deptno NOT IN (
SELECT
 dt.mindeptno
FROM
 (
  SELECT
   min(deptno) AS mindeptno
  FROM
   dept
  GROUP BY
   dname
  HAVING
   count(1) > 1
 ) dt
)

b. ☆第二種方法(與上面查詢的第二種方法對應,只是將select改成delete):

DELETE
FROM
 dept
WHERE
 deptno NOT IN (
  SELECT
   dt.minno
  FROM
   (
    SELECT
     MIN(deptno) AS minno
    FROM
     dept
    GROUP BY
     dname
   ) dt
 )

c. 補充第三種方法(評論區推薦的一種方法):

DELETE 
FROM
 table_name AS ta 
WHERE
 ta.惟一鍵 <> (
SELECT
 t.maxid 
FROM
 ( SELECT max( tb.惟一鍵 ) AS maxid FROM table_name AS tb WHERE ta.判斷重複的列 = tb.判斷重複的列 ) t 
 );

雙11了,今天開始,限量秒殺

1核2G,1年62元,3年200元,秒殺

1核2G,1年62元,3年200元,秒殺

1核2G,1年62元,3年200元,秒殺

2、多個字段的操做:

單個字段的若是會了,多個字段也很是簡單。就是將group by 的字段增長爲你想要的便可。搜索Java知音公衆號,回覆「後端面試」,送你一份Java面試題寶典.pdf

此處只寫一個,其餘方法請仿照一個字段的寫便可。

DELETE
FROM
 dept
WHERE
 (dname, db_source) IN (
  SELECT
   t.dname,
   t.db_source
  FROM
   (
    SELECT
     dname,
     db_source
    FROM
     dept
    GROUP BY
     dname,
     db_source
    HAVING
     count(1) > 1
   ) t
 )
AND deptno NOT IN (
 SELECT
  dt.mindeptno
 FROM
  (
   SELECT
    min(deptno) AS mindeptno
   FROM
    dept
   GROUP BY
    dname,
    db_source
   HAVING
    count(1) > 1
  ) dt
)

總結:

其實上面的方法還有不少須要優化的地方,若是數據量太大的話,執行起來很慢,能夠考慮加優化一下:

  • 在常常查詢的字段上加上索引

  • 將*改成你須要查詢出來的字段,不要所有查詢出來

  • 小表驅動大表用IN,大表驅動小表用EXISTS。IN適合的狀況是外表數據量小的狀況,而不是外表數據大的狀況,由於IN會遍歷外表的所有數據,假設a表100條,b表10000條那麼遍歷次數就是100*10000次,而exists則是執行100次去判斷a表中的數據是否在b表中存在,它只執行了a.length次數。至於哪個效率高是要看狀況的,由於in是在內存中比較的,而exists則是進行數據庫查詢操做的

- END -

推薦案例

溫暖提示

爲了方便你們更好的學習,本公衆號常常分享一些完整的單個功能案例代碼給你們去練習, 若是本公衆號沒有你要學習的功能案例,你能夠聯繫小編(微信:xxf960513)提供你的小需求給我,我安排咱們這邊的開發團隊免費幫你完成你的案例。
注意:只能提單個功能的需求不能要求功能太多,好比要求用什麼技術,有幾個頁面,頁面要求怎麼樣?


請長按識別二維碼

想學習更多的java功能案例請關注

Java項目開發

若是你以爲這個案例以及咱們的分享思路不錯,對你有幫助,請分享給身邊更多須要學習的朋友。別忘了《留言+點在看》給做者一個鼓勵哦!

本文分享自微信公衆號 - web項目開發(javawebkaifa)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索