최신 N (id desc로 정렬 됨)을 제외하고 테이블에서 모든 레코드를 제거하기 위해 단일 mysql 쿼리 (변수 없음)를 작성할 수 있습니까?
이런 식으로 작동하지 않습니다. :)
delete from table order by id ASC limit ((select count(*) from table ) - N)
감사.
답변:
그런 식으로 레코드를 삭제할 수 없습니다. 주요 문제는 하위 쿼리를 사용하여 LIMIT 절의 값을 지정할 수 없다는 것입니다.
이것은 작동합니다 (MySQL 5.0.67에서 테스트 됨).
DELETE FROM `table`
WHERE id NOT IN (
SELECT id
FROM (
SELECT id
FROM `table`
ORDER BY id DESC
LIMIT 42 -- keep this many records
) foo
);
중간 하위 쿼리 가 필요합니다. 이것이 없으면 두 가지 오류가 발생합니다.
다행히 중간 하위 쿼리를 사용하면 이러한 제한 사항을 모두 우회 할 수 있습니다.
Nicole은이 쿼리가 특정 사용 사례 (예 :이 사례)에 대해 상당히 최적화 될 수 있다고 지적했습니다. 귀하의 답변에 맞는지 확인하기 위해 그 답변 을 읽는 것이 좋습니다 .
나는 꽤 오래된 질문을 부활시키고 있다는 것을 알고 있지만 최근 에이 문제에 직면했지만 많은 수로 확장되는 것이 필요했습니다 . 기존 성능 데이터가 없었고,이 질문에 상당한 관심이 있었기 때문에 내가 찾은 것을 게시 할 것이라고 생각했습니다.
실제로 작동하는 솔루션은 Alex Barrett의 이중 하위 쿼리 /NOT IN
메서드 ( Bill Karwin의 )와 Quassnoi의LEFT JOIN
방법이었습니다.
불행히도 위의 두 방법 모두 매우 큰 중간 임시 테이블을 만들고 삭제 되지 않는 레코드 수가 많아지면 성능이 빠르게 저하 됩니다.
내가 정한 것은 Alex Barrett의 이중 하위 쿼리 (감사합니다!)를 사용하지만 <=
대신 사용 합니다 NOT IN
.
DELETE FROM `test_sandbox`
WHERE id <= (
SELECT id
FROM (
SELECT id
FROM `test_sandbox`
ORDER BY id DESC
LIMIT 1 OFFSET 42 -- keep this many records
) foo
)
그것은 사용 OFFSET
의 ID를 얻기 위해 N 번째 기록과 그 기록 및 이전의 모든 기록을 삭제합니다.
주문은 이미이 문제 ( ORDER BY id DESC
) 의 가정이므로 <=
완벽하게 적합합니다.
서브 쿼리에 의해 생성 된 임시 테이블에는 N 대신 하나의 레코드 만 포함되므로 훨씬 빠릅니다. .
위의 세 가지 작업 방법과 두 가지 테스트 사례에서 새로운 방법을 테스트했습니다.
두 테스트 사례 모두 10000 개의 기존 행을 사용하는 반면 첫 번째 테스트는 9000 개 (가장 오래된 1000 개 삭제)를 유지하고 두 번째 테스트는 50 개 (가장 오래된 9950 개 삭제)를 유지합니다.
+-----------+------------------------+----------------------+
| | 10000 TOTAL, KEEP 9000 | 10000 TOTAL, KEEP 50 |
+-----------+------------------------+----------------------+
| NOT IN | 3.2542 seconds | 0.1629 seconds |
| NOT IN v2 | 4.5863 seconds | 0.1650 seconds |
| <=,OFFSET | 0.0204 seconds | 0.1076 seconds |
+-----------+------------------------+----------------------+
흥미로운 점은이 <=
방법이 전반적으로 더 나은 성능을 보이지만 실제로는 더 나쁘지 않고 더 많이 유지할수록 더 좋아진다는 것입니다.
ROW_NUMBER()
다음을 사용하여 특정 행 오프셋을 찾을 수 있습니다 . stackoverflow.com/questions/603724/…
불행하게도, 당신은 할 수 없습니다 다른 사람에 의해 주어진 모든 답변 DELETE
과 SELECT
같은 쿼리에서 특정 테이블에서.
DELETE FROM mytable WHERE id NOT IN (SELECT MAX(id) FROM mytable);
ERROR 1093 (HY000): You can't specify target table 'mytable' for update
in FROM clause
LIMIT
하위 쿼리에서 MySQL을 지원할 수도 없습니다 . 이것은 MySQL의 한계입니다.
DELETE FROM mytable WHERE id NOT IN
(SELECT id FROM mytable ORDER BY id DESC LIMIT 1);
ERROR 1235 (42000): This version of MySQL doesn't yet support
'LIMIT & IN/ALL/ANY/SOME subquery'
제가 생각해 낼 수있는 가장 좋은 대답은 다음 두 단계로 수행하는 것입니다.
SELECT id FROM mytable ORDER BY id DESC LIMIT n;
ID를 수집하여 쉼표로 구분 된 문자열로 만듭니다.
DELETE FROM mytable WHERE id NOT IN ( ...comma-separated string... );
(일반적으로 쉼표로 구분 된 목록을 SQL 문에 삽입하면 SQL 삽입 위험이 있지만이 경우 값은 신뢰할 수없는 소스에서 가져온 것이 아니며 데이터베이스 자체의 정수 값으로 알려져 있습니다.)
참고 : 이렇게해도 단일 쿼리로 작업이 완료되지는 않지만 때로는 더 간단한 get-it-done 솔루션이 가장 효과적입니다.
DELETE FROM mytable WHERE id NOT IN (SELECT id FROM mytable ORDER BY id DESC LIMIT 3);
잘 작동합니다.
ID가 증분이면 다음과 같은 것을 사용하십시오.
delete from table where id < (select max(id) from table)-N
마지막 N 을 제외한 모든 레코드를 삭제하려면 아래보고 된 쿼리를 사용할 수 있습니다.
단일 쿼리이지만 많은 문이 있으므로 원래 질문에서 의도 한 방식대로 실제로 단일 쿼리 가 아닙니다 .
또한 MySQL의 버그로 인해 변수와 내장 (쿼리에) 준비된 문이 필요합니다.
어쨌든 유용 할 수 있기를 바랍니다 ...
nnn 은 보관할 행 이고 theTable 은 작업중인 테이블입니다.
id 라는 자동 증가 레코드가 있다고 가정합니다.
SELECT @ROWS_TO_DELETE := COUNT(*) - nnn FROM `theTable`;
SELECT @ROWS_TO_DELETE := IF(@ROWS_TO_DELETE<0,0,@ROWS_TO_DELETE);
PREPARE STMT FROM "DELETE FROM `theTable` ORDER BY `id` ASC LIMIT ?";
EXECUTE STMT USING @ROWS_TO_DELETE;
이 방법의 좋은 점은 성능입니다 . 마지막 1,000 개를 유지하면서 약 13,000 개의 레코드가있는 로컬 DB에서 쿼리를 테스트했습니다. 0.08 초 안에 실행됩니다.
받아 들여진 답변의 스크립트 ...
DELETE FROM `table`
WHERE id NOT IN (
SELECT id
FROM (
SELECT id
FROM `table`
ORDER BY id DESC
LIMIT 42 -- keep this many records
) foo
);
0.55 초 걸립니다. 약 7 배 더.
테스트 환경 : SSD가있는 2011 년 후반 i7 MacBookPro의 mySQL 5.5.25
DELETE FROM table WHERE ID NOT IN
(SELECT MAX(ID) ID FROM table)
이 작업에 id를 사용하는 것은 대부분의 경우 옵션이 아닙니다. 예 : 트위터 상태가있는 테이블. 다음은 지정된 타임 스탬프 필드가있는 변형입니다.
delete from table
where access_time >=
(
select access_time from
(
select access_time from table
order by access_time limit 150000,1
) foo
)
MySQL 대신 Microsoft SQL Server를 사용하는 모든 사람들을 위해 이것을 혼합하고 싶었습니다. 'Limit'키워드는 MSSQL에서 지원되지 않으므로 대안을 사용해야합니다. 이 코드는 SQL 2008에서 작동했으며이 SO 게시물을 기반으로합니다. https://stackoverflow.com/a/1104447/993856
-- Keep the last 10 most recent passwords for this user.
DECLARE @UserID int; SET @UserID = 1004
DECLARE @ThresholdID int -- Position of 10th password.
SELECT @ThresholdID = UserPasswordHistoryID FROM
(
SELECT ROW_NUMBER()
OVER (ORDER BY UserPasswordHistoryID DESC) AS RowNum, UserPasswordHistoryID
FROM UserPasswordHistory
WHERE UserID = @UserID
) sub
WHERE (RowNum = 10) -- Keep this many records.
DELETE UserPasswordHistory
WHERE (UserID = @UserID)
AND (UserPasswordHistoryID < @ThresholdID)
물론 이것은 우아하지 않습니다. 이를 Microsoft SQL에 최적화 할 수 있다면 솔루션을 공유하십시오. 감사!
오랜 시간이 지난 후 대답 ... 같은 상황에 이르렀고 언급 된 대답을 사용하는 대신 아래에 왔습니다.
DELETE FROM table_name order by ID limit 10
이렇게하면 처음 10 개의 기록이 삭제되고 최신 기록이 유지됩니다.