MYSQL 删除所有行,但 select return 上的相同查询只有 3 行
MYSQL delete all rows, but the same query on select return only 3 rows
我不明白为什么 select 和删除的相同查询有不同的行为。
我需要删除除 5 个最新行之外的所有行。
我知道我对这个任务的解决方案不好,但我的问题是为什么 MySQL 不删除相同的行,即 return select 对于相同的查询子句
查看代码
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 |
# +----+-----+------------+
我哭了;
我们可以尝试在此处进行删除限制连接:
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;
这个 anti-join 背后的想法是,我们将删除 不 匹配前五个记录之一的任何记录,按 [= 降序排列11=]列。
请注意,我们不能在此处使用 WHERE IN
查询,因为 MySQL 会 return 可怕的错误消息,即此版本尚不支持 LIMIT
.
使用LIMIT
和OFFSET
获取要删除的最高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;
您可以将两个查询合二为一。在 WHERE 子句中使用子查询:
delete tbl
from tbl
where id <= (select id from(
select id
from tbl
order by id desc
limit 1
offset 5
)x);
(注意需要将子查询结果包装成派生的table,避免报错:"You can't specify target table 'tbl' for update in FROM clause"。)
或通过加入 single-row 子查询:
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;
我不明白为什么 select 和删除的相同查询有不同的行为。
我需要删除除 5 个最新行之外的所有行。
我知道我对这个任务的解决方案不好,但我的问题是为什么 MySQL 不删除相同的行,即 return select 对于相同的查询子句
查看代码
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 |
# +----+-----+------------+
我哭了;
我们可以尝试在此处进行删除限制连接:
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;
这个 anti-join 背后的想法是,我们将删除 不 匹配前五个记录之一的任何记录,按 [= 降序排列11=]列。
请注意,我们不能在此处使用 WHERE IN
查询,因为 MySQL 会 return 可怕的错误消息,即此版本尚不支持 LIMIT
.
使用LIMIT
和OFFSET
获取要删除的最高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;
您可以将两个查询合二为一。在 WHERE 子句中使用子查询:
delete tbl
from tbl
where id <= (select id from(
select id
from tbl
order by id desc
limit 1
offset 5
)x);
(注意需要将子查询结果包装成派生的table,避免报错:"You can't specify target table 'tbl' for update in FROM clause"。)
或通过加入 single-row 子查询:
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;