最新の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
);
中間サブクエリが必要です。これがないと、2つのエラーが発生します。
幸い、中間サブクエリを使用すると、これらの制限の両方を回避できます。
ニコールは、このクエリは特定のユースケース(このような)向けに大幅に最適化できると指摘しています。その答えも読んで、自分に合っているかどうかを確認することをお勧めします。
私はかなり古い質問を復活させていることを知っています、しかし私は最近この問題に遭遇しました、しかし私は大きな数にうまくスケーリングする何かを必要としていました。既存のパフォーマンスデータはなく、この質問はかなり注目されていたので、見つけたものを投稿したいと思いました。
実際に機能したソリューションは、AlexBarrettのdoublesubquery /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
)
N番目のレコードのOFFSETIDを取得するために使用し、そのレコードと以前のすべてのレコードを削除します。
順序付けはすでにこの問題(ORDER BY id DESC)の前提で<=あるため、完全に適合します。
サブクエリによって生成された一時テーブルには、Nレコードではなく1つのレコードしか含まれていないため、はるかに高速です。
上記の3つの作業方法と新しい方法を2つのテストケースでテストしました。
どちらのテストケースも10000の既存の行を使用しますが、最初のテストは9000を保持し(最も古い1000を削除)、2番目のテストは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
また、MySQLLIMITはサブクエリでサポートできません。これらは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'
私が思いつくことができる最善の答えは、これを2つの段階で行うことです。
SELECT id FROM mytable ORDER BY id DESC LIMIT n;
IDを収集し、カンマ区切りの文字列にします。
DELETE FROM mytable WHERE id NOT IN ( ...comma-separated string... );
(通常、コンマ区切りのリストをSQLステートメントに補間すると、SQLインジェクションのリスクが発生しますが、この場合、値は信頼できないソースからのものではなく、データベース自体からの整数値であることがわかっています。)
注:これでは1つのクエリで作業を完了できませんが、より単純な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;
このアプローチの良いところはパフォーマンスです。ローカルDBで約13,000レコードのクエリをテストし、最後の1,000レコードを保持しました。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年後半のi7MacBookPro上のmySQL5.5.25
DELETE FROM table WHERE ID NOT IN
(SELECT MAX(ID) ID FROM table)
以下のクエリを試してください:
DELETE FROM tablename WHERE id < (SELECT * FROM (SELECT (MAX(id)-10) FROM tablename ) AS a)
内側のサブクエリは上位10個の値を返し、外側のクエリは上位10個を除くすべてのレコードを削除します。
多くの場合、このタスクにidを使用することはできません。たとえば、Twitterのステータスを含むテーブル。これは、指定されたタイムスタンプフィールドを持つバリアントです。
delete from table
where access_time >=
(
select access_time from
(
select access_time from table
order by access_time limit 150000,1
) foo
)
MySQLの代わりにMicrosoftSQL Serverを使用している人のために、これをミックスに入れたかっただけです。キーワード「制限」はMSSQLでサポートされていないため、別のキーワードを使用する必要があります。このコードはSQL2008で機能し、この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)
確かに、これはエレガントではありません。これをMicrosoftSQL用に最適化できる場合は、ソリューションを共有してください。ありがとう!
何故なの
DELETE FROM table ORDER BY id DESC LIMIT 1, 123456789
2番目のLIMIT引数として非常に大きな数値を使用して、最初の行を除くすべてを削除します(順序はDESCです!)。こちらをご覧ください
久しぶりにこれに答える…同じ状況に出くわし、上記の答えを使う代わりに、以下のものを用意しました-
DELETE FROM table_name order by ID limit 10
これにより、最初の10レコードが削除され、最新のレコードが保持されます。