MYSQL удаляет все строки, но тот же запрос на select возвращает только 3 строки - PullRequest
1 голос
/ 23 июня 2019

Я не могу понять, почему один и тот же запрос для выбора и удаления имеет разное поведение.
Мне нужно удалить все строки, кроме 5 новых строк.
Я знаю, что мое решение для этой задачи не годится, но моевопрос в том, почему MySQL не удаляет те же строки, которые возвращают 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 |
    #     +----+------------+---------------------+

теперь мне нужно удалить строки с идентификатором 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 |
# +----+-----+------------+

Я плачу;

Ответы [ 2 ]

2 голосов
/ 23 июня 2019

Мы можем попробовать выполнить объединение с ограничением на удаление здесь:

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 голос
/ 23 июня 2019

Получите максимальный идентификатор для удаления, используя LIMIT и OFFSET:

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

Затем удалите все строки с идентификатором, равным или меньшим указанного значения:

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);

(Обратите внимание, что вам нужно заключить результат подзапроса в производную таблицу, чтобы избежать ошибки: «Нельзя указывать целевую таблицу« tbl »дляобновление в предложении FROM ".)

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

...