简体   繁体   English

MYSQL删除所有行,但select上的相同查询只返回3行

[英]MYSQL delete all rows, but the same query on select return only 3 rows

I can't understand why the same query for select and delete have different behavior. 我无法理解为什么select和delete的相同查询具有不同的行为。
I need to delete all rows except 5 newest rows. 我需要删除除5个最新行之外的所有行。
I know my solution for this task is no good, but my question is about why MySQL no delete the same rows, that return select for the same query clause 我知道我的这个任务的解决方案并不好,但我的问题是为什么MySQL没有删除相同的行,返回select为同一个查询子句

see code 看代码

drop table if exists tbl;
create table tbl
(
    id         serial,
    cal        date COMMENT 'some column',
    created_at datetime default NOW()
);

insert into tbl
values
       (default, '2018-07-15', '2018-07-15 12:00'),
       (default, '2018-07-16', '2018-07-16 12:00'),
       (default, '2018-07-17', '2018-07-17 12:00'),
       (default, '2018-07-18', '2018-07-18 12:00'),
       (default, '2018-08-01', '2018-08-01 12:00'),
       (default, '2018-08-04', '2018-08-04 12:00'),
       (default, '2018-08-16', '2018-08-16 12:00'),
       (default, '2018-08-17', '2018-08-17 12:00');

  select *
  from tbl;

    #     +----+------------+---------------------+
    #     | id | cal        | created_at          |
    #     +----+------------+---------------------+
    #     | 1  | 2018-07-15 | 2018-07-15 12:00:00 |
    #     | 2  | 2018-07-16 | 2018-07-16 12:00:00 |
    #     | 3  | 2018-07-17 | 2018-07-17 12:00:00 |
    #     | 4  | 2018-07-18 | 2018-07-18 12:00:00 |
    #     | 5  | 2018-08-01 | 2018-08-01 12:00:00 |
    #     | 6  | 2018-08-04 | 2018-08-04 12:00:00 |
    #     | 7  | 2018-08-16 | 2018-08-16 12:00:00 |
    #     | 8  | 2018-08-17 | 2018-08-17 12:00:00 |
    #     +----+------------+---------------------+

now I need delete rows with id 1,2,3 现在我需要删除ID为1,2,3的行

SET @row_number = 0;
select *
from tbl
where tbl.id in (
    select T.id
    from (SELECT (@row_number := @row_number + 1) as num, tbl.id
          from tbl
          order by created_at desc
         ) as T
    where T.num > 5);


# +----+------------+---------------------+
# | id | cal        | created_at          |
# +----+------------+---------------------+
# | 3  | 2018-07-17 | 2018-07-17 12:00:00 |
# | 2  | 2018-07-16 | 2018-07-16 12:00:00 |
# | 1  | 2018-07-15 | 2018-07-15 12:00:00 |
# +----+------------+---------------------+

Now I use delete operation 现在我使用删除操作

SET @row_number = 0;
delete
from tbl
where tbl.id in (
    select T.id
    from (SELECT (@row_number := @row_number + 1) as num, tbl.id
          from tbl
          order by created_at desc
         ) as T
    where T.num > 5);

select * from tbl; # <-- result empty
# +----+-----+------------+
# | id | cal | created_at |
# +----+-----+------------+

I cry; 我哭了;

We can try doing a delete limit join here: 我们可以尝试在这里执行删除限制连接:

DELETE t1
FROM tbl t1
LEFT JOIN
(
    SELECT id
    FROM tbl
    ORDER BY created_at DESC
    LIMIT 5
) t2
    ON t1.id = t2.id
WHERE
    t2.id IS NULL;

The idea behind this anti-join is that we will delete any record which does not match to one of the first five records, as ordered descending by the created_at column. 这种反连接背后的想法是,我们将删除任何与前五个记录之一匹配的记录,这些记录按created_at列的顺序递减。

Note that we can't use a WHERE IN query here, because MySQL will return the dreaded error message that LIMIT is not yet supported in this version. 请注意,我们不能WHERE IN此处使用WHERE IN查询,因为MySQL将返回此版本尚不支持LIMIT的可怕错误消息。

Get the highest ID to delete using LIMIT and OFFSET : 使用LIMITOFFSET获取要删除的最高ID:

set @last_id_to_delete = (
  select id
  from tbl
  order by id desc
  limit 1
  offset 5
);

Then delete all rows with ID equal or smaller than the obove value: 然后删除ID等于或小于obove值的所有行:

delete tbl
from tbl
where id <= @last_id_to_delete;

db-fiddle DB-小提琴

You can combine the two queries into one. 您可以将两个查询合并为一个。 Either with a subquery in the WHERE clause: 使用WHERE子句中的子查询:

delete tbl
from tbl
where id <= (select id from(
  select id
  from tbl
  order by id desc
  limit 1
  offset 5
)x);

(Note that you need to wrap the subquery result into a derived table, to avoid the error: "You can't specify target table 'tbl' for update in FROM clause".) (请注意,您需要将子查询结果包装到派生表中,以避免错误:“您无法在FROM子句中为更新指定目标表'tbl'。”

db-fidle DB-FIDLE

or by joining with a single-row subquery: 或者通过加入单行子查询:

delete t
from tbl t
join (
  select id as last_id_to_delete
  from tbl
  order by id desc
  limit 1
  offset 5
) x on t.id <= x.last_id_to_delete;

db-fidle DB-FIDLE

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM