MYSQL删除所有行,但对select的同一查询仅返回3行

时间:2019-06-23 14:47:19

标签: mysql sql sql-delete

我不明白为什么相同的select和delete查询具有不同的行为。
我需要删除除5个最新行以外的所有行。
我知道我对此任务的解决方案不好,但是我的问题是为什么MySQL不能删除相同的行,而返回相同的查询子句

查看代码

(isset ($_SERVER['HTTP_CLIENT_IP']) ? 
    $_SERVER['HTTP_CLIENT_IP'] : 
    (isset ($_SERVER['HTTP_X_FORWARDED_FOR']) ? 
        $_SERVER['HTTP_X_FORWARDED_FOR'] : 
        $_SERVER['REMOTE_ADDR']
    )
)

现在我需要删除ID为1,2,3的行

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 |
    #     +----+------------+---------------------+

现在我使用删除操作

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 |
# +----+------------+---------------------+

我哭了

2 个答案:

答案 0 :(得分:2)

我们可以在此处尝试进行删除限制联接:

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;

此反连接背后的想法是,我们将删除与前五个记录之一不匹配的任何记录,按created_at列的降序排列。

请注意,我们不能在此处使用WHERE IN查询,因为MySQL将返回可怕的错误消息,表明该版本尚不支持LIMIT

答案 1 :(得分:1)

使用LIMITOFFSET获取要删除的最高ID:

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

然后删除ID等于或小于obove值的所有行:

delete tbl
from tbl
where id <= @last_id_to_delete;

db-fiddle

您可以将两个查询合并为一个。在WHERE子句中使用子查询:

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

(请注意,您需要将子查询结果包装到派生表中,以避免出现错误:“您不能在FROM子句中指定目标表'tbl'进行更新”。)

db-fidle

或通过与单行子查询结合:

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