【问题标题】:MYSQL delete all rows, but the same query on select return only 3 rowsMYSQL 删除所有行,但 select 上的相同查询仅返回 3 行
【发布时间】:2019-11-05 13:49:30
【问题描述】:

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

查看代码

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

现在我需要删除 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 |
# +----+------------+---------------------+

现在我使用删除操作

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

我哭了;

【问题讨论】:

  • 既然有id,为什么还要使用@row_number
  • ID可能与row_number不同,例如ID可以以1000开头...
  • 我认为您需要重新表述您的问题。
  • 我会避免在查询中更新用户变量。它们何时更新将取决于查询计划。在这种情况下,SELECT 查询中的 IN 子查询将转换为半连接,而在单表 DELETE 中则不会。如果您使用多表 DELETE 语法(请参阅oysteing.blogspot.com/2014/07/…),您可能会获得相同的查询计划,但我建议您应该查看 MySQL 8.0 中引入的 windows 函数。
  • 谢谢,@oysteing,我像你写的那样尝试:DELETE part FROM part WHERE p_partkey IN ... 它有帮助。我很纳闷,可惜我的知识还不够。

标签: mysql sql sql-delete


【解决方案1】:

我们可以在这里尝试做一个删除限制加入:

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

【讨论】:

    【解决方案2】:

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

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

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

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-02
      • 2021-08-17
      • 2015-01-22
      • 1970-01-01
      相关资源
      最近更新 更多