Delete all rows except first N from a table having single column

后端 未结 3 993
醉梦人生
醉梦人生 2021-01-03 01:12

I need a single query. Delete all rows from the table except the top N rows. The table has only one column. Like,

|friends_name|
==============
         


        
3条回答
  •  太阳男子
    2021-01-03 01:46

    I just wanted to follow up on this relatively old question because the existing answers don't capture the requirement and/or are incorrect. The question states the names can be repeated, but only the top N must be preserved. Other answers will delete incorrect rows and/or incorrect number of them.

    For example, if we have this table:

    |friends_name|
    ==============
    | Arunji     |
    | Roshit     |
    | Misbahu    |
    | Misbahu    |
    | Roshit     |
    | Misbahu    |
    | Rohan      |
    

    And we want to delete all but top 3 rows (N = 3), the expected result would be:

    |friends_name|
    ==============
    | Arunji     |
    | Roshit     |
    | Misbahu    |
    

    The DELETE statement from the currently selected answer will result in:

    |friends_name|
    ==============
    | Arunji     |
    | Misbahu    |
    | Misbahu    |
    | Misbahu    |
    

    See this sqlfiddle. The reason for this is that it first sorts names alphabetically, then takes top 3, then deletes all that don't equal that. But since they are sorted by name they may not be the top 3 we want, and there's no guarantee that we'll end up with only 3.

    In the absence of unique indexes and other fields to determine what "top N" means, we go by the order returned by the database. We could be tempted to do something like this (substitute 99999 with however high number):

    DELETE FROM names LIMIT 99999 OFFSET 3
    

    But according to MySQL docs, while the DELETE supports the LIMIT clause, it does not support OFFSET. So, doing this in a single query, as requested, does not seem to be possible; we must perform the steps manually.

    Solution 1 - temporary table to hold top 3

    CREATE TEMPORARY TABLE temp_names LIKE names;
    INSERT INTO temp_names SELECT * FROM names LIMIT 3;
    DELETE FROM names;
    INSERT INTO names SELECT * FROM temp_names;
    

    Here's the sqlfiddle for reference.

    Solution 2 - new table with rename

    CREATE TABLE new_names LIKE names;
    INSERT INTO new_names SELECT * FROM names LIMIT 3;
    RENAME TABLE names TO old_names, new_names TO names;
    DROP TABLE old_names;
    

    Here's the sqlfiddle for this one.

    In either case, we end up with top 3 rows in our original table:

    |friends_name|
    ==============
    | Arunji     |
    | Roshit     |
    | Misbahu    |
    

提交回复
热议问题